java中的反射及其優(yōu)點說明
java反射及優(yōu)點
反射是java的一大特性,而且是有些框架實現(xiàn)了IoC/DI的原理,本文就來探討下java中的反射及其優(yōu)點。
首先是普通的java靜態(tài)類加載,java靜態(tài)類是通過new實現(xiàn)的,在編譯時刻就要加載所有可能用到的類,這樣實際上存在一些缺點的,比如只要有一個類沒有找到或者出現(xiàn)重大的問題編譯便不會通過,導致其他存在的類也無法使用。另一方面,如果要加載其他的類就要重新進行編譯,有的時候是非常不方便的。下面就引入反射機制來動態(tài)加載類來解決這個問題。
首先創(chuàng)建一個ClassInterface接口,放在com.classTest包下:
package com.classTest;? public interface ClassInterface { ? ? public void start(); ? ? ? //接口約定start()方法 }
然后分別使用Class1Impl和Class2Impl類去實現(xiàn)這個接口,也放在com.classTest包下:
package com.classTest;? public class Class1Impl implements ClassInterface { ? ? ? @Override ? ? public void start() { ? ? ? ? // TODO Auto-generated method stub ? ? ? ? System.out.println("This is Class1Impl"); ? //實現(xiàn)start() ? ? } }
package com.classTest;? public class Class2Impl implements ClassInterface { ? ? ? @Override ? ? public void start() { ? ? ? ? // TODO Auto-generated method stub ? ? ? ? System.out.println("This is Class2Impl"); ? //實現(xiàn)start() ? ? }? }
下面先用正常的類加載來說明正常類加載的缺點:
package com.test;? import java.util.Scanner;? import com.classTest.ClassInterface; ? public class LoadClassTest { ? ? ClassInterface c1 = new Class1Impl(); ? ? ClassInterface c2 = new Class2Impl(); ? ? c1.start(); ? ? c2.start(); }
上面是正常的new方法生成的對象,然后調用接口里的方法,但是一旦Class1Impl類或者Class2Impl類沒有,那有的哪個類也就無法使用,而且想要使用新的ClassInterface接口下的方法還是需要重新寫類,重新寫測試方法,重新生成測試對象,重新編譯...
下面介紹利用反射機制動態(tài)加載類來解決這個問題:
package com.test;? import java.util.Scanner;? import com.classTest.ClassInterface; ? public class LoadClassTest { ? ? public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException{ ? ? ? ? //new 創(chuàng)建對象是靜態(tài)加載類,在編譯時刻就需要加載所有可能用到的類 ? ? ? ? //動態(tài)記載類可以解決這個問題 ? ? ? ? Scanner sc = new Scanner(System.in); ? ? ? ? String className = sc.nextLine(); ? ? ? ? //動態(tài)加載類,在運行的時刻進行加載 ? ? ? ? //使用類類型(class type) 通過Class這個類的forName(className)方法動態(tài)創(chuàng)建類對象 ? ? ? ? Class c = Class.forName(className); ? ? ? ? //通過類類型 用newInstance()方法創(chuàng)建該類對象 ? ? ? ? ClassInterface ci = (ClassInterface) c.newInstance(); ? ? ? ? ci.start();//動態(tài)加載類 調用其方法 ? ? } }
有沒有看出這其中的差異,想要加載其他類只需要輸入另外的類名即可(這里的類名是包含包的),forName()方法可以通過類名動態(tài)創(chuàng)建類類型的對象,然后用類類型的對象的newInstance()方法創(chuàng)建該類類型對象(也就是一個類)的對象(調用newInstance()方法的類類型對象需要進行轉化),之后就可以用生成對象調用接口下的方法了。
那這時候可能就會有人問了,要是在我們不知道接口名稱為ClassInterface的情況下,我們該如何訪問className這個類類型生成的c這個類對象下面的方法呢?我們來實現(xiàn)一下:
Class c = Class.forName("com.classTest.Foo");//創(chuàng)建一個類對象c Method m1 = c.getMethod("fun1", int.class,int.class);//通過getMethod方法獲取c的方法,第一個參數(shù)表示想要獲取的方法名稱fun1,之后的參數(shù)列表表示fun1這個函數(shù)的所有參數(shù)的類類型。 Object o = m1.invoke(foo1,new Object[]{10,20} );//通過invoke方法來調用我們前面通過getMethod()獲得的方法,第一個參數(shù)表示執(zhí)行我們所獲得方法的對象,第二個參數(shù)表示我們所獲取方法的參數(shù),這里是兩個int類型。如果沒有返回值返回null,有返回值則返回具體的返回值,這里將返回結果保存在o對象里。
這樣的優(yōu)點是顯而易見的,類無需在編譯前全部加載,而且修改接口實現(xiàn)類的時候也不用重新編譯。
這只是反射的一小部分,但是也是很重要的一部分,也是Spring中IoC/DI的實現(xiàn)基礎。
(另外告訴大家一個小tip:所謂的泛型數(shù)據容器的數(shù)據類型(也就是HashMap<String,String>中的<String,String>)不能寫入其他數(shù)據類型實際上只是存在于編譯層面的,是為了防止開發(fā)者往里面誤寫入自己不想寫入的東西,然而通過泛型可以跳過編譯層面實現(xiàn)往這個泛型數(shù)據容器里存入和這個泛型數(shù)據容器數(shù)據類型不一樣的數(shù)據,雖然這樣之后用foreach遍歷就會報錯...)
java反射機制(Reflection)
1.什么是反射?反射有什么作用?
能夠分析類能力的程序稱為反射。反射機制允許程序在運行狀態(tài)借助Reflection API取得任何類的內部信息,并能直接操作任意對象的內部屬性及方法。
反射機制的作用
- 在運行時判斷任意一個對象所屬的類
- 在運行時構造任意一個類的對象
- 在運行時判斷任意一個類所具有的成員變量和方法
- 在運行時調用任意一個對象的成員變量和方法
- 在運行時獲取泛型信息
- 在運行時處理注解
- 生成 動態(tài)代理
2.反射相關的主要API
java.lang.Class
:代表一個類java.lang.reflect.Method
:代表類的方法java.lang.reflect. Field
:代表類的成員變量java.lang.reflect.Constructor
:代表類的構造器
3.什么是Class類
對Class類的深入理解
- Class本身也是一個類
- Class 對象只能由系統(tǒng)建立對象(Class 對象是在加載類時由Java 虛擬機以及通過調用類加載器中的 defineClass 方法自動構造的)
- 一個類在 JVM 中只會有一個Class實例
- 一個Class對象對應的是一個加載到JVM中的一個.class文件
- 每個類的實例都會記得自己是由哪個 Class 實例所生成
- 通過Class可以完整地得到一個類中的完整結構
類的加載過程:
程序經過javac.exe命令以后,會生成一 個或多個字節(jié)碼文件(.class結尾)。
接著我們使用java.exe命令對某個字節(jié)碼文件進行解釋運行。相當于將某個字節(jié)碼文件加載到內存中。此過程就稱為類的加載。加載到內存中的類,我們就稱為運行時類,此運行時類,就作為CLass的一個實例。
java.Lang.Class實例的獲?。ㄋ姆N方式)
package package03; import org.junit.Test; public class ReflectiveTeat01 { //獲取 Class 類的實例 @Test public void test01() throws Exception { //方式一:通過調用運行時類得屬性:.class Class clazz1 = Person.class; System.out.println(clazz1);//class package03.Person //方式二:通過運行時類的對象,調用 getClass() 方法 Person p1 = new Person(); Class clazz2 = p1.getClass(); System.out.println(clazz2);//class package03.Person //方式三:調用 Class 的靜態(tài)方法:forName(String classPath) Class clazz3 = Class.forName("package03.Person"); System.out.println(clazz3);//class package03.Person //方法四:使用類的加載器:ClassLoader ClassLoader classLoader = ReflectiveTeat01.class.getClassLoader(); Class clazz4 = classLoader. loadClass("package03.Person") ; System. out . println(clazz4);//class package03.Person } }
4.調用運行時類的指定結構
如何操作運行時類中指定的屬性
@Test public void testField() throws Exception { Class clazz = Person.class; //創(chuàng)建運行時類的對象 P Person p = (Person) clazz.newInstance(); //獲取指定的屬性 Field fieldName = clazz.getDeclaredField("id"); //fieldName 要操作的屬性名賦值給 fieldName 變量名 //保證當前屬性是可訪問的 fieldName .setAccessible(true); /* 設置當前屬性的值 set():參數(shù)1:指明設置哪個對象的屬性 參數(shù)2:將此屬性值設置為多少 */ fieldName.set(p,1001); /* 獲取當前屬性的值 get():參數(shù)1:獲取哪個對象的當前屬性值 */ int intName = (int) fieldName.get(p); System.out.println(intName ); }
操作運行時類的方法
已調用 show 方法例子:該方法有 注解,參數(shù),返回值,權限?。╬rivate)
@MyAnonotation//自定義注解 ? ? private String show(String nation) { ? ? ? ? System.out.println("我的國籍是:" + nation); ? ? ? ? return nation; ? ? }
@Test public void testMethod() throws Exception { Class clazz = Person.class; //創(chuàng)建運行時類對象 Person p = (Person) clazz.newInstance(); /* 1.獲取指定的某個方法 getDeclaredMethod():參數(shù)1:指明獲取的方法的名稱 參數(shù)2:指明獲取的方法的形參列表 * */ Method show = clazz.getDeclaredMethod("show", String.class); //2.保證當前方法時可訪問的 show.setAccessible(true); /* 3.調用方法的invoke():參數(shù)1:方法的調用者 參數(shù)2:給方法形參賦值的實參 invoke()方法的返回值即為對應類中調用的方法的返回值 */ show.invoke(p, "CHN"); //調用靜態(tài)方法時: //private static void showDesc() Method showDesc = clazz.getDeclaredMethod("showDesc"); showDesc.setAccessible(true); //如果調用的運行時類中的方法沒有返回值,則 invoke() 返回 null Object invoke = showDesc.invoke(Person.class); System.out.println(invoke); }
上面代碼用到的 Person 類
public class Person { private String name; private int age; public int id; //空參構造器 public Person() { } //全參構造器 public Person(String name, int age, int id) { this.name = name; this.age = age; this.id = id; } @MyAnonotation private String show(String nation) { System.out.println("我的國籍是:" + nation); return nation; } //靜態(tài)方法 private static void showDesc(){ System.out.println("我是一個可愛的人"); } // get、set 方法 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; } }
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
springboot如何從數(shù)據庫獲取數(shù)據,用echarts顯示(數(shù)據可視化)
這篇文章主要介紹了springboot如何從數(shù)據庫獲取數(shù)據,用echarts顯示(數(shù)據可視化),具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12IDEA啟動tomcat控制臺中文亂碼問題的解決方法(100%有效)
很多人在idea中啟動項目時會出現(xiàn)控制臺的中文亂碼,其實也無傷大雅,但是本人看著不舒服,下面這篇文章主要給大家介紹了關于IDEA啟動tomcat控制臺中文亂碼問題的解決方法,需要的朋友可以參考下2022-09-09淺析 ArrayList 和 LinkedList 有什么區(qū)別
ArrayList 和 LinkedList 有什么區(qū)別,是面試官非常喜歡問的一個問題。今天通過本文給大家詳細介紹下,感興趣的朋友跟隨小編一起看看吧2020-10-10