- Date:2015-5-6
- Tag:java,反射
- Author:踏雪
- Email:[email protected]
一、What(是什麼?)
反射就是將java類中的各種成分映射成相應的java類,分別是Field、Method、Constructor、Package等等。
(1)Java類是描述同一類事物的屬性,包括方法、屬性、包名、構造函數、類加載器、子類、父類等等。
而Java類的實例對象則是同一屬性下的不同屬性值的對象。
(2)Class類是描述java類的各個方面的信息,如屬性、方法、包、構造函數等等。
代碼實例:
/**
* 知識點1
* Class類是描述java類的各個方面的信息,如屬性、方法、包、構造函數等等...
*/
String str = new String("wgc");
System.out.println(str.getClass().getName());
/**
* 知識點2
* 每個類對象對應的Class字節碼是相同的(有相同的描述)
*/
System.out.println(int.class);
System.out.println(void.class);
Integer i1 = 3;
Integer i2 = 4;
System.out.println(i1.getClass() == i2.getClass());
int[] array1 = new int[]{1,2,3};
int[] array2 = new int[]{2,2,3};
System.out.println(array1.getClass() == array2.getClass());
Person p1 = new Person("wgc");
Person p2 = new Person("apple");
System.out.println(p1.getClass() == p2.getClass());
二、How(如何使用?)
1、獲取Class的途徑
(1)類名.class
例如:String.class;
(2)對象.getClass()
(3)Class.forName(“類名”)
例如:Class.forName(“java.lang.String”);
代碼實例:
System.out.println(String.class);
ystem.out.println(str.getClass());
System.out.println(Class.forName("java.lang.String"));
2、構造方法的反射
(1)得到某個類的所有構造方法
Constructor[] | getConstructors() | Returns an array containing Constructor objects reflecting all the public constructors of the class represented by this Class object. |
---|
代碼實例:
Constructor[] constructors = Class.forName("java.lang.String").getConstructors();
for(Constructor constructor : constructors) {
System.out.println(constructor);
}
(2)得到某一個構造方法
Constructor < T > | getConstructor(Class… parameterTypes) | Returns a Constructor object that reflects the specified public constructor of the class represented by this Class object. |
---|
代碼實例:
//public java.lang.String()
Constructor constructor1 = Class.forName("java.lang.String").getConstructor();
//public java.lang.String(java.lang.StringBuffer)
Constructor constructor2 = Class.forName("java.lang.String").getConstructor(StringBuffer.class);
//public java.lang.String(char[])
Constructor constructor3 = Class.forName("java.lang.String").getConstructor(char[].class);
(3)創建一個實例對象
T | newInstance() | Creates a new instance of the class represented by this Class object |
---|
代碼實例:
String str1 = (String)constructor1.newInstance();
String str2 = (String)constructor2.newInstance(new StringBuffer("wgc"));
String str3 = (String)constructor3.newInstance(new char[]{'w','g','c'});
System.out.println(str1);
System.out.println(str2);
System.out.println(str3);
3、成員變量的反射
類型 | 函數名 | 說明 |
---|---|---|
Field[] | getFields() | 獲取所有public屬性的field |
Field[] | getDeclaredFields() | 獲取所有屬性的field |
Field | getField(String name) | 獲取指定public屬性的field |
Field | getDeclaredField(String name) | 獲取指定屬性的field |
代碼實例:
Student student = new Student("Hnu", "shuwoom");
Field[] fields1 = student.getClass().getFields();
for(Field field : fields1) {
System.out.println(field);
}
結果:
System.out.println("=======================================");
Field[] fields2 = student.getClass().getDeclaredFields();
for(Field field : fields2) {
System.out.println(field);
}
結果:
//getField是獲取public field的方法
Field fieldSchool = student.getClass().getField("school");
// Field fieldSchool = student.getClass().getDeclaredField("school");
// Field fieldName = student.getClass().getField("name");
//getDeclaredField是獲取各種權限field的方法,其中private field需要設置setAccessible
Field fieldName = student.getClass().getDeclaredField("name");
fieldName.setAccessible(true);
System.out.println(fieldSchool.get(student));
System.out.println(fieldName.get(student));
public class Student {
private String name;
public String school;
public Student(String school,String name) {
this.school = school;
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
private void test(){
System.out.println("This is private test function");
}
}
4、成員方法的反射
類型 | 函數名 | 說明 |
---|---|---|
Method[] | getMethods() | 獲取所有public屬性的method |
Method [] | getDeclaredMethods() | 獲取所有屬性的method |
Method | getDeclaredMethod(String name, Class… parameterTypes) | 獲取指定public屬性的method |
Method | getMethod(String name, Class… parameterTypes) | 獲取指定屬性的method |
(1)獲取所有成員方法
代碼實例:
System.out.println("=======================================");
Method[] methods1 = student.getClass().getMethods();//獲取所有public方法
for(Method method : methods1) {
System.out.println(method);
}
結果:
System.out.println("=======================================");
Method[] methods2 = student.getClass().getDeclaredMethods();//獲取所有方法
for(Method method : methods2) {
System.out.println(method);
}
結果:
(2)獲取某一個成員方法
Student student2 = new Student("Hnu", "shuwoom");
Method setNameMethod = student.getClass().getMethod("setName", String.class);
setNameMethod.invoke(student2, "shuwoom2");
System.out.println(student2.getName());
Method getNameMethod = student.getClass().getMethod("getName", null);
System.out.println(getNameMethod.invoke(student2, null));
結果:
(3)對有數組的成員方法進行反射
如果Student類中有一個接收數組的方法,如下:
public int getScore(int[]scores) {
int sum = 0;
for(int score : scores) {
sum += score;
}
return sum;
}
則其調用方法如下:
Method getScoreMethod = student2.getClass().getMethod("getScore", int[].class);
System.out.println(getScoreMethod.invoke(student2, new int[]{90,80,90}));
5、JavaBean、PropertyDescriptor類、Introspector類和BeanInfo類
JavaBean是一種特殊的Java類,其成員屬性都是私有的,並且只能通過set和get函數來設置和獲取。
(1)PropertyDescriptor類
PropertyDescriptor是專門處理JavaBean的類,用於獲取其屬性的方法。
常用方法:
構造函數 |
---|
PropertyDescriptor (String propertyName, Class beanClass) |
返回類型 | 方法名 |
---|---|
Method | getReadMethod() |
Method | getWriteMethod() |
代碼實例:
Student student5 = new Student("Hnu", "shuwoom5");
String propertyName = "name";
PropertyDescriptor nameProperty = new PropertyDescriptor(propertyName, student5.getClass());
Method getNameMethod2 = nameProperty.getReadMethod();
System.out.println(getNameMethod2.invoke(student5, null));
Method setNameMethod2 = nameProperty.getWriteMethod();
setNameMethod2.invoke(student5, "Walk");
System.out.println(getNameMethod2.invoke(student5, null));
(2) BeanInfo類
返回值類型 | 方法名 |
---|---|
PropertyDescriptor[] | getPropertyDescriptors() |
(3)Introspector類
返回值類型 | 方法名 |
---|---|
static BeanInfo | getBeanInfo(Class beanClass) |
代碼實例:
BeanInfo beanInfo = Introspector.getBeanInfo(student5.getClass());
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
Object retVal = null;
for(PropertyDescriptor pd : pds) {
if(pd.getName().equals(propertyName)) {
Method methodGetName = pd.getReadMethod();
retVal = methodGetName.invoke(student5);
break;
}
}
System.out.println(retVal);
三、When(何時使用?)
Java語言反射提供一種動態鏈接程序組件的多功能方法。它允許程序創建和控制任何類的對象(根據安全性限制),無需提前硬編碼目標類。這些特性使得反射特別適用於創建以非常普通的方式與對象協作的庫。例如,反射經常在持續存儲對象爲數據庫、XML或其它外部格式的框架中使用。
代碼實例:
config.properties文件:
InputStream ips = new FileInputStream("config.properties");
Properties props = new Properties();
props.load(ips);
ips.close();
String className = props.getProperty("className");
// Collection collection = new ArrayList();
// Collection collection = new HashSet();
Collection collection = (Collection) Class.forName(className).newInstance();
Student s1 = new Student("Hnu", "張三");
Student s2 = new Student("Hnu", "李四");
Student s3 = new Student("Hnu", "王五");
collection.add(s1);
collection.add(s2);
collection.add(s3);
collection.add(s1);
System.out.println(collection.size());
四、優缺點
優點:
反射是一種強大的工具。它使您能夠創建靈活的代碼,這些代碼可以在運行時裝配,無需在組件之間進行源代表鏈接。
缺點:
(1) 第一個是性能問題。當用於字段和方法接入時反射要遠慢於直接代碼。性能問題的程度取決於程序中是如何使用反射的。如果它作爲程序運行中相對很少涉及的部分,緩慢的性能將不會是一個問題。即使測試中最壞情況下的計時圖顯示的反射操作只耗用幾微秒。僅反射在性能關鍵的應用的核心邏輯中使用時性能問題才變得至關重要。
(2) 許多應用更嚴重的一個缺點是使用反射會模糊程序內部實際要發生的事情。程序人員希望在源代碼中看到程序的邏輯,反射等繞過了源代碼的技術會帶來維護問題。反射代碼比相應的直接代碼更復雜,正如性能比較的代碼實例中看到的一樣。解決這些問題的最佳方案是保守地使用反射– 僅在它可以真正增加靈活性的地方 – 記錄其在目標類中的使用。
代碼實例:
//使用反射
Student student4 = new Student("Hnu", "shuwoom");
Method method = student4.getClass().getMethod("setName", String.class);
Long beginTime = System.currentTimeMillis();
for(int i = 0; i < 10000000; i++)
method.invoke(student4, "shuwoom2");
long endTime = System.currentTimeMillis();
System.out.println("使用反射:" + (endTime - beginTime));
//直接調用
beginTime = System.currentTimeMillis();
for(int i = 0; i < 10000000; i++)
student4.setName("shuwoom2");
endTime = System.currentTimeMillis();
System.out.println("直接調用:" + (endTime - beginTime));