Classes
使用Java反射,可以在运行时检查Java类。在使用Reflection时,可以从Classes获取以下信息:
- Class Name
- Class Modifies(public,private,synchronized etc.)
- Package Info
- Superclass
- Implemented Interfaces
- Constructors
- Methods
- Fields
- Anonotations
更具体的API列表参见JavaDoc
The Class Object
Java中所有类型(包括int,long,float等原始类型、数组)均有相关的Class对象,如果你在编译阶段知道某个类的名字,则可以使用如下方法获得这个Class对象:1
Class myObjectClass = MyObject.class
如果在编译阶段不知道类的名字,但在运行阶段有类的名字,可以使用如下方法得到Class对象:1
2String className = ...//obtain class name as string at runtime
Class class = Class.forName(className)
当使用Class.forName()方法的时候,必须提供全路径的类名(包括包名),此方法在classpath下找不到指定的类时,会抛出ClassNotFoundException异常。
Class Name
从Class对象中获取其名字有两种方法,分别是getName()和getSimpleName(),其中getName()方法包括包名,getSimpleName()没有包名。1
2
3Class aClass = ... //obtain Class object. see The Class Object section
String className = aClass.getName();
String simpleClassName = aClass.getSimpleName();
Modifiers
使用Class对象可以知道类的public private static等信息。1
2Class aClass = ...//obtain Class object. See prev. section
int modifiers = aClass.getModifiers();
信息被存储在返回的int型modifier中的二进制位上,使用java.lang.reflect.Modifier提供的方法来测试。1
2
3
4
5
6
7
8
9
10
11
12Modifier.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)
Package Info
使用Class对象也可以获得包信息:1
2Class aClass = ... //obtain Class object. See prev. section
Package package = aClass.getPackage();
可以使用Package对象获得包的相关信息,具体参看java.lang.package
Superclass
获得当前Class对象的Superclass对象的方法如下:1
Class superclass = aClass.getSuperclass();
Implemented Interfaces
得到类实现的接口列表的方法如下:1
2Class aClass = ... //obtain Class object. See prev. section
Class[] interfaces = aClass.getInterfaces();
会返回当前类中声明实现的接口列表,对于父类中实现的接口而在子类中未声明实现,则不会返回到接口列表中,如果要获得当前类实现的所有接口列表需要递归父类的接口列表进行合并。
Constructors
获得构造器的方法如下:1
Constructor[] constructors = aClass.getConstructors();
对构造器的说明将在下面章节中更详细的介绍。
Methods
获得方法列表的方法如下:1
Method[] method = aClass.getMethods();
更详细的说明见下面Methods章节。
Fields
获得域列表的方法如下:1
Field[] method = aClass.getFields();
更详细的说明见下面Fields章节。
Annotations
获得注解列表的方法如下:1
Annotation[] annotations = aClass.getAnnotations();
更详细的说明见下面Annotations章节。
Constructors
Obtaining Constructor Objects
使用Class对象可以得到Constructor对象:1
2Class aClass = ...//obtain class object
Constructor[] constructors = aClass.getConstructors();
会返回类中所有声明的public构造器,如果已知构造i器的参数个数和类型,则可以获得具体的构造器。比如:1
2Class aClass = ...//obtain class object
Constructor constructor = aClass.getConstructor(new Class[]{String.class});
如果没有匹配参数的构造器,则会抛出NoSuchMethodException异常。
Constructor Parameters
通过getParameterTypes()方法得到Constructor的参数列表:1
2Constructor constructor = ...// obtain constructor -see above
Class [] parameterTypes = constructor.getParameterTypes();
Instantiating Objects using Constructor Object
使用Constructor初始化对象:1
2
3
4//get constructor that takes a String as argument
Constructor constructor = MyObject.class.getConstructor(String.class);
MyObject myObject = (MyObject) constructor.newInstance("constructor-arg1");
Constructor.newInstance()方法根据指定的参数创建对象。
Fields
使用Java反射你可以在运行时得到类的成员变量并进行get/set。
Obtaining Field Objects
1 | Class aClass = ...//obtain class object |
返回的Field[]数组包含类中所有的public成员变量。
如果知道成员变量的名字,则可以使用getField()和getDeclaredField()方法获得成员变量对象。1
2
3Class aClass = MyObject.class
Field field = aClass.getField("someField");
Field otherField = aClass.getDeclaredField("someField")
如果没有指定名字的成员变量会抛出NoSuchFieldException异常。
getField(String name)与getDeclaredField(String name)的区别是getField只能返回public的成员变量,而getDeclaredField还可以返回private修饰的成员变量。
Field Name
使用Field对象获得成员变量名字的方法如下:1
2Field = ...//obtain object
String fieldName = .getName();
Field Type
得到成员变量类型的方法如下:1
2Field field = aClass.getField("someField");
Object fieldType = field.getType();
Getting and Setting Field Values
当我们得到Field对象时,就可以使用Field.get()和Field.set()方法来获得和设置成员变量的值。1
2
3
4
5
6Class aClass = MyObject.class
Field field = aClass.getField("someField");
MyObject objectInstance = new MyObject();
Object value = field.get(objectInstance);
field.set(objectInstance,value);
如果是一个static的成员,则Field的get和set方法传null作为参数。
Methods
Obtaining Method Objects
1 | Class aClass = ...//obtain class object |
返回类中定义的public方法列表。
如果知道方法的名字和参数列表,则可以使用getMethod()直接得到Method对象。1
2
3Class aClass = ...//obtain class object
Method method =
aClass.getMethod("doSomething", new Class[]{String.class});
如果没有找到则会抛出NoSuchMethodException异常。
如果方法没有参数,则传递null1
2
3Class aClass = ...//obtain class object
Method method =
aClass.getMethod("doSomething", null);
Method Parameters and Return Types
通过Method对象获得方法的参数列表1
2Method method = ... // obtain method - see above
Class[] parameterTypes = method.getParameterTypes();
得到返回类型:1
2Method method = ... // obtain method - see above
Class returnType = method.getReturnType();
Invoking Methods using Method Object
使用Method对象调用类想对应的方法。1
2
3
4//get method that takes a String as argument
Method method = MyObject.class.getMethod("doSomething", String.class);
MyObject instance = new MyObject();
Object returnType = method.invoke(instance,"parameter-value1")
method.invoke的第一个参数为需要在哪个实例上调用的method方法的对象,弱如果是static修饰的方法则此参数传递null即可。
Getters and Setters
Java反射无法直接得到类的Getter和Setter方法,但可以通过遍历Method[]数组,根据Getter和Setter方法的特征得到。具体特征约定如下:
Getter
0参数,并且方法名以get开头。Setter
1个参数,并且方法名以set开头。
下面是实现举例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public static void printGettersSetters(Class aClass){
Method[] methods = aClass.getMethods();
for(Method method: methods){
if(isGetter(method)) System.out.println("getter: "+method);
if(isSetter(method)) System.out.println("setter: "+method);
}
}
public static boolean isGetter(Method method){
if(!method.getName.startsWith("get")) return false;
if(method.getParameterTypes().length !=0) return false;
if(void.class.equals(method.getReturnType()) return false;
return true;
}
public static boolean isSetter(Method method){
if(!method.getName().startsWith("set")) return false;
if(method.getParameterTypes().length != 1) return false;
return true;
}
Private Fields and Methods
Accessing Private Fields
访问private field需要使用Class.getDeclaredField(String name)或者Class.getDeclaredFields()方法。Class.getField(String name)和Class.getFields()方法只会返回公有域public field。
例如:1
2
3
4
5
6public class PrivateObject{
private String privateString = null;
public PrivateObject(String privateString){
this.privateString = privateString;
}
}
1 | PrivateObject privateObject = new PrivateObject("The Private Value"); |
Accessing Private Methods
访问private method使用Class.getDeclaredMethod(String name, Class[] parameterTypes) 或者Class.getDeclaredMethods()1
2
3
4
5
6
7
8
9
10
11
12public class PrivateObject {
private String privateString = null;
public PrivateObject(String privateString) {
this.privateString = privateString;
}
private String getPrivateString(){
return this.privateString;
}
}
1 | PrivateObject privateObject = new PrivateObject("The Private Value"); |
Annotations
What are Java Annotations?
Annotation是一种在Java代码中插入的注释或元数据,这些注释可以在编译时通过预编译工具进行处理,也可以在运行时通过反射进行处理。1
2
3
public class TheClass{
}
TheClass类具有@MyAnnotation的注释,注释像接口一样定义。1
2
3
4
5
6
7@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyAnnotation{
public String name();
public String value();
}
interface前的@表明是定义Annotation
@Retention(RetentionPolicy.RUNTIME)表示注释在运行时可以通过反射来访问,如果没有指定,则注释在运行时不会保留。
@Target(ElementType.TYPE)表示注释只能用于类型(类和接口)的顶部,也可以指定用于方法和成员变量。
Java Annotations Purposes
Java注解用于以下目的:
- Compiler instructions
- Build-time instructions
- Runtime instructions
Annotation Basics
一个简单的注解如下所示:1
@告诉编译器这是一个注解,@之后为注解的名字,这里为Entity
Annotation Elements
注解可以包含可以为其设置值的元素,元素就像一个属性或参数。1
在上面的例子中包含了一个名为tableName的元素,并且设置其值为vehicles。元素需要被括号包在其中,如果注解没有元素,则不需要后面的括号。
一个注解可以包含多个元素1
如果一个注解只包含一个元素,则可以将其命名为value1
如果一个注解只包含一个名为value的元素,则可以省略名字1
Annotation Placement
注解可以放在classes、interfaces、methods、methods parameters、fields and local variables。
1 |
|
表示在类上的一个名为Entity的注解。
下面为在class fields methods parameters and local variables上的注解1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31// class
public class Vehicle {
// field
protected String vehicleName = null;
//method
public String getVehicleName() {
return this.vehicleName;
}
//method parameter
public void setVehicleName(@Optional vehicleName) {
this.vehicleName = vehicleName;
}
public List addVehicleNameToList(List names) {
// local variable
List localNames = names;
if(localNames == null) {
localNames = new ArrayList();
}
localNames.add(getVehicleName());
return localNames;
}
}
Built-in Java Annotations
Java有三类compiler instructions的注解,分别是:
- @Deprecated
- @Override
- @SupressWarnings
@Deprecated
@Deprecated注解用来表明class method 或者field 被弃用,不应该再被使用,如果代码中使用这些被弃用的类、方法、或域则编译器会给出warning。1
2
3
public class MyComponent{
}
在使用@Deprecated注解的时候,最好和Java注释一起使用,说明被弃用的原因,以及替代的新类、方法或域。1
2
3
4
5
6
7
/**
@deprecated Use MyNewComponent instead.
*/
public class MyComponent {
}
@Override
@Override在方法上使用,用来表示对父类同名方法的重写覆盖。如果方法与父类中找不到同名方法,使用@Override编译器会给出错误。
在覆盖父类的方法时,@Override注解不是必须的,但建议加上,防止父类修改同名方法,子类未加@Override注解发现不了。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public class MySuperClass {
public void doTheThing() {
System.out.println("Do the thing");
}
}
public class MySubClass extends MySuperClass{
public void doTheThing() {
System.out.println("Do it differently");
}
}
@SuppressWarnings
@SuppressWarnings可以略过编译器的警告。1
2
3
4
5
public void methodWithWarning() {
}
Creating Your Own Annotations
1 | @interface MyAnnotation { |
上面定义了一个包含四个元素的注解,关键字@interface向编译器表明这是注解的定义。
注解中的元素由数据类型和名字组成,类似interface接口中的抽象方法。但数据类型只能是基本数据类型或者是基本数据类型的数组,而不能是复杂对象。
使用上面注解的例子如下:1
2
3
4
5
6
7
8
9
10@MyAnnotation(
value="123",
name="Jakob",
age=37,
newNames={"Jenkov", "Peterson"}
)
public class MyClass {
}
Element Default Values
定义注解时,可以为其中的元素指定默认值,这样在添加注解时可以省略具有默认值的元素。1
2
3
4
5
6
7
8
9@interface MyAnnotation {
String value() default "";
String name();
int age();
String[] newNames();
}
value元素指定了默认值,所以可以不用给它赋值。1
2
3
4
5
6
7
8
9@MyAnnotation(
name="Jakob",
age=37,
newNames={"Jenkov", "Peterson"}
)
public class MyClass {
}
@Retention
定义该注解的生命周期,表示在什么级别保留此信息。
RetentionPolicy.SOURCE – 在编译阶段丢弃。这些注解在编译结束之后就不再有任何意义,所以它们不会写入字节码。@Override, @SuppressWarnings都属于这类注解。
RetentionPolicy.CLASS – 在类加载的时候丢弃。在字节码文件的处理中有用。注解默认使用这种方式。(默认值)
RetentionPolicy.RUNTIME– 始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。
1 | import java.lang.annotation.Retention; |
@Target
表示该注解用于什么地方。如果不明确指出,该注解可以放在任何地方。
- ElementType.TYPE:用于描述类、接口或enum声明 注解
- ElementType.FIELD:用于描述实例变量
- ElementType.METHOD:方法声明
- ElementType.PARAMETER:参数声明
- ElementType.CONSTRUCTOR:构造器的声明
- ElementType.LOCAL_VARIABLE:局部变量声明
- ElementType.ANNOTATION_TYPE 另一个注释(ANNOTATION_TYPE目标意味着注释只能用于注释其他注释。像@Target和@Retention注释一样。)
- ElementType.PACKAGE 用于记录java文件的package信息。
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target({ElementType.METHOD})
public @interface MyAnnotation {
String value();
}
@Inherited
允许子类继承父类中的注解
java.lang.annotation.Inherited
@Inherited
public @interface MyAnnotation{
}
@MyAnnotation
public class MySuperClass {...}
public class MySubClass extends MySuperClass {...}
MySubClass可以继承MySuperClass的注解,因为声明了@Inherited
@Documented
一个简单的Annotations标记注解,表示是否将注解信息添加在java文档中。1
2
3
4
5
6import java.lang.annotation.Documented;
@Documented
public @interface MyAnnotation {
}
1 |
|
当为MySuperClass类生成JavaDoc时, @MyAnnotation注解将会被包含进JavaDoc。
Java注解的例子和注解处理器等内容将在新的文章中讲解 (待补充)
Class Annotations
使用反射可以在运行时得到类、方法、域变量的注解。
类:1
2
3
4
5
6
7
8
9
10Class 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());
}
}
已知注解的名字还可以直接得到注解。1
2
3
4
5
6
7
8Class 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());
}
Method Annotations
1 | public class TheClass { |
1 | Method method = ... //obtain method object |
已知注解的名字:1
2
3
4
5
6
7
8Method method = ... // obtain method object
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());
}
Parameter Annotations
1 | public class TheClass { |
1 | Method method = ... //obtain method object |
Method.getParameterAnnotations()返回一个二维Annotation数组,每个参数对应一个注解数组,所以返回二维Annotation数组。
Field Annotations
1 | public class TheClass { |
1 | Field field = ... //obtain field object |
已知注解的名字:
1 | Field field = ... // obtain method object |
例子:
注解的定义:
测试类:
1 | package cn.iaihe.Annotations; |
运行结果:
Generics
The Generics Reflection Rule of Thumb
使用Java泛型通常属于以下两种情况之一:
- Declaring a class/interface as being parameterizable.(声明类/接口是可参数化的)
- Using a parameterizable class.(使用可参数化的类)
Generic Method Return Types
1 | public class MyClass { |
可以获得getStringList()方法的返回值类型。1
2
3
4
5
6
7
8
9
10
11
12Method 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);
}
}
方法的返回值类型为泛型时,需要使用Method.getGenericReturnType()方法来获得返回类型。
例子:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61package cn.iaihe.Generics;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
/**
* Created by zlshi on 17-11-23.
*/
public class MyClass {
protected List<String> stringList = Arrays.asList("test");
public List<String> getStringList()
{
return this.stringList;
}
public List<?> test()
{
return new ArrayList<>();
}
public int testUnGeneric()
{
return 0;
}
public static void main(String[] args)throws Exception {
Method method1 = MyClass.class.getDeclaredMethod("getStringList");
Type type1 = method1.getReturnType(); //获取不到List里的String类型
System.out.println(type1);
type1 = method1.getGenericReturnType();
System.out.println(type1);
if(type1 instanceof ParameterizedType){
ParameterizedType type = (ParameterizedType) type1;
Type[] typeArguments = type.getActualTypeArguments();
for(Type typeArgument : typeArguments){
Class typeArgClass = (Class) typeArgument;
System.out.println("typeArgClass = " + typeArgClass);
}
}
System.out.println("===================================");
Method method2 = MyClass.class.getMethod("test",null);
Type type2 = method2.getGenericReturnType();
System.out.println(type2);
if(type2 instanceof ParameterizedType){
ParameterizedType type = (ParameterizedType) type2;
Type[] typeArguments = type.getActualTypeArguments();
for(Type typeArgument : typeArguments){
try {
Class typeArgClass = (Class) typeArgument; //抛出异常
System.out.println("typeArgClass = " + typeArgClass);
}catch(Exception ex)
{
System.out.println("ExceptionInfo: "+ex.getMessage());
}
}
}
System.out.println("===================================");
Method method3 = MyClass.class.getMethod("testUnGeneric",null);
Type type3 = method3.getReturnType(); //getReturnType()就可以返回类型
System.out.println(type3); //输出int
}
}
运行结果:
Generic Method Parameter Types
也可以通过反射得到方法中的泛型参数1
2
3
4
5
6
7public class MyClass {
protected List<String> stringList = ...;
public void setStringList(List<String> list){
this.stringList = list;
}
}
1 | method = Myclass.class.getMethod("setStringList", List.class); |
Type[] genericParameterTypes = method.getGenericParameterTypes();1
2
3
4
5
6
7
8
9
10for(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);
}
}
}
Generic Field Types
1 | public class MyClass { |
1 | Field field = MyClass.class.getField("stringList"); |
Arrays
java.lang.reflect.Array
使用java.lang.reflect.Array类处理数组的反射。
Creating Arrays
1 | int[] intArray = (int[]) Array.newInstance(int.class, 3); |
第一个参数为数组的类型,第二个参数为数组的大小。
Accessing Arrays
通过Array.get()和Array.set()方法访问数组。1
2
3
4
5
6
7
8
9int[] intArray = (int[]) Array.newInstance(int.class, 3);
Array.set(intArray, 0, 123);
Array.set(intArray, 1, 456);
Array.set(intArray, 2, 789);
System.out.println("intArray[0] = " + Array.get(intArray, 0));
System.out.println("intArray[1] = " + Array.get(intArray, 1));
System.out.println("intArray[2] = " + Array.get(intArray, 2));
上面的代码会打印:1
2
3intArray[0] = 123
intArray[1] = 456
intArray[2] = 789
Obtaining the Class Object of an Array
获取数组的类对象
对于原始类型:1
Class intArray = Class.forName("[I");
JVM使用I来表示int类型,[
表示是一个int数组。其它primitive类型类似。
对于对象:1
Class stringArrayClass = Class.forName("[Ljava.lang.String;");
[L
表示是对象数组。
不能使用Class.forName()来获得原始类型的类对象,会抛出ClassNotFoundException异常。
1 | Class intClass1 = Class.forName("I"); |
1 | public Class getClass(String className){ |
1 | //得到数组中元素的类型 |
检查是否为数组类型:1
2Class stringArrayClass = Array.newInstance(String.class, 0).getClass();
System.out.println("is array: " + stringArrayClass.isArray());
Obtaining the Component Type of an Array
使用Class.getComponentType()获得数组中元素的类型:1
2
3
4String[] strings = new String[3];
Class stringArrayClass = strings.getClass();
Class stringArrayComponentType = stringArrayClass.getComponentType();
System.out.println(stringArrayComponentType); // will print java.lang.String
Dynamic Proxies
使用java.lang.reflect.Proxy可以在运行时创建接口的动态实现。
Creating Proxies
使用Proxy.newProxyInstance()方法来创建动态代理,方法需要如下三个参数:
- 类加载器ClassLoader用来装载动态代理类。
- 要实现的接口对象数组。
- InvocationHandler转发处理代理的方法。
1 | InvocationHandler handler = new MyInvocationHandler(); |
InvocationHandler’s
1 | public interface InvocationHandler{ |
1 | public class MyInvocationHandler implements InvocationHandler{ |
invoke()中的proxy对象为实现接口的动态代理对象。
Method对象表示接口中方法的动态代理实现,可以使用反射得到方法名,参数列表以及返回类型。
Object[] args数组表示方法的具体参数对象。
Known Use Cases
动态代理的使用情形:
- Database Connection and Transaction Management
- Dynamic Mock Objects for Unit Testing
- Adaptation of DI Container to Custom Factory Interfaces
- AOP-like Method Interception
Dynamic Class Loading and Reloading
The ClassLoader
Java中类的加载由java.lang.ClassLoader的子类完成。当一个类被加载时,它所引用的所有类也被加载。这个类加载模式是递归的,直到所有需要的类都被加载。这可能不是应用程序中的所有类。未引用的类只有在引用时才加载。
The ClassLoader Hierarchy
Java中的类加载器被组织成一个层次结构。当您创建新的标准Java ClassLoader时,您必须提供一个父类ClassLoader。如果一个ClassLoader被要求加载一个类,它会要求它的父类加载器加载它。如果父类加载器找不到该类,那么子类加载器将尝试自己加载它。
Class Loading
类加载器在加载类时的步骤如下:
- Check if the class was already loaded.(检查类是否已经被加载)
- If not loaded, ask parent class loader to load the class.(如果没有被加载,则请求父类加载器加载)
- If parent class loader cannot load class, attempt to load it in this class loader.(如果父加载器不能加载,则尝试自己加载类)
Dynamic Class Loading
1 | public class MainClass { |
Dynamic Class Reloading
Java的内置Class加载器在加载之前总是检查一个类是否已经被加载。因此,使用Java的内置类加载器不能重新加载类。重新加载一个类,你将不得不实现你自己的ClassLoader子类。每个加载的类都需要链接。这是使用ClassLoader.resolve()方法完成的。这个方法是final的,因此不能在你的ClassLoader子类中重写。resolve()方法将不允许任何给定的ClassLoader实例连接两次相同的类。
因此,每次你想重新加载一个类,你都必须使用ClassLoader子类的一个新实例。这不是不可能的,但是在设计类重新加载时需要知道。
Designing your Code for Class Reloading
如前所述,您不能使用已经加载该类的ClassLoader重载一个类。因此,您将不得不使用不同的ClassLoader实例重新加载类。加载到Java应用程序中的每个类都由其完全限定的名称(包名称+类名称)和加载它的ClassLoader实例标识。这意味着,类加载器A加载的类MyObject与载类加载器B的MyObject类不是同一个类。1
2MyObject object = (MyObject)
myClassReloadingFactory.newInstance("MyObject");
如果myClassReloadingFactory对象工厂使用与上面的代码所在的类不同的类加载器重新加载MyObject类,您不能将重新装入的MyObject类的实例转换为对象变量的MyObject类型。由于两个MyObject类用不同的类加载器加载,因此即使它们具有相同的完全限定类名,它们也被视为不同的类。试图将一个类的对象转换为另一个类的引用将导致ClassCastException。
使用如下方式解决:
- Use an interface as the variable type, and just reload the implementing class.(使用一个接口作为变量类型,并重新加载实现类。)
- Use a superclass as the variable type, and just reload a subclass.(使用超类作为变量类型,并重新加载一个子类。)
1 | MyObjectInterface object = (MyObjectInterface) |
1 | MyObjectSuperclass object = (MyObjectSuperclass) |
当你的类加载器被要求加载MyObject类时,它也会被要求加载MyObjectInterface类或者MyObjectSuperclass类,因为这些类是从MyObject类中引用的。您的类加载器必须将这些类的加载委托给加载包含接口或超类型变量的类的相同类加载器。
ClassLoader Load / Reload Example
1 | public class MyClassLoader extends ClassLoader{ |
1 | public static void main(String[] args) throws |
这是使用类加载器加载的reflection.MyObject类。注意它是如何扩展一个超类并实现一个接口的。1
2
3
4public class MyObject extends MyObjectSuperClass implements AnInterface2{
//... body of class ... override superclass methods
// or implement interface methods
}