實現文字橫向滑動
阿新 • • 發佈:2020-11-16
目錄
1. 什麼是反射?
Java的反射是指在執行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意方法和屬性;這種動態獲取資訊以及動態呼叫物件方法的功能稱為Java語言的反射機制。
2. 反射的用途
我們知道反射機制允許程式在執行時取得任何一個已知名稱的class的內部資訊,包括包括其modifiers(修飾符)、fields(屬性)、methods(方法)等,並可於執行時改變fields內容或呼叫methods。
那麼我們便可以更靈活的編寫程式碼,程式碼可以在執行時裝配,無需在元件之間進行原始碼連結,降低程式碼的耦合度;還有動態代理的實現等等;但是需要注意的是反射使用不當會造成很高的資源消耗!
3. 反射的API介紹
我們以一個Person類為例:
public class Person<T> { private int age=18; public String name="tom"; public Person() { } public Person(int age, String name) { this.age = age; this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void show(){ System.out.println("show run!"); } private void say(){ System.out.println("say run!"); } }
要對該類進行反射,我們先要獲取這個類對應的位元組碼檔案物件。
3.1 獲取位元組碼檔案物件的三種方式
//1、通過物件呼叫 getClass() 方法來獲取,通常應用在:比如你傳過來一個 Object // 型別的物件,而我不知道你具體是什麼類,用這種方法 Person p1 = new Person(); Class c1 = p1.getClass(); //2、直接通過 類名.class 的方式得到,該方法最為安全可靠,程式效能更高 // 這說明任何一個類都有一個隱含的靜態成員變數 class Class c2 = Person.class; //3、通過 Class 物件的 forName() 靜態方法來獲取,用的最多,引數為類的全名 // 但可能丟擲 ClassNotFoundException 異常 Class c3 = Class.forName("com.lfx.test.Person");
注意:一個類在虛擬機器中只會有一個 Class 例項,即我們對上面獲取的 c1,c2,c3進行 equals 比較,發現都是true
查閱API之後,我們發現位元組碼檔案物件(即Class物件)有很多獲取類資訊的方法:
getName()
:獲得類的完整名字。getFields()
:獲得類的public型別的屬性。getDeclaredFields()
:獲得類的所有屬性,包括private宣告的。getMethods()
:獲得類的public型別的方法。getDeclaredMethods()
:獲得類的所有方法,包括private宣告的getMethod(String name, Class[] parameterTypes)
:獲得類的特定方法,name引數指定方法的名字,parameterTypes 引數指定方法的引數型別。getConstructors()
:獲得類的public型別的構造方法。getConstructor(Class[] parameterTypes)
:獲得類的特定構造方法,parameterTypes 引數指定構造方法的引數型別。newInstance()
:通過類的不帶引數的構造方法建立這個類的一個物件。
3.2 獲取物件的屬性、方法、構造器
獲取到了Class物件後,我們可以呼叫Class的方法,獲取物件的屬性、方法、構造器。
示例程式碼:
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException {
//通過第二種方式獲取Class物件
Class c2 = Person.class;
//獲得類完整的名字
String className = c2.getName();
System.out.println(className);
//獲得類的public型別的屬性。
Field[] fields = c2.getFields();
for(Field field : fields){
System.out.println(field.getName());
}
//獲得類的所有屬性。包括私有的
Field [] allFields = c2.getDeclaredFields();
for(Field field : allFields){
System.out.println(field.getName());
}
//獲得類的public型別的方法。這裡包括 Object 類的一些方法
Method[] methods = c2.getMethods();
for(Method method : methods){
System.out.println(method.getName());//wait equls toString hashCode等
}
//獲得類的所有方法。
Method [] allMethods = c2.getDeclaredMethods();
for(Method method : allMethods){
System.out.println(method.getName());//show say
}
//獲得指定的屬性
Field f1 = c2.getField("name");
System.out.println(f1);
//獲得指定的私有屬性
Field f2 = c2.getDeclaredField("age");
//啟用和禁用訪問安全檢查的開關,值為 true,則表示反射的物件在使用時應該取消 java 語言的訪問檢查;反之不取消
f2.setAccessible(true);
System.out.println(f2);
//建立這個類的一個物件
Object p2 = c2.newInstance();
//將 p2 物件的 f2 屬性賦值為 20,f2 屬性即為 私有屬性 age
f2.set(p2,20);
//使用反射機制可以打破封裝性,導致了java物件的屬性不安全。
System.out.println(f2.get(p2)); //20
//獲取構造方法
Constructor[] constructors = c2.getConstructors();
for(Constructor constructor : constructors){
System.out.println(constructor.toString());
}
}
3.3 獲取泛型型別
我們定義一個Person的子類來演示如何獲取泛型型別:
public class Student extends Person<String> {
public static void main(String[] args) {
Student st=new Student();
Class clazz=st.getClass();
//getSuperclass()獲得該類的父類:class com.lfx.test.Person
System.out.println(clazz.getSuperclass());
//getGenericSuperclass()獲得帶有泛型的父類
//Type是 Java 程式語言中所有型別的公共高階介面。它們包括原始型別、引數化型別、陣列型別、型別變數和基本型別。
Type type=clazz.getGenericSuperclass();
//輸出type: com.lfx.test.Person<java.lang.String>
System.out.println(type);
//ParameterizedType是Type的子類,表示引數化型別,即泛型
ParameterizedType p=(ParameterizedType)type;
//輸出p: com.lfx.test.Person<java.lang.String>
System.out.println(p);
//getActualTypeArguments獲取引數化型別的陣列,泛型可能有多個,例如Map<String,Object>
Class c=(Class) p.getActualTypeArguments()[0];
//輸出泛型型別:class java.lang.String
System.out.println(c);
}
}
3.4 獲取註解
我們先自定義一些執行期的註解:
public class MyAnnotation {
/**
* 註解類
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyClassAnnotation {
String value();
}
/**
* 構造方法註解
*/
@Target(ElementType.CONSTRUCTOR)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyConstructorAnnotation {
String value();
}
/**
* 方法註解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyMethodAnnotation {
String value();
}
/**
* 欄位註解
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyFieldAnnotation {
String value();
}
/**
* 可以同時應用到類和方法上
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface MyClassAndMethodAnnotation {
String value();
}
}
我們對Person類新增自定義的註解,用於測試反射是如何獲取註解的:
@MyClassAnnotation("測試獲取類註解")
@MyClassAndMethodAnnotation("測試獲取多個類註解")
public class Person<T> {
@MyFieldAnnotation("測試獲取私有屬性註解")
private int age=18;
@MyFieldAnnotation("測試獲取公共屬性註解")
public String name="tom";
@MyConstructorAnnotation("測試獲取空參構造器註解")
public Person() {
}
@MyConstructorAnnotation("測試獲取有參構造器註解")
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@MyMethodAnnotation("測試獲取公共方法註解")
public void show(){
System.out.println("show run!");
}
@MyMethodAnnotation("測試獲取私有方法註解")
private void say(){
System.out.println("say run!");
}
}
反射獲取註解程式碼:
public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException {
//通過第二種方式獲取Class物件
Class c2 = Person.class;
//獲得類完整的名字
String className = c2.getName();
System.out.println(className);
//獲取類上指定名稱的註解
MyClassAnnotation myClassAnnotation = (MyClassAnnotation) c2.getAnnotation(MyClassAnnotation.class);
//輸出類指定註解的value值:
System.out.println(myClassAnnotation.value());
//獲取類上所有註解
Annotation[] annotations = c2.getAnnotations();
//輸出類上所有註解:
for(Annotation annotation : annotations){
System.out.println(annotation.toString());
}
//獲取所有構造方法的指定註解:
Constructor[] constructors = c2.getConstructors();
for(Constructor constructor : constructors){
MyConstructorAnnotation myConstructorAnnotation = (MyConstructorAnnotation) constructor.getAnnotation(MyConstructorAnnotation.class);
//輸出所有構造方法指定註解的value值
System.out.println(myConstructorAnnotation.value());
}
//獲得公有欄位指定名稱的註解。
Field field = c2.getField("name");
MyFieldAnnotation fieldAnnotation = field.getAnnotation(MyFieldAnnotation.class);
//輸出公有欄位指定名稱註解的value值
System.out.println(fieldAnnotation.value());
//獲得私有欄位指定名稱的註解。
Field privateField = c2.getDeclaredField("age");
MyFieldAnnotation privateFieldAnnotation = privateField.getAnnotation(MyFieldAnnotation.class);
//輸出私有欄位指定名稱註解的value值
System.out.println(privateFieldAnnotation.value());
//獲得公有方法指定名稱的註解
Method method = c2.getMethod("show");
MyMethodAnnotation myMethodAnnotation = method.getAnnotation(MyMethodAnnotation.class);
//輸出公有方法指定名稱的註解的value
System.out.println(myMethodAnnotation.value());
//獲得私有方法指定名稱的註解
Method privateMethod = c2.getDeclaredMethod("say");
MyMethodAnnotation myPrivateMethodAnnotation =privateMethod.getAnnotation(MyMethodAnnotation.class);
//輸出公有方法指定名稱的註解的value
System.out.println(myPrivateMethodAnnotation.value());
}