摘要:可以通過(guò)提供的方法,獲取對(duì)象,具體如下方法返回值方法名稱方法說(shuō)明返回一個(gè)指定參數(shù)的對(duì)象,該對(duì)象反映此對(duì)象所表示的類或接口的指定已聲明方法。
我來(lái)學(xué)習(xí)反射 1.為什么我們要學(xué)習(xí)反射?
通過(guò)反射機(jī)制可以獲取到一個(gè)類的完整信息,例如:所有(包含private修飾)屬性和方法,包信息等。
換句話說(shuō),Class本身表示一個(gè)類的本身,通過(guò)Class可以完整獲取一個(gè)類中的完整結(jié)構(gòu),包含此類中的方法定義,屬性定義等。
反射就是把Java類中的各種成分映射成一個(gè)個(gè)的Java對(duì)象
例如:一個(gè)類有:成員變量、方法、構(gòu)造方法、包等等信息,利用反射技術(shù)可以對(duì)一個(gè)類進(jìn)行解剖,把個(gè)個(gè)組成部分映射成一個(gè)個(gè)對(duì)象。2.反射的核心是什么?
我個(gè)人認(rèn)為:一切的操作都是講使用Object完成,類或者數(shù)組的引用是可以用Object進(jìn)行接收。
也就是我們之前說(shuō)Java中的我認(rèn)為很重要的多態(tài),對(duì)象的多態(tài):Object object= 任何引用類型的實(shí)例對(duì)象
3.類的加載過(guò)程類的正常加載過(guò)程:反射的原理在與Class對(duì)象
Class對(duì)象的由來(lái)是將class文件讀入內(nèi)存,并為之創(chuàng)建一個(gè)Class對(duì)象,那么Class對(duì)象在反射中起到什么作用?
我們用圖片已經(jīng)說(shuō)明了很清楚了,我們?cè)趤?lái)看一下官方的解釋
For every type of object, the Java virtual machine instantiates an immutable instance of java.lang.Class which provides methods to examine the runtime properties of the object including its members and type information. Class also provides the ability to create new classes and objects. Most importantly, it is the entry point for all of the Reflection APIs.對(duì)于每一種類,Java虛擬機(jī)都會(huì)初始化出一個(gè)Class類型的實(shí)例,每當(dāng)我們編寫(xiě)并且編譯一個(gè)新創(chuàng)建的類就會(huì)產(chǎn)生一個(gè)對(duì)應(yīng)Class對(duì)象,并且這個(gè)Class對(duì)象會(huì)被保存在同名.class文件里。當(dāng)我們new一個(gè)新對(duì)象或者引用靜態(tài)成員變量時(shí),Java虛擬機(jī)(JVM)中的類加載器系統(tǒng)會(huì)將對(duì)應(yīng)Class對(duì)象加載到JVM中,然后JVM再根據(jù)這個(gè)類型信息相關(guān)的Class對(duì)象創(chuàng)建我們需要實(shí)例對(duì)象或者提供靜態(tài)變量的引用值。
如上圖所示,比如創(chuàng)建編譯一個(gè)Student類,那么,JVM就會(huì)創(chuàng)建一個(gè)Student對(duì)應(yīng)Class類的Class實(shí)例,該Class實(shí)例保存了Student類相關(guān)的類型信息,包括屬性,方法,構(gòu)造方法等等,通過(guò)這個(gè)Class實(shí)例可以在運(yùn)行時(shí)訪問(wèn)Student對(duì)象的屬性和方法等。另外通過(guò)Class類還可以創(chuàng)建出一個(gè)新的Student對(duì)象。這就是反射能夠?qū)崿F(xiàn)的原因,可以說(shuō)Class是反射操作的基礎(chǔ)。
需要特別注意的是,每個(gè)class(注意class是小寫(xiě),代表普通類)類,無(wú)論創(chuàng)建多少個(gè)實(shí)例對(duì)象,在JVM中都對(duì)應(yīng)同一個(gè)Class對(duì)象。
4.Class API簡(jiǎn)要說(shuō)明跟我們之前學(xué)習(xí)查看Math、String類一樣的過(guò)程
Class 類的實(shí)例表示正在運(yùn)行的 Java 應(yīng)用程序中的類和接口。也就是JVM中有N多的實(shí)例每個(gè)類都有該Class對(duì)象。(包括基本數(shù)據(jù)類型)
Class 沒(méi)有公共構(gòu)造方法。Class 對(duì)象是在加載類時(shí)由 Java 虛擬機(jī)以及通過(guò)調(diào)用類加載器中的defineClass 方法自動(dòng)構(gòu)造的。也就是這不需要我們自己去處理創(chuàng)建,JVM已經(jīng)幫我們創(chuàng)建好了。
沒(méi)有公共的構(gòu)造方法,方法共有64個(gè)太多了。下面用到哪個(gè)就詳解哪個(gè)吧
5.反射的使用Java 提供反射機(jī)制,依賴于 Class 類和 java.lang.reflect 類庫(kù)。其主要的類如下:
Class:表示類或者接口
Field:表示類中的成員變量
Method:表示類中的方法
Constructor:表示類的構(gòu)造方法
Array:該類提供了動(dòng)態(tài)創(chuàng)建數(shù)組和訪問(wèn)數(shù)組元素的靜態(tài)方法
先自己設(shè)置一個(gè)Student類來(lái)完成對(duì)應(yīng)的測(cè)試,代碼如下:
package com.pangsir.model; public class Student { public int no; public String sex; private String name; private int age; 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; } /* * 構(gòu)造方法 */ Student(String str){ System.out.println("(默認(rèn))的構(gòu)造方法 s = " + str); } //無(wú)參構(gòu)造方法 public Student(){ System.out.println("調(diào)用了公有、無(wú)參構(gòu)造方法執(zhí)行了。。。"); } //有一個(gè)參數(shù)的構(gòu)造方法 protected Student(char name){ System.out.println("姓名:" + name); } //有多個(gè)參數(shù)的構(gòu)造方法 public Student(String name ,int age){ System.out.println("姓名:"+name+"年齡:"+ age);//這的執(zhí)行效率有問(wèn)題,以后解決。 } //私有構(gòu)造方法 private Student(int age){ System.out.println("私有的構(gòu)造方法 年齡:"+ age); } }5.1 獲取Class對(duì)象的三種方式
說(shuō)Class是反射能夠?qū)崿F(xiàn)的基礎(chǔ)的另一個(gè)原因是:Java反射包java.lang.reflect中的所有類都沒(méi)有public構(gòu)造方法,要想獲得這些類實(shí)例,只能通過(guò)Class類獲取。所以說(shuō)如果想使用反射,必須得獲得Class對(duì)象。
/* * Constructor. Only the Java Virtual Machine creates Class * objects. */ private Class() {}
Object.getClass() 方法(對(duì)象.getClass())
如果我們有一個(gè)類的對(duì)象,那么我們可以通過(guò) Object.getClass 方法獲得該類的 Class 對(duì)象。
// String 對(duì)象的 getClass 方法 Class clazz1 = "hello".getClass(); // 數(shù)組對(duì)象的 getClass 方法 Class clazz2 = (new byte[1024]).getClass(); System.out.println(class2) // 會(huì)輸出 [B, [ 代表是數(shù)組, B 代表是 byte。即 byte 數(shù)組的類類型
然而對(duì)于基本類型無(wú)法使用這種方法:
boolean b; Class c = b.getClass(); // compile-time error
任何數(shù)據(jù)類型(包括基本數(shù)據(jù)類型)都有一個(gè)“靜態(tài)”的class屬性,若我們知道要獲取的類類型的名稱時(shí),我們可以使用 class 語(yǔ)法獲取該類類型的對(duì)象
// 類 Class clazz = Integer.class; // 數(shù)組 Class clazz2 = int [][].class;
對(duì)于基本類型和 void 都有對(duì)應(yīng)的包裝類。在包裝類中有一個(gè)靜態(tài)屬性 TYPE,保存了該來(lái)的類類型。以 Integer 類為例,其源碼中定義了如下的靜態(tài)屬性:
@SuppressWarnings("unchecked") public static final ClassTYPE = (Class ) Class.getPrimitiveClass("int");
生成 Class 類實(shí)例的方法:
Class clazz1 = Integer.TYPE; Class clazz2 = Void.TYPE;
Class.forName() 方法
通過(guò)Class類的靜態(tài)方法:forName(String className)(常用)
可以通過(guò) Class 的 forName 方法獲取 Class 實(shí)例,其中類的名稱要寫(xiě)類的完整路徑。
該方法只能用于獲取引用類型的類類型對(duì)象。
// 這種方式會(huì)使用當(dāng)前的類的加載器加載,并且會(huì)將 Class 類實(shí)例初始化 Class> clazz = Class.forName("java.lang.String"); // 上面的調(diào)用方式等價(jià)于 Class> clazz = Class.forName("java.lang.String", true, currentLoader);
對(duì)于數(shù)組比較特殊:
Class cDoubleArray = Class.forName("[D"); //相當(dāng)于double[].class Class cStringArray = Class.forName("[[Ljava.lang.String;"); //相當(dāng)于String[][].class
使用該方法可能會(huì)拋出 ClassNotFoundException 異常,這個(gè)異常發(fā)生在類的加載階段,原因如下:
類加載器在類路徑中沒(méi)有找到該類(檢查:查看所在加載的類以及其所依賴的包是否在類路徑下)
該類已經(jīng)被某個(gè)類加載器加載到 JVM 內(nèi)存中,另外一個(gè)類加載器又嘗試從同一個(gè)包中加載
5.2 Student類獲取Classpackage com.pangsir; import java.lang.reflect.Constructor; public class Main { public static void main(String[] args) throws ClassNotFoundException { Student student = new Student(); /* * JAVA反射--獲取Class對(duì)象的三種方式 */ // 通過(guò)對(duì)象名.getClass()方法獲取 Class stuClass = student.getClass(); System.out.println("stuClass is "+stuClass.getName()); // 通過(guò)類名.class方式獲得 Class stuClass1 = Student.class; System.out.println("stuClass1 is "+stuClass1.getName()); System.out.println(stuClass == stuClass1); // 通過(guò)Class.forName()方法獲得 Class stuClass2 = Class.forName("com.pangsir.model.Student"); System.out.println("stuClass2 is "+stuClass2); System.out.println(stuClass1 == stuClass2); } } Output: stuClass is com.pangsir.model.Student stuClass1 is com.pangsir.model.Student true stuClass2 is class com.pangsir.model.Student true
代碼說(shuō)明:在運(yùn)行期間,一個(gè)類,只有一個(gè)Class對(duì)象產(chǎn)生。三種方式常用第三種,第一種對(duì)象都有了還要反射干什么。第二種需要導(dǎo)入類的包,依賴太強(qiáng),不導(dǎo)包就拋編譯錯(cuò)誤。一般都選第三種,一個(gè)字符串可以傳入也可寫(xiě)在配置文件中等多種方法。5.3 Member & AccessibleObject
在講 Field、Method、Constructor 之前,先說(shuō)說(shuō) Member 和 AccessibleObject。Member 是一個(gè)接口,表示 Class 的成員,前面的三個(gè)類都是其實(shí)現(xiàn)類。
AccessibleObject 是 Field、Method、Constructor 三個(gè)類共同繼承的父類,它提供了將反射的對(duì)象標(biāo)記為在使用時(shí)取消默認(rèn) Java 語(yǔ)言訪問(wèn)控制檢查的能力。通過(guò) setAccessible 方法可以忽略訪問(wèn)級(jí)別,從而訪問(wèn)對(duì)應(yīng)的內(nèi)容。并且 AccessibleObject 實(shí)現(xiàn)了 AnnotatedElement 接口,提供了與獲取注解相關(guān)的能力。
Constructor 提供了有關(guān)類的構(gòu)造方法的信息,以及對(duì)它的動(dòng)態(tài)訪問(wèn)的能力。
可以通過(guò) Class 提供的方法,獲取 Constructor 對(duì)象,具體如下:
方法返回值 | 方法名稱 | 方法說(shuō)明 |
---|---|---|
Constructor |
getConstructor(Class>... parameterTypes) | 返回指定參數(shù)類型、具有public訪問(wèn)權(quán)限的構(gòu)造函數(shù)對(duì)象 |
Constructor>[] | getConstructors() | 返回所有具有public訪問(wèn)權(quán)限的構(gòu)造函數(shù)的Constructor對(duì)象數(shù)組 |
Constructor |
getDeclaredConstructor(Class>... parameterTypes) | 返回指定參數(shù)類型、所有聲明的(包括private)構(gòu)造函數(shù)對(duì)象 |
Constructor>[] | getDeclaredConstructor() | 返回所有聲明的(包括private)構(gòu)造函數(shù)對(duì)象 |
package com.pangsir.test; import java.lang.reflect.Constructor; public class Main { public static void main(String[] args) throws ClassNotFoundException { // 通過(guò)Class.forName()方法獲得Class對(duì)象 Class stuClass = Class.forName("com.pangsir.model.Student"); System.out.println("************返回所有public構(gòu)造方法************"); Constructor[] constructors = stuClass.getConstructors(); for (Constructor constructor : constructors) { System.out.println(constructor); } /* * Output: * ************返回所有public構(gòu)造方法************ * public com.pangsir.model.Student() * public com.pangsir.model.Student(java.lang.String,int) */ System.out.println("************所有的構(gòu)造方法(包括:私有、受保護(hù)、默認(rèn)、公有)***************"); Constructor[] constructors2 = stuClass.getDeclaredConstructors(); for (Constructor constructor : constructors2) { System.out.println(constructor); } /* * Output: * ************所有的構(gòu)造方法(包括:私有、受保護(hù)、默認(rèn)、公有)*************** * private com.pangsir.model.Student(int) * public com.pangsir.model.Student() * protected com.pangsir.model.Student(char) * public com.pangsir.model.Student(java.lang.String,int) * com.pangsir.model.Student(java.lang.String) */ Constructor constructor; System.out.println("************返回指定類型的 public構(gòu)造器************"); try { constructor = stuClass.getConstructor(String.class, int.class); System.out.println(constructor); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } /* * Output: * ************返回指定類型的 public構(gòu)造器************ * public com.pangsir.model.Student(java.lang.String,int) * * 如果指定參數(shù)的構(gòu)造器是非public類型的 則拋出java.lang.NoSuchMethodException異常 */ System.out.println("************返回指定類型的構(gòu)造器************"); try { constructor = stuClass.getDeclaredConstructor(int.class); System.out.println(constructor); // char.class } catch (NoSuchMethodException e) { // String.class, int.class e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } /* * Output: * ************返回指定類型的構(gòu)造器************ * public com.pangsir.model.Student(java.lang.String,int) * protected com.pangsir.model.Student(char) * private com.pangsir.model.Student(int) */ System.out.println("********獲取私有構(gòu)造方法,并調(diào)用**********"); constructor = clazz.getDeclaredConstructor(char.class); System.out.println(con); //調(diào)用構(gòu)造方法 con.setAccessible(true);//暴力訪問(wèn)(忽略掉訪問(wèn)修飾符) obj = con.newInstance("男"); /* * Output: * ************返回指定類型的構(gòu)造器************ * public com.pangsir.model.Student(char) * 姓名:男 */ } }5.5 獲取變量
Field 提供了有關(guān)類或接口的單個(gè)屬性的信息,以及對(duì)它的動(dòng)態(tài)訪問(wèn)的能力。
可以通過(guò) Class 提供的方法,獲取 Field 對(duì)象,具體如下:
方法返回值 | 方法名稱 | 方法說(shuō)明 |
---|---|---|
Field | getDeclaredField(String name) | 獲取指定name名稱的(包含private修飾的)字段,不包括繼承的字段 |
Field[] | getDeclaredField() | 獲取Class對(duì)象所表示的類或接口的所有(包含private修飾的)字段,不包括繼承的字段 |
Field | getField(String name) | 獲取指定name名稱、具有public修飾的字段,包含繼承字段 |
Field[] | getField() | 獲取修飾符為public的字段,包含繼承字段 |
public class Student { public Student(){ } //**********字段*************// public String name; protected int age; char sex; private String phoneNum; @Override public String toString() { return "Student [name=" + name + ", age=" + age + ", sex=" + sex + ", phoneNum=" + phoneNum + "]"; } }
測(cè)試類
package com.pangsir.field; import java.lang.reflect.Field; /* * 獲取成員變量并調(diào)用: * * 1.批量的 * 1).Field[] getFields():獲取所有的"公有字段" * 2).Field[] getDeclaredFields():獲取所有字段,包括:私有、受保護(hù)、默認(rèn)、公有; * 2.獲取單個(gè)的: * 1).public Field getField(String fieldName):獲取某個(gè)"公有的"字段; * 2).public Field getDeclaredField(String fieldName):獲取某個(gè)字段(可以是私有的) * * 設(shè)置字段的值: * Field --> public void set(Object obj,Object value): * 參數(shù)說(shuō)明: * 1.obj:要設(shè)置的字段所在的對(duì)象; * 2.value:要為字段設(shè)置的值; * */ public class Fields { public static void main(String[] args) throws Exception { //1.獲取Class對(duì)象 Class stuClass = Class.forName("com.pangsir.model.Student"); //2.獲取字段 System.out.println("************獲取所有公有的字段********************"); Field[] fieldArray = stuClass.getFields(); for(Field f : fieldArray){ System.out.println(f); } /* * Output: * ***********獲取所有公有的字段******************** * public java.lang.String com.pangsir.model.Student.name */ System.out.println("******獲取所有的字段(包括私有、受保護(hù)、默認(rèn)的)***********"); fieldArray = stuClass.getDeclaredFields(); for(Field f : fieldArray){ System.out.println(f); } /* * Output: ************獲取所有的字段(包括私有、受保護(hù)、默認(rèn)的)******************** * public java.lang.String com.pangsir.model.Student.name * protected int com.pangsir.model.Student.age * char com.pangsir.model.Student.sex * private java.lang.String com.pangsir.model.Student.phoneNum */ System.out.println("******獲取公有字段并調(diào)用*********"); Field f = stuClass.getField("name"); System.out.println(f); //獲取一個(gè)對(duì)象 Object obj = stuClass.getConstructor().newInstance();//產(chǎn)生Student對(duì)象--》Student stu = new Student(); //為字段設(shè)置值 f.set(obj, "劉德華");//為Student對(duì)象中的name屬性賦值--》stu.name = "劉德華" //驗(yàn)證 Student stu = (Student)obj; System.out.println("驗(yàn)證姓名:" + stu.name); /* * Output: *************獲取公有字段**并調(diào)用*********************************** * public java.lang.String com.pangsir.model.Student.name * 驗(yàn)證姓名:劉德華 */ System.out.println("*****獲取私有字段****并調(diào)用***************"); f = stuClass.getDeclaredField("phoneNum"); System.out.println(f); f.setAccessible(true);//暴力反射,解除私有限定 f.set(obj, "12345768901"); System.out.println("驗(yàn)證電話:" + stu); /* * Output: **************獲取私有字段****并調(diào)用******************************** * private java.lang.String fanshe.field.Student.phoneNum * 驗(yàn)證電話:Student [name=劉德華, age=0, sex= , phoneNum=12345768901] */ } }
代碼分析:調(diào)用字段時(shí):需要傳遞兩個(gè)參數(shù):5.6 獲取方法
Object obj = stuClass.getConstructor().newInstance();//產(chǎn)生Student對(duì)象--》Student stu = new Student();
//為字段設(shè)置值
f.set(obj, "劉德華");//為Student對(duì)象中的name屬性賦值--》stu.name = "劉德華"
第一個(gè)參數(shù):要傳入設(shè)置的對(duì)象,第二個(gè)參數(shù):要傳入實(shí)參
Method 提供了有關(guān)類或接口的單個(gè)方法的信息,以及對(duì)它的動(dòng)態(tài)訪問(wèn)的能力。
可以通過(guò) Class 提供的方法,獲取 Field 對(duì)象,具體如下:
方法返回值 | 方法名稱 | 方法說(shuō)明 |
---|---|---|
Method | getDeclaredMethod(String name, Class>... parameterTypes) | 返回一個(gè)指定參數(shù)的Method對(duì)象,該對(duì)象反映此 Class 對(duì)象所表示的類或接口的指定已聲明方法。 |
Method[] | getDeclaredMethod() | 返回 Method 對(duì)象的一個(gè)數(shù)組,這些對(duì)象反映此 Class 對(duì)象表示的類或接口聲明的所有方法,包括公共、保護(hù)、默認(rèn)(包)訪問(wèn)和私有方法,但不包括繼承的方法。 |
Method | getMethod(String name, Class>... parameterTypes) | 返回一個(gè) Method 對(duì)象,它反映此 Class 對(duì)象所表示的類或接口的指定公共成員方法。 |
Method[] | getMethods() | 返回一個(gè)包含某些 Method 對(duì)象的數(shù)組,這些對(duì)象反映此 Class 對(duì)象所表示的類或接口(包括那些由該類或接口聲明的以及從超類和超接口繼承的那些的類或接口)的公共 member 方法。 |
Student類:
public class Student { //**************成員方法***************// public void show1(String s){ System.out.println("調(diào)用了:公有的,String參數(shù)的show1(): s = " + s); } protected void show2(){ System.out.println("調(diào)用了:受保護(hù)的,無(wú)參的show2()"); } void show3(){ System.out.println("調(diào)用了:默認(rèn)的,無(wú)參的show3()"); } private String show4(int age){ System.out.println("調(diào)用了,私有的,并且有返回值的,int參數(shù)的show4(): age = " + age); return "abcd"; } }
測(cè)試類:
package com.pangsir.method; import java.lang.reflect.Method; /* * 獲取成員方法并調(diào)用: * * 1.批量的: * public Method[] getMethods():獲取所有"公有方法";(包含了父類的方法也包含Object類) * public Method[] getDeclaredMethods():獲取所有的成員方法,包括私有的(不包括繼承的) * 2.獲取單個(gè)的: * public Method getMethod(String name,Class>... parameterTypes): * 參數(shù): * name : 方法名; * Class ... : 形參的Class類型對(duì)象 * public Method getDeclaredMethod(String name,Class>... parameterTypes) * * 調(diào)用方法: * Method --> public Object invoke(Object obj,Object... args): * 參數(shù)說(shuō)明: * obj : 要調(diào)用方法的對(duì)象; * args:調(diào)用方式時(shí)所傳遞的實(shí)參; ): */ public class MethodClass { public static void main(String[] args) throws Exception { //1.獲取Class對(duì)象 Class stuClass = Class.forName("fanshe.method.Student"); //2.獲取所有公有方法 System.out.println("***************獲取所有的”公有“方法*******************"); Method[] methodArray = stuClass.getMethods(); for(Method m : methodArray){ System.out.println(m); } /* * Output: ***************獲取所有的”公有“方法******************* public void com.pangsir.model.Student.show1(java.lang.String) 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 final void java.lang.Object.wait() throws java.lang.InterruptedException public boolean java.lang.Object.equals(java.lang.Object) public java.lang.String java.lang.Object.toString() 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() */ System.out.println("***************獲取所有的方法,包括私有的*******************"); methodArray = stuClass.getDeclaredMethods(); for(Method m : methodArray){ System.out.println(m); } /* * Output: ***************獲取所有的方法,包括私有的******************* public void om.pangsir.model.Student.show1(java.lang.String) private java.lang.String om.pangsir.model.Student.show4(int) protected void om.pangsir.model.Student.show2() void om.pangsir.model.Student.show3() */ System.out.println("***************獲取公有的show1()方法*******************"); Method m = stuClass.getMethod("show1", String.class); System.out.println(m); //實(shí)例化一個(gè)Student對(duì)象 Object obj = stuClass.getConstructor().newInstance(); m.invoke(obj, "劉德華"); /* * Output: ***************獲取公有的show1()方法******************* public void om.pangsir.model.Student.show1(java.lang.String) 調(diào)用了:公有的,String參數(shù)的show1(): s = 劉德華 */ System.out.println("***************獲取私有的show4()方法******************"); m = stuClass.getDeclaredMethod("show4", int.class); System.out.println(m); m.setAccessible(true);//解除私有限定 Object result = m.invoke(obj, 20);//需要兩個(gè)參數(shù),一個(gè)是要調(diào)用的對(duì)象(獲取有反射),一個(gè)是實(shí)參 System.out.println("返回值:" + result); /* * Output: ***************獲取私有的show4()方法****************** private java.lang.String om.pangsir.model.Student.show4(int) 調(diào)用了,私有的,并且有返回值的,int參數(shù)的show4(): age = 20 返回值:abcd */ } }
代碼分析:由此可見(jiàn):5.7 反射main(主方法)
m = stuClass.getDeclaredMethod("show4", int.class);//調(diào)用制定方法(所有包括私有的),需要傳入兩個(gè)參數(shù),第一個(gè)是調(diào)用的方法名稱,第二個(gè)是方法的形參類型,切記是類型。
System.out.println(m);
m.setAccessible(true);//解除私有限定
Object result = m.invoke(obj, 20);//需要兩個(gè)參數(shù),一個(gè)是要調(diào)用的對(duì)象(獲取有反射),一個(gè)是實(shí)參
System.out.println("返回值:" + result);
Student類
package com.pangsir.main; public class Student { public static void main(String[] args) { System.out.println("main方法執(zhí)行了。。。"); } }
測(cè)試類:
package fanshe.main; import java.lang.reflect.Method; /** * 獲取Student類的main方法、不要與當(dāng)前的main方法搞混了 */ public class Main { public static void main(String[] args) { try { //1、獲取Student對(duì)象的字節(jié)碼 Class clazz = Class.forName("fanshe.main.Student"); //2、獲取main方法 Method methodMain = clazz.getMethod("main", String[].class);//第一個(gè)參數(shù):方法名稱,第二個(gè)參數(shù):方法形參的類型, //3、調(diào)用main方法 // methodMain.invoke(null, new String[]{"a","b","c"}); //第一個(gè)參數(shù),對(duì)象類型,因?yàn)榉椒ㄊ莝tatic靜態(tài)的,所以為null可以,第二個(gè)參數(shù)是String數(shù)組,這里要注意在jdk1.4時(shí)是數(shù)組,jdk1.5之后是可變參數(shù) //這里拆的時(shí)候?qū)? new String[]{"a","b","c"} 拆成3個(gè)對(duì)象。。。所以需要將它強(qiáng)轉(zhuǎn)。 methodMain.invoke(null, (Object)new String[]{"a","b","c"});//方式一 // methodMain.invoke(null, new Object[]{new String[]{"a","b","c"}});//方式二 } catch (Exception e) { e.printStackTrace(); } } }5.8 通過(guò)反射越過(guò)泛型檢查
泛型用在編譯期,編譯過(guò)后泛型擦除(消失掉)。所以是可以通過(guò)反射越過(guò)泛型檢查的
import java.lang.reflect.Method; import java.util.ArrayList; /* * 通過(guò)反射越過(guò)泛型檢查 * * 例如:有一個(gè)String泛型的集合,怎樣能向這個(gè)集合中添加一個(gè)Integer類型的值? */ public class Demo { public static void main(String[] args) throws Exception{ ArrayList5.9 通過(guò)反射運(yùn)行配置文件內(nèi)容strList = new ArrayList<>(); strList.add("aaa"); strList.add("bbb"); // strList.add(100); //獲取ArrayList的Class對(duì)象,反向的調(diào)用add()方法,添加數(shù)據(jù) Class listClass = strList.getClass(); //得到 strList 對(duì)象的字節(jié)碼 對(duì)象 //獲取add()方法 Method m = listClass.getMethod("add", Object.class); //調(diào)用add()方法 m.invoke(strList, 100); //遍歷集合 for(Object obj : strList){ System.out.println(obj); System.out.println(obj.getClass()); } /* *Output: * aaa * class java.lang.String * bbb * class java.lang.String * 100 * class java.lang.Integer */ } }
Student類
public class Student { public void show(){ System.out.println("is show()"); } }
配置文件以txt文件為例子(os.properties):
className = com.pangsir.model.Student methodName = show
測(cè)試類:
import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.lang.reflect.Method; import java.util.Properties; /* * 我們利用反射和配置文件,可以使:應(yīng)用程序更新時(shí),對(duì)源碼無(wú)需進(jìn)行任何修改 * 我們只需要將新類發(fā)送給客戶端,并修改配置文件即可 */ public class Demo { public static void main(String[] args) throws Exception { //通過(guò)反射獲取Class對(duì)象 Class stuClass = Class.forName(getValue("className"));//"cn.fanshe.Student" //2獲取show()方法 Method m = stuClass.getMethod(getValue("methodName"));//show //3.調(diào)用show()方法 m.invoke(stuClass.getConstructor().newInstance()); } //此方法接收一個(gè)key,在配置文件中獲取相應(yīng)的value public static String getValue(String key) throws IOException{ Properties pro = new Properties();//獲取配置文件的對(duì)象 FileReader in = new FileReader("os.properties");//獲取輸入流 pro.load(in);//將流加載到配置文件對(duì)象中 in.close(); return pro.getProperty(key);//返回根據(jù)key獲取的value值 } } /* *Output: * is show() */
需求:
當(dāng)我們升級(jí)這個(gè)系統(tǒng)時(shí),不要Student類,而需要新寫(xiě)一個(gè)Student2的類時(shí),這時(shí)只需要更改pro.txt的文件內(nèi)容就可以了。代碼就一點(diǎn)不用改動(dòng)
Student2類:
public class Student2 { public void show2(){ System.out.println("is show2()"); } }
修改配置文件如下
className = com.pangsir.model.Student2 methodName = show25.10 利用ParameterizedType獲取java泛型參數(shù)類
//超類 package test; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; @SuppressWarnings("unchecked") public class Person6.反射的缺點(diǎn){ private Class clazz; public Person() { // 使用反射技術(shù)得到T的真實(shí)類型 ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass(); // 獲取當(dāng)前new的對(duì)象的 泛型的父類 類型 this.clazz = (Class ) pt.getActualTypeArguments()[0]; // 獲取第一個(gè)類型參數(shù)的真實(shí)類型 System.out.println("clazz ---> " + clazz); } } //子類 package test; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; public class Student extends Person { } //測(cè)試類 package test; public class TestGetClass { /** * @param args */ public static void main(String[] args) { Student student = new Student(); } }
沒(méi)有任何一項(xiàng)技術(shù)是十全十美的,Java反射擁有強(qiáng)大功能的同時(shí)也帶來(lái)了一些副作用。
性能開(kāi)銷
反射涉及類型動(dòng)態(tài)解析,所以JVM無(wú)法對(duì)這些代碼進(jìn)行優(yōu)化。因此,反射操作的效率要比那些非反射操作低得多。我們應(yīng)該避免在經(jīng)常被執(zhí)行的代碼或?qū)π阅芤蠛芨叩某绦蛑惺褂梅瓷洹?/p>
安全限制
使用反射技術(shù)要求程序必須在一個(gè)沒(méi)有安全限制的環(huán)境中運(yùn)行。如果一個(gè)程序必須在有安全限制的環(huán)境中運(yùn)行,如Applet,那么這就是個(gè)問(wèn)題了。
內(nèi)部曝光
由于反射允許代碼執(zhí)行一些在正常情況下不被允許的操作(比如訪問(wèn)私有的屬性和方法),所以使用反射可能會(huì)導(dǎo)致意料之外的副作用--代碼有功能上的錯(cuò)誤,降低可移植性。反射代碼破壞了抽象性,因此當(dāng)平臺(tái)發(fā)生改變的時(shí)候,代碼的行為就有可能也隨著變化。
使用反射的一個(gè)原則:如果使用常規(guī)方法能夠?qū)崿F(xiàn),那么就不要用反射。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/72580.html
摘要:棧上各個(gè)變量申請(qǐng)的內(nèi)存,返回的地址是這段連續(xù)內(nèi)存的最小的地址。為什么用一個(gè)位的十六進(jìn)制來(lái)呢因?yàn)閭€(gè)字節(jié),一個(gè)字節(jié)有位,每位有兩個(gè)狀態(tài),那么就是,也就是。為什么用,純屬演示方便。結(jié)構(gòu)體里的字節(jié)對(duì)齊以成員中自身對(duì)齊值最大的那個(gè)值為標(biāo)準(zhǔn)。 原文:我的個(gè)人博客 https://mengkang.net/1046.html初中級(jí) phper 有多久沒(méi)給自己充電了呢,安利一波我的直播 PHP 進(jìn)階之...
摘要:的分句會(huì)創(chuàng)建一個(gè)塊作用域,其聲明的變量?jī)H在中有效。而閉包的神奇作用是阻止此事發(fā)生。依然持有對(duì)該作用域的引用,而這個(gè)引用就叫做閉包。當(dāng)然,無(wú)論使用何種方式對(duì)函數(shù)類型的值進(jìn)行傳遞,當(dāng)函數(shù)在別處被調(diào)用時(shí)都可以觀察到閉包。 date: 16.12.8 Thursday 第一章 作用域是什么 LHS:賦值操作的目標(biāo)是誰(shuí)? 比如: a = 2; RHS:誰(shuí)是賦值操作的源頭? 比如: conso...
摘要:遮蔽效應(yīng)作用域查找會(huì)在找到第一個(gè)匹配的標(biāo)識(shí)符時(shí)停止,不會(huì)繼續(xù)往上層作用域查找,這就會(huì)產(chǎn)生遮蔽效應(yīng)。會(huì)發(fā)現(xiàn)每一次輸出的都是為啥勒所有的回調(diào)函數(shù)回在循環(huán)結(jié)束后才會(huì)執(zhí)行事件循環(huán)。 三劍客 編譯,顧名思義,就是源代碼執(zhí)行前會(huì)經(jīng)歷的過(guò)程,分三個(gè)步驟, 分詞/詞法分析,將我們寫(xiě)的代碼字符串分解成多個(gè)詞法單元 解析/語(yǔ)法分析,將詞法單元集合生成抽象語(yǔ)法樹(shù)(AST) 代碼生成,抽象語(yǔ)法樹(shù)(AST)轉(zhuǎn)...
摘要:最近剛剛看完了你不知道的上卷,對(duì)有了更進(jìn)一步的了解。你不知道的上卷由兩部分組成,第一部分是作用域和閉包,第二部分是和對(duì)象原型。附錄詞法這一章并沒(méi)有說(shuō)明機(jī)制,只是介紹了中的箭頭函數(shù)引入的行為詞法。第章混合對(duì)象類類理論類的機(jī)制類的繼承混入。 最近剛剛看完了《你不知道的 JavaScript》上卷,對(duì) JavaScript 有了更進(jìn)一步的了解。 《你不知道的 JavaScript》上卷由兩部...
摘要:可選的參數(shù)規(guī)定效果的時(shí)長(zhǎng),它可以取以下值,或毫秒。五其他核心方法中停止動(dòng)畫(huà)方法用于停止動(dòng)畫(huà)或效果,在它們完成之前。默認(rèn)是,即僅停止活動(dòng)的動(dòng)畫(huà),運(yùn)行任何排入隊(duì)列的動(dòng)畫(huà)向后執(zhí)行。這些動(dòng)畫(huà)不會(huì)開(kāi)始,知道第一個(gè)完成。 本文主要跟大家分享jQuery隱藏與顯示(hide,show,toggle) 上卷與下拉(slideDown,slideUp,slideToggle) 淡入淡出(fadeOut,...
閱讀 3029·2021-11-16 11:42
閱讀 3688·2021-09-08 09:36
閱讀 962·2019-08-30 12:52
閱讀 2498·2019-08-29 14:12
閱讀 787·2019-08-29 13:53
閱讀 3604·2019-08-29 12:16
閱讀 655·2019-08-29 12:12
閱讀 2482·2019-08-29 11:16