java學習(二)-反射機制

java反射介紹

  • 什麼是反射?
    • 可以在運行過程中,動態的獲取對象信息,操作對象,從而修改程序的狀態、行爲
    • 反射是java中非常重要的一環,是框架設計的靈魂
  • 什麼是框架:
    • 框架是一個半成品的軟件,在框架的基礎上進行開發,可以極大的簡化編碼

java Class對象

  • 在瞭解java發射之前,需要了解Class對象,這是反射的基礎:
    • Class類的實例,表示正在運行的Java應用程序的類和接口
    • 存在於java.lang中的一個包,擁有私有構造函數
    • Class對象:在加載時由Java虛擬機調用類加載器中defineClass對象構造的
  • 獲取Class對象的四種方式:
    • 方式1:Class.forName(“全類名”):動態加載字節碼進內存:多用於配置文件讀取,比如spring中的配置中讀取driverClass
    • 方式2:類名.class:獲取反射對象,多用於參數傳遞
    • 方式3:Object.getClass():多用於對象的獲取字節碼的方式
    • 方式4:內置的類型的TYPE(部分java內置的存在):Integer.TYPE.getClass();
  • 注意1:方式2和方式3返回類型是不一樣的:
類名.class Object.getClass()
class是類的屬性 getClass是類的方法
靜態解析,編譯器解析 動態加載,運行期確定
返回確定:Class<類> 返回爲向上限定,動態:Class<? Extends Object>
Class<Bird> birdClass = Bird.class;  // 返回具體類,爲靜態解析
Class<? extends Bird> aClass = bird.getClass();  // 返回向上限定類,可以返回子類信息,有多態能力
  • 注意2:同一個字節碼文件,只會被加載器加載一次,故:
    • 上面四種方式獲取的Class對象,都是同一個:可以通過hashCode()方法判斷
    • 都可以通過newInstance創建對象
class BirdTest {
    public static void main(String[] args) throws Exception{
        // 三種獲取Class的方式,並且都可以newInstance創建對象
        Bird bird = Bird.class.newInstance();
        Bird bird1 = (Bird) Class.forName("classTest.Bird").newInstance();
        Bird bird2 = new Bird().getClass().newInstance();
        System.out.println(bird);
        System.out.println(bird1);
        System.out.println(bird2);
    }
}
  • 萬物皆對象Class:
@Test
public void testAllIsClass() {
    System.out.println("對象Class:" + Object.class);
    System.out.println("接口Class:" + Comparable.class);
    System.out.println("一維數組Class:" + String[].class);
    System.out.println("二維數組Class" + int[][].class);
    System.out.println("註解Class:" + Override.class);
    System.out.println("枚舉Class:" + ElementType.class);
    System.out.println("基礎類型Class:" + int.class);
    System.out.println("void類型Class:" + void.class);
    System.out.println("Class自身的class:" + Class.class);

    System.out.println("類型一樣,維度一樣的數組,擁有同樣的Class(hascode一樣):");
    System.out.println(new int[10].hashCode());
    System.out.println(new int[50].hashCode());
}

輸出:

對象Class:class java.lang.Object
接口Class:interface java.lang.Comparable
一維數組Class:class [Ljava.lang.String;      # 一維數組,有一箇中括號
二維數組Classclass [[I             # 二維數組,有兩個中括號
註解Class:interface java.lang.Override
枚舉Class:class java.lang.annotation.ElementType
基礎類型Class:int
void類型Class:void
Class自身的class:class java.lang.Class
類型一樣,維度一樣的數組,擁有同樣的Class(hascode一樣):
2121744517
2121744517

Class對象使用

可以獲取/操作如下對象:
  • 成員變量Filed
  • 成員函數Method
  • 構造函數Constructor
  • 註解Annotation
  • 實現的接口Interfaces
  • 父類SuperClass
  • 包名Package
  • 枚舉元素EnumConstants
  • 判斷是否是接口、註解、實例匹配等等
代碼:
  • 創建測試類Anamal:
package classTest;

public interface Anamal {
    void sing();
}

  • 創建測試類Fly:
package classTest;

class Fly {
}

  • 創建測試註解:
package classTest;


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
// 注意,發射獲取的註解,需要爲RUNTIME.  因爲SOURCE和CLASS無法在運行階段被獲取
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "";
    String user() default "lanyyyy";
}

  • 創建測試類Bird:
package classTest;

@MyAnnotation(value = "我的註解")
public class Bird extends Fly implements Anamal {
    public String name;
    private int age;

    enum xEnums {
        a,
        b,
        c
    }

    public Bird(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Bird() {
    }

    private Bird(int age) {
        this.age = age;
    }

    private void fly() {
        System.out.println("I can fly...");
    }

    @Override
    public void sing() {
        System.out.println("I can sing...");
    }

    @MyAnnotation
    protected void sleep(int a) {
        System.out.println("I am sleep " + a + " s");
    }

    @Override
    @MyAnnotation
    public String toString() {
        return "Bird{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

}
  • Field操作:
    @Test
    public void test1Field() throws Exception {
        // 1. 獲取成員變量
        System.out.println("################# 1. 獲取成員變量 #################");
        Field name = birdClass.getField("name");    // 獲取公有變量
        Field[] fields = birdClass.getFields();         // 獲取所有公有變量
        Field[] declaredFields = birdClass.getDeclaredFields(); // 獲取所有公有變量
        Field age = birdClass.getDeclaredField("age");  // 獲取指定名稱的變量
        System.out.println("getFiled: " + name);
        System.out.println("getDeclaredField" + age);
        for (Field field : fields) {
            System.out.println("getFileds: " + field);
        }
        for (Field declaredField : declaredFields) {
            System.out.println("getDeclaredFields: " + declaredField);
        }
        System.out.println("setAccessible是啓動、禁用訪問安全檢查的開關");
        System.out.println("暴力反射修改變量前:" + bird);
        age.setAccessible(true);
        age.setInt(bird, 22);
        System.out.println("暴力反射修改變量後:" + bird);

    }

輸出:

################# 1. 獲取成員變量 #################
getFiled: public java.lang.String classTest.Bird.name
getDeclaredFieldprivate int classTest.Bird.age
getFileds: public java.lang.String classTest.Bird.name
getDeclaredFields: public java.lang.String classTest.Bird.name
getDeclaredFields: private int classTest.Bird.age
setAccessible是啓動、禁用訪問安全檢查的開關
暴力反射修改變量前:Bird{name='null', age=0}
暴力反射修改變量後:Bird{name='null', age=22}
  • 操作方法:
    @Test
    public void test2Method() throws Exception {
        // 2. 獲取成員方法
        System.out.println("################# 2. 獲取成員方法 #################");
        // 注意區別:
        //  getMethods:當前類公共方法 + 父類公共方法
        //  getDeclaredMethods:當前類所有方法
        Method sing = birdClass.getMethod("sing");  // 獲取sing方法(必須是公共的)
        Method[] methods = birdClass.getMethods();
        Method say = birdClass.getDeclaredMethod("fly");
        Method[] declaredMethods = birdClass.getDeclaredMethods();
        System.out.println("getMethod: " + sing);
        System.out.println("getDeclaredMethod: " + say);
        for (Method method : methods) {
            System.out.println("getMethods: " + method);
        }
        for (Method declaredMethod : declaredMethods) {
            System.out.println("getDeclaredMethods: " + declaredMethod);
        }
        System.out.println("調用一下私有方法-需要暴力反射: ");
        Method fly = birdClass.getDeclaredMethod("fly");
        fly.setAccessible(true);
        fly.invoke(bird);
        System.out.println("調用一下公有方法-帶參: ");
        for (Method declaredMethod : declaredMethods) {
            if (declaredMethod.getName().equals("sleep")) {
                declaredMethod.invoke(bird, 1111);
            }
        }
    }

輸出:

################# 2. 獲取成員方法 #################
getMethod: public void classTest.Bird.sing()
getDeclaredMethod: private void classTest.Bird.fly()
getMethods: public void classTest.Bird.sing()
getMethods: public java.lang.String classTest.Bird.toString()
getMethods: public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
getMethods: public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
getMethods: public final void java.lang.Object.wait() throws java.lang.InterruptedException
getMethods: public boolean java.lang.Object.equals(java.lang.Object)
getMethods: public native int java.lang.Object.hashCode()
getMethods: public final native java.lang.Class java.lang.Object.getClass()
getMethods: public final native void java.lang.Object.notify()
getMethods: public final native void java.lang.Object.notifyAll()
getDeclaredMethods: public void classTest.Bird.sing()
getDeclaredMethods: private void classTest.Bird.fly()
getDeclaredMethods: public java.lang.String classTest.Bird.toString()
getDeclaredMethods: protected void classTest.Bird.sleep(int)
調用一下私有方法-需要暴力反射: 
I can fly...
調用一下公有方法-帶參: 
I am sleep 1111 s
  • 操作構造函數
    @Test
    public void test3Constructor() throws Exception {
        System.out.println("################# 3. 獲取構造函數 #################");
        Constructor<?>[] constructors = birdClass.getConstructors();    // 獲取公共構造函數
        Constructor<?>[] declaredConstructors = birdClass.getDeclaredConstructors();
        System.out.println("getConstructors 公共構造函數");
        for (Constructor<?> constructor : constructors) {
            System.out.println("getConstructors: " + constructor.toString());
        }
        System.out.println("getDeclaredConstructors  所有構造函數");
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println("getDeclaredConstructors: " + declaredConstructor.toString());
            System.out.println("構造函數參數個數:" + declaredConstructor.getParameterCount());
        }

        // 利用反射創建對象
        System.out.println("直接new:" + new Bird());
        // newInstance默認調用的是無參public構造函數,如果構造爲私有,會報錯異常
        System.out.println("newInstance(默認調用空構造函數,且爲public):" + Bird.class.newInstance());
        // 獲取構造函數,測試:獲取私有的、int入參的構造函數
        Constructor<Bird> declaredConstructor = birdClass.getDeclaredConstructor(int.class);
        declaredConstructor.setAccessible(true);  // 需要暴力反射
        Bird birdRef = declaredConstructor.newInstance(100);
        System.out.println("反射獲取構造函數: " + birdRef);
    }

輸出:

################# 3. 獲取構造函數 #################
getConstructors 公共構造函數
getConstructors: public classTest.Bird(java.lang.String,int)
getConstructors: public classTest.Bird()
getDeclaredConstructors  所有構造函數
getDeclaredConstructors: public classTest.Bird(java.lang.String,int)
構造函數參數個數:2
getDeclaredConstructors: private classTest.Bird(int)
構造函數參數個數:1
getDeclaredConstructors: public classTest.Bird()
構造函數參數個數:0
直接new:Bird{name='null', age=0}
newInstance(默認調用空構造函數,且爲public):Bird{name='null', age=0}
反射獲取構造函數: Bird{name='null', age=100}
  • 操作註解:
    /**
     * 測試獲取註解
     */
    @Test
    public void test4Annotation() {
        System.out.println("################# 4. 獲取所有註解 #################");
        System.out.println("---------獲取類上註解:");
        // 注意自定義註解的範圍
        Annotation[] annotations = birdClass.getDeclaredAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
        System.out.println("---------獲取指定註解:");
        MyAnnotation declaredAnnotation = birdClass.getDeclaredAnnotation(MyAnnotation.class);
        System.out.println("指定註解value值:" + declaredAnnotation.value());
        System.out.println("指定註解user值:" + declaredAnnotation.user());

        System.out.println("---------獲取方法上的註解: ");
        Method[] declaredMethods = birdClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            Annotation[] annotations1 = declaredMethod.getDeclaredAnnotations();
            if (annotations1 != null && annotations.length != 0) {
                System.out.println(declaredMethod.getName() + "上的註解爲:" + Arrays.toString(annotations1));
            }
        }
    }

輸出:

################# 4. 獲取所有註解 #################
---------獲取類上註解:
@classTest.MyAnnotation(user=lanyyyy, value=我的註解)
---------獲取指定註解:
指定註解value值:我的註解
指定註解user值:lanyyyy
---------獲取方法上的註解: 
sing上的註解爲:[]
fly上的註解爲:[]
toString上的註解爲:[@classTest.MyAnnotation(user=lanyyyy, value=)]
sleep上的註解爲:[@classTest.MyAnnotation(user=lanyyyy, value=)]
  • 操作接口:
    /**
     * 測試父接口類
     */
    @Test
    public void test5Interfaces() {
        System.out.println("################# 5. 測試父接口類 #################");
        Class<?>[] interfaces = birdClass.getInterfaces();
        System.out.println("父接口爲:" + Arrays.toString(interfaces));
    }

輸出:

################# 5. 測試父接口類 #################
父接口爲:[interface classTest.Anamal]
  • 操作直接父類
    /**
     * 獲取直接父類
     */
    @Test
    public void test6SuperClass() {
        System.out.println("################# 6. 獲取直接父類 #################");
        Class<? super Bird> superclass = birdClass.getSuperclass();
        System.out.println("直接父類爲:" + superclass);
    }

輸出:

```################# 6. 獲取直接父類 #################
直接父類爲:class classTest.Fly
  • 操作包
    /**
     * 測試包
     */
    @Test
    public void test7Package() {
        System.out.println("################# 7. 測試包名 #################");
        Package aPackage = birdClass.getPackage();
        System.out.println("包名爲:" + aPackage);
    }

輸出:

################# 7. 測試包名 #################
包名爲:package classTest
  • 操作枚舉
    /**
     * 獲取枚舉
     */
    @Test
    public void test8Enum() {
        System.out.println("################# 8. 獲取枚舉:必須是枚舉的class才能調用 #################");
        Bird.xEnums[] enumConstants1 = Bird.xEnums.class.getEnumConstants();
        System.out.println("枚舉裏的變量爲:" + Arrays.toString(enumConstants1));
        System.out.println("Bird.xEnums.class 是否是枚舉? " + Bird.xEnums.class.isEnum());
        System.out.println("Bird.class 是否是枚舉? " + Bird.class.isEnum());
    }

輸出:

################# 8. 獲取枚舉:必須是枚舉的class才能調用 #################
枚舉裏的變量爲:[a, b, c]
Bird.xEnums.class 是否是枚舉? true
Bird.class 是否是枚舉? false
  • 其它操作
    /**
     * 判斷是否是接口、註解、實例匹配等等
     */
    @Test
    public void test9Self() {
        System.out.println("################# 9. 判斷是否是接口、註解、實例匹配等等 #################");
        System.out.println("birdClass是否是接口? " + birdClass.isInterface());
        System.out.println("birdClass是否是數組? " + birdClass.isArray());
        System.out.println("birdClass是否是註解? " + birdClass.isAnnotation());
        System.out.println("birdClass是否是基本類型?(int,float...)? " + birdClass.isPrimitive());
        System.out.println("MyAnnotation是否是註解? " + MyAnnotation.class.isAnnotation());
        System.out.println("bird實例是否是Bird.class? " + birdClass.isInstance(bird));
        System.out.println("bird實例是否是Bird.class? " + bird.getClass().isInstance(bird));
        System.out.println("bird的類加載器爲:" + bird.getClass().getClassLoader());
        System.out.println(bird.getClass().getClasses());
        System.out.println("bird的返回底層類的立即封閉類:" + bird.getClass().getEnclosingClass());
        System.out.println("bird的返回底層類的立即封閉類:" + bird.getClass().getEnclosingConstructor());
        System.out.println("bird的返回底層類的立即封閉類:" + bird.getClass().getEnclosingMethod());
    }

輸出:

################# 9. 判斷是否是接口、註解、實例匹配等等 #################
birdClass是否是接口? false
birdClass是否是數組? false
birdClass是否是註解? false
birdClass是否是基本類型?(int,float...)false
MyAnnotation是否是註解? true
bird實例是否是Bird.class? true
bird實例是否是Bird.class? true
bird的類加載器爲:sun.misc.Launcher$AppClassLoader@18b4aac2
[Ljava.lang.Class;@7e774085
bird的返回底層類的立即封閉類:null
bird的返回底層類的立即封閉類:null
bird的返回底層類的立即封閉類:null

反射操作泛型:

  • 【未完待續】

反射問題:

  • 反射的執行效率:
    • 跟new方式的對象調用,反射調用效率有明顯降低。故,非特殊時,請不要使用反射;
    • 反射經常被調用的時候,可以將setAccessible置爲true,可提升效率
  • 反射的跟JVM加載有強相關,這裏暫時不對JVM做贅述

參考

  • 狂神說java
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章