摘要:反射機制相關類介紹類的實例表示正在運行的應用程序中的類和接口。包括基本數據類型沒有公共構造方法。越過泛型檢查遍歷集合輸出結果越過泛型檢查通過反射機制獲得數組信息并修改數組的大小和值通過反射機制分別修改和類型的數組的大小并修改數組的第一個值。
什么是Java的反射機制?
Java 反射機制是在運行狀態中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱為 Java 語言的反射機制。
簡單來說,反射就是可以在程序運行的時候動態裝載類,查看類的信息,生成對象,或操作生成的對象。
Java反射機制相關API Class類介紹Class 類的實例表示正在運行的 Java 應用程序中的類和接口。JVM 中有 N 多的實例,每個類的實例都有 Class 對象。(包括基本數據類型)
Class 沒有公共構造方法。Class 對象是在加載類時由 Java 虛擬機以及通過調用類加載器中的 defineClass 方法自動構造的。也就是這不需要我們自己去處理創建,JVM 已經幫我們創建好了。
Java 類的加載過程:
在 Java 中,類裝載器把一個類裝入 Java 虛擬機中,要經過三個步驟來完成:裝載、鏈接和初始化,其中鏈接又可以分成校驗、準備和解析三步:
裝載:查找和導入類或接口的二進制數據;
鏈接:執行下面的校驗、準備和解析步驟,其中解析步驟是可以選擇的;
校驗:檢查導入類或接口的二進制數據的正確性;
準備:給類的靜態變量分配并初始化存儲空間;
解析:將符號引用轉成直接引用;
初始化:激活類的靜態變量的初始化 Java 代碼和靜態 Java 代碼塊。
如果知道一個實例,那么可以通過實例的getClass()方法獲得運行實例的 Class(該類型的字節碼文件對象),如果你知道一個類型,那么你也可以使用.class的方法獲得運行實例的 Class。
部分相關api如下所示
//返回與給定字符串名稱的類或接口相關聯的類對象。 public static Class <?> forName(String className)throws ClassNotFoundException //返回類表示此所表示的實體(類,接口,基本類型或void)的超類類 。 public Class<? super T> getSuperclass() //確定由該對象表示的類或接口實現的接口。 public Class< ? >[] getInterfaces() //創建此 Class 對象所表示的類的一個新實例 public T newInstance() throws InstantiationException, IllegalAccessException //返回表示此類公共構造方法的 Constructor 對象數組 public Constructor< ? >[] getConstructors() throws SecurityException //返回 Constructor 對象的一個數組 public Constructor>[] getDeclaredConstructors() throws SecurityException //返回表示公共字段的 Field 對象的數組 public Field[] getFields() throws SecurityException //返回表示此類所有已聲明字段的 Field 對象的數組 public Field[] getDeclaredFields() throws SecurityException //表示此類中公共方法的 Method 對象的數組 public Method[] getMethods() throws SecurityException //表示此類所有聲明方法的 Method 對象的數組 public Method[] getDeclaredMethods() throws SecurityException //返回該類的類加載器。 public ClassLoader getClassLoader() //返回:使用參數 args 在 obj 上指派該對象所表示方法的結果 public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException //查找具有給定名稱的資源。 public InputStream getResourceAsStream(String name) //返回一個指定接口的代理類實例,該接口可以將方法調用指派到指定的調用處理程序。 public static Object newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h) throws IllegalArgumentException
具體相關方法及其使用方法可以查看API文檔
Java 反射機制舉例以下例子基于Person類
package DateTest; public class Person implements Comparable{ public String name; private int age; public int id; protected String phone; public Person() { System.out.println("默認的無參構造方法執行了"); } public Person(String name) { System.out.println("姓名:"+name); } public Person(String name,int age) { System.out.println("姓名:"+name+"年齡:"+age); } //受保護的構造方法 protected Person(boolean b) { System.out.println("這是一個受保護的構造方法:b="+b); } //私有構造方法 private Person(int age) { System.out.println("這是一個私有構造方法,年齡="+age); } @Override public int compareTo(Object arg0) { // TODO Auto-generated method stub return 0; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } }通過一個對象獲取某個類的完整包名和類名
通過實例的 getClass() 方法獲得運行實例的字節碼文件對象,然后通過 getName() 方法獲得類的完整包名和類名。
public void test1() { Person person = new Person(); System.out.println(person.getClass().getName()); } //輸出結果: 默認的無參構造方法執行了 DateTest.Person獲取 Class 對象
方式1:通過 Class 類的靜態方法獲取 Class 類對象
方式2:因為所有類都繼承 Object 類。因而可通過調用 Object 類中的 getClass 方法來獲取
方式3:任何數據類型(包括基本數據類型)都有一個“靜態”的 class 屬性
public void test2() throws ClassNotFoundException { Class> class1 = null; Class> class2 = null; Class> class3 = null; class1 = Class.forName("DateTest.Person"); class2 = new Person().getClass(); class3 = Person.class; System.out.println("類名稱1:"+class1.getName()); System.out.println("類名稱2:"+class2.getName()); System.out.println("類名稱3:"+class3.getName()); } //輸出結果 默認的無參構造方法執行了 類名稱1:DateTest.Person 類名稱2:DateTest.Person 類名稱3:DateTest.Person獲取一個對象的父類和實現的接口
為了測試getInterfaces()方法,此處將Person類繼承Comparable接口
public void test3() throws ClassNotFoundException { Class> clazz = Class.forName("DateTest.Person"); //獲取父類 Class> parentClass = clazz.getSuperclass(); System.out.println("父類為:"+parentClass.getName()); //獲取所有接口 Class> interf[] = clazz.getInterfaces(); System.out.println("實現的接口有:"); for(int i = 0; i < interf.length; i++) { System.out.println((i+1)+":"+interf[i].getName()); } } //輸出結果 父類為:java.lang.Object 實現的接口有: 1:java.lang.Comparable獲取某個類的全部構造函數并調用私有構造方法
獲取 Student 類的全部構造函數,并使用 Class 對象的 newInstance() 方法來創建 Class 對象對應類 Person 的實例來調用私有構造方法。
public void test4() throws Exception { //1.加載Class對象 Class clazz = Class.forName("DateTest.Person"); //2.獲取所有公有構造方法 System.out.println("所有公有構造方法"); Constructor[] conArray = clazz.getConstructors(); for(Constructor c : conArray) { System.out.println(c); } System.out.println("----------------------------"); System.out.println("所有構造方法"); conArray = clazz.getDeclaredConstructors(); for(Constructor c : conArray) { System.out.println(c); } System.out.println("----------------------------"); System.out.println("公有、無參的構造方法"); Constructor con = clazz.getConstructor(null); System.out.println("con = " + con); //調用構造方法 Object obj = con.newInstance(); System.out.println("obj = " + obj); System.out.println("----------------------------"); System.out.println("獲取私有的構造方法,并調用"); con = clazz.getDeclaredConstructor(int.class); System.out.println(con); //調用構造方法 con.setAccessible(true); obj = con.newInstance(20); } //輸出結果 所有公有構造方法 public DateTest.Person(java.lang.String,int) public DateTest.Person(java.lang.String) public DateTest.Person() ---------------------------- 所有構造方法 private DateTest.Person(int) protected DateTest.Person(boolean) public DateTest.Person(java.lang.String,int) public DateTest.Person(java.lang.String) public DateTest.Person() ---------------------------- 公有、無參的構造方法 con = public DateTest.Person() 默認的無參構造方法執行了 obj = DateTest.Person@7a4f0f29 ---------------------------- 獲取私有的構造方法,并調用 private DateTest.Person(int) 這是一個私有構造方法,年齡=20獲取某個類的全部屬性
獲取 Person類的全部屬性并調用:
public void test5() throws Exception { //1.獲取Class對象 Class pclass = getClass().forName("DateTest.Person"); //2.獲取字段 System.out.println("獲取所有公有字段"); Field[] fieldArray = pclass.getFields(); for(Field f : fieldArray) { System.out.println(f); } System.out.println("----------------------------"); System.out.println("獲取所有字段"); fieldArray = pclass.getDeclaredFields(); for(Field f : fieldArray) { System.out.println(f); } System.out.println("----------------------------"); Field f = pclass.getField("name"); System.out.println(f); //獲取一個對象 Object obj = pclass.getConstructor().newInstance(); f.set(obj,"張三"); //驗證 Person p = (Person)obj; System.out.println("驗證的姓名:"+p.getName()); System.out.println("----------------------------"); f = pclass.getDeclaredField("age"); System.out.println(f); f.setAccessible(true); f.set(obj, 21); System.out.println("驗證年齡:"+p); } //輸出結果 獲取所有公有字段 public java.lang.String DateTest.Person.name public int DateTest.Person.id ---------------------------- 獲取所有字段 public java.lang.String DateTest.Person.name private int DateTest.Person.age public int DateTest.Person.id protected java.lang.String DateTest.Person.phone ---------------------------- public java.lang.String DateTest.Person.name 默認的無參構造方法執行了 驗證的姓名:張三 ---------------------------- private int DateTest.Person.age 驗證年齡:Person [name=張三, age=21]獲取某個類的全部方法
public void test6() throws Exception { //1.獲取Class對象 Class pClass = Class.forName("DateTest.Person"); //2.獲取所有公有方法 System.out.println("獲取所有的公有方法*"); pClass.getMethods(); Method[] methodArray = pClass.getMethods(); for(Method m : methodArray){ System.out.println(m); } System.out.println("----------------------------"); System.out.println("獲取所有的方法"); methodArray = pClass.getDeclaredMethods(); for(Method m : methodArray){ System.out.println(m); } System.out.println("獲取公有的test1()方法"); Method m = pClass.getMethod("test1", String.class); System.out.println(m); //實例化一個Student對象 Object obj = pClass.getConstructor().newInstance(); m.invoke(obj, "lisi"); System.out.println("獲取私有的test4()方法*"); m = pClass.getDeclaredMethod("test4", int.class); System.out.println(m); m.setAccessible(true);//解除私有限定 Object result = m.invoke(obj, 20);//需要兩個參數,一個是要調用的對象(獲取有反射),一個是實參 System.out.println("返回值:" + result); } //輸出結果 獲取所有的公有方法* public java.lang.String DateTest.Person.toString() public int DateTest.Person.compareTo(java.lang.Object) public java.lang.String DateTest.Person.getName() public int DateTest.Person.getId() public void DateTest.Person.setName(java.lang.String) public void DateTest.Person.test1(java.lang.String) public int DateTest.Person.getAge() public java.lang.String DateTest.Person.getPhone() public void DateTest.Person.setAge(int) public void DateTest.Person.setPhone(java.lang.String) public void DateTest.Person.setId(int) public final void java.lang.Object.wait() throws java.lang.InterruptedException public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException public boolean java.lang.Object.equals(java.lang.Object) public native int java.lang.Object.hashCode() public final native java.lang.Class java.lang.Object.getClass() public final native void java.lang.Object.notify() public final native void java.lang.Object.notifyAll() ---------------------------- 獲取所有的方法 public java.lang.String DateTest.Person.toString() public int DateTest.Person.compareTo(java.lang.Object) public java.lang.String DateTest.Person.getName() public int DateTest.Person.getId() public void DateTest.Person.setName(java.lang.String) public void DateTest.Person.test1(java.lang.String) protected void DateTest.Person.test2() private java.lang.String DateTest.Person.test4(int) void DateTest.Person.test3() public int DateTest.Person.getAge() public java.lang.String DateTest.Person.getPhone() public void DateTest.Person.setAge(int) public void DateTest.Person.setPhone(java.lang.String) public void DateTest.Person.setId(int) 獲取公有的test1()方法 public void DateTest.Person.test1(java.lang.String) 默認的無參構造方法執行了 調用了:公有的,String參數的test1(): name = lisi 獲取私有的test4()方法* private java.lang.String DateTest.Person.test4(int) 調用了,私有的,并且有返回值的,int參數的test4(): age = 20 返回值:abcd調用某個類的方法
Java 反射獲取 Class 對象并使用invoke()方法調用方法 reflect1() 和 reflect2():
public void test7() throws Exception{ Class> clazz = Class.forName("DateTest.ReflectTest"); Method method = clazz.getMethod("show1"); method.invoke(clazz.newInstance()); method = clazz.getMethod("show2", int.class, String.class); method.invoke(clazz.newInstance(), 20, "張三"); } public void show1() { System.out.println("調用show1()"); } public void show2(int age, String name) { System.out.println("調用show2()"); System.out.println("age: " + age + "name: " + name); } //輸出結果 調用show1() 調用show2() age: 20name: 張三反射機制的動態代理
首先需要定義一個 InvocationHandler 接口的子類,完成代理的具體操作
package DateTest; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class MyInvocationHandler implements InvocationHandler{ private Object obj = null; public Object bind(Object obj) { this.obj = obj; return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object temp = method.invoke(this.obj, args); return temp; } }
package DateTest; public interface Animal { public void eat(String name); }
package DateTest; public class Dog implements Animal{ public void eat(String name) { System.out.println(name+"eat!"); } }
新建類實現接口Animal,使用 MyInvocationHandler.bind() 方法獲取即可實現調用。
public void test8(){ MyInvocationHandler invo = new MyInvocationHandler(); Animal sub = (Animal) invo.bind(new Dog()); sub.eat("小狗"); } //輸出結果 小狗eat!通過反射越過泛型檢查
泛型作用在編譯期,編譯過后泛型擦除(消失掉)。所以是可以通過反射越過泛型檢查的。
public void test9() throws Exception{ ArrayList通過反射機制獲得數組信息并修改數組的大小和值list = new ArrayList (); list.add(111); list.add(222); Method method = list.getClass().getMethod("add", Object.class); method.invoke(list, "越過泛型檢查"); //遍歷集合 for(Object obj : list){ System.out.println(obj); } } //輸出結果 111 222 越過泛型檢查
通過反射機制分別修改int和String類型的數組的大小并修改int數組的第一個值。
public static void test10() throws Exception{ int[] temp = { 12,45,65,5,1,32,4,56,12}; int[] newTemp = (int[]) arrayInc(temp, 15); print(newTemp); Array.set(newTemp, 0, 100); System.out.println("修改之后數組第一個元素為: " + Array.get(newTemp, 0)); print(newTemp); String[] atr = { "a", "b", "c" }; String[] str1 = (String[]) arrayInc(atr, 8); print(str1); } // 修改數組大小 public static Object arrayInc(Object obj, int len) { Class> arr = obj.getClass().getComponentType(); Object newArr = Array.newInstance(arr, len); int co = Array.getLength(obj); System.arraycopy(obj, 0, newArr, 0, co); return newArr; } // 打印 public static void print(Object obj) { Class> c = obj.getClass(); if (!c.isArray()) { return; } Class> arr = obj.getClass().getComponentType(); System.out.println("數組類型: " + arr.getName()); System.out.println("數組長度為: " + Array.getLength(obj)); for (int i = 0; i < Array.getLength(obj); i++) { System.out.print(Array.get(obj, i) + " "); } System.out.println(); } //輸出結果 數組類型: int 數組長度為: 15 12 45 65 5 1 32 4 56 12 0 0 0 0 0 0 修改之后數組第一個元素為: 100 數組類型: int 數組長度為: 15 100 45 65 5 1 32 4 56 12 0 0 0 0 0 0 數組類型: java.lang.String 數組長度為: 8 a b c null null null null null將反射機制應用于工廠模式
對于普通的工廠模式當我們在添加一個子類的時候,就需要對應的修改工廠類。 當我們添加很多的子類的時候,會很麻煩。
package DateTest; public interface Animal { public abstract void sleep(); }
package DateTest; public class Dog implements Animal{ @Override public void sleep() { System.out.println("dog"); } }
package DateTest; public class Cat implements Animal{ @Override public void sleep() { System.out.println("cat"); } }
package DateTest; public class Factory { public static Animal getInstance(String ClassName) { Animal a = null; try { a = (Animal) Class.forName(ClassName).newInstance(); } catch (Exception e) { e.printStackTrace(); } return a; } }
public void test11()throws Exception{ Animal a = Factory.getInstance("DateTest.Dog"); if (a != null) { a.sleep(); } } //輸出結果 dog加載配置文件
//配置文件內容為 //className = DateTest.ReflectTest public void test13() throws IOException { Properties pro = new Properties();//獲取配置文件的對象 InputStream in=new Person().getClass().getResourceAsStream("/pro.txt"); pro.load(in);//將流加載到配置文件對象中 in.close(); System.out.println(pro.getProperty("className")); } //輸出結果 默認的無參構造方法執行了 DateTest.ReflectTest獲取 ClassLoader 類加載器
public void test12() throws ClassNotFoundException { //1、獲取一個系統的類加載器 ClassLoader classLoader = ClassLoader.getSystemClassLoader(); System.out.println(classLoader); //2、獲取系統類加載器的父類加載器 classLoader = classLoader.getParent(); System.out.println(classLoader); //3、獲取擴展類加載器的父類加載器 //輸出為Null,引導類加載器無法被Java程序直接引用 classLoader = classLoader.getParent(); System.out.println(classLoader); //4、測試當前類由哪個類加載器進行加載 ,結果是系統的類加載器 classLoader = Class.forName("DateTest.ReflectTest").getClassLoader(); System.out.println(classLoader); //5、測試JDK提供的Object類由哪個類加載器負責加載的 classLoader = Class.forName("java.lang.Object").getClassLoader(); System.out.println(classLoader); } //輸出結果 sun.misc.Launcher$AppClassLoader@6bc7c054 sun.misc.Launcher$ExtClassLoader@2ef1e4fa null sun.misc.Launcher$AppClassLoader@6bc7c054 null總結
反射機制的應用雖然有很多優點,但是反射會額外消耗一定的系統資源,因此,反射操作的效率要比那些非反射操作低得多。另外,反射允許代碼執行一般情況下不被允許的操作,所以一般能用其它方式實現的就盡量不去用反射。
參考文章https://blog.csdn.net/sinat_3...
https://www.cnblogs.com/tech-...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/74833.html
近期在維護公司項目的時候遇到一個問題,因為實體類中的 set 方法涉及到了業務邏輯,因此在給對象賦值的過程中不能夠使用 set 方法,為了實現功能,所以采用了反射的機制給對象屬性賦值,借此機會也了解了反射的一些具體用法和使用場景,分以下兩點對反射進行分析: 反射的優勢和劣勢 反射的應用場景 反射的優勢和劣勢 ??個人理解,反射機制實際上就是上帝模式,如果說方法的調用是 Java 正確的打開方式...
摘要:反射機制的應用實例在泛型為的中存放一個類型的對象。工廠模式可以參考現在我們利用反射機制實現工廠模式,可以在不修改工廠類的情況下添加任意多個子類。 學習交流群:669823128java 反射 定義 功能 示例概要:Java反射機制詳解| |目錄 1反射機制是什么 2反射機制能做什么 3反射機制的相關API 通過一個對象獲得完整的包名和類名 實例化Class類對象 獲取一個對象的父類與...
摘要:反射機制是什么反射機制是在運行狀態中,對于任意一個類,都能夠知道這個類的所有屬性和方法對于任意一個對象,都能夠調用它的任意一個方法和屬性這種動態獲取的信息以及動態調用對象的方法的功能稱為語言的反射機制反射機制能做什么反射機制主要提供了以下功 反射機制是什么 反射機制是在運行狀態中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調用它的任意一個方法和屬性;這種...
摘要:反射機制一結合官方通過編寫的反射教程,復習一下反射的知識。反射的概念反射是一種在運行時獲取以及修改應用行為的一種工具。因為反射需要動態的解析類的信息,相比于非反射使用的方式要慢。反射需要獲取一定的運行時權限,在特定的安全環境下不一定存在。 Java反射機制(一) 結合Oracle官方通過JDK8編寫的反射教程,復習一下反射的知識。結尾篇補一個小例子。 主要內容 這次博客的主要內容就是簡...
摘要:與都繼承自類,在中也是使用字符數組保存字符串,,這兩種對象都是可變的。采用字節碼的好處語言通過字節碼的方式,在一定程度上解決了傳統解釋型語言執行效率低的問題,同時又保留了解釋型語言可移植的特點。 String和StringBuffer、StringBuilder的區別是什么?String為什么是不可變的? String和StringBuffer、StringBuilder的區別 可變性...
閱讀 883·2021-11-22 12:04
閱讀 2087·2021-11-02 14:46
閱讀 614·2021-08-30 09:44
閱讀 2098·2019-08-30 15:54
閱讀 714·2019-08-29 13:48
閱讀 1586·2019-08-29 12:56
閱讀 3440·2019-08-28 17:51
閱讀 3279·2019-08-26 13:44