能夠分析類能力的程序稱為反射
反射可以用來
- 在運行的時候分析類的能力
- 在運行的時候檢查對象
- 實現(xiàn)泛型數(shù)組操作代碼
- 利用Method對象
①Class類
Java運行時虛擬機始終為所有對象維護一個運行時類型標(biāo)記等浊,這個信息會跟蹤每個對象所屬的類
用一個叫做Class的特殊的類保存這些信息
- Object類中的getClass()方法會返回一個Class類型的實例
Employee e;
Class cl = e.getClass();
Class對象描述一個特定類的屬性她紫,上面代碼中的對象cl描述的就是Employee類的屬性
-
Class類常用方法getName()
返回class對象對應(yīng)的類的名稱
System.out.println(e.getClass().getName()+" "+e.getName());
//e是員工晌姚,輸出"Employee Harry Hacker"
//e是經(jīng)理邑蒋,輸出"Manager Harry Hacker"
-
forName()方法
作用:加載指定類名的class對象
Class cl = Class.forName("Employee");
上例代碼中,forName方法通過參數(shù)"Employee"指定了class對象cl描述的類是Employee類
注意:常與異常處理器配合使用
- T.class快捷方式獲得class對象
Class cl = int.class;
這里自動給cl匹配了class對象(根據(jù)int類型分配了Integer類)
虛擬機為每個類型管理一個唯一的class對象萎羔,因此可以使用==實現(xiàn)兩個類對象的比較
if(e.getClass()==Employee.getClass()){
...
}
-
獲取類的構(gòu)造方法
使用getConstructor()方法獲取當(dāng)前class對象所指的類的構(gòu)造方法
再使用newInstance()創(chuàng)建一個所指的類的對象
Class cl = Employee.class;
Employee e = cl.getConstructor().newInstance();
上面代碼用Employee類的無參構(gòu)造方法構(gòu)造了一個新的Employee對象
②異常處理
無法確定通過Class.forName方法指定的類一定存在
需要在方法名上增加一個throws子句來處理這個異常
public static void doSomethingWithClass(String name)
throws ReflectiveOperationException
{
Class cl = Class.forName(name);//可能name指定的類是不存在的
...
}
調(diào)用這個方法的任何方法也需要一個throws聲明液走,包括main方法
③使用反射分析類的能力
通過java.lang.reflect包中的三個類Field,Method和Constructor來描述一個類的結(jié)構(gòu),配合Modifier類描述類外驱、方法育灸、字段的訪問權(quán)限腻窒。通常在構(gòu)造一個class對象之后通過getter方法來構(gòu)造這三個類(返回值通常存放在數(shù)組里)
Class cl = Class.forName(name);
Field[] fields = cl.getDeclaredFields();
Method[] methods = cl.getDeclaredMethods();
Constructor[] constructors = cl.getDeclaredConstructors();
for(Field f:fields){
String modifiers = Modifier.toString(f.getModifiers());//獲取權(quán)限
}
...
下面代碼的作用是輸入一個類名昵宇,輸出它的構(gòu)造器、方法儿子、字段
/**
* @author whl
* @date 2020/7/7 - 15:04
*/
import java.util.*;
import java.lang.reflect.*;
public class ReflectionTest {
public static void main(String[] args) throws ReflectiveOperationException{
String name;
/**
*可以在命令行運行這個類的時候加參數(shù)
* 加了就存放在args數(shù)組里
*/
if (args.length>0)
name=args[0];
else {
Scanner in = new Scanner(System.in);
System.out.println("Enter class name(e.g. java.util.Date):");
name=in.next();
}
Class cl = Class.forName(name);
Class supercl = cl.getSuperclass();//cl的父類
String modifiers = Modifier.toString(cl.getModifiers());//cl的權(quán)限修飾符
if (modifiers.length()>0)
System.out.print(modifiers+" ");
System.out.print("class "+name);
if (supercl!=null && supercl!=Object.class)//父類不能是null瓦哎,同時父類不能是object
System.out.print(" extends "+supercl.getName());//顯示cl繼承于什么類
System.out.print("\n{\n");
printConstructors(cl);//輸出構(gòu)造器
System.out.println();
printMethods(cl);//輸出方法
System.out.println();
printFields(cl);//輸出成員
System.out.println("}");
}
private static void printFields(Class cl) {
/*
* Field類用于描述一個類的成員
* getDeclaredFields()方法返回這個類中定義的字段的數(shù)組
* getFields()方法返回這個類及其父類定義的字段的數(shù)組
* */
Field[] fields = cl.getDeclaredFields();//構(gòu)造一個存放Field對象的數(shù)組
for (Field f : fields) {
Class type = f.getType();//字段的類型
String name = f.getName();//字段的名稱
System.out.print(" ");//行首縮進
String modifiers = Modifier.toString(f.getModifiers());//字段的權(quán)限修飾符
if (modifiers.length()>0)
System.out.print(modifiers+" ");
System.out.println(type.getName()+" "+name+";");
}
}
private static void printMethods(Class cl) {
/*
* Method類用于描述一個類的方法
* getDeclaredMethods()方法返回這個類中定義的方法的數(shù)組
* getMethods()方法返回這個類及其父類定義的全部公共方法的數(shù)組
* */
Method[] methods = cl.getDeclaredMethods();
for (Method m : methods) {
Class retType = m.getReturnType();//方法的返回類型
String name = m.getName();//方法名稱
System.out.print(" ");
String modifiers = Modifier.toString(m.getModifiers());//方法的權(quán)限修飾符
if (modifiers.length()>0)
System.out.print(modifiers + " ");
System.out.print(retType.getName()+" "+name+"(");
/*
用for循環(huán)輸出方法的參數(shù)
*/
Class[] paraType = m.getParameterTypes();
for (int i = 0; i < paraType.length; i++) {
if (i>0)
System.out.print(", ");
System.out.print(paraType[i].getName());
}
System.out.println(");");
}
}
private static void printConstructors(Class cl) {
/*
* Constructor類用于描述一個類的構(gòu)造器
* getDeclaredConstructors()方法返回這個類中全部構(gòu)造器的數(shù)組
* getConstructors()方法返回這個類所有公共構(gòu)造器的數(shù)組
* */
Constructor[] constructors = cl.getDeclaredConstructors();
for (Constructor c:constructors){
String name = c.getName();//構(gòu)造器的名稱
System.out.print(" ");
String modifiers = Modifier.toString(c.getModifiers());//構(gòu)造器的權(quán)限修飾符
if (modifiers.length()>0)
System.out.print(modifiers+" ");
System.out.print(name+"(");
//用for循環(huán)輸出構(gòu)造器的參數(shù)
Class[] paraTypes = c.getParameterTypes();
for (int i = 0; i < paraTypes.length; i++) {
if (i>0)
System.out.print(", ");
System.out.print(paraTypes[i].getName());
}
System.out.println(");");
}
}
}
例:輸入java.util.Date,輸出結(jié)果如下
public class java.util.Date
{
public java.util.Date(int, int, int, int, int, int);
public java.util.Date(java.lang.String);
public java.util.Date();
public java.util.Date(long);
public java.util.Date(int, int, int);
public java.util.Date(int, int, int, int, int);
public boolean after(java.util.Date);
public boolean before(java.util.Date);
public boolean equals(java.lang.Object);
public java.lang.String toString();
public int hashCode();
public java.lang.Object clone();
public int compareTo(java.util.Date);
public volatile int compareTo(java.lang.Object);
private void readObject(java.io.ObjectInputStream);
private void writeObject(java.io.ObjectOutputStream);
private final sun.util.calendar.BaseCalendar$Date normalize(sun.util.calendar.BaseCalendar$Date);
private final sun.util.calendar.BaseCalendar$Date normalize();
public static long parse(java.lang.String);
public static java.util.Date from(java.time.Instant);
public long getTime();
public void setTime(long);
public int getDate();
public static long UTC(int, int, int, int, int, int);
private static final java.lang.StringBuilder convertToAbbr(java.lang.StringBuilder, java.lang.String);
private final sun.util.calendar.BaseCalendar$Date getCalendarDate();
private static final sun.util.calendar.BaseCalendar getCalendarSystem(int);
private static final sun.util.calendar.BaseCalendar getCalendarSystem(sun.util.calendar.BaseCalendar$Date);
private static final sun.util.calendar.BaseCalendar getCalendarSystem(long);
public int getDay();
public int getHours();
private static final synchronized sun.util.calendar.BaseCalendar getJulianCalendar();
static final long getMillisOf(java.util.Date);
public int getMinutes();
public int getMonth();
public int getSeconds();
private final long getTimeImpl();
public int getTimezoneOffset();
public int getYear();
public void setDate(int);
public void setHours(int);
public void setMinutes(int);
public void setMonth(int);
public void setSeconds(int);
public void setYear(int);
public java.lang.String toGMTString();
public java.time.Instant toInstant();
public java.lang.String toLocaleString();
private static final sun.util.calendar.BaseCalendar gcal;
private static sun.util.calendar.BaseCalendar jcal;
private transient long fastTime;
private transient sun.util.calendar.BaseCalendar$Date cdate;
private static int defaultCenturyStart;
private static final long serialVersionUID;
private static final [Ljava.lang.String; wtb;
private static final [I ttb;
}
?
④使用反射在運行時分析對象
利用反射可以查看在編譯時還不知道的對象字段
- 利用Field類中的get方法來查看對象字段的值
- 利用Field類中的set方法設(shè)置對象字段的值
- 利用Field,Method,Constructor對象中的setAccessible方法覆蓋Java的訪問控制
Employee harry = new Employee("Harry Hacker",50000,10,1,1989);
Class cl = harry.getClass();
Field f = cl.getDeclaredField("name");//此時只是有一個field對象描述name字段
f.setAccessible();//由于name是私有字段,所以要使它可訪問可修改
Object v = f.get(harry);//v是String對象蒋譬,內(nèi)容為"Harry Hacker"
⑤調(diào)用任意方法和構(gòu)造器
- 使用Method類中的invoke方法割岛,調(diào)用包裝在當(dāng)前Method對象中的方法
Employee harry = new Employee(...);
Class cl = harry.getClass();
Method ml = cl.getMethod("getName");
String n = (String)ml.invoke(harry);//在employee對象harry中調(diào)用了method對象ml描述的方法
-
也可以在調(diào)用invoke時指定參數(shù)類型
getMethod("raiseSalary",double.class);//raiseSalary的參數(shù)是double型
invoke方法的簽名
Object invoke(Object obj,Object... args)