JDK源碼(十四):Class

JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意方法和屬性;這種動態獲取信息以及動態調用對象方法的功能稱爲java語言的反射機制。

在日常開發過程中,會遇到某個類的某個成員變量、方法或是屬性是私有的或是隻對系統應用開放,這時候就可以利用Java的反射機制通過反射來獲取所需的私有成員或是方法。比如JDBC鏈接:

Class.forName("com.mysql.cj.jdbc.Driver");

反射機制的相關類

Class代表類的實例,表示正在運行的Java應用程序中的類和接口。Class沒有公共構造函數。相反,Class對象由Java虛擬機在加載類時自動構造,並通過調用類加載器中的defineClass方法來構造。

類名

public final class Class<T> 
                  implements java.io.Serializable,
                  GenericDeclaration,
                  Type,
                  AnnotatedElement

在Class中有個靜態代碼塊,此native方法表示向JVM註冊本地方法。

private static native void registerNatives();
static {
    registerNatives();
}

forName(String className)

@CallerSensitive
public static Class<?> forName(String className)
                throws ClassNotFoundException {
	//返回調用這個方法的類對象
    Class<?> caller = Reflection.getCallerClass();
    return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}

返回給定字符串名稱的類或接口關聯的類對象,@CallerSensitive爲了防止通過構造雙重反射來提升權限

T newInstance()

    @CallerSensitive
    public T newInstance()
        throws InstantiationException, IllegalAccessException
    {
        if (System.getSecurityManager() != null) {
			//檢查是否允許訪問成員。如果拒絕訪問,則拋出SecurityException
            checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false);
        }

        // Constructor lookup
        if (cachedConstructor == null) {
            if (this == Class.class) {
                throw new IllegalAccessException(
                    "Can not call newInstance() on the Class for java.lang.Class"
                );
            }
            try {
                Class<?>[] empty = {};
				//先獲得Constructor數組,拿到所有的構造,包括非public的
                final Constructor<T> c = getConstructor0(empty, Member.DECLARED);
                // 禁用構造函數的可訪問性檢查
                java.security.AccessController.doPrivileged(
                    new java.security.PrivilegedAction<Void>() {
                        public Void run() {
                                c.setAccessible(true);
                                return null;
                            }
                        });
                cachedConstructor = c;
            } catch (NoSuchMethodException e) {
                throw (InstantiationException)
                    new InstantiationException(getName()).initCause(e);
            }
        }
        Constructor<T> tmpConstructor = cachedConstructor;
        int modifiers = tmpConstructor.getModifiers();
		//檢查是否是public,如果是public,返回true
        if (!Reflection.quickCheckMemberAccess(this, modifiers)) {
            Class<?> caller = Reflection.getCallerClass();
            if (newInstanceCallerCache != caller) {
                Reflection.ensureMemberAccess(caller, this, null, modifiers);
                newInstanceCallerCache = caller;
            }
        }
        // Run constructor
        try {
			//創建並初始化具有指定初始化參數的構造函數的聲明類的新實例
            return tmpConstructor.newInstance((Object[])null);
        } catch (InvocationTargetException e) {
            Unsafe.getUnsafe().throwException(e.getTargetException());
            // Not reached
            return null;
        }
    }

栗子:

public class Student implements Serializable {

    private static final long serialVersionUID = -1L;
    private String id;

    private String name;

    public Integer age;

    public Student(){}

    public Student(String id, String name) {
        this.id = id;
        this.name = name;
    }

    private int sum(int i, int j){
        return i + j;
    }

    public Student getStudent(int id){
        return this;
    }

    public Student getStudent(String name){
        return new Student();
    }

    public String getInfo(){
        return id + " " + name;
    }
}

Student student = Student.class.newInstance();

但是在jdk9以上,這個方法被棄用了,而使用下面的方法:

Student student = 
    Student.class.getDeclaredConstructor().newInstance()

getMethods()

    @CallerSensitive
    public Method[] getMethods() throws SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        return copyMethods(privateGetPublicMethods());
    }

返回一個數組,該數組包含Method對象,這些對象包含該class對象表示的類或接口的所有公共方法,包括由該類或接口聲明的方法以及從父類和父接口繼承的方法。裏面調用了privateGetPublicMethods方法

private Method[] privateGetPublicMethods() {
        checkInitted();
        Method[] res;
        ReflectionData<T> rd = reflectionData();
        if (rd != null) {
            res = rd.publicMethods;
            if (res != null) return res;
        }
        //沒有可用的緩存值;遞歸計算值。從獲取公共聲明的方法開始
        MethodArray methods = new MethodArray();
        {
            Method[] tmp = privateGetDeclaredMethods(true);
            methods.addAll(tmp);
        }
		// 首先檢查父接口,這樣我們可以更容易地過濾掉最後從父類繼承的具體實現。
        MethodArray inheritedMethods = new MethodArray();
        for (Class<?> i : getInterfaces()) {
            inheritedMethods.addInterfaceMethods(i.privateGetPublicMethods());
        }
        if (!isInterface()) { //如果不是接口
            Class<?> c = getSuperclass();//得到父類
            if (c != null) {
                MethodArray supers = new MethodArray();
				//父類中的public方法
                supers.addAll(c.privateGetPublicMethods());
                // 過濾掉任何接口方法的具體實現
                for (int i = 0; i < supers.length(); i++) {
                    Method m = supers.get(i);
                    if (m != null &&
                            !Modifier.isAbstract(m.getModifiers()) &&
                            !m.isDefault()) {
                        inheritedMethods.removeByNameAndDescriptor(m);
                    }
                }
                // 在父接口之前插入父類的繼承方法以滿足getMethod的搜索順序
                supers.addAll(inheritedMethods);
                inheritedMethods = supers;
            }
        }
        // 從繼承的方法中篩選出所有本地方法
        for (int i = 0; i < methods.length(); i++) {
            Method m = methods.get(i);
            inheritedMethods.removeByNameAndDescriptor(m);
        }
        methods.addAllIfNotPresent(inheritedMethods);
        methods.removeLessSpecifics();
        methods.compactAndTrim();
        res = methods.getArray();
        if (rd != null) {
            rd.publicMethods = res;
        }
        return res;
    }

與getMethods()方法對應的還有一個getDeclaredMethods(),這個方法返回一個Method對象數組,其中包含該Class對象表示的類或接口的所有已聲明方法,包括public、protected、default(package)訪問和private方法,但不包括繼承的方法。

    @CallerSensitive
    public Method[] getDeclaredMethods() throws SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        return copyMethods(privateGetDeclaredMethods(false));
    }
    private Method[] privateGetDeclaredMethods(boolean publicOnly) {
        checkInitted();
        Method[] res;
        ReflectionData<T> rd = reflectionData();
        if (rd != null) {
            res = publicOnly ? rd.declaredPublicMethods : rd.declaredMethods;
            if (res != null) return res;
        }
		//getDeclaredMethods0底層方法獲取本類方法
        res = Reflection.filterMethods(this, getDeclaredMethods0(publicOnly));
        if (rd != null) {
            if (publicOnly) {
                rd.declaredPublicMethods = res;
            } else {
                rd.declaredMethods = res;
            }
        }
        return res;
    }

getFields() 

@CallerSensitive
    public Field[] getFields() throws SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        return copyFields(privateGetPublicFields(null));
    }

返回一個包含Field對象的數組,該數組包含Class對象表示的類或接口的所有可訪問公共字段。包括父類和父接口的公共字段。調用的privateGetPublicFields方法如下:

private Field[] privateGetPublicFields(Set<Class<?>> traversedInterfaces) {
        checkInitted();
        Field[] res;
        ReflectionData<T> rd = reflectionData();
        if (rd != null) {
            res = rd.publicFields;
            if (res != null) return res;
        }
        List<Field> fields = new ArrayList<>();
        if (traversedInterfaces == null) {
            traversedInterfaces = new HashSet<>();
        }
        // 類本身的字段
        Field[] tmp = privateGetDeclaredFields(true);
        addAll(fields, tmp);
        // 遍歷父接口
        for (Class<?> c : getInterfaces()) {
            if (!traversedInterfaces.contains(c)) {
                traversedInterfaces.add(c);
                addAll(fields, c.privateGetPublicFields(traversedInterfaces));
            }
        }
        // 父類
        if (!isInterface()) {
            Class<?> c = getSuperclass();
            if (c != null) {
                addAll(fields, c.privateGetPublicFields(traversedInterfaces));
            }
        }

        res = new Field[fields.size()];
        fields.toArray(res);
        if (rd != null) {
            rd.publicFields = res;
        }
        return res;
    }

getFields()也有一個對應的方法getDeclaredFields(),這個方法返回Field對象的數組,該數組包含該Class對象表示的類或接口聲明的所有字段。這包括公共、受保護、默認(包)訪問和私有字段,但不包括繼承字段。

    @CallerSensitive
    public Field[] getDeclaredFields() throws SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        return copyFields(privateGetDeclaredFields(false));
    }
   private Field[] privateGetDeclaredFields(boolean publicOnly) {
        checkInitted();
        Field[] res;
        ReflectionData<T> rd = reflectionData();
        if (rd != null) {
            res = publicOnly ? rd.declaredPublicFields : rd.declaredFields;
            if (res != null) return res;
        }
        // 調用底層方法
        res = Reflection.filterFields(this, getDeclaredFields0(publicOnly));
        if (rd != null) {
            if (publicOnly) {
                rd.declaredPublicFields = res;
            } else {
                rd.declaredFields = res;
            }
        }
        return res;
    }

getResourceAsStream(String name)

public InputStream getResourceAsStream(String name) {
		//如果名稱不是絕對的,則添加包名稱前綴如果名稱是絕對的,則刪除前導“/”
        name = resolveName(name);
        ClassLoader cl = getClassLoader0();
        if (cl==null) {
            // A system class.
            return ClassLoader.getSystemResourceAsStream(name);
        }
        return cl.getResourceAsStream(name);
    }

此方法查找具有給定名稱的資源。搜索與給定類關聯的資源的規則是通過定義類的ClassLoader來實現的。此方法委託給此對象的類加載器。如果此對象是由引導類加載器加載的,則方法將委託給getSystemResourceAsStream。如果name以'/'開頭,則資源的絕對名稱是'/'後面的部分。如果名稱不是絕對的,則添加包名稱前綴,如果名稱是絕對的,則刪除前導“/”。


InputStream resourceAsStream = 
                Student.class.getResourceAsStream("application.xml");

更多精彩內容請關注微信公衆號:

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