java反射机制详解_Java反射机制详解
Java反射機(jī)制是在運(yùn)行狀態(tài)中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調(diào)用它的任意一個方法和屬性;這種動態(tài)獲取的信息以及動態(tài)調(diào)用對象的方法的功能稱為Java語言的反射機(jī)制。
1、關(guān)于Class
1、Class是一個類,一個描述類的類(也就是描述類本身),封裝了描述方法的Method,描述字段的Filed,描述構(gòu)造器的Constructor等屬性
2、對象照鏡子后(反射)可以得到的信息:某個類的數(shù)據(jù)成員名、方法和構(gòu)造器、某個類到底實現(xiàn)了哪些接口。
3、對于每個類而言,JRE 都為其保留一個不變的 Class 類型的對象。
一個 Class 對象包含了特定某個類的有關(guān)信息。
4、Class 對象只能由系統(tǒng)建立對象
5、一個類在 JVM 中只會有一個Class實例
package?com.java.reflection;
public?class?Person?{
String?name;
private?int?age;
public?Person()?{
System.out.println("無參構(gòu)造器");
}
public?Person(String?name,?int?age)?{
System.out.println("有參構(gòu)造器");
this.name?=?name;
this.age?=?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;
}
@Override
public?String?toString()?{
return?"Person{"?+
"name='"?+?name?+?'\''?+
",?age="?+?age?+
'}';
}
}
2、反射機(jī)制獲取類有三種方法
/**
*?反射機(jī)制獲取類有三種方法
*/
@Test
public?void?testGetClass()?throws?ClassNotFoundException?{
Class?clazz?=?null;
//1?直接通過類名.Class的方式得到
clazz?=?Person.class;
System.out.println("通過類名:?"?+?clazz);
//2?通過對象的getClass()方法獲取,這個使用的少(一般是傳的是Object,不知道是什么類型的時候才用)
Object?obj?=?new?Person();
clazz?=?obj.getClass();
System.out.println("通過getClass():?"?+?clazz);
//3?通過全類名獲取,用的比較多,但可能拋出ClassNotFoundException異常
clazz?=?Class.forName("com.java.reflection.Person");
System.out.println("通過全類名獲取:?"?+?clazz);
}
通過類名: class com.java.reflection.Person
無參構(gòu)造器
通過getClass(): class com.java.reflection.Person
通過全類名獲取: class com.java.reflection.Person
3、利用newInstance創(chuàng)建對象:調(diào)用的類必須有無參的構(gòu)造器
/**
*?Class類的newInstance()方法,創(chuàng)建類的一個對象。
*/
@Test
public?void?testNewInstance()
throws?ClassNotFoundException,?IllegalAccessException,?InstantiationException?{
Class?clazz?=?Class.forName("com.java.reflection.Person");
//使用Class類的newInstance()方法創(chuàng)建類的一個對象
//實際調(diào)用的類的那個?無參數(shù)的構(gòu)造器(這就是為什么寫的類的時候,要寫一個無參數(shù)的構(gòu)造器,就是給反射用的)
//一般的,一個類若聲明了帶參數(shù)的構(gòu)造器,也要聲明一個無參數(shù)的構(gòu)造器
Object?obj?=?clazz.newInstance();
System.out.println(obj);
}
無參構(gòu)造器
Person{name='null', age=0}
4、ClassLoader類加載器
/**
*?ClassLoader類裝載器
*/
@Test
public?void?testClassLoader1()?throws?ClassNotFoundException,?IOException?{
//1、獲取一個系統(tǒng)的類加載器
ClassLoader?classLoader?=?ClassLoader.getSystemClassLoader();
System.out.println("系統(tǒng)的類加載器-->"?+?classLoader);
//2、獲取系統(tǒng)類加載器的父類加載器(擴(kuò)展類加載器(extensions?classLoader))
classLoader?=?classLoader.getParent();
System.out.println("擴(kuò)展類加載器-->"?+?classLoader);
//3、獲取擴(kuò)展類加載器的父類加載器
//輸出為Null,無法被Java程序直接引用
classLoader?=?classLoader.getParent();
System.out.println("啟動類加載器-->"?+?classLoader);
//
//4、測試當(dāng)前類由哪個類加載器進(jìn)行加載?,結(jié)果就是系統(tǒng)的類加載器
classLoader?=?Class.forName("com.java.reflection.Person").getClassLoader();
System.out.println("當(dāng)前類由哪個類加載器進(jìn)行加載-->"+classLoader);
//5、測試JDK提供的Object類由哪個類加載器負(fù)責(zé)加載的
classLoader?=?Class.forName("java.lang.Object").getClassLoader();
System.out.println("JDK提供的Object類由哪個類加載器加載-->"?+?classLoader);
}
系統(tǒng)的類加載器-->sun.misc.Launcher$AppClassLoader@43be2d65
擴(kuò)展類加載器-->sun.misc.Launcher$ExtClassLoader@7a9664a1
啟動類加載器-->null
當(dāng)前類由哪個類加載器進(jìn)行加載-->sun.misc.Launcher$AppClassLoader@43be2d65
JDK提供的Object類由哪個類加載器加載-->null
4.1、getResourceAsStream方法
@Test
public?void?testGetResourceAsStream()?throws?ClassNotFoundException,?IOException?{
//??????????這么寫的話,文件需要放到src目錄下
//???????InputStream?in?=?new?FileInputStream("test.properties");
//5、關(guān)于類加載器的一個主要方法
//調(diào)用getResourceAsStream?獲取類路徑下的文件對應(yīng)的輸入流
InputStream?in?=?this.getClass().getClassLoader()
.getResourceAsStream("com/java/reflection/test.properties");
System.out.println("in:?"?+in);
Properties?properties?=?new?Properties();
properties.load(in);
String?driverClass?=?properties.getProperty("dirver");
String?jdbcUrl?=?properties.getProperty("jdbcUrl");
//中文可能會出現(xiàn)亂碼,需要轉(zhuǎn)換一下
String?user?=?new?String(properties.getProperty("user").getBytes("ISO-8859-1"),?"UTF-8");
String?password?=?properties.getProperty("password");
System.out.println("diverClass:?"+driverClass);
System.out.println("user:?"?+?user);
}
test.properties內(nèi)容如下:
dirver=com.mysql.jdbc.Driver;
jdbcUrl=jdbc:mysql://192.168.42.108:3306/test
user=測試
password=993803
結(jié)果:
in: java.io.BufferedInputStream@2aca0115
diverClass: com.mysql.jdbc.Driver;
user: 測試
5、Method: 對應(yīng)類中的方法
public?class?Person?{
private?String?name;
private?int?age;
//新增一個私有方法
private?void?privateMthod(){
}
public?Person()?{
System.out.println("無參構(gòu)造器");
}
public?Person(String?name,?int?age)?{
System.out.println("有參構(gòu)造器");
this.name?=?name;
this.age?=?age;
}
public?String?getName()?{
return?name;
}
public?void?setName(String?name)?{
this.name?=?name;
}
/**
*
*?@param?age??類型用Integer,不用int
*/
public?void?setName(String?name?,?int?age){
System.out.println("name:?"?+?name);
System.out.println("age:"+?age);
}
public?int?getAge()?{
return?age;
}
public?void?setAge(int?age)?{
this.age?=?age;
}
@Override
public?String?toString()?{
return?"Person{"?+
"name='"?+?name?+?'\''?+
",?age="?+?age?+
'}';
}
}
@Test
public?void?testMethod()?throws?ClassNotFoundException,?NoSuchMethodException,
IllegalAccessException,?InstantiationException,?InvocationTargetException?{
Class?clazz?=?Class.forName("com.java.reflection.Person");
//1、得到clazz?對應(yīng)的類中有哪些方法,不能獲取private方法
Method[]?methods?=clazz.getMethods();
System.out.print("????????getMethods:?");
for?(Method?method?:?methods){
System.out.print(method.getName()?+?",?");
}
//2、獲取所有的方法(且只獲取當(dāng)著類聲明的方法,包括private方法)
Method[]?methods2?=?clazz.getDeclaredMethods();
System.out.print("\ngetDeclaredMethods:?");
for?(Method?method?:?methods2){
System.out.print(method.getName()?+?",?");
}
//3、獲取指定的方法
Method?method?=?clazz.getDeclaredMethod("setName",String.class);//第一個參數(shù)是方法名,后面的是方法里的參數(shù)
System.out.println("\nmethod?:?"?+?method);
Method?method2?=?clazz.getDeclaredMethod("setName",String.class?,int.class);//第一個參數(shù)是方法名,后面的是方法里的參數(shù)
System.out.println("method2:?"?+?method2);
//4、執(zhí)行方法!
Object?obj?=?clazz.newInstance();
method2.invoke(obj,?"changwen",?22);
}
getMethods: toString, getName, setName, setName, setAge,
getAge, wait, wait, wait, equals, hashCode, getClass, notify, notifyAll,
getDeclaredMethods: toString, getName, setName, setName, setAge, getAge, privateMthod,
method : public void com.java.reflection.Person.setName(java.lang.String)
method2: public void com.java.reflection.Person.setName(java.lang.String,int)
無參構(gòu)造器
name: changwen
age:22
6、invoke方法
public?class?PersonInvoke?{
public?PersonInvoke()?{
}
private?String?method2()?{
return?"Person?private?String?method2";
}
}
public?class?StudentInvoke?extends?PersonInvoke{
private?void?method1(Integer?age)?{
System.out.println("Student?private?void?method1,?age=:"?+age);
}
}
獲取當(dāng)前類的父類定義的私有方法
/**
*?獲取當(dāng)前類的父類中定義的私有方法
*?直接調(diào)用getSuperclass()
*/
@Test
public?void?testGetSuperClass()?throws?Exception?{
String?className?=?"com.java.reflection.StudentInvoke";
Class?clazz?=?Class.forName(className);
Class?superClazz?=?clazz.getSuperclass();
System.out.println(superClazz);
//輸出結(jié)果:class?com.java.reflection.PersonInvoke
}
另一種寫法
/**
*?@param?className??某個類的全類名
*?@param?methodName?類的一個方法的方法名,該方法也可能是私有方法
*?@param?args??調(diào)用該方法需要傳入的參數(shù)?...可變參數(shù)的意思
*?@return?調(diào)用方法后的返回值
*/
public?Object?invoke(String?className,?String?methodName,?Object?...?args)?{
Object?obj?=?null;
try?{
obj?=?Class.forName(className).newInstance();
return?invoke(obj,?methodName,?args);
}?catch?(InstantiationException?e)?{
e.printStackTrace();
}?catch?(IllegalAccessException?e)?{
e.printStackTrace();
}?catch?(ClassNotFoundException?e)?{
e.printStackTrace();
}
return?invoke(null,?methodName,?args);
}
/**
*?@param?obj??方法執(zhí)行的那個對象
*?@param?methodName?類的一個方法的方法名,該方法也可能是私有方法,還可能是該方法在父類中定義的私有方法
*?@param?args??調(diào)用該方法需要傳入的參數(shù)?...可變參數(shù)的意思
*?@return?調(diào)用方法后的返回值
*/
public?Object?invoke(Object?obj,?String?methodName,?Object?...?args)?{
//1、獲取Method對象
Class?[]?parameterTypes?=?new?Class[args.length];
for?(int?i=0?;?i
parameterTypes[i]?=?args[i].getClass();
}
try?{
//2、執(zhí)行Method方法
Method?method?=?getMethod(obj.getClass(),?methodName,parameterTypes);
//通過反射執(zhí)行private方法
method.setAccessible(true);
//3、返回方法的返回值
return?method.invoke(obj,args);
}?catch?(Exception?e)?{
}
return?null;
}
/**
*?獲取clazz?的methodName?方法,?該方法可能是私有方法,還可能是父類中的私有方法
*/
public?Method?getMethod(Class?clazz,?String?methodName,?Class?...?parameterTypes)?{
//注意這個循環(huán)里的內(nèi)容!!!
for?(;?clazz?!=?Object.class;?clazz?=?clazz.getSuperclass()){
try?{
return?clazz.getDeclaredMethod(methodName,?parameterTypes);
}?catch?(Exception?e)?{?//這里要寫Exception,不然會出錯,應(yīng)該是有部分異常沒有捕獲
}
}
return?null;
}
測試:
@Test
public?void?testInvoke2()?{
Object?obj?=?new?StudentInvoke();
invoke(obj,?"method1",?10);
Object?result?=?invoke(obj,?"method2");
System.out.println(result);
}
private void method1,age:10
Person private String method2
7、Field字段
public?class?Person?{
public?String?name;
private?Integer?age;
public?Person()?{
}
public?Person(String?name,?Integer?age)?{
this.name?=?name;
this.age?=?age;
}
}
/**
*?Field:?封裝了字段的信息
*/
@Test
public?void?testField()?throws
ClassNotFoundException,?NoSuchFieldException,?IllegalAccessException?{
Class?clazz?=?Class.forName("com.java.reflection.Person");
//1、獲取字段
//1.1?獲取Field的數(shù)組,私有字段也能獲取
Field[]?fields?=?clazz.getDeclaredFields();
for?(Field?field:?fields)?{
System.out.print(field.getName()?+?",?");
}
//1.2?獲取指定名字的Field(如果是私有的,見下面的4)
Field?field?=?clazz.getDeclaredField("name");
System.out.println("\n獲取指定Field名=:?"?+?field.getName());
Person?person?=?new?Person("ABC",?12);
//2、獲取指定對象的Field的值
Object?val?=?field.get(person);
System.out.println("獲取指定對象字段'name'的Field的值=:?"?+?val);
//3、設(shè)置指定對象的Field的值
field.set(person,?"changwen2");
System.out.println("設(shè)置指定對象字段'name'的Field的值=:?"?+?person.name);
//4、若該字段是私有的,需要調(diào)用setAccessible(true)方法
Field?field2?=?clazz.getDeclaredField("age");
field2.setAccessible(true);
System.out.println("獲取指定私有字段名=:?"?+?field2.getName());
}
name, age,
獲取指定Field名=: name
獲取指定對象字段'name'的Field的值=: ABC
設(shè)置指定對象字段'name'的Field的值=: changwen2
獲取指定私有字段名=: age
/**
*?一個實例:
*?反射獲取一個繼承Person2的Student類
*?設(shè)置字段"age"=20(該字段可能為私有,可能在其父類中)
*/
@Test
public?void?testClassField()?throws?ClassNotFoundException,?IllegalAccessException,?InstantiationException?{
String?className?=?"com.java.reflection.Student";
String?fieldName?=?"age";??//可能為私有,可能在其父類中
Object?val?=?20;
//創(chuàng)建className?對應(yīng)類的對象,并為其fieldName賦值為val
Class?clazz?=?Class.forName(className);
Field?field?=?null;
for?(Class?clazz2?=?clazz;?clazz2?!=?Object.class;?clazz2?=?clazz2.getSuperclass()){
try?{
field?=?clazz2.getDeclaredField(fieldName);
}?catch?(Exception?e)?{
}
}
Object?obj?=?clazz.newInstance();
assert?field?!=?null;
field.setAccessible(true);
field.set(obj,?val);
Student?stu?=?(Student)?obj;
System.out.println("age?=?"?+?stu.getAge());
}
8、構(gòu)造器(Constructor)
/**
*?構(gòu)造器:開發(fā)用的比較少
*/
@Test
public?void?testConstructor()?throws?ClassNotFoundException,?NoSuchMethodException,
IllegalAccessException,?InvocationTargetException,?InstantiationException?{
String?className?=?"com.java.reflection.Person";
Class?clazz?=?(Class)?Class.forName(className);
//1.獲取Constructor對象
Constructor[]?constructors?=
(Constructor[])?Class.forName(className).getConstructors();
for?(Constructor?constructor:?constructors)?{
System.out.println(constructor);
}
Constructor?constructor?=?clazz.getConstructor(String.class,?Integer.class);
System.out.println("指定的-->"?+?constructor);
//2.調(diào)用構(gòu)造器的newInstance()方法創(chuàng)建對象
Object?obj=?constructor.newInstance("changwen",?11);
}
public com.java.reflection.Person()
public com.java.reflection.Person(java.lang.String,java.lang.Integer)
指定的-->public com.java.reflection.Person(java.lang.String,java.lang.Integer)
9、注解(Annotation)
?從?JDK5.0?開始,Java?增加了對元數(shù)據(jù)(MetaData)的支持,也就是Annotation(注釋)
?Annotation其實就是代碼里的特殊標(biāo)記,這些標(biāo)記可以在編譯,類加載,?運(yùn)行時被讀取,并執(zhí)行相應(yīng)的處理.通過使用Annotation,程序員可以在不改變原有邏輯的情況下,在源文件中嵌入一些補(bǔ)充信息.
?Annotation?可以像修飾符一樣被使用,可用于修飾包,類,構(gòu)造器,?方法,成員變量,?參數(shù),局部變量的聲明,這些信息被保存在Annotation的?“name=value”對中.
?Annotation能被用來為程序元素(類,方法,成員變量等)設(shè)置元數(shù)據(jù)
基本的?Annotation
?使用?Annotation時要在其前面增加@符號,并把該Annotation?當(dāng)成一個修飾符使用.用于修飾它支持的程序元素
?三個基本的Annotation:
–@Override:限定重寫父類方法,該注釋只能用于方法
–@Deprecated:用于表示某個程序元素(類,方法等)已過時
–@SuppressWarnings:抑制編譯器警告.
自定義?Annotation
?定義新的?Annotation類型使用@interface關(guān)鍵字
?Annotation?的成員變量在Annotation?定義中以無參數(shù)方法的形式來聲明.其方法名和返回值定義了該成員的名字和類型.
?可以在定義Annotation的成員變量時為其指定初始值,指定成員變量的初始值可使用default關(guān)鍵字
?沒有成員定義的Annotation稱為標(biāo)記;包含成員變量的Annotation稱為元數(shù)據(jù)Annotation
@Retention(RetentionPolicy.RUNTIME)?//運(yùn)行時檢驗
@Target(value?=?{ElementType.METHOD})??//作用在方法上
public?@interface?AgeValidator?{
int?min();
int?max();
}
/**
*?通過反射才能獲取注解
*/
@Test
public?void?testAnnotation()?throws?Exception?{
//這樣的方式不能使用注解
Person3?person3?=?new?Person3();
person3.setAge(10);*/
String?className?=?"com.java.reflection.Person3";
Class?clazz?=?Class.forName(className);
Object?obj?=?clazz.newInstance();
Method?method?=?clazz.getDeclaredMethod("setAge",Integer.class);
int?val?=40;
//獲取注解
Annotation?annotation?=?method.getAnnotation(AgeValidator.class);
if?(annotation?!=?null){
if?(annotation?instanceof?AgeValidator){
AgeValidator?ageValidator?=?(AgeValidator)?annotation;
if?(valageValidator.max()){
throw?new?RuntimeException("數(shù)值超出范圍");
}
}
}
method.invoke(obj,?val);
System.out.println(obj);
}
提取?Annotation信息
?JDK5.0?在?java.lang.reflect包下新增了?AnnotatedElement接口,該接口代表程序中可以接受注釋的程序元素
?當(dāng)一個?Annotation類型被定義為運(yùn)行時Annotation后,該注釋才是運(yùn)行時可見,當(dāng)?class文件被載入時保存在?class文件中的?Annotation才會被虛擬機(jī)讀取
?程序可以調(diào)用AnnotationElement對象的如下方法來訪問?Annotation信息
–獲取?Annotation實例:
?getAnnotation(Class?annotationClass)
JDK?的元Annotation
?JDK?的元Annotation?用于修飾其他Annotation?定義
?@Retention:只能用于修飾一個?Annotation定義,用于指定該?Annotation可以保留多長時間,@Rentention包含一個RetentionPolicy類型的成員變量,使用?@Rentention時必須為該?value成員變量指定值:
–RetentionPolicy.CLASS:編譯器將把注釋記錄在?class文件中.當(dāng)運(yùn)行?Java程序時,JVM?不會保留注釋.這是默認(rèn)值
–RetentionPolicy.RUNTIME:編譯器將把注釋記錄在class文件中.?當(dāng)運(yùn)行?Java?程序時, JVM?會保留注釋.?程序可以通過反射獲取該注釋
–RetentionPolicy.SOURCE:編譯器直接丟棄這種策略的注釋
?@Target:?用于修飾Annotation?定義,用于指定被修飾的?Annotation能用于修飾哪些程序元素.@Target?也包含一個名為?value的成員變量.
?@Documented:用于指定被該元?Annotation修飾的?Annotation類將被?javadoc工具提取成文檔.
?@Inherited:被它修飾的?Annotation將具有繼承性.如果某個類使用了被@Inherited?修飾的Annotation,?則其子類將自動具有該注釋
總結(jié)
以上是生活随笔為你收集整理的java反射机制详解_Java反射机制详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ubuntu java 全屏显示_jav
- 下一篇: java美元兑换,(Java实现) 美元