概述
當你準備招待客人時,如果你知道你的客人是誰,那麼直接去迎合他的喜好就好了。
但如果你還並不清楚具體客人是誰,那你需要注意了,你所做的準備,要適合所有的客人。
類是對對象的抽象,類的屬性、方法、繼承關係,其對象都同樣擁有。同理,在很多場景下,我們需要對不同的類做一定的歸類、合併和統一處理。這樣,我們就需要對不同的類做一定的抽象,才能達到我們的目的。也就是說,在編譯階段,我們並不知道具體的類是誰,而真正的類是動態賦予的,由此,引出反射系列。
API
反射相關的類主要在java.lang 和 java.lang.reflect包下。
其中,java.lang下主要有以下兩個。
1、Class
類是對象的抽象,Class就是所有類的抽象。
也可以說,Class表示類的類類型。
public final class Class<T> implements java.io.Serializable,
GenericDeclaration,
Type,
AnnotatedElement {
}
私有構造方法,僅JVM能創建Class對象。
private Class(ClassLoader loader) {
// Initialize final field for classLoader. The initialization value of non-null
// prevents future JIT optimizations from assuming this final field is null.
classLoader = loader;
}
重要方法:
a.創建類型的方法
newInstance()
創建類的新的實例-通過類類型可以創建類的實例對象(需要有無參構造方法)
b.查詢類型的方法
forName()
根據給定的string 類名、類加載器(可選)返回相對應的Class對象
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
public static Class<?> forName(String name, boolean initialize,
ClassLoader loader)
throws ClassNotFoundException
{
Class<?> caller = null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
// Reflective call to get caller class is only needed if a security manager
// is present. Avoid the overhead of making this call otherwise.
caller = Reflection.getCallerClass();
if (sun.misc.VM.isSystemDomainLoader(loader)) {
ClassLoader ccl = ClassLoader.getClassLoader(caller);
if (!sun.misc.VM.isSystemDomainLoader(ccl)) {
sm.checkPermission(
SecurityConstants.GET_CLASSLOADER_PERMISSION);
}
}
}
return forName0(name, initialize, loader, caller);
}
/** Called after security check for system loader access checks have been made. */
private static native Class<?> forName0(String name, boolean initialize,
ClassLoader loader,
Class<?> caller)
throws ClassNotFoundException;
可以看到,forName()重載方法,最終都調用了native方法forName0()。其中,forName(String className)方法中設置initialize爲true。
注意:
a.指定的類加載器是用於加載類或者接口的。
b.若該方法中,未給出指定的加載器,會通過bootstrap類加載來加載相應的類。
c.當參數中的initialize爲true,並且該類之前未被初始化過,纔會對類進行初始化。
補充:其他獲取Class的方式
類名爲A
- A.class (class是類的隱含的靜態成員變量)
- new A().getClass() (Object的方法getClass)
涉及到類加載機制的部分,下一篇詳細說明。
其他查詢方法:
getName()
getClassLoader()
getField()
getMethod()
getConstructors()
2、ClassLoader
public abstract class ClassLoader {
}
默認類加載器
先看幾個簡單的例子:
ps:註釋爲打印結果,其中,由於Bootstrap ClassLoader爲C++語言編寫,在java下無法打印,因此得到的打印結果爲null。
System.out.println(ClassTest.class.getClassLoader());//AppClassLoader
System.out.println(ClassTest.class.getClassLoader().getParent());//ExtClassLoader加載器
System.out.println(ClassTest.class.getClassLoader().getParent().getParent());//Bootstrap加載器,打印結果爲null
由上可知,類加載器有三種默認類型:Bootstrap ClassLoader、ExtClassLoader、AppClassLoader。那這三個有什麼分別呢?
- Bootstrap ClassLoader:Java中最頂層的類加載器,負責加載JDK中的核心類庫,如:rt.jar、resources.jar、charsets.jar等。
- ExtClassLoader:主要負責加載Java的擴展類庫,負責加載JDK中的擴展類庫。其父類加載器爲Bootstrap ClassLoader。
- AppClassLoader:應用類加載器(系統類加載器),負責加載classpath路徑下的jar包。其父類加載器爲ExtClassLoader。
除了這三個默認類加載器,當然用戶可以自定義類加載器,但自定義類加載器的父類是AppClassLoader。關係如下:
那麼一個類到底是如何加載的呢?這幾個默認類加載器之間是如何工作的呢?
雙親委派
首先,明確下類加載親在jvm內存模型中的位置。
什麼是雙親委派?
若一個類加載器要加載一個類,先看其父類是否能加載,以此類推,直到最頂層的Bootstrap ClassLoader,若頂層類加載器可以加載即進行加載,若無法加載,纔有最初的類加載器進行加載。
通俗的說,兒子接到活之後,一律不自己做,先給了爸爸,爸爸給了爺爺,爺爺如果可以做就做,不能做,再由兒子一層一層檢查是否可以做。
爲什麼要用這種實現方式?
-
安全
若用戶自定義了核心或擴展類庫中的同名類,則不會被成功加載,因爲這些系統默認類已由默認類加載器加載。 -
避免重複加載
類加載器之間有了層次優先級關係,就避免了類的重複加載。
java.lang.reflect包下的主要類有:
1、Field
該類提供了動態訪問、設置類或接口的字段功能。
public final
class Field extends AccessibleObject implements Member {
}
2、Method
3、Constructor
4、Array
小結
本篇介紹了反射中的常用的相關類,Class和ClassLoader,介紹了Class抽象的思維,以及類加載機制的“雙親委派”機制。下篇我們重點介紹本文中說明的reflect包的代理。