反射的概念
反射的引入:
Object obj = new Student();
若程序運行時接收到外部傳入的一個對象,該對象的編譯類型是Object,但程序又需要調用該對象運行類型的方法:
1.若編譯和運行類型都知道,使用 instanceof判斷后,強轉。
2.編譯時根本無法預知該對象屬于什么類,程序只能依靠運行時信息來發現對象的真實信息,這時就必須使用反射了。
3.要是想得到對象真正的類型,就得使用反射。
什么是反射機制?
簡單的來說,反射機制指的是程序在運行時能夠獲取自身的信息。在JAVA中,只要給定類的名字,那么就可以通過反射機制來獲得類的所有信息。
反射機制的優點與缺點:
為什么要用反射機制?直接創建對象不就可以了嗎,這就涉及到了動態與靜態的概念
靜態編譯:在編譯時確定類型,綁定對象,即通過。
動態編譯:運行時確定類型,綁定對象。動態編譯最大限度發揮了java的靈活性,體現了多態的應用,有以降低類之間的藕合性。
一句話,反射機制的優點就是可以實現動態創建對象和編譯,體現出很大的靈活性,特別是在J2EE的開發。
它的缺點是對性能有影響。
使用反射基本上是一種解釋操作,我們可以告訴JVM,我們希望做什么并且它滿足我們的要求。這類操作總是慢于只直接執行相同的操作。
Class類和Class類實例
Java程序中的各個Java類屬于同一類事物,描述這類事物的Java類就是Class類。
對比提問:眾多的人用一個什么類表示?眾多的Java類用一個什么類表示?
人 ? Person
Java類 ? Class
對比提問: Person類代表人,它的實例對象就是張三,李四這樣一個個具體的人,Class類代表Java類,它的各個實例對象又分別對應什么呢?
對應各個類在內存中的字節碼,例如,Person類的字節碼,ArrayList類的字節碼,等等;
一個類被類加載器加載到內存中,占用一片存儲空間,這個空間里面的內容就是類的字節碼,不同的類的字節碼是不同的,所以它們在內存中的內容是不同的;

用類來描述對象,類:描述數據的結構
用元數據來描述Class,MetaData(元數據):描述數據結構的結構;
反射就是得到元數據的行為;
備注:一個類在虛擬機中只有一份字節碼;
獲得Class對象
如何得到各個字節碼對應的實例對象?
每個類被加載后,系統會為該類生成對應的Class對象,通過Class對象可以訪問到JVM中的這個類,
3種方式:
1、調用某個類的class屬性獲取Class對象,如Date.class會返回Date類對應的Class對象(其實就是得到一個類的一份字節碼文件);
2、使用Class類的forName(String className)靜態方法,className表示全限定名;如String的全限定名:java.lang.String;
3、調用某個對象的getClass()方法。該方法屬于Object類;
Class<?> clz = new Date().getClass();
public class ClassDemo1 { public static void main(String[] args) throws Exception { //獲得Class對象的方法(三種) //一:調用屬性 Class<String> c = String.class; System.out.println(c);//打印結果:class java.lang.String String.class就表示JVM中一份表示String類的字節碼 Class<String> c2 = String.class; System.out.println(c == c2);//true都是String類的字節碼 一個類在虛擬機中只有一份字節碼; //二:使用forName()方法 //Class cla = Class.forName("String");//ERROR, Class<String> cla = (Class<String>)Class.forName("java.lang.String");//必須用上全限定名,否則報錯 System.out.println(c == cla);//true //三:利用對象調用Object的getClass方法; Class c3 = new String().getClass(); System.out.println(c == c3);//ture } }
我的總結:獲取Class對象最常用的是利用屬性的方法!
九個預定義Class對象
基本的 Java 類型(boolean、byte、char、short、int、long、float 、double)和關鍵字void通過class屬性也表示為 Class 對象;
Class類中boolean isPrimitive() :判定指定的 Class 對象是否表示一個基本類型。
包裝類和Void類的靜態TYPE字段;
Integer.TYPE == int.class ;
Integer.class == int.class;
數組類型的Class實例對象:
Class<String[]> clz = String[].class;
數組的Class對象如何比較是否相等? 數組的維數和數組的類型;
Class類中 boolean isArray() :判定此 Class 對象是否表示一個數組類型。
public class PreClassDemo2 { public static void main(String[] args) { Class<?> in = int.class; System.out.println(in);//int Class<?> in2 = Integer.class; //包裝類都有一個常量TYPE,用來表示其基本數據類型的字節碼 Class<?> in3 = Integer.TYPE; System.out.println(in2);//class java.lang.Integer System.out.println(in3);//int System.out.println(in3 == in);//true 包裝類都有一個常量TYPE,用來表示其基本數據類型的字節碼,所以這里會相等! System.out.println(in3 == in2);//false Class<String[]> s = String [].class; Class<int[]> i = int [].class; //System.out.println(i ==s);//編譯根本就通過不了,一個是int,一個是String } //這兩個自定義的方法是可以的,一個int,一個Integer//包裝類與基本數據類型的字節碼是不一樣的 public void show(int i){} public void show(Integer i){} }
利用Class獲取類的屬性信息
import java.lang.reflect.Modifier;
class A { } interface B{ } interface C{ }
public class BaseDemo3 extends A implements B,C{ //內部類 public class C{} public interface D{} public static void main(String[] args) { //類可以,接口也可以 Class<BaseDemo3> c = BaseDemo3.class; System.out.println(c);//class junereflect624.BaseDemo3 //得到包名 System.out.println(c.getPackage());//package junereflect62; //得到全限定名 System.out.println(c.getName());//junereflect624.BaseDemo3 //得到類的簡稱 System.out.println(c.getSimpleName());//BaseDemo3 //得到父類 /** * Class<? super T> getSuperclass() 此處super表示下限 返回表示此 Class 所表示的實體(類、接口、基本類型或 void)的超類的 Class。 */ System.out.println(c.getSuperclass().getSimpleName());//A,先獲取父類,再獲取父類的簡稱 //得到接口 System.out.println(c.getInterfaces());//[Ljava.lang.Class;@1b60280 Class[] arr = c.getInterfaces(); for (Class cla : arr) { System.out.println(cla);//interface junereflect624.B interface junereflect624.C } //獲得public修飾的類 /** * Class<?>[] getClasses() 返回一個包含某些 Class 對象的數組,這些對象表示屬于此 Class 對象所表示的類的成員的所有公共類和接口。 (如果內部類前面沒有加上public的話那么得不到!) */ Class[] cl = c.getClasses(); System.out.println(cl.length);//在內部類沒有加上public修飾的時候長度為0,加上就是2(獲取的是公共的) for (Class class1 : cl) { System.out.println(class1); } //獲得修飾符 int i = c.getModifiers(); System.out.println(i);//常量值1表示public System.out.println(Modifier.toString(i));//直接打印出public } }
Class中得到構造方法Constructor、方法Method、字段Field
常用方法:
Constructor類用于描述類中的構造方法:
Constructor<T> getConstructor(Class<?>... parameterTypes)
返回該Class對象表示類的指定的public構造方法;
Constructor<?>[] getConstructors()
返回該Class對象表示類的所有public構造方法;
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
返回該Class對象表示類的指定的構造方法,和訪問權限無關;
Constructor<?>[] getDeclaredConstructors()
返回該Class對象表示類的所有構造方法,和訪問權限無關;
Method類用于描述類中的方法:
Method getMethod(String name, Class<?> ... parameterTypes)
返回該Class對象表示類和其父類的指定的public方法;
Method[] getMethods():
返回該Class對象表示類和其父類的所有public方法;
Method getDeclaredMethod(String name, Class<?>... parameterTypes)
返回該Class對象表示類的指定的方法。和訪問權限無關,但不包括繼承的方法;
Method[] getDeclaredMethods(): 獲得類所有的方法,包括公共、保護、默認(包)訪問和私有方法,但不包括繼承的方法;
Eg:
import java.lang.reflect.Constructor;
class Emp{ private String name; private int age; private Emp() { } Emp(String name){ } public Emp(String name,int age){ } }
public class ConstructorDemo4 { public static void main(String[] args) throws Exception { //得到所有的構造器(先得到類) Class<Emp> c = Emp.class; /** * Constructor<?>[] getConstructors() 返回一個包含某些 Constructor 對象的數組,這些對象反映此 Class 對象所表示的類的所有公共構造方法。 */ Constructor[] con = c.getConstructors();//前面的修飾符必須是public才可以在這個方法下獲取到 for (Constructor cons : con) { System.out.println("c.getConstructors()"+cons);//如果上面的某構造器public去掉,則顯示不出 /**打印 public junereflect624.Emp(java.lang.String,int) */ } //得到指定的構造器,也是必須public Constructor c1 = c.getConstructor(String.class,int.class); System.out.println(c1);//public junereflect624.Emp(java.lang.String,int) System.out.println("===================================="); //現在想獲得不受public影響的,getDeclaredConstructors(),暴力反射 con = c.getDeclaredConstructors(); for (Constructor cons : con) { System.out.println("c.getDeclaredConstructors()=="+cons);//此時不受修飾符的影響 /**打印 * public junereflect624.Emp() public junereflect624.Emp(java.lang.String) public junereflect624.Emp(java.lang.String,int) */ } } }
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
class AB{ protected String name; protected String id; }
@Deprecated public class MethodDemo5 extends AB{ void show(){} public void say(){} private int age; public char c; private boolean b; public static void main(String[] args) throws Exception { Class<MethodDemo5> c = MethodDemo5.class; //獲取所有的(包含父類的方法)public修飾的方法 Method[] m = c.getMethods(); for (Method method : m) { System.out.println(method); } //總結:4個方法,獲取全部,獲取特定;不受修飾符影響的全部,不受修飾符影響的特定;(前兩個都還是受限制的) //獲取指定的方法 Method me = c.getMethod("main", String[].class); System.out.println("main "+me);//main public static void junereflect624.MethodDemo5.main(java.lang.String[]) throws java.lang.Exception //訪問所有方法,不受訪問權限影響 m = c.getDeclaredMethods(); for (Method method : m) { System.out.println("不受影響的:"+method); } me = c.getDeclaredMethod("show"); System.out.println(me);//void junereflect624.MethodDemo.show() me = c.getMethod("toString"); System.out.println(me);//public java.lang.String java.lang.Object.toString() /** * Method[] getDeclaredMethods() 返回 Method 對象的一個數組,這些對象反映此 Class 對象表示的類或接口聲明的所有方法,包括公共、保護、默認(包)訪問和私有方法,但不包括繼承的方法,只可以對當前類有效 */ /*me = c.getDeclaredMethod("toString");//ERROR,c.getDeclaredMethod()不能得到繼承的方法 System.out.println(me);//public java.lang.String java.lang.Object.toString() */ //得到字段 Field[] f = c.getFields(); for (Field field : f) {//只得到了public的 System.out.println("字段"+field); } //特定字段 Field fi = c.getField("c");//""里面是名稱 System.out.println(fi);//public char junereflect624.MethodDemo.c //得到不受限定名限定的全部字段 f = c.getDeclaredFields(); for (Field field : f) {//得到不受修飾符限定的字段,但是只對當前類有效 System.out.println("全部字段:"+field); /** * 全部字段:private int junereflect624.MethodDemo.age * 全部字段:public char junereflect624.MethodDemo.c * 全部字段:private boolean junereflect624.MethodDemo.b */ } //注釋 Annotation Annotation[] a = c.getAnnotations(); System.out.println(a.length); for (Annotation annotation : a) { System.out.println(annotation); } //特定注解 Deprecated d = c.getAnnotation(Deprecated.class); System.out.println(d); } }
獲取當前對象的字段:
import java.lang.reflect.Field; class Stu{ public String name; public String sex; public int age; public Stu(String name, String sex, int age) { super(); this.name = name; this.sex = sex; this.age = age; } } public class ReflectDemo6 { public static void main(String[] args) throws Exception { Stu s = new Stu("劉昭", "男", 12); Class<Stu> c = Stu.class; Field f = c.getField("name"); System.out.println(f.get(s));////從哪個對象身上取!此時顯示劉昭! // 修改對象的值 /** Field f = c.getField("name"); f.set(s,"章澤天"); System.out.println(f.get(s));//從哪個對象身上取!//此時顯示章澤天 */ } }
我的總結:對于方法,字段,構造方法之類用類獲取記住四個:獲取全部,獲取特定,暴力獲取全部,暴力獲取特定!
利用反射創建對象
創建對象:
1、使用Class對象的newInstance()方法創建該Class對象的實例,此時該Class對象必須要有無參數的構造方法。
2、使用Class對象獲取指定的Constructor對象,再調用Constructor的newInstance()方法創建對象類的實例,此時可以選擇使用某個構造方法。如果這個構造方法被私有化起來,那么必須先申請訪問,將可以訪問設置為true;
Eg:
最簡單的:
class User{ //將默認的構造方法私有化的話就不可以再創建對象,兩種方法都是這樣 /*private User(){}*/ public String toString() { return "User對象創建成功!"; } }
public class NewInstanceDemo6 { public static void main(String[] args) throws Exception { //傳統方式創建對象 System.out.println(new User()); //使用反射的方式 Class<User> c = User.class; User u = c.newInstance();(直接newInstance的話必須保證默認的構造方法正常存在,也就是沒有被私有化!這是前提條件) System.out.println(u); } }
復雜點的:更強大的第二種:
使用指定構造方法來創建對象:
獲取該類的Class對象。
利用Class對象的getConstructor()方法來獲取指定的構造方法。
調用Constructor的newInstance()方法創建對象。
AccessibleObject對象的setAccessible(boolean flag)方法,當flag為true的時候,就會忽略訪問權限(可訪問私有的成員)。
其子類有Field, Method, Constructor;
若要訪問對象private的成員?
在調用之前使用setAccessible(true),
Xxx x = getDeclaredXxxx();//才能得到私有的類字段.
總結步驟:
1、獲取該類的Class對象。
2、利用Class對象的getConstructor()方法來獲取指定的構造方法。
3、申請訪問(設置為可訪問)
4、調用Constructor(構造方法)的newInstance()方法創建對象。
例子
import java.lang.reflect.Constructor;
class Per{ private String name; private int age; private Per(){ } private Per(String name){ } public String toString() { return "對象!!!"; } }
public class NewInstanceDemo7 { public static void main(String[] args) throws Exception { Class<Per> c = Per.class; //System.out.println(c.newInstance());;//證明利用無參的可以 ////先獲得需要被調用的構造器(private 修飾的構造方法) Constructor<Per> con = c.getDeclaredConstructor();//調用默認的,什么都不要寫 System.out.println(con);//private junereflect624.Per() /*con = c.getDeclaredConstructor(String.class);獲取指定的構造方法 System.out.println(con);//private junereflect624.Per(java.lang.String)*/ //現在只需要執行這個構造器, /** * T newInstance(Object... initargs) 使用此 Constructor 對象表示的構造方法來創建該構造方法的聲明類的新實例,并用指定的初始化參數初始化該實例。 */ //私有的成員是受保護的,不能直接訪問 //若要訪問私有的成員,得先申請一下 con.setAccessible(true);//允許訪問 Per p = con.newInstance();//成功,通過私有的受保護的構造方法創建了對象 System.out.println("無參構造方法"+p); con = c.getDeclaredConstructor(String.class); System.out.println(con);//private junereflect624.Per(java.lang.String); con.setAccessible(true);//允許訪問 p = con.newInstance("liuzhao");//成功,通過私有的受保護的構造方法創建了對象 System.out.println("String構造方法"+p); } }
備注:對于此時的話,單例模式就不再安全了!反射可破之!!
使用反射調用方法
每個Method的對象對應一個具體的底層方法。獲得Method對象后,程序可以使用Method里面的invoke方法來執行該底層方法。
Object invoke(Object obj,Object ... args):obj表示調用底層方法的對象,后面的args表示傳遞的實際參數。
如果底層方法是靜態的,那么可以忽略指定的 obj 參數。該參數可以為 null,想想為什么?
如果底層方法所需的形參個數為 0,則所提供的 args 數組長度可以為 0 或 null。
不寫,null,或 new Object[]{}
若底層方法返回的是數組類型,invoke方法返回的不是底層方法的值,而是底層方法的返回類型;
import java.lang.reflect.Method;
class Dept{ public String show(String name){//用反射的方法來調用正常的方法 return name+",您好!"; } private void privateshow(){//用反射來實現對私有化方法的調用 System.out.println("privateshow"); } public static void staticshow(){ System.out.println("staticshow"); } }
public class InvokeDemo9 { public static void main(String[] args) throws Exception { /* 傳統方式: String name = new Dept().show("劉昭"); System.out.println(name);*/ /** * Method getMethod(String name, Class<?>... parameterTypes) 返回一個 Method 對象,它反映此 Class 對象所表示的類或接口的指 定公共成員方法。 name - 方法名 parameterTypes - 參數列表 */ //想要通過反射來調用Dept中的方法 Class<Dept> c = Dept.class; Method m = c.getMethod("show", String.class); Object o = m.invoke(c.newInstance(), "劉昭"); System.out.println(o); //私有化的方法 m = c.getDeclaredMethod("privateshow");//無參方法 m.setAccessible(true); o = m.invoke(c.newInstance()); //靜態方法的調用 m = c.getMethod("staticshow"); m.invoke(null);//staticshow為靜態方法,不需創建對象,所以這里會是null } }
打印
劉昭,您好!
privateshow
staticshow
使用反射操作字段
Field提供兩組方法操作字段:
xxx getXxx(Object obj):獲取obj對象該Field的字段值,此處的xxx表示8個基本數據類型。若該字段的類型是引用數據類型則使用,Object get(Object obj);
void setXxx(Object obj,xxx val):將obj對象的該Field字段設置成val值,此處的xxx表示8個基本數據類型。若該字段的類型是引用數據類型則使用,void set(Object obj, Object value);
package junereflect624;
//獲取字符,并且賦值,然后再取出來(對應的去查看api,比如這個是Field,別的比如Constructor,Method)
步驟:
獲取類
獲取字段
賦值(set(c.newInstance(),””));{如果為私有的話設置可接受}
import java.lang.reflect.Field;
class Cat{ private String name; public int age; private String color; }
public class FieldDemo12 { public static void main(String[] args) throws Exception { Class<Cat> clz = Cat.class; Field[] f = clz.getDeclaredFields(); for (Field field : f) { System.out.println(field); } Field fi = clz.getDeclaredField("name"); System.out.println(fi); System.out.println(fi.getName());//name //核心開始 /** * void set(Object obj, Object value) 將指定對象變量上此 Field 對象表示的字段設置為指定的新值。 */ Cat c = clz.newInstance(); fi.setAccessible(true); fi.set(c, "劉昭");//賦值成功 Object o = fi.get(c); System.out.println(o);//取出成功 fi = clz.getDeclaredField("age"); fi.setAccessible(true); fi.set(c, 21); int i = fi.getInt(c);//左邊的接受類型已經寫成了int,右邊的返回類型就也必須是int System.out.println(i);//獲取成功 } }
打印
private java.lang.String junereflect624.Cat.name
public int junereflect624.Cat.age
private java.lang.String junereflect624.Cat.color
private java.lang.String junereflect624.Cat.name
name
劉昭
21
反射和泛型-反射來獲取泛型信息
通過指定對應的Class對象,程序可以獲得該類里面所有的Field,不管該Field使用private 方法public。獲得Field對象后都可以使用getType()來獲取其類型。
Class<?> type = f.getType();//獲得字段的類型
但此方法只對普通Field有效,若該Field有泛型修飾,則不能準確得到該Field的泛型參數,如Map<String,Integer>;
為了獲得指定Field的泛型類型,我們采用:
Type gType = f.getGenericType();得到泛型類型
然后將Type對象強轉為ParameterizedType,其表示增加泛型后的類型
Type getRawType()//返回被泛型限制的類型;
Type[] getActualTypeArguments()//返回泛型參數類型;
利用反射來獲取泛型的類型(泛型信息)
步驟:
獲取當前類
獲取目標字段
獲取包含泛型類型的類型 getGenericType()
強轉至子類ParameterizedType 因為Type沒有任何對應的方法
獲得泛型真正的類型 getActualTypeArguments()
import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.HashMap; import java.util.Map; public class GetGenericTypeDemo14 { Map<String,Integer> map = new HashMap<String,Integer>(); public static void main(String[] args) throws Exception { Class c = GetGenericTypeDemo14.class; Field f = c.getDeclaredField("map"); System.out.println(f); System.out.println(f.getName());//map // Class<?> getType() 返回一個 Class 對象,它標識了此 Field 對象所表示字段的聲明類型。 Class cl = f.getType(); System.out.println("獲得其類型:"+cl); //獲得其類型:interface java.util.Map /** * Type getGenericType() 返回一個 Type 對象,它表示此 Field 對象所表示字段的聲明類型。 * Type是Class的接口; */ Type t = f.getGenericType();//包含泛型的類型 System.out.println(t); //java.util.Map<java.lang.String, java.lang.Integer> /** * Type這個類里面沒有任何的方法,所以需要調用子類的方法,那么大的類型轉到小的類型,需要強轉! */ ParameterizedType pt = (ParameterizedType)t;//強轉到其子類 /** * Type[] getActualTypeArguments() 返回表示此類型實際類型參數的 Type對象的數組。 Type getOwnerType() 返回 Type 對象,表示此類型是其成員之一的類型。 Type getRawType() 返回 Type 對象,表示聲明此類型的類或接口。 */ t = pt.getRawType();//類型的類或接口 System.out.println(t); Type[] ts = pt.getActualTypeArguments(); for (Type type : ts) { System.out.println(type); /** * class java.lang.String class java.lang.Integer */ } } }
打印:
java.util.Map junereflect624.GetGenericTypeDemo14.map
map
獲得其類型:interface java.util.Map
java.util.Map<java.lang.String, java.lang.Integer>
interface java.util.Map
class java.lang.String
class java.lang.Integer
我的總結:多查找api,參考api中方法使用的限制,比如是否靜態、返回值類型等。