什么是java反射機(jī)制
動(dòng)態(tài)語言
當(dāng)程序運(yùn)行時(shí)碌更,允許改變程序結(jié)構(gòu)或變量類型涯竟,這種語言稱為動(dòng)態(tài)語言。
靜態(tài)語言
靜態(tài)語言是在編譯時(shí)變量的數(shù)據(jù)類型即可確定的語言袖订。
反射機(jī)制
JAVA反射機(jī)制是在運(yùn)行狀態(tài)中慰毅,對(duì)于任意一個(gè)類隘截,都能夠知道這個(gè)類的所有屬性和方法;對(duì)于任意一個(gè)對(duì)象汹胃,都能夠調(diào)用它的任意一個(gè)方法和屬性婶芭;這種動(dòng)態(tài)獲取的信息以及動(dòng)態(tài)調(diào)用對(duì)象的方法的功能稱為java語言的反射機(jī)制。
IT行業(yè)里這么說着饥,沒有反射也就沒有框架犀农,現(xiàn)有的框架都是以反射為基礎(chǔ)。在實(shí)際項(xiàng)目開發(fā)中宰掉,用的最多的是框架呵哨,填的最多的是類,反射這一概念就是將框架和類揉在一起的調(diào)和劑轨奄。所以孟害,反射才是接觸項(xiàng)目開發(fā)的敲門磚!
java反射機(jī)制常用的類
Class
在面向?qū)ο蟮氖澜缋锱材猓f事萬物皆是對(duì)象挨务。而在java語言中,static修飾的東西不是對(duì)象玉组,但是它屬于類谎柄。普通的數(shù)據(jù)類型不是對(duì)象,例如:int a = 5;它不是面向?qū)ο蠊喏ǎ撬衅浒b類 Integer 或者分裝類來彌補(bǔ)了它谷誓。
實(shí)例化對(duì)象有三種表示方法:
public class Demo(){
F f=new F();
//第一種表達(dá)方式
Class c1=F.class;//這種表達(dá)方式同時(shí)也告訴了我們?nèi)魏我粋€(gè)類都有一個(gè)隱含的靜態(tài)成員變量class
//第二種表達(dá)方式
Class c2=f.getClass();//這種表達(dá)方式在已知了該類的對(duì)象的情況下通過getClass方法獲取
//第三種表達(dá)方式
Class c3 = null;
try {
c3 = Class.forName("com.text.F");//類的全稱
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
class F{}
其次他們都是同一個(gè)Class嗎
System.out.println(c1 == c2)? or System.out.println(c1 == c3)?
答案肯定是ture。這表明不論c1,orc2,orc3都代表了F類的類類型反粥,也就是說一個(gè)類只可能是Class類的一個(gè)實(shí)例對(duì)象。
理解了Class的概念糙臼,我們也可以通過類的類類型創(chuàng)建該類的對(duì)象實(shí)例,用c1 or c2 or c3的newInstance()方法:
Public class Demo1{
try {
Foo foo = (Foo)c1.newInstance();//foo就表示F類的實(shí)例化對(duì)象
foo.print();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}}
class F{
void print(){
}
}
方法的反射
public class ClassUtil {
public static void printClassMethodMessage(Object obj){
//要獲取類的信息》》首先我們要獲取類的類類型
Class c = obj.getClass();
//我們知道Object類是一切類的父類恩商,所以我們傳遞的是哪個(gè)子類的對(duì)象变逃,c就是該子類的類類型。
//接下來我們要獲取類的名稱
System.out.println("類的名稱是:"+c.getName());
/*
*我們知道怠堪,萬事萬物都是對(duì)象揽乱,方法也是對(duì)象,是誰的對(duì)象呢粟矿?
* 在java里面凰棉,方法是Method類的對(duì)象
*一個(gè)成員方法就是一個(gè)Method的對(duì)象,那么Method就封裝了對(duì)這個(gè)成員
*方法的操作
*/
//如果我們要獲得所有的方法陌粹,可以用getMethods()方法撒犀,這個(gè)方法獲取的是所有的Public的函數(shù),包括父類繼承而來的掏秩。如果我們要獲取所有該類自己聲明的方法或舞,就可以用getDeclaredMethods()方法,這個(gè)方法是不問訪問權(quán)限的蒙幻。
Method[] ms = c.getMethods();//c.getDeclaredMethods()
//接下來我們拿到這些方法之后干什么映凳?我們就可以獲取這些方法的信息,比如方法的名字邮破。
//首先我們要循環(huán)遍歷這些方法
for(int i = 0; i < ms.length;i++){
//然后可以得到方法的返回值類型的類類型
Class returnType = ms[i].getReturnType();
//得到方法的返回值類型的名字
System.out.print(returnType.getName()+" ");
//得到方法的名稱
System.out.print(ms[i].getName()+"(");
//獲取參數(shù)類型--->得到的是參數(shù)列表的類型的類類型
Class[] paramTypes = ms[i].getParameterTypes();
for (Class class1 : paramTypes) {
System.out.print(class1.getName()+",");
}
System.out.println(")");
}
}
}
總結(jié)思路:
通過方法的反射得到該類的名稱步驟:
- 獲取該類的類類型
- 通過類類型獲取類的方法(getMethods())
- 循環(huán)遍歷所獲取到的方法
- 通過這些方法的getReturnType()得到返回值類型的類類型诈豌,又通過該類類型得到返回值類型的名字
- getName()得到方法的名稱,getParameterTypes()獲取這個(gè)方法里面的參數(shù)類型的類類型决乎。
成員變量的反射
首先我們需要認(rèn)識(shí)到成員變量也是對(duì)象队询,是java.lang.reflect.Field類的對(duì)象,那么也就是說Field類封裝了關(guān)于成員變量的操作构诚。既然它封裝了成員變量蚌斩,我們又該如何獲取這些成員變量呢?它有這么一個(gè)方法:
public class ClassUtil {
public static void printFieldMessage(Object obj){
Class c = obj.getClass();
//Field[] fs = c.getFields();
}
這里的getFields()方法獲取的所有的public的成員變量的信息范嘱。和方法的反射那里public的成員變量送膳,也有一個(gè)獲取所有自己聲明的成員變量的信息:
Field[] fs = c.getDeclaredFields();
我們得到它之后,可以進(jìn)行遍歷(既然封裝了Field的信息丑蛤,那么我們就可以得到Field類型)
for (Field field : fs) {
//得到成員變量的類型的類類型
Class fieldType = field.getType();
String typeName = fieldType.getName();
//得到成員變量的名稱
String fieldName = field.getName();
System.out.println(typeName+" "+fieldName);
}
構(gòu)造函數(shù)的反射
不論是方法的反射叠聋、成員變量的反射、構(gòu)造函數(shù)的反射受裹,我們只需要知道:要想獲取類的信息碌补,首先得獲取類的類類型虏束。
public static void printConMessage(Object obj){
Class c = obj.getClass();
/*
* 首先構(gòu)造函數(shù)也是對(duì)象,是java.lang.Constructor類的對(duì)象
* 也就是java.lang. Constructor中封裝了構(gòu)造函數(shù)的信息
* 和前面說到的一樣厦章,它也有兩個(gè)方法:
* getConstructors()方法獲取所有的public的構(gòu)造函數(shù)
* getDeclaredConstructors()方法得到所有的自己聲明的構(gòu)造函數(shù)
*/
//Constructor[] cs = c.getConstructors();
Constructor[] cs = c.getDeclaredConstructors();
for (Constructor constructor : cs) {
//我們知道構(gòu)造方法是沒有返回值類型的镇匀,但是我們可以:
System.out.print(constructor.getName()+"(");
//獲取構(gòu)造函數(shù)的參數(shù)列表》》得到的是參數(shù)列表的類類型
Class[] paramTypes = constructor.getParameterTypes();
for (Class class1 : paramTypes) {
System.out.print(class1.getName()+",");
}
System.out.println(")");
}
}
Class類的動(dòng)態(tài)加載類
靜態(tài)加載類
一般就是指new ClassName(),這個(gè)在編譯時(shí)就會(huì)檢查。無論你這個(gè)類用不用袜啃,代碼邏輯能不能執(zhí)行到汗侵,編譯期都會(huì)對(duì)其編譯。不存在這個(gè)類無法通過編譯群发。
動(dòng)態(tài)加載類
我們想要的就是我用那個(gè)類就加載那個(gè)類晰韵,也就是常說的運(yùn)行時(shí)刻加載,動(dòng)態(tài)加載類熟妓。Class.forName("ClassName")雪猪,就是所謂的動(dòng)態(tài)加載,動(dòng)態(tài)加載的類在源程序編譯時(shí)可以缺席滑蚯。
我們可以建這么一個(gè)類:
Class All{
Public static void start(){
try{
Class cl= Class.forName(args[0]);
//通過類類型浪蹂,創(chuàng)建該類的對(duì)象
cl.newInstance();
}catch(Exception e){
e.printStackTrace();
}
}
}
前面我們?cè)诜治鯟lass實(shí)例化對(duì)象的方式的時(shí)候,Class.forName(“類的全稱”)告材,它不僅僅表示了類的類類型坤次,還表示了動(dòng)態(tài)加載類。當(dāng)我們javac All.java的時(shí)候斥赋,它不會(huì)報(bào)任何錯(cuò)誤缰猴,也就是說在編譯的時(shí)候是沒有錯(cuò)誤的。只有當(dāng)我們具體用某個(gè)類的時(shí)候疤剑,那個(gè)類不存在滑绒,它才會(huì)報(bào)錯(cuò)。
如果加載的類是B類隘膘,就需要:
B bt = (B) cl.newInstance();
萬一加載的是C類呢疑故,可以改成
C ct = (C) cl.newInstance();
但是如果我想用很多的類或者加載很多的類,該怎么辦弯菊?我們可以統(tǒng)一一個(gè)標(biāo)準(zhǔn)纵势,不論C類還是B類或者其他的類,比如定義一個(gè)標(biāo)準(zhǔn)
Stand s = (Stand) cl.newInstance();
只要B類和C類都是這個(gè)標(biāo)準(zhǔn)的就行了管钳。
Class All{
Public static void start(){
try{
Class cl= Class.forName(args[0]);
//通過類類型钦铁,創(chuàng)建該類的對(duì)象
Stand s = (Stand) cl.newInstance();
s.start();
}catch(Exception e){
e.printStackTrace();
}
}
}
interface Stand {
Public void start();
}
現(xiàn)在如果我想要用B類,我們只需要:
Class B implements Stand{
Public void start(){
System.out.print("B...satrt");
}
}
結(jié)果
B...satrt