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)重大的問題編譯便不會通過,導(dǎo)致其他存在的類也無法使用。另一方面,如果要加載其他的類就要重新進行編譯,有的時候是非常不方便的。下面就引入反射機制來動態(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方法生成的對象,然后調(diào)用接口里的方法,但是一旦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)加載類 調(diào)用其方法
? ? }
}有沒有看出這其中的差異,想要加載其他類只需要輸入另外的類名即可(這里的類名是包含包的),forName()方法可以通過類名動態(tài)創(chuàng)建類類型的對象,然后用類類型的對象的newInstance()方法創(chuàng)建該類類型對象(也就是一個類)的對象(調(diào)用newInstance()方法的類類型對象需要進行轉(zhuǎn)化),之后就可以用生成對象調(diào)用接口下的方法了。
那這時候可能就會有人問了,要是在我們不知道接口名稱為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方法來調(diào)用我們前面通過getMethod()獲得的方法,第一個參數(shù)表示執(zhí)行我們所獲得方法的對象,第二個參數(shù)表示我們所獲取方法的參數(shù),這里是兩個int類型。如果沒有返回值返回null,有返回值則返回具體的返回值,這里將返回結(jié)果保存在o對象里。這樣的優(yōu)點是顯而易見的,類無需在編譯前全部加載,而且修改接口實現(xiàn)類的時候也不用重新編譯。
這只是反射的一小部分,但是也是很重要的一部分,也是Spring中IoC/DI的實現(xiàn)基礎(chǔ)。
(另外告訴大家一個小tip:所謂的泛型數(shù)據(jù)容器的數(shù)據(jù)類型(也就是HashMap<String,String>中的<String,String>)不能寫入其他數(shù)據(jù)類型實際上只是存在于編譯層面的,是為了防止開發(fā)者往里面誤寫入自己不想寫入的東西,然而通過泛型可以跳過編譯層面實現(xiàn)往這個泛型數(shù)據(jù)容器里存入和這個泛型數(shù)據(jù)容器數(shù)據(jù)類型不一樣的數(shù)據(jù),雖然這樣之后用foreach遍歷就會報錯...)
java反射機制(Reflection)
1.什么是反射?反射有什么作用?
能夠分析類能力的程序稱為反射。反射機制允許程序在運行狀態(tài)借助Reflection API取得任何類的內(nèi)部信息,并能直接操作任意對象的內(nèi)部屬性及方法。
反射機制的作用
- 在運行時判斷任意一個對象所屬的類
- 在運行時構(gòu)造任意一個類的對象
- 在運行時判斷任意一個類所具有的成員變量和方法
- 在運行時調(diào)用任意一個對象的成員變量和方法
- 在運行時獲取泛型信息
- 在運行時處理注解
- 生成 動態(tài)代理
2.反射相關(guān)的主要API
java.lang.Class:代表一個類java.lang.reflect.Method:代表類的方法java.lang.reflect. Field:代表類的成員變量java.lang.reflect.Constructor:代表類的構(gòu)造器
3.什么是Class類
對Class類的深入理解
- Class本身也是一個類
- Class 對象只能由系統(tǒng)建立對象(Class 對象是在加載類時由Java 虛擬機以及通過調(diào)用類加載器中的 defineClass 方法自動構(gòu)造的)
- 一個類在 JVM 中只會有一個Class實例
- 一個Class對象對應(yīng)的是一個加載到JVM中的一個.class文件
- 每個類的實例都會記得自己是由哪個 Class 實例所生成
- 通過Class可以完整地得到一個類中的完整結(jié)構(gòu)
類的加載過程:
程序經(jīng)過javac.exe命令以后,會生成一 個或多個字節(jié)碼文件(.class結(jié)尾)。
接著我們使用java.exe命令對某個字節(jié)碼文件進行解釋運行。相當(dāng)于將某個字節(jié)碼文件加載到內(nèi)存中。此過程就稱為類的加載。加載到內(nèi)存中的類,我們就稱為運行時類,此運行時類,就作為CLass的一個實例。
java.Lang.Class實例的獲?。ㄋ姆N方式)
package package03;
import org.junit.Test;
public class ReflectiveTeat01 {
//獲取 Class 類的實例
@Test
public void test01() throws Exception {
//方式一:通過調(diào)用運行時類得屬性:.class
Class clazz1 = Person.class;
System.out.println(clazz1);//class package03.Person
//方式二:通過運行時類的對象,調(diào)用 getClass() 方法
Person p1 = new Person();
Class clazz2 = p1.getClass();
System.out.println(clazz2);//class package03.Person
//方式三:調(diào)用 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.調(diào)用運行時類的指定結(jié)構(gòu)
如何操作運行時類中指定的屬性
@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 變量名
//保證當(dāng)前屬性是可訪問的
fieldName .setAccessible(true);
/*
設(shè)置當(dāng)前屬性的值
set():參數(shù)1:指明設(shè)置哪個對象的屬性
參數(shù)2:將此屬性值設(shè)置為多少
*/
fieldName.set(p,1001);
/*
獲取當(dāng)前屬性的值
get():參數(shù)1:獲取哪個對象的當(dāng)前屬性值
*/
int intName = (int) fieldName.get(p);
System.out.println(intName );
}
操作運行時類的方法
已調(diào)用 show 方法例子:該方法有 注解,參數(shù),返回值,權(quán)限?。╬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.保證當(dāng)前方法時可訪問的
show.setAccessible(true);
/*
3.調(diào)用方法的invoke():參數(shù)1:方法的調(diào)用者
參數(shù)2:給方法形參賦值的實參
invoke()方法的返回值即為對應(yīng)類中調(diào)用的方法的返回值
*/
show.invoke(p, "CHN");
//調(diào)用靜態(tài)方法時:
//private static void showDesc()
Method showDesc = clazz.getDeclaredMethod("showDesc");
showDesc.setAccessible(true);
//如果調(diào)用的運行時類中的方法沒有返回值,則 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;
//空參構(gòu)造器
public Person() {
}
//全參構(gòu)造器
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;
}
}
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
springboot如何從數(shù)據(jù)庫獲取數(shù)據(jù),用echarts顯示(數(shù)據(jù)可視化)
這篇文章主要介紹了springboot如何從數(shù)據(jù)庫獲取數(shù)據(jù),用echarts顯示(數(shù)據(jù)可視化),具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12
IDEA啟動tomcat控制臺中文亂碼問題的解決方法(100%有效)
很多人在idea中啟動項目時會出現(xiàn)控制臺的中文亂碼,其實也無傷大雅,但是本人看著不舒服,下面這篇文章主要給大家介紹了關(guān)于IDEA啟動tomcat控制臺中文亂碼問題的解決方法,需要的朋友可以參考下2022-09-09
淺析 ArrayList 和 LinkedList 有什么區(qū)別
ArrayList 和 LinkedList 有什么區(qū)別,是面試官非常喜歡問的一個問題。今天通過本文給大家詳細(xì)介紹下,感興趣的朋友跟隨小編一起看看吧2020-10-10
SpringBoot配置使用H2數(shù)據(jù)庫的簡單教程
H2是一個Java編寫的關(guān)系型數(shù)據(jù)庫,它可以被嵌入Java應(yīng)用程序中使用,或者作為一個單獨的數(shù)據(jù)庫服務(wù)器運行。本文將介紹SpringBoot如何配置使用H2數(shù)據(jù)庫2021-05-05

