1. 程式人生 > 其它 >反射(reflection),通過反射建立物件

反射(reflection),通過反射建立物件

簡單嘗試:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class animal {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class name 
= Class.forName("com.zjl.test.reflection.cat"); Object o = name.newInstance(); Method who = name.getMethod("who"); who.invoke(o); } }

一、反射機制:

1.反射機制允許程式在執行期藉助於Reflection
API取得任何類的內部資訊(比如成員變數,構造器,成員方法等等),並能操作物件的屬性及方法。反射在設計模式和框架底層都會用到
2.載入完類之後,在堆中就產生了一個Class型別
的物件(一個類只有一個Class物件),這個物件包含了類的完整結構資訊。通過這個物件得到類的結構。這個物件就像一面鏡子,透過這個鏡子看到類的結構,所以,形象的稱之為:反射

應用例項:

獲取class類的四種方法:

public class GetClass_ {
    public static void main(String[] args) throws ClassNotFoundException {

        //1. Class.forName
        String classAllPath = "com.hspedu.Car"; //通過讀取配置檔案獲取
        Class<?> cls1 = Class.forName(classAllPath);
        System.out.println(cls1);

        
//2. 類名.class , 應用場景: 用於引數傳遞 Class cls2 = Car.class; System.out.println(cls2); //3. 物件.getClass(), 應用場景,有物件例項 Car car = new Car(); Class cls3 = car.getClass(); System.out.println(cls3); //4. 通過類載入器【4種】來獲取到類的Class物件 //(1)先得到類載入器 car ClassLoader classLoader = car.getClass().getClassLoader(); //(2)通過類載入器得到Class物件 Class cls4 = classLoader.loadClass(classAllPath); System.out.println(cls4); //cls1 , cls2 , cls3 , cls4 其實是同一個物件 System.out.println(cls1.hashCode()); System.out.println(cls2.hashCode()); System.out.println(cls3.hashCode()); System.out.println(cls4.hashCode()); //5. 基本資料(int, char,boolean,float,double,byte,long,short) 按如下方式得到Class類物件 Class<Integer> integerClass = int.class; Class<Character> characterClass = char.class; Class<Boolean> booleanClass = boolean.class; System.out.println(integerClass);//int //6. 基本資料型別對應的包裝類,可以通過 .TYPE 得到Class類物件 Class<Integer> type1 = Integer.TYPE; Class<Character> type2 = Character.TYPE; //其它包裝類BOOLEAN, DOUBLE, LONG,BYTE等待 System.out.println(type1); //兩個型別是相同的: System.out.println(integerClass.hashCode());//? System.out.println(type1.hashCode());//? } }

二、類載入

基本說明:

反射機制是java實現動態語言的關鍵,也就是通過反射實現類動態載入。

1.靜態載入:編譯時載入相關的類,如果沒有則報錯,依賴性太強
2動態載入:執行時載入需要的類,如果執行時不用該類,則不報錯,降低了依賴性

3.舉例說明
類載入時機:
1.當建立物件時(new) //靜態載入

2.當子類被載入時,父類也載入 //靜態載入
3.呼叫類中的靜態成員時 //靜態載入

4.通過反射 //動態載入

類載入:

類載入各階段完成任務:

載入階段:

JVM在該階段的主要目的是將位元組碼從不同的資料來源(可能是 class 檔案、也可能是jar包,甚至網路)轉化為二進位制位元組流載入到記憶體中,並生成一個代表該類的
java.lang.Class物件

連線階段-驗證:

連線階段-準備:

變數的準備簡介:

連線階段-解析:

1.虛擬機器將常量池內的符號引用替換為直接引用的過程。

初始化(Initiaalization):

1.到初始化階段,才真正開始執行類中定義的Java程式程式碼,此階段是執行
<clinit>0方法的過程。
2.<clinit>()方法是由編譯器按語句在原始檔中出現的順序,依次自動收集類中的所有
靜態變數的賦值動作和靜態程式碼塊中的語句,並進行合併。
3.虛擬機器會保證一個類的<clinit>()方法在多執行緒環境中被正確地加鎖、同步,如果
多個執行緒同時去初始化一個類,那麼只會有一個執行緒去執行這個類的<clinit>()方法,其他執行緒都需要阻塞等待,直到活動執行緒執行<clinit>()方法完畢[debug原始碼]

通過反射獲取類的結構資訊:

java.lang.class類:

1. getName:獲取全類名
2. getSimpleName:獲取簡單類名
3. getFields:獲取所有public修飾的屬性,包含本類以及父類的

4. getDeclaredFields:獲取本類中所有屬性
5. getMethods:獲取所有public修飾的方法,包含本類以及父類的

6. getDeclaredMethods:獲取本類中所有方法
7. getConstructors: 獲取所有public修飾的構造器,本類

8. getDeclaredConstructors:獲取本類中所有構造器
9. getPackage:以Package形式返回包資訊
10.getSuperClass:以Class形式返回父類資訊

11.getlnterfaces:以Class[形式返回介面資訊
12.getAnnotations:以Annotation[]形式返回註解資訊

java.lang.reflect.Field類:

1. getModifiers:以int形式返回修飾符
[說明:預設修飾符是0,public是1,private是2,protected是4,static是8,final是16], public(1) + static (8) = 9
2.getType:以Class形式返回型別
3.getName:返回屬性名

java.lang.reflect.Method類:

1.getModifiers:以int形式返回修飾符
[說明:預設修飾符是0,public 是1,private是2,protected是4,static是8,final是16]
2.getReturnType:以Class形式獲取返回型別

3.getName:返回方法名
4. getParameterTypes:以Class[返回引數型別陣列

java.lang.reflect.Constructor類:

1.getModifiers: 以int形式返回修飾符

2.getName:返回構造器名(全類名)
3.getParameterTypes:以Class[]返回引數型別陣列

三、通過反射實現類載入:

訪問構造器:

1、方法一:呼叫類中的public修飾的無參構造器

2、方式二:呼叫類中指定構造器

3. Class類相關方法
newlnstance:呼叫類中的無參構造器,獲取對應類的物件
getConstructor(Class...clazz):根據引數列表,獲取對應的public構造器物件getDecalaredConstructor(Class...clazz):根據引數列表,獲取對應的所有構造器物件
4. Constructor類相關方法

setAccessible:暴破
newlnstance(Object...obj):呼叫構造器

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class ReflectCreateInstance {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        //1、獲取User的class物件
        Class<?> aClass = Class.forName("com.zjl.test.reflection.User");
        //2、通過public的無參構造器建立例項
        Object o = aClass.newInstance();
        System.out.println(o);
        //3、通過public的有參構造器建立例項
        //得到構造器物件
        Constructor<?> constructor = aClass.getConstructor(String.class);
        //建立例項,並傳入引數
        Object who = constructor.newInstance("who");
        System.out.println(who);
        //4.通過非public的有參構造器建立例項
        //得到一個private的構造器物件
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(int.class, String.class);
        //對該構造器進行暴破,使得可以通過反射語句訪問private的構造器,方法,屬性
        declaredConstructor.setAccessible(true);
        //建立一個private物件的例項並賦值
        Object o1 = declaredConstructor.newInstance(20, "Where");
        System.out.println(o1);
    }
}
class User{
    private int age = 10;
    private String name = "hello";

    public User(){

    }

    public User(String name) {
        this.name = name;
    }

     private User(int age, String name) {
        this.age = age;
        this.name = name;
    }

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

訪問類中的屬性成員:

1.根據屬性名獲取Field物件
Field f =clazz物件.getDeclaredField(屬性名);
2.暴破:f.setAccessible(true);1/f 是Field
3.訪問
f.set(o,值);1/ o表示物件

syso(f.get(o))://o表示物件

4.注意:如果是靜態屬性,則set和get中的引數o,可以寫成null

通過反射訪問類中的方法成員:

1.根據方法名和引數列表獲取Method方法物件:Method m =
clazz.getDeclaredMethod(方法名,XX.class);
2.獲取物件:Object o=clazz.newlnstance);
3.暴破:m.setAccessible(true);
4.訪問:Object returnValue = m.invoke(o,實參列表);

5.注意:如果是靜態方法,則invoke的引數o,可以寫成null!