Java 反射教程

Java 反射機(jī)制教程

Java 反射機(jī)制可以讓我們?cè)诰幾g期(Compile Time)之外的運(yùn)行期(Runtime)檢查類(lèi),接口埃碱,變量以及方法的信息划咐。反射還可以讓我們?cè)谶\(yùn)行期實(shí)例化對(duì)象,調(diào)用方法宠漩,通過(guò)調(diào)用 get/set 方法獲取變量的值柜蜈。

具體的理論細(xì)節(jié)可以通過(guò)某度進(jìn)行搜索. 可以查詢(xún)到更多理論性的解答..今天我們就以代碼的形式進(jìn)行科普

我們先進(jìn)行一個(gè)簡(jiǎn)單的例子介紹

Method[] methods = MyClass.class.getMethods();
for(Method method : methods){
    System.out.println("method = " + method.getName());
}

在這個(gè)例子中通過(guò)調(diào)用 MyClass 類(lèi)的 class 屬性獲取對(duì)應(yīng)的 Class 類(lèi)的對(duì)象仗谆,通過(guò)這個(gè) Class 類(lèi)的對(duì)象獲取 MyObject 類(lèi)中的方法集合指巡。迭代這個(gè)方法的集合并且打印每個(gè)方法的名字。

java 類(lèi)

使用 Java 反射機(jī)制可以在運(yùn)行時(shí)期檢查 Java 類(lèi)的信息隶垮,檢查 Java 類(lèi)的信息往往是你在使用 Java 反射機(jī)制的時(shí)候所做的第一件事情厌处,通過(guò)獲取類(lèi)的信息你可以獲取以下相關(guān)的內(nèi)容:

  • Class 對(duì)象
  • 類(lèi)名
  • 修飾符
  • 包信息
  • 父類(lèi)
  • 實(shí)現(xiàn)的接口
  • 構(gòu)造器
  • 方法
  • 變量
  • 注解
    除了上述這些內(nèi)容,還有很多的信息你可以通過(guò)反射機(jī)制獲得,請(qǐng)參考 官方文檔.

Class 對(duì)象

在你想檢查一個(gè)類(lèi)的信息之前岁疼,你首先需要獲取類(lèi)的 Class 對(duì)象。Java 中的所有類(lèi)型包括基本類(lèi)型(int, long, float等等)缆娃,即使是數(shù)組都有與之關(guān)聯(lián)的 Class 類(lèi)的對(duì)象捷绒。如果你在編譯期知道一個(gè)類(lèi)的名字的話(huà),那么你可以使用如下的方式獲取一個(gè)類(lèi)的 Class 對(duì)象贯要。

Class myClass = MyClass.class;

如果你在編譯期不知道類(lèi)的名字暖侨,但是你可以在運(yùn)行期獲得到類(lèi)名的字符串,那么你則可以這么做來(lái)獲取 Class 對(duì)象:

String className = ... ;//在運(yùn)行期獲取的類(lèi)名字符串
Class class = Class.forName(className);

在使用 Class.forName() 方法時(shí),你必須提供一個(gè)類(lèi)的全名崇渗,這個(gè)全名包括類(lèi)所在的包的名字字逗。例如 MyClass 類(lèi)位于 com.enoch.driver 包,那么他的全名就是 com.enoch.driver.MyClass宅广。 如果在調(diào)用Class.forName()方法時(shí)葫掉,沒(méi)有在編譯路徑下(classpath)找到對(duì)應(yīng)的類(lèi),那么將會(huì)拋出ClassNotFoundException跟狱。

大家看到Class.forName()的同學(xué)應(yīng)該都會(huì)想到JDBC連接數(shù)據(jù)庫(kù)時(shí),驅(qū)動(dòng)的調(diào)用就是通過(guò)此方法

類(lèi)名

你可以從 Class 對(duì)象中獲取兩個(gè)版本的類(lèi)名俭厚。
通過(guò) getName() 方法返回類(lèi)的全限定類(lèi)名(包含包名)

Class aClass = ... //獲取Class對(duì)象,具體方式可見(jiàn)Class對(duì)象小節(jié)
String className = aClass.getName();

如果你僅僅只是想獲取類(lèi)的名字(不包含包名)驶臊,那么你可以使用 getSimpleName()方法:

Class aClass = ... //獲取Class對(duì)象挪挤,具體方式可見(jiàn)Class對(duì)象小節(jié)
String simpleClassName = aClass.getSimpleName();

修飾符

可以通過(guò) Class 對(duì)象來(lái)訪問(wèn)一個(gè)類(lèi)的修飾符, 即public,private,static 等等的關(guān)鍵字关翎,你可以使用如下方法來(lái)獲取類(lèi)的修飾符:

Class  aClass = ... //獲取Class對(duì)象扛门,具體方式可見(jiàn)Class對(duì)象小節(jié)
int modifiers = aClass.getModifiers();

修飾符都被包裝成一個(gè)int類(lèi)型的數(shù)字,這樣每個(gè)修飾符都是一個(gè)位標(biāo)識(shí)(flag bit)纵寝,這個(gè)位標(biāo)識(shí)可以設(shè)置和清除修飾符的類(lèi)型论寨。 可以使用 java.lang.reflect.Modifier 類(lèi)中的方法來(lái)檢查修飾符的類(lèi)型
Modifier.isAbstract(int modifiers); Modifier.isFinal(int modifiers); Modifier.isInterface(int modifiers); Modifier.isNative(int modifiers); Modifier.isPrivate(int modifiers); Modifier.isProtected(int modifiers); Modifier.isPublic(int modifiers); Modifier.isStatic(int modifiers); Modifier.isStrict(int modifiers); Modifier.isSynchronized(int modifiers); Modifier.isTransient(int modifiers); Modifier.isVolatile(int modifiers);

包信息

可以使用 Class 對(duì)象通過(guò)如下的方式獲取包信息:

Class  aClass = ... //獲取Class對(duì)象,具體方式可見(jiàn)Class對(duì)象小節(jié)
Package package = aClass.getPackage();

通過(guò) Package 對(duì)象你可以獲取包的相關(guān)信息店雅,比如包名政基,你也可以通過(guò) Manifest 文件訪問(wèn)位于編譯路徑下 jar 包的指定信息,比如你可以在 Manifest 文件中指定包的版本編號(hào)闹啦。更多的 Package 類(lèi)信息可以閱讀 java.lang.Package沮明。

父類(lèi)

通過(guò) Class 對(duì)象你可以訪問(wèn)類(lèi)的父類(lèi),如下例:

Class superclass = aClass.getSuperclass();

可以看到 superclass 對(duì)象其實(shí)就是一個(gè) Class 類(lèi)的實(shí)例窍奋,所以你可以繼續(xù)在這個(gè)對(duì)象上進(jìn)行反射操作荐健。

實(shí)現(xiàn)的接口

可以通過(guò)如下方式獲取指定類(lèi)所實(shí)現(xiàn)的接口集合:

Class  aClass = ... //獲取Class對(duì)象酱畅,具體方式可見(jiàn)Class對(duì)象小節(jié)
Class[] interfaces = aClass.getInterfaces();

由于一個(gè)類(lèi)可以實(shí)現(xiàn)多個(gè)接口,因此 getInterfaces(); 方法返回一個(gè) Class 數(shù)組江场,在 Java 中接口同樣有對(duì)應(yīng)的 Class 對(duì)象纺酸。 注意:getInterfaces() 方法僅僅只返回當(dāng)前類(lèi)所實(shí)現(xiàn)的接口。當(dāng)前類(lèi)的父類(lèi)如果實(shí)現(xiàn)了接口址否,這些接口是不會(huì)在返回的 Class 集合中的餐蔬,盡管實(shí)際上當(dāng)前類(lèi)其實(shí)已經(jīng)實(shí)現(xiàn)了父類(lèi)接口。

構(gòu)造器

你可以通過(guò)如下方式訪問(wèn)一個(gè)類(lèi)的構(gòu)造方法:

Constructor[] constructors = aClass.getConstructors();

方法

你可以通過(guò)如下方式訪問(wèn)一個(gè)類(lèi)的所有方法:

Method[] method = aClass.getMethods();

變量

你可以通過(guò)如下方式訪問(wèn)一個(gè)類(lèi)的成員變量:

Field[] method = aClass.getFields();

注解

你可以通過(guò)如下方式訪問(wèn)一個(gè)類(lèi)的注解:

Annotation[] annotations = aClass.getAnnotations();

獲取 Constructor 對(duì)象

我們可以通 過(guò) Class 對(duì)象來(lái)獲取 Constructor 類(lèi)的實(shí)例:

Class aClass = ...//獲取Class對(duì)象
Constructor[] constructors = aClass.getConstructors();

返回的 Constructor 數(shù)組包含每一個(gè)聲明為公有的(Public)構(gòu)造方法佑附。 如果你知道你要訪問(wèn)的構(gòu)造方法的方法參數(shù)類(lèi)型樊诺,你可以用下面的方法獲取指定的構(gòu)造方法,這例子返回的構(gòu)造方法的方法參數(shù)為 String 類(lèi)型:

Class aClass = ...//獲取Class對(duì)象
Constructor constructor = aClass.getConstructor(new Class[]{String.class});

構(gòu)造方法參數(shù)

你可以通過(guò)如下方式獲取指定構(gòu)造方法的方法參數(shù)信息:

Constructor constructor = ... //獲取Constructor對(duì)象
Class[] parameterTypes = constructor.getParameterTypes();

利用 Constructor 對(duì)象實(shí)例化一個(gè)類(lèi)

你可以通過(guò)如下方法實(shí)例化一個(gè)類(lèi):

Constructor constructor = MyClass.class.getConstructor(String.class);
MyClass myObject = (MyClass)
 constructor.newInstance("一個(gè)構(gòu)造函數(shù)");

constructor.newInstance()方法的方法參數(shù)是一個(gè)可變參數(shù)列表音同,但是當(dāng)你調(diào)用構(gòu)造方法的時(shí)候你必須提供精確的參數(shù)词爬,即形參與實(shí)參必須一一對(duì)應(yīng)。在這個(gè)例子中構(gòu)造方法需要一個(gè) String 類(lèi)型的參數(shù)权均,那我們?cè)谡{(diào)用 newInstance 方法的時(shí)候就必須傳入一個(gè) String 類(lèi)型的參數(shù)顿膨。

內(nèi)容索引

  • 獲取 Field 對(duì)象
  • 變量名稱(chēng)
  • 變量類(lèi)型
  • 獲取或設(shè)置(get/set)變量值

獲取 Field 對(duì)象

可以通過(guò) Class 對(duì)象獲取 Field 對(duì)象,如下例:

Class aClass = ...//獲取Class對(duì)象
Field[] methods = aClass.getFields();

返回的 Field 對(duì)象數(shù)組包含了指定類(lèi)中聲明為公有的(public)的所有變量集合叽赊。 如果你知道你要訪問(wèn)的變量名稱(chēng)恋沃,你可以通過(guò)如下的方式獲取指定的變量:

Class  aClass = MyClass.class
Field field = aClass.getField("someField");

上面的例子返回的Field類(lèi)的實(shí)例對(duì)應(yīng)的就是在 MyClass 類(lèi)中聲明的名為 someField 的成員變量,就是這樣:

public class MyClass{
  public String someField = null;
}

在調(diào)用 getField()方法時(shí)必指,如果根據(jù)給定的方法參數(shù)沒(méi)有找到對(duì)應(yīng)的變量芽唇,那么就會(huì)拋出 NoSuchFieldException。

變量名稱(chēng)

一旦你獲取了 Field 實(shí)例取劫,你可以通過(guò)調(diào)用 Field.getName()方法獲取他的變量名稱(chēng)匆笤,如下例:

Field field = ... //獲取Field對(duì)象
String fieldName = field.getName();

變量類(lèi)型

Field field = aClass.getField("someField");
Object fieldType = field.getType();

獲取或設(shè)置(get/set)變量值

一旦你獲得了一個(gè) Field 的引用,你就可以通過(guò)調(diào)用 Field.get()或 Field.set()方法谱邪,獲取或者設(shè)置變量的值炮捧,如下例:

Class  aClass = MyClass.class
Field field = aClass.getField("someField");
MyClass objectInstance = new MyClass();
Object value = field.get(objectInstance);
field.set(objetInstance, value);

傳入 Field.get()/Field.set()方法的參數(shù) objetInstance 應(yīng)該是擁有指定變量的類(lèi)的實(shí)例。在上述的例子中傳入的參數(shù)是 MyObjec t類(lèi)的實(shí)例惦银,是因?yàn)?someField 是 MyObject 類(lèi)的實(shí)例咆课。 如果變量是靜態(tài)變量的話(huà)(public static)那么在調(diào)用 Field.get()/Field.set()方法的時(shí)候傳入 null 做為參數(shù)而不用傳遞擁有該變量的類(lèi)的實(shí)例。(譯者注:你如果傳入擁有該變量的類(lèi)的實(shí)例也可以得到相同的結(jié)果)

內(nèi)容索引

  • 獲取 Method 對(duì)象
  • 方法參數(shù)以及返回類(lèi)型
  • 通過(guò) Method 對(duì)象調(diào)用方法

使用 Java 反射你可以在運(yùn)行期檢查一個(gè)方法的信息以及在運(yùn)行期調(diào)用這個(gè)方法扯俱,通過(guò)使用 java.lang.reflect.Method 類(lèi)就可以實(shí)現(xiàn)上述功能书蚪。在本節(jié)會(huì)帶你深入了解 Method 對(duì)象的信息。

獲取 Method 對(duì)象

可以通過(guò) Class 對(duì)象獲取 Method 對(duì)象迅栅,如下例:

Class aClass = ...//獲取Class對(duì)象
Method[] methods = aClass.getMethods();

返回的 Method 對(duì)象數(shù)組包含了指定類(lèi)中聲明為公有的(public)的所有變量集合殊校。

如果你知道你要調(diào)用方法的具體參數(shù)類(lèi)型,你就可以直接通過(guò)參數(shù)類(lèi)型來(lái)獲取指定的方法读存,下面這個(gè)例子中返回方法對(duì)象名稱(chēng)是“doSomething”为流,他的方法參數(shù)是 String 類(lèi)型:

Class  aClass = ...//獲取Class對(duì)象
Method method = aClass.getMethod("doSomething", new Class[]{String.class});

如果根據(jù)給定的方法名稱(chēng)以及參數(shù)類(lèi)型無(wú)法匹配到相應(yīng)的方法呕屎,則會(huì)拋出 NoSuchMethodException。 如果你想要獲取的方法沒(méi)有參數(shù)敬察,那么在調(diào)用 getMethod()方法時(shí)第二個(gè)參數(shù)傳入 null 即可秀睛,就像這樣:

Class  aClass = ...//獲取Class對(duì)象
Method method = aClass.getMethod("doSomething", null);

方法參數(shù)以及返回類(lèi)型

你可以獲取指定方法的方法參數(shù)是哪些:

Method method = ... //獲取Class對(duì)象
Class[] parameterTypes = method.getParameterTypes();

你可以獲取指定方法的返回類(lèi)型:

Method method = ... //獲取Class對(duì)象
Class returnType = method.getReturnType();

通過(guò) Method 對(duì)象調(diào)用方法

你可以通過(guò)如下方式來(lái)調(diào)用一個(gè)方法:

//獲取一個(gè)方法名為doSomesthing,參數(shù)類(lèi)型為String的方法

Method method = MyClass.class.getMethod("doSomething", String.class);
Object returnValue = method.invoke(null, "parameter-value1");

傳入的 null 參數(shù)是你要調(diào)用方法的對(duì)象莲祸,如果是一個(gè)靜態(tài)方法調(diào)用的話(huà)則可以用 null 代替指定對(duì)象作為 invoke()的參數(shù)蹂安,在上面這個(gè)例子中,如果 doSomething 不是靜態(tài)方法的話(huà)锐帜,你就要傳入有效的 MyClass 實(shí)例而不是 null藤抡。 Method.invoke(Object target, Object … parameters)方法的第二個(gè)參數(shù)是一個(gè)可變參數(shù)列表,但是你必須要傳入與你要調(diào)用方法的形參一一對(duì)應(yīng)的實(shí)參抹估。就像上個(gè)例子那樣,方法需要 String 類(lèi)型的參數(shù)弄兜,那我們必須要傳入一個(gè)字符串药蜻。

Java 注解

內(nèi)容索引

  • 什么是注解
  • 類(lèi)注解
  • 方法注解
  • 參數(shù)注解
  • 變量注解

利用 Java 反射機(jī)制可以在運(yùn)行期獲取 Java 類(lèi)的注解信息。

什么是注解

注解是 Java 5 的一個(gè)新特性替饿。注解是插入你代碼中的一種注釋或者說(shuō)是一種元數(shù)據(jù)(meta data)语泽。這些注解信息可以在編譯期使用預(yù)編譯工具進(jìn)行處理(pre-compiler tools),也可以在運(yùn)行期使用 Java 反射機(jī)制進(jìn)行處理视卢。下面是一個(gè)類(lèi)注解的例子:

@MyAnnotation(name="someName",  value = "Hello World")
public class TheClass {
}

在 TheClass 類(lèi)定義的上面有一個(gè)@MyAnnotation 的注解踱卵。注解的定義與接口的定義相似,下面是MyAnnotation注解的定義:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)

public @interface MyAnnotation {
  public String name();
  public String value();
}

在 interface 前面的@符號(hào)表名這是一個(gè)注解据过,一旦你定義了一個(gè)注解之后你就可以將其應(yīng)用到你的代碼中惋砂,就像之前我們的那個(gè)例子那樣。 在注解定義中的兩個(gè)指示@Retention(RetentionPolicy.RUNTIME)和@Target(ElementType.TYPE)绳锅,說(shuō)明了這個(gè)注解該如何使用西饵。 @Retention(RetentionPolicy.RUNTIME)表示這個(gè)注解可以在運(yùn)行期通過(guò)反射訪問(wèn)。如果你沒(méi)有在注解定義的時(shí)候使用這個(gè)指示那么這個(gè)注解的信息不會(huì)保留到運(yùn)行期鳞芙,這樣反射就無(wú)法獲取它的信息眷柔。 @Target(ElementType.TYPE) 表示這個(gè)注解只能用在類(lèi)型上面(比如類(lèi)跟接口)。你同樣可以把Type改為Field或者M(jìn)ethod原朝,或者你可以不用這個(gè)指示驯嘱,這樣的話(huà)你的注解在類(lèi),方法和變量上就都可以使用了喳坠。 關(guān)于 Java 注解更詳細(xì)的講解可以訪問(wèn) Java Annotations tutorial鞠评。

類(lèi)注解

你可以在運(yùn)行期訪問(wèn)類(lèi),方法或者變量的注解信息壕鹉,下是一個(gè)訪問(wèn)類(lèi)注解的例子:

Class aClass = TheClass.class;
Annotation[] annotations = aClass.getAnnotations();

for(Annotation annotation : annotations){
    if(annotation instanceof MyAnnotation){
        MyAnnotation myAnnotation = (MyAnnotation) annotation;
        System.out.println("name: " + myAnnotation.name());
        System.out.println("value: " + myAnnotation.value());
    }
}

你還可以像下面這樣指定訪問(wèn)一個(gè)類(lèi)的注解:

Class aClass = TheClass.class;
Annotation annotation = aClass.getAnnotation(MyAnnotation.class);

if(annotation instanceof MyAnnotation){
    MyAnnotation myAnnotation = (MyAnnotation) annotation;
    System.out.println("name: " + myAnnotation.name());
    System.out.println("value: " + myAnnotation.value());
}

方法注解

下面是一個(gè)方法注解的例子:

public class TheClass {
  @MyAnnotation(name="someName",  value = "Hello World")
  public void doSomething(){}
}

你可以像這樣訪問(wèn)方法注解:

Method method = ... //獲取方法對(duì)象
Annotation[] annotations = method.getDeclaredAnnotations();

for(Annotation annotation : annotations){
    if(annotation instanceof MyAnnotation){
        MyAnnotation myAnnotation = (MyAnnotation) annotation;
        System.out.println("name: " + myAnnotation.name());
        System.out.println("value: " + myAnnotation.value());
    }
}

你可以像這樣訪問(wèn)指定的方法注解:

Method method = ... // 獲取方法對(duì)象
Annotation annotation = method.getAnnotation(MyAnnotation.class);

if(annotation instanceof MyAnnotation){
    MyAnnotation myAnnotation = (MyAnnotation) annotation;
    System.out.println("name: " + myAnnotation.name());
    System.out.println("value: " + myAnnotation.value());
}

參數(shù)注解

方法參數(shù)也可以添加注解谢澈,就像下面這樣:

public class TheClass {
  public static void doSomethingElse(
        @MyAnnotation(name="aName", value="aValue") String parameter){
  }
}

你可以通過(guò) Method對(duì)象來(lái)訪問(wèn)方法參數(shù)注解:

Method method = ... //獲取方法對(duì)象
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
Class[] parameterTypes = method.getParameterTypes();

int i=0;
for(Annotation[] annotations : parameterAnnotations){
  Class parameterType = parameterTypes[i++];

  for(Annotation annotation : annotations){
    if(annotation instanceof MyAnnotation){
        MyAnnotation myAnnotation = (MyAnnotation) annotation;
        System.out.println("param: " + parameterType.getName());
        System.out.println("name : " + myAnnotation.name());
        System.out.println("value: " + myAnnotation.value());
    }
  }
}

需要注意的是 Method.getParameterAnnotations()方法返回一個(gè)注解類(lèi)型的二維數(shù)組煌贴,每一個(gè)方法的參數(shù)包含一個(gè)注解數(shù)組。

變量注解

下面是一個(gè)變量注解的例子:

public class TheClass {

  @MyAnnotation(name="someName",  value = "Hello World")
  public String myField = null;
}

你可以像這樣來(lái)訪問(wèn)變量的注解:

Field field = ... //獲取方法對(duì)象
Annotation[] annotations = field.getDeclaredAnnotations();

for(Annotation annotation : annotations){
   if(annotation instanceof MyAnnotation){
     MyAnnotation myAnnotation = (MyAnnotation) annotation;
     System.out.println("name: " + myAnnotation.name());
     System.out.println("value: " + myAnnotation.value());
   }
}

你可以像這樣訪問(wèn)指定的變量注解:

Field field = ...//獲取方法對(duì)象
Annotation annotation = field.getAnnotation(MyAnnotation.class);

if(annotation instanceof MyAnnotation){
   MyAnnotation myAnnotation = (MyAnnotation) annotation;
   System.out.println("name: " + myAnnotation.name());
   System.out.println("value: " + myAnnotation.value());
}

Java 泛型

內(nèi)容索引

  • 運(yùn)用泛型反射的經(jīng)驗(yàn)法則
  • 泛型方法返回類(lèi)型
  • 泛型方法參數(shù)類(lèi)型
  • 泛型變量類(lèi)型

泛型方法返回類(lèi)型

 public class MyClass {

  protected List<String> stringList = ...;

  public List<String> getStringList(){
    return this.stringList;
  }
}

我們可以獲取 getStringList()方法的泛型返回類(lèi)型锥忿,換句話(huà)說(shuō)牛郑,我們可以檢測(cè)到 getStringList()方法返回的是 List 而不僅僅只是一個(gè) List。如下例:

Method method = MyClass.class.getMethod("getStringList", null);

Type returnType = method.getGenericReturnType();

if(returnType instanceof ParameterizedType){
    ParameterizedType type = (ParameterizedType) returnType;
    Type[] typeArguments = type.getActualTypeArguments();
    for(Type typeArgument : typeArguments){
        Class typeArgClass = (Class) typeArgument;
        System.out.println("typeArgClass = " + typeArgClass);
    }
}

這段代碼會(huì)打印出 “typeArgClass = java.lang.String”敬鬓,Type[]數(shù)組typeArguments 只有一個(gè)結(jié)果 – 一個(gè)代表 java.lang.String 的 Class 類(lèi)的實(shí)例淹朋。Class 類(lèi)實(shí)現(xiàn)了 Type 接口。

泛型方法參數(shù)類(lèi)型

你同樣可以通過(guò)反射來(lái)獲取方法參數(shù)的泛型類(lèi)型钉答,下面這個(gè)例子定義了一個(gè)類(lèi)础芍,這個(gè)類(lèi)中的方法的參數(shù)是一個(gè)被參數(shù)化的 List:

public class MyClass {
  protected List<String> stringList = ...;

  public void setStringList(List<String> list){
    this.stringList = list;
  }
}

你可以像這樣來(lái)獲取方法的泛型參數(shù):

method = Myclass.class.getMethod("setStringList", List.class);

Type[] genericParameterTypes = method.getGenericParameterTypes();

for(Type genericParameterType : genericParameterTypes){
    if(genericParameterType instanceof ParameterizedType){
        ParameterizedType aType = (ParameterizedType) genericParameterType;
        Type[] parameterArgTypes = aType.getActualTypeArguments();
        for(Type parameterArgType : parameterArgTypes){
            Class parameterArgClass = (Class) parameterArgType;
            System.out.println("parameterArgClass = " + parameterArgClass);
        }
    }
}

這段代碼會(huì)打印出”parameterArgType = java.lang.String”。Type[]數(shù)組 parameterArgTypes 只有一個(gè)結(jié)果 – 一個(gè)代表 java.lang.String 的 Class 類(lèi)的實(shí)例数尿。Class 類(lèi)實(shí)現(xiàn)了Type接口仑性。

泛型變量類(lèi)型

同樣可以通過(guò)反射來(lái)訪問(wèn)公有(Public)變量的泛型類(lèi)型,無(wú)論這個(gè)變量是一個(gè)類(lèi)的靜態(tài)成員變量或是實(shí)例成員變量右蹦。你可以在“Java Reflection: Fields”中閱讀到有關(guān)如何獲取 Field 對(duì)象的相關(guān)內(nèi)容诊杆。這是之前的一個(gè)例子,一個(gè)定義了一個(gè)名為 stringList 的成員變量的類(lèi)何陆。

public class MyClass {
  public List<String> stringList = ...;
}

Field field = MyClass.class.getField("stringList");

Type genericFieldType = field.getGenericType();

if(genericFieldType instanceof ParameterizedType){
    ParameterizedType aType = (ParameterizedType) genericFieldType;
    Type[] fieldArgTypes = aType.getActualTypeArguments();
    for(Type fieldArgType : fieldArgTypes){
        Class fieldArgClass = (Class) fieldArgType;
        System.out.println("fieldArgClass = " + fieldArgClass);
    }
}

這段代碼會(huì)打印出”fieldArgClass = java.lang.String”晨汹。Type[]數(shù)組 fieldArgClass 只有一個(gè)結(jié)果 – 一個(gè)代表 java.lang.String 的 Class 類(lèi)的實(shí)例。Class 類(lèi)實(shí)現(xiàn)了 Type 接口贷盲。

Java 動(dòng)態(tài)類(lèi)加載與重載

  • 類(lèi)加載器
  • 類(lèi)加載體系
  • 類(lèi)加載
  • 動(dòng)態(tài)類(lèi)加載
  • 動(dòng)態(tài)類(lèi)重載
  • 自定義類(lèi)重載
  • 類(lèi)加載/重載示例
    Java 允許你在運(yùn)行期動(dòng)態(tài)加載和重載類(lèi)淘这,但是這個(gè)功能并沒(méi)有像人們希望的那么簡(jiǎn)單直接。這篇文章將闡述在 Java 中如何加載以及重載類(lèi)巩剖。 你可能會(huì)質(zhì)疑為什么 Java 動(dòng)態(tài)類(lèi)加載特性是 Java 反射機(jī)制的一部分而不是 Java 核心平臺(tái)的一部分铝穷。不管怎樣,這篇文章被放到了 Java 反射系列里面而且也沒(méi)有更好的系列來(lái)包含它了佳魔。

類(lèi)加載器

所有 Java 應(yīng)用中的類(lèi)都是被 java.lang.ClassLoader 類(lèi)的一系列子類(lèi)加載的氧骤。因此要想動(dòng)態(tài)加載類(lèi)的話(huà)也必須使用 java.lang.ClassLoader 的子類(lèi)。

一個(gè)類(lèi)一旦被加載時(shí)吃引,這個(gè)類(lèi)引用的所有類(lèi)也同時(shí)會(huì)被加載筹陵。類(lèi)加載過(guò)程是一個(gè)遞歸的模式,所有相關(guān)的類(lèi)都會(huì)被加載镊尺。但并不一定是一個(gè)應(yīng)用里面所有類(lèi)都會(huì)被加載朦佩,與這個(gè)被加載類(lèi)的引用鏈無(wú)關(guān)的類(lèi)是不會(huì)被加載的,直到有引用關(guān)系的時(shí)候它們才會(huì)被加載庐氮。

類(lèi)加載體系

在 Java 中類(lèi)加載是一個(gè)有序的體系语稠。當(dāng)你新創(chuàng)建一個(gè)標(biāo)準(zhǔn)的 Java 類(lèi)加載器時(shí)你必須提供它的父加載器。當(dāng)一個(gè)類(lèi)加載器被調(diào)用來(lái)加載一個(gè)類(lèi)的時(shí)候,首先會(huì)調(diào)用這個(gè)加載器的父加載器來(lái)加載仙畦。如果父加載器無(wú)法找到這個(gè)類(lèi)输涕,這時(shí)候這個(gè)加載器才會(huì)嘗試去加載這個(gè)類(lèi)。

類(lèi)加載

類(lèi)加載器加載類(lèi)的順序如下: 1慨畸、檢查這個(gè)類(lèi)是否已經(jīng)被加載莱坎。 2、如果沒(méi)有被加載寸士,則首先調(diào)用父加載器加載檐什。 3、如果父加載器不能加載這個(gè)類(lèi)弱卡,則嘗試加載這個(gè)類(lèi)乃正。

當(dāng)你實(shí)現(xiàn)一個(gè)有重載類(lèi)功能的類(lèi)加載器,它的順序與上述會(huì)有些不同婶博。類(lèi)重載不會(huì)請(qǐng)求的他的父加載器來(lái)進(jìn)行加載瓮具。在后面的段落會(huì)進(jìn)行講解。

動(dòng)態(tài)類(lèi)加載

動(dòng)態(tài)加載一個(gè)類(lèi)十分簡(jiǎn)單凡人。你要做的就是獲取一個(gè)類(lèi)加載器然后調(diào)用它的 loadClass()方法名党。下面是個(gè)例子:


  public static void main(String[] args){

    ClassLoader classLoader = MainClass.class.getClassLoader();

    try {
        Class aClass = classLoader.loadClass("com.enoch.MyClass");
        System.out.println("aClass.getName() = " + aClass.getName());
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

}

動(dòng)態(tài)類(lèi)重載

動(dòng)態(tài)類(lèi)重載有一點(diǎn)復(fù)雜。Java 內(nèi)置的類(lèi)加載器在加載一個(gè)類(lèi)之前會(huì)檢查它是否已經(jīng)被加載划栓。因此重載一個(gè)類(lèi)是無(wú)法使用 Java 內(nèi)置的類(lèi)加載器的,如果想要重載一個(gè)類(lèi)你需要手動(dòng)繼承 ClassLoader条获。

在你定制 ClassLoader 的子類(lèi)之后忠荞,你還有一些事需要做。所有被加載的類(lèi)都需要被鏈接帅掘。這個(gè)過(guò)程是通過(guò) ClassLoader.resolve()方法來(lái)完成的委煤。由于這是一個(gè) final 方法,因此這個(gè)方法在 ClassLoader 的子類(lèi)中是無(wú)法被重寫(xiě)的修档。resolve()方法是不會(huì)允許給定的 ClassLoader 實(shí)例鏈接一個(gè)類(lèi)兩次碧绞。所以每當(dāng)你想要重載一個(gè)類(lèi)的時(shí)候你都需要使用一個(gè)新的 ClassLoader 的子類(lèi)。你在設(shè)計(jì)類(lèi)重載功能的時(shí)候這是必要的條件吱窝。

自定義類(lèi)重載

在前面已經(jīng)說(shuō)過(guò)你不能使用已經(jīng)加載過(guò)類(lèi)的類(lèi)加載器來(lái)重載一個(gè)類(lèi)讥邻。因此你需要其他的 ClassLoader 實(shí)例來(lái)重載這個(gè)類(lèi)。但是這又帶來(lái)了一些新的挑戰(zhàn)院峡。

所有被加載到 Java 應(yīng)用中的類(lèi)都以類(lèi)的全名(包名 + 類(lèi)名)作為一個(gè)唯一標(biāo)識(shí)來(lái)讓 ClassLoader 實(shí)例來(lái)加載兴使。這意味著,類(lèi) MyObject 被類(lèi)加載器 A 加載照激,如果類(lèi)加載器 B 又加載了 MyObject 類(lèi)发魄,那么兩個(gè)加載器加載出來(lái)的類(lèi)是不同的。看看下面的代碼:

MyClass object = (MyClass)
    myClassReloadingFactory.newInstance("com.enoch.MyClass");

MyObject 類(lèi)在上面那段代碼中被引用励幼,它的變量名是 object汰寓。這就導(dǎo)致了 MyObject 這個(gè)類(lèi)會(huì)被這段代碼所在類(lèi)的類(lèi)加載器所加載。

如果 myClassReloadingFactory 工廠對(duì)象使用不同的類(lèi)加載器重載 MyClass 類(lèi)苹粟,你不能把重載的 MyClass t類(lèi)的實(shí)例轉(zhuǎn)換(cast)到類(lèi)型為 MyClass 的對(duì)象變量有滑。一旦 MyClass 類(lèi)分別被兩個(gè)類(lèi)加載器加載,那么它就會(huì)被認(rèn)為是兩個(gè)不同的類(lèi)六水,盡管它們的類(lèi)的全名是完全一樣的俺孙。你如果嘗試把這兩個(gè)類(lèi)的實(shí)例進(jìn)行轉(zhuǎn)換就會(huì)報(bào) ClassCastException。 你可以解決這個(gè)限制掷贾,不過(guò)你需要從以下兩個(gè)方面修改你的代碼: 1睛榄、標(biāo)記這個(gè)變量類(lèi)型為一個(gè)接口,然后只重載這個(gè)接口的實(shí)現(xiàn)類(lèi)想帅。 2场靴、標(biāo)記這個(gè)變量類(lèi)型為一個(gè)超類(lèi),然后只重載這個(gè)超類(lèi)的子類(lèi)港准。

請(qǐng)看下面這兩個(gè)例子:

yObjectInterface object = (MyObjectInterface)
    myClassReloadingFactory.newInstance("com.enoch.MyClass");

MyObjectSuperclass object = (MyObjectSuperclass)
    myClassReloadingFactory.newInstance("com.enoch.MyClass");

只要保證變量的類(lèi)型是超類(lèi)或者接口旨剥,這兩個(gè)方法就可以正常運(yùn)行,當(dāng)它們的子類(lèi)或是實(shí)現(xiàn)類(lèi)被重載的時(shí)候超類(lèi)跟接口是不會(huì)被重載的浅缸。

為了保證這種方式可以運(yùn)行你需要手動(dòng)實(shí)現(xiàn)類(lèi)加載器然后使得這些接口或超類(lèi)可以被它的父加載器加載轨帜。當(dāng)你的類(lèi)加載器加載 MyObject 類(lèi)時(shí),超類(lèi) MyObjectSuperclass 或者接口 MyObjectSuperclass 也會(huì)被加載衩椒,因?yàn)樗鼈兪?MyObject 的依賴(lài)蚌父。你的類(lèi)加載器必須要代理這些類(lèi)的加載到同一個(gè)類(lèi)加載器,這個(gè)類(lèi)加載器加載這個(gè)包括接口或者超類(lèi)的類(lèi)毛萌。

Java 動(dòng)態(tài)代理

  • 創(chuàng)建代理
  • InvocationHandler 接口

常見(jiàn)用例

  • 數(shù)據(jù)庫(kù)連接以及事物管理
  • 單元測(cè)試中的動(dòng)態(tài) Mock 對(duì)象
  • 自定義工廠與依賴(lài)注入(DI)容器之間的適配器
  • 類(lèi)似 AOP 的方法攔截器

利用Java反射機(jī)制你可以在運(yùn)行期動(dòng)態(tài)的創(chuàng)建接口的實(shí)現(xiàn)苟弛。 java.lang.reflect.Proxy 類(lèi)就可以實(shí)現(xiàn)這一功能。這個(gè)類(lèi)的名字(譯者注:Proxy 意思為代理)就是為什么把動(dòng)態(tài)接口實(shí)現(xiàn)叫做動(dòng)態(tài)代理阁将。動(dòng)態(tài)的代理的用途十分廣泛膏秫,比如數(shù)據(jù)庫(kù)連接和事物管理(transaction management)還有單元測(cè)試時(shí)用到的動(dòng)態(tài) mock 對(duì)象以及 AOP 中的方法攔截功能等等都使用到了動(dòng)態(tài)代理。

創(chuàng)建代理

你可以通過(guò)使用 Proxy.newProxyInstance()方法創(chuàng)建動(dòng)態(tài)代理做盅。 newProxyInstance()方法有三個(gè)參數(shù): 1缤削、類(lèi)加載器(ClassLoader)用來(lái)加載動(dòng)態(tài)代理類(lèi)。 2吹榴、一個(gè)要實(shí)現(xiàn)的接口的數(shù)組僻他。 3、一個(gè) InvocationHandler 把所有方法的調(diào)用都轉(zhuǎn)到代理上腊尚。 如下例:

InvocationHandler handler = new MyInvocationHandler();
MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
                            MyInterface.class.getClassLoader(),
                            new Class[] { MyInterface.class },
                            handler);

在執(zhí)行完這段代碼之后吨拗,變量 proxy 包含一個(gè) MyInterface 接口的的動(dòng)態(tài)實(shí)現(xiàn)。所有對(duì) proxy 的調(diào)用都被轉(zhuǎn)向到實(shí)現(xiàn)了 InvocationHandler 接口的 handler 上。有關(guān) InvocationHandler 的內(nèi)容會(huì)在下一段介紹劝篷。

InvocationHandler 接口

在前面提到了當(dāng)你調(diào)用 Proxy.newProxyInstance()方法時(shí)哨鸭,你必須要傳入一個(gè) InvocationHandler 接口的實(shí)現(xiàn)。所有對(duì)動(dòng)態(tài)代理對(duì)象的方法調(diào)用都會(huì)被轉(zhuǎn)向到 InvocationHandler 接口的實(shí)現(xiàn)上娇妓,下面是 InvocationHandler 接口的定義:

public interface InvocationHandler{
  Object invoke(Object proxy, Method method, Object[] args)
         throws Throwable;
}

下面是它的實(shí)現(xiàn)類(lèi)的定義:

public class MyInvocationHandler implements InvocationHandler{

  public Object invoke(Object proxy, Method method, Object[] args)
  throws Throwable {
    //do something "dynamic"
  }
}

傳入 invoke()方法中的 proxy 參數(shù)是實(shí)現(xiàn)要代理接口的動(dòng)態(tài)代理對(duì)象像鸡。通常你是不需要他的。

invoke()方法中的 Method 對(duì)象參數(shù)代表了被動(dòng)態(tài)代理的接口中要調(diào)用的方法哈恰,從這個(gè) method 對(duì)象中你可以獲取到這個(gè)方法名字只估,方法的參數(shù),參數(shù)類(lèi)型等等信息着绷。關(guān)于這部分內(nèi)容可以查閱之前有關(guān) Method 的文章蛔钙。

Object 數(shù)組參數(shù)包含了被動(dòng)態(tài)代理的方法需要的方法參數(shù)。注意:原生數(shù)據(jù)類(lèi)型(如int荠医,long等等)方法參數(shù)傳入等價(jià)的包裝對(duì)象(如Integer吁脱, Long等等)。

數(shù)據(jù)庫(kù)連接以及事物管理

Spring 框架中有一個(gè)事物代理可以讓你提交/回滾一個(gè)事物彬向。它的具體原理在 Advanced Connection and Transaction Demarcation and Propagation一文中有詳細(xì)描述兼贡,所以在這里我就簡(jiǎn)短的描述一下,方法調(diào)用序列如下:

web controller --> proxy.execute(...);
  proxy --> connection.setAutoCommit(false);
  proxy --> realAction.execute();
    realAction does database work
  proxy --> connection.commit();

自定義工廠與依賴(lài)注入(DI)容器之間的適配器

依賴(lài)注入容器 Butterfly Container 有一個(gè)非常強(qiáng)大的特性可以讓你把整個(gè)容器注入到這個(gè)容器生成的 bean 中娃胆。但是遍希,如果你不想依賴(lài)這個(gè)容器的接口,這個(gè)容器可以適配你自己定義的工廠接口里烦。你僅僅需要這個(gè)接口而不是接口的實(shí)現(xiàn)凿蒜,這樣這個(gè)工廠接口和你的類(lèi)看起來(lái)就像這樣:

public interface IMyFactory {
  Bean   bean1();
  Person person();
  ...
}

public class MyAction{

  protected IMyFactory myFactory= null;

  public MyAction(IMyFactory factory){
    this.myFactory = factory;
  }

  public void execute(){
    Bean bean = this.myFactory.bean();
    Person person = this.myFactory.person();
  }

}

當(dāng) MyAction 類(lèi)調(diào)用通過(guò)容器注入到構(gòu)造方法中的 IMyFactory 實(shí)例的方法時(shí),這個(gè)方法調(diào)用實(shí)際先調(diào)用了 IContainer.instance()方法招驴,這個(gè)方法可以讓你從容器中獲取實(shí)例篙程。這樣這個(gè)對(duì)象可以把 Butterfly Container 容器在運(yùn)行期當(dāng)成一個(gè)工廠使用枷畏,比起在創(chuàng)建這個(gè)類(lèi)的時(shí)候進(jìn)行注入别厘,這種方式顯然更好。而且這種方法沒(méi)有依賴(lài)到 Butterfly Container 中的任何接口拥诡。

類(lèi)似 AOP 的方法攔截器

Spring 框架可以攔截指定 bean 的方法調(diào)用触趴,你只需提供這個(gè) bean 繼承的接口。Spring 使用動(dòng)態(tài)代理來(lái)包裝 bean渴肉。所有對(duì) bean 中方法的調(diào)用都會(huì)被代理攔截冗懦。代理可以判斷在調(diào)用實(shí)際方法之前是否需要調(diào)用其他方法或者調(diào)用其他對(duì)象的方法,還可以在 bean 的方法調(diào)用完畢之后再調(diào)用其他的代理方法仇祭。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末披蕉,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌没讲,老刑警劉巖眯娱,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扣墩,死亡現(xiàn)場(chǎng)離奇詭異惰爬,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)田绑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)嘁信,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)于样,“玉大人,你說(shuō)我怎么就攤上這事潘靖〈┢剩” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵秘豹,是天一觀的道長(zhǎng)携御。 經(jīng)常有香客問(wèn)我,道長(zhǎng)既绕,這世上最難降的妖魔是什么啄刹? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮凄贩,結(jié)果婚禮上誓军,老公的妹妹穿的比我還像新娘。我一直安慰自己疲扎,他們只是感情好昵时,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著椒丧,像睡著了一般壹甥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上壶熏,一...
    開(kāi)封第一講書(shū)人閱讀 51,692評(píng)論 1 305
  • 那天句柠,我揣著相機(jī)與錄音,去河邊找鬼棒假。 笑死溯职,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的帽哑。 我是一名探鬼主播谜酒,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼妻枕!你這毒婦竟也來(lái)了僻族?” 一聲冷哼從身側(cè)響起粘驰,我...
    開(kāi)封第一講書(shū)人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎述么,沒(méi)想到半個(gè)月后晴氨,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡碉输,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年籽前,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片敷钾。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡枝哄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出阻荒,到底是詐尸還是另有隱情挠锥,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布侨赡,位于F島的核電站蓖租,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏羊壹。R本人自食惡果不足惜蓖宦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望油猫。 院中可真熱鬧稠茂,春花似錦、人聲如沸情妖。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)毡证。三九已至电爹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間料睛,已是汗流浹背丐箩。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留秦效,地道東北人雏蛮。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓涎嚼,卻偏偏與公主長(zhǎng)得像阱州,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子法梯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 整理來(lái)自互聯(lián)網(wǎng) 1苔货,JDK:Java Development Kit犀概,java的開(kāi)發(fā)和運(yùn)行環(huán)境,java的開(kāi)發(fā)工具...
    Ncompass閱讀 1,539評(píng)論 0 6
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類(lèi)型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,103評(píng)論 1 32
  • 一:java概述: 1夜惭,JDK:Java Development Kit姻灶,java的開(kāi)發(fā)和運(yùn)行環(huán)境,java的開(kāi)發(fā)...
    慕容小偉閱讀 1,790評(píng)論 0 10
  • 一:java概述:1诈茧,JDK:Java Development Kit产喉,java的開(kāi)發(fā)和運(yùn)行環(huán)境,java的開(kāi)發(fā)工...
    ZaneInTheSun閱讀 2,654評(píng)論 0 11
  • 第二部分 自動(dòng)內(nèi)存管理機(jī)制 第二章 java內(nèi)存異常與內(nèi)存溢出異常 運(yùn)行數(shù)據(jù)區(qū)域 程序計(jì)數(shù)器:當(dāng)前線(xiàn)程所執(zhí)行的字節(jié)...
    小明oh閱讀 1,172評(píng)論 0 2