Java Reflection

来源:互联网 发布:推荐系统实战所用数据 编辑:程序博客网 时间:2024/06/01 23:21

Java Reflection

Reflection Class

Class对象

       检查一个类的信息之前,首先需要获取类的Class对象。Java中得所有类型包括基本类型(int,long,float…),即使是数组都有与之关联的Class类的对象。

如果在编译期知道一个类的名字的话,使用如下方式获取一个类的Class对象:

ClassobjClass = MyObject.Class;

       如果在编译期不知道类的名字,但是可以在运行期获得类名的字符串:

StringclassName = “objClass”;

ClassobjClass = Class.forName(className);

使用Class.forName()时,必须提供一个类的全名,即包含包名,可能会抛出ClassNotFoundException

类名

       从Class对象中获取两个版本的类名

String className = objClass.getName();//包含包名

String simpleClassName =objClass.getSimpleName();//不包含包名

修饰符

       可以通过Class对象来访问一个类的修饰符

int modifiers = objClass.getModifiers();

修饰符都被包装成一个int类型的数字,这样每个修饰符都是一个位标识(flag bit),这个位标识可以设置和清除修饰符的类型。使用Modifier类中的方法检查修饰符的类型:

Modifier.isAbstract(int modifiers);

Modifier.isFinal(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.isVolatile(int modifiers);

 

包信息

Package package = objClass.getPackage();

通过Package对象可以获取包的相关信息(包名),也可以通过Manifest文件访问

父类

Class superClass =objClass.getSuperClass();

 

实现的接口

Class[] interfaces =objClass.getInterfaces();

此方法仅仅返回当前类实现的借口,父类的不包括。

构造器

Constructor[] constructors =objClass.getConstructors();

方法

Method[] methods = objClass.getMethods();

变量

Field[] fields = objClass.getFields();

注解

Annotation[] annotations =objClass.getAnnotations();

构造器

       利用java的反射机制可以检查一个类的构造方法,并且可以在运行期间创建一个对象。

Constructor对象

       通过Class对象来获取Constructor类的实例

Constructor[] constructors =objClass.getConstructors();

返回的Constructor数组包含每一个声明为public的构造方法。如果知道构造方法的参数类型:

Constructor constructor =objClass.getConstructor(new Class[]{String.class});

如果没有指定的构造方法能满足匹配的方法参数会抛出:NoSuchMethodException

构造方法参数

Class[] paraneterType = constructor.getParameterTypes();

利用Constructor对象实例化一个类           

Object obj = constructor.newInstance(“constructor-arg1”);

newInstance()参数是一个可变参数列表,调用构造方法的时候必须提供精确的参数,形成一一对应。此例子中传入的是一个String类型的参数

变量

       使用java反射机制可以在运行期检查一个类的变量信息(成员变量)或者获取或者设置变量的值。

获取Field对象

Field[] fields = objClass.getFields();

返回的Field对象数组包含了指定类中声明为public的所有变量集合。如果知道要访问的变量名称:

Field field = objClass.getField(“someField”);

可能会抛出NoSuchFieldException

 

变量名称

String fieldname = field.getName();

变量类型

Object fieldType = field.getType();

获取或设置(get/set)变量值

Class objClass = MyObject.Class;

Field field = objClass.getField(“someField”);

MyObject objectInstance = new MyObject();

Object value = field.get(objectInstance);

field.set(objectInstance, value);

set()传入的参数objectInstance应该拥有指定变量的类的实例。上述例子中传入的参数是MyObject类的实例,是因为someField是Myobject类的实例

如果变量是静态变量的话,传入null作为参数而不用传递改变量的类的实例。

方法

       使用java反射可以在运行期检查一个方法的信息以及在运行期调用这个方法

获取Method对象

Method[] methods = objClass.getMethods();

返回的Method对象数组包含了指定类中声明为public的所有方法集合。

如果知道要调用方法的具体参数类型:

Method method = objClass.getMethod(“doSomething”,new Class[]{String,class});

可能会抛出NoSuchMethodException

如果获得方法没有参数,第二个参数传入null。

方法参数以及返回类型

Class[] parameterTypes =method.getParemeterTypes();

Class returnType = method.getReturnType();

通过Method对象调用方法

Method method = Myobject.class.getMethod(“donSomething”,String.class);

Object returnValue = method.invoke(null, “parameter-value”);

传入的null参数是你要调用方法的对象,如果是一个静态方法调用的话可以用null代替指定对象作为invoke()的参数,如果doSomething不是静态方法的话,需要传入有效的MyObject实例。第二个参数是一个可变参数列表,但是必须传入与调用方法的形参一一对应的实参。

getters、setters

       使用反射可以在运行期检查一个方法的信息以及在运行期调用这个方法,使用这个功能同样可以获取指定类的getters和setters。

Getter

       以get开头,没有方法参数,返回一个值。

Setter

       以set开头,有一个方法参数,一般没有返回值。

Eg.

public 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);

         }

       }

        

       publicstatic 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;

       }

        

       publicstatic boolean isSetter(Method method){

         if(method.getParameterTypes().length != 1)return false;

         return true;

       }

私有变量、私有方法

       通常情况下从对象的外部访问私有变量以及方法是不允许的,使用反射机制可以做到这一点。SecurityManager会现在这样使用。

 

访问私有变量

PrivateObject privateObject = new PrivateObject("The PrivateValue");

       FieldprivateStringField = PrivateObject.class.

                  getDeclaredField("privateString");

       privateStringField.setAccessible(true);

       StringfieldValue = (String) privateStringField.get(privateObject);

       System.out.println("fieldValue= " + fieldValue);

       这个例子会输出”fieldValue = The PrivateValue”,privateString是PrivateObject实例的私有变量,调用getDeclaredField(“privateString”)方法会返回一个私有变量,调用setAccessible()方法会关闭指定类Field实例的反射访问检查,执行后不论私有还是其他的访问作用域都可以访问。

访问私有方法

       PrivateObjectprivateObject = new PrivateObject("The Private Value");

       MethodprivateStringMethod = PrivateObject.class.

               getDeclaredMethod("getPrivateString",null);

        

       privateStringMethod.setAccessible(true);    

       StringreturnValue = (String)

              privateStringMethod.invoke(privateObject, null);      

       System.out.println("returnValue= " + returnValue);

 

注解

       利用反射机制在运行期获取java类的注解信息。

简介

       注解是插入代码中得一种注释或者说是一种元数据(meta data)。这些注解信息可以在编译期使用预编译工具进行处理(pre-compilertools)。也可以在运行期使用java反射机制进行处理。

元注解:

@Target 表示该注解可以用于什么地方,可能的ElementType有:

       CONSTRUCTOR:构造器的声明

       FIFLD:域声明(包括enum实例)

       LOCAL_VARIABLE:局部变量声明

       METHOD:方法声明

       PACKAGE:包声明

       PARAMETER:参数声明

       TYPE:类,接口(包括注解类型)或enum声明

 

@Retention 表示需要在什么级别保存改注解信息。可选的RetentionPolicy参数

       SOURCE:注解将被编译器丢弃

       CLASS:注解在class文件中可用,但会被VM丢弃

       RUNTIME:VM将在运行期间保留注解,因此可以通过反射机制读取注解的信息

      

@Documented 将注解包含在Javadoc中

@Inherited 允许子类继承父类中得注解

 

注解定义:

       @Retention(RetentionPolicy.RUNTIME)

       @Target(ElementType.TYPE)

       public@interface MyAnnotation {

         public String name();

         public String value();

       }

使用:

@MyAnnotation(name="someName",  value = "Hello World")

    public class TheClass {

    }

在interface前面的@符号表明这是一个注解。

@Target(ElementType.Type),说明这个注解改怎么使用

@Retention(RetentionPolicy.RUNTIME)表示这个注解可以运行通过反射访问。

类注解

可以在运行期访问类,方法或者变量的注解信息

       ClassaClass = TheClass.class;

       Annotation[]annotations = aClass.getAnnotations();

       for(Annotationannotation : annotations){

           if(annotation instanceof MyAnnotation){

               MyAnnotation myAnnotation =(MyAnnotation) annotation;

               System.out.println("name: " +myAnnotation.name());

               System.out.println("value: "+ myAnnotation.value());

           }

       }

指定一个类的注解

Class aClass =TheClass.class;

       Annotationannotation = aClass.getAnnotation(MyAnnotation.class);

       if(annotationinstanceof MyAnnotation){

           MyAnnotation myAnnotation = (MyAnnotation)annotation;

           System.out.println("name: " +myAnnotation.name());

           System.out.println("value: " +myAnnotation.value());

       }

方法注解

       下面是一个方法注解的例子:

       publicclass TheClass {

         @MyAnnotation(name="someName",  value = "Hello World")

         public void doSomething(){}

       }

可以像这样访问方法注解:

       Methodmethod = ... //获取方法对象

       Annotation[]annotations = method.getDeclaredAnnotations();

        

       for(Annotationannotation : annotations){

           if(annotation instanceof MyAnnotation){

               MyAnnotation myAnnotation =(MyAnnotation) annotation;

               System.out.println("name: " +myAnnotation.name());

               System.out.println("value: "+ myAnnotation.value());

           }

       }

可以像这样访问指定的方法注解:

       Methodmethod = ... // 获取方法对象

       Annotationannotation = method.getAnnotation(MyAnnotation.class);      

       if(annotationinstanceof MyAnnotation){

           MyAnnotation myAnnotation = (MyAnnotation)annotation;

           System.out.println("name: " +myAnnotation.name());

           System.out.println("value: " +myAnnotation.value());

       }

参数注解

方法参数也可以添加注解,就像下面这样:

       publicclass TheClass {

         public static void doSomethingElse(

               @MyAnnotation(name="aName",value="aValue") String parameter){

         }

       }

你可以通过Method对象来访问方法参数注解:

       Methodmethod = ... //获取方法对象

       Annotation[][]parameterAnnotations = method.getParameterAnnotations();

       Class[]parameterTypes = method.getParameterTypes();

        

       inti=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()方法返回一个注解类型的二维数组,每一个方法的参数包含一个注解数组。

变量注解

下面是一个变量注解的例子:

       publicclass TheClass {

        

         @MyAnnotation(name="someName",  value = "Hello World")

         public String myField = null;

       }

可以像这样来访问变量的注解:

       Fieldfield = ... //获取方法对象</pre>

       <pre>Annotation[]annotations = field.getDeclaredAnnotations();

        

       for(Annotationannotation : annotations){

        if(annotation instanceof MyAnnotation){

        MyAnnotation myAnnotation = (MyAnnotation)annotation;

        System.out.println("name: " +myAnnotation.name());

        System.out.println("value: " +myAnnotation.value());

        }

       }

可以像这样访问指定的变量注解:

       Fieldfield = ...//获取方法对象</pre>

       <pre>

       Annotationannotation = field.getAnnotation(MyAnnotation.class);

        

       if(annotationinstanceof MyAnnotation){

        MyAnnotation myAnnotation = (MyAnnotation)annotation;

        System.out.println("name: " + myAnnotation.name());

        System.out.println("value: " +myAnnotation.value());

       }

泛型

运用泛型反射的经验法则

       经典使用泛型的场景

1.      声明一个需要被参数化(parameterizable)的类/接口

2.      声明一个参数化类

List接口就是典型的例子

泛型方法返回类型

获得了java.lang.reflect.Method对象,那么你就可以获取到这个方法的泛型返回类型信息。如果方法是在一个被参数化类型之中那么你无法获取他的具体类型,但是如果方法返回一个泛型类(那么你就可以获得这个泛型类的具体参数化类型。你可以在“Java Reflection: Methods”中阅读到有关如何获取Method对象的相关内容。下面这个例子定义了一个类这个类中的方法返回类型是一个泛型类型:

         public class MyClass {

        

         protected List<String> stringList =...;

        

         public List<String> getStringList(){

           return this.stringList;

         }

       }

可以获取getStringList()方法的泛型返回类型,换句话说,我们可以检测到getStringList()方法返回的是List而不仅仅只是一个List。如下例:

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

        

       TypereturnType = method.getGenericReturnType();

        

       if(returnTypeinstanceof ParameterizedType){

           ParameterizedType type =(ParameterizedType) returnType;

           Type[] typeArguments =type.getActualTypeArguments();

           for(Type typeArgument : typeArguments){

               Class typeArgClass = (Class)typeArgument;

               System.out.println("typeArgClass =" + typeArgClass);

           }

       }

这段代码会打印出 “typeArgClass = java.lang.String”,Type[]数组typeArguments只有一个结果 – 一个代表java.lang.String的Class类的实例。Class类实现了Type接口。

泛型方法参数类型

可以通过反射来获取方法参数的泛型类型,下面这个例子定义了一个类,这个类中的方法的参数是一个被参数化的List:

       publicclass MyClass {

         protected List<String> stringList =...;

        

         public void setStringList(List<String>list){

           this.stringList = list;

         }

       }

可以像这样来获取方法的泛型参数:

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

        

       Type[]genericParameterTypes = method.getGenericParameterTypes();

        

       for(TypegenericParameterType : genericParameterTypes){

           if(genericParameterType instanceofParameterizedType){

               ParameterizedType aType =(ParameterizedType) genericParameterType;

               Type[] parameterArgTypes =aType.getActualTypeArguments();

               for(Type parameterArgType :parameterArgTypes){

                   Class parameterArgClass = (Class)parameterArgType;

                  System.out.println("parameterArgClass = " +parameterArgClass);

               }

           }

       }

这段代码会打印出”parameterArgType= java.lang.String”。Type[]数组parameterArgTypes只有一个结果 – 一个代表java.lang.String的Class类的实例。Class类实现了Type接口。

泛型变量类型

同样可以通过反射来访问公有(Public)变量的泛型类型,无论这个变量是一个类的静态成员变量或是实例成员变量。你可以在“Java Reflection: Fields”中阅读到有关如何获取Field对象的相关内容。这是之前的一个例子,一个定义了一个名为stringList的成员变量的类。

       publicclass MyClass {     public List<String> stringList = ...;

       }

 

       Fieldfield = MyClass.class.getField("stringList");

        

       TypegenericFieldType = field.getGenericType();

        

       if(genericFieldTypeinstanceof ParameterizedType){

           ParameterizedType aType = (ParameterizedType)genericFieldType;

           Type[] fieldArgTypes =aType.getActualTypeArguments();

           for(Type fieldArgType : fieldArgTypes){

               Class fieldArgClass = (Class)fieldArgType;

               System.out.println("fieldArgClass= " + fieldArgClass);

           }

       }

这段代码会打印出”fieldArgClass= java.lang.String”。Type[]数组fieldArgClass只有一个结果 – 一个代表java.lang.String的Class类的实例。Class类实现了Type接口。

数组

java.lang.reflect.Array

       反射机制中是的数组,不要和java.util.Array混淆。

创建一个数组

int[] 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));

获取数组的Class对象

通常会用下面这个方法来获取普通对象以及原生对象的Class对象:

public Class getClass(String className){

 if("int" .equals(className)) return int .class;

 if("long".equals(className)) return long.class;

  ...

 return Class.forName(className);

}

一旦你获取了类型的Class对象,你就有办法轻松的获取到它的数组的Class对象,你可以通过指定的类型创建一个空的数组,然后通过这个空的数组来获取数组的Class对象。这样做有点讨巧,不过很有效。如下例:

Class theClass = getClass(theClassName);

Class stringArrayClass =Array.newInstance(theClass, 0).getClass();

这是一个特别的方式来获取指定类型的指定数组的Class对象。无需使用类名或其他方式来获取这个Class对象。

为了确保Class对象是不是代表一个数组,你可以使用Class.isArray()方法来进行校验:

Class stringArrayClass =Array.newInstance(String.class, 0).getClass();

System.out.println("is array: " +stringArrayClass.isArray());

获取数组的成员类型

获取了一个数组的Class对象,你就可以通过Class.getComponentType()方法获取这个数组的成员类型。成员类型就是数组存储的数据类型。例如,数组int[]的成员类型就是一个Class对象int.class。String[]的成员类型就是java.lang.String类的Class对象。

下面是一个访问数组成员类型的例子:

String[] strings = new String[3];

Class stringArrayClass =strings.getClass();

Class stringArrayComponentType = stringArrayClass.getComponentType();

System.out.println(stringArrayComponentType);

下面这个例子会打印“java.lang.String”代表这个数组的成员类型是字符串。

动态代理

       利用反射机制在运行期动态的创建接口的实现。

创建代理

可以通过使用Proxy.newProxyInstance()方法创建动态代理。newProxyInstance()方法有三个参数:

1、类加载器(ClassLoader)用来加载动态代理类。

2、一个要实现的接口的数组。

3、一个InvocationHandler把所有方法的调用都转到代理上。

如下例:

       InvocationHandlerhandler = new MyInvocationHandler();

       MyInterfaceproxy = (MyInterface) Proxy.newProxyInstance(

                                  MyInterface.class.getClassLoader(),

                                   new Class[] { MyInterface.class },

                                   handler);

在执行完这段代码之后,变量proxy包含一个MyInterface接口的的动态实现。所有对proxy的调用都被转向到实现了InvocationHandler接口的handler上。

InvocationHandler接口

调用Proxy.newProxyInstance()方法时,必须要传入一个InvocationHandler接口的实现。所有对动态代理对象的方法调用都会被转向到InvocationHandler接口的实现上,下面是InvocationHandler接口的定义:

       publicinterface InvocationHandler{

         Object invoke(Object proxy, Method method,Object[] args)

                throws Throwable;

       }

下面是它的实现类的定义:

       publicclass MyInvocationHandler implements InvocationHandler{

        

         public Object invoke(Object proxy, Methodmethod, Object[] args)

         throws Throwable {

           //do something "dynamic"

         }

       }

传入invoke()方法中的proxy参数是实现要代理接口的动态代理对象。通常你是不需要他的。

invoke()方法中的Method对象参数代表了被动态代理的接口中要调用的方法,从这个method对象中你可以获取到这个方法名字,方法的参数,参数类型等等信息。关于这部分内容可以查阅之前有关Method的文章。

Object数组参数包含了被动态代理的方法需要的方法参数。注意:原生数据类型(如int,long等等)方法参数传入等价的包装对象(如Integer, Long等等)。

常见用例

动态代理常被应用到以下几种情况中

1.数据库连接以及事物管理

2.单元测试中的动态Mock对象

3.自定义工厂与依赖注入(DI)容器之间的适配器

4.类似AOP的方法拦截器

动态类加载、重载

类加载器

所有Java应用中的类都是被java.lang.ClassLoader类的一系列子类加载的。因此要想动态加载类的话也必须使用java.lang.ClassLoader的子类。

一个类一旦被加载时,这个类引用的所有类也同时会被加载。类加载过程是一个递归的模式,所有相关的类都会被加载。但并不一定是一个应用里面所有类都会被加载,与这个被加载类的引用链无关的类是不会被加载的,直到有引用关系的时候它们才会被加载。

 

类加载体系

在Java中类加载是一个有序的体系。当你新创建一个标准的Java类加载器时你必须提供它的父加载器。当一个类加载器被调用来加载一个类的时候,首先会调用这个加载器的父加载器来加载。如果父加载器无法找到这个类,这时候这个加载器才会尝试去加载这个类。

类加载

类加载器加载类的顺序如下:

1、检查这个类是否已经被加载。

2、如果没有被加载,则首先调用父加载器加载。

3、如果父加载器不能加载这个类,则尝试加载这个类。

当你实现一个有重载类功能的类加载器,它的顺序与上述会有些不同。类重载不会请求的他的父加载器来进行加载。在后面的段落会进行讲解。

动态类加载

       动态加载一个类十分简单。你要做的就是获取一个类加载器然后调用它的loadClass()方法。下面是个例子:

       publicclass MainClass {

         public static void main(String[] args){    

           ClassLoader classLoader =MainClass.class.getClassLoader();    

           try {

               Class aClass =classLoader.loadClass("com.jenkov.MyClass");

               System.out.println("aClass.getName()= " + aClass.getName());

           } catch (ClassNotFoundException e) {

               e.printStackTrace();

           }

        

       }

动态类重载

动态类重载有一点复杂。Java内置的类加载器在加载一个类之前会检查它是否已经被加载。因此重载一个类是无法使用Java内置的类加载器的,如果想要重载一个类你需要手动继承ClassLoader。

在你定制ClassLoader的子类之后,你还有一些事需要做。所有被加载的类都需要被链接。这个过程是通过ClassLoader.resolve()方法来完成的。由于这是一个final方法,因此这个方法在ClassLoader的子类中是无法被重写的。resolve()方法是不会允许给定的ClassLoader实例链接一个类两次。所以每当你想要重载一个类的时候你都需要使用一个新的ClassLoader的子类。你在设计类重载功能的时候这是必要的条件。

自定义类重载

在前面已经说过你不能使用已经加载过类的类加载器来重载一个类。因此你需要其他的ClassLoader实例来重载这个类。但是这又带来了一些新的挑战。

所有被加载到Java应用中的类都以类的全名(包名 + 类名)作为一个唯一标识来让ClassLoader实例来加载。这意味着,类MyObject被类加载器A加载,如果类加载器B又加载了MyObject类,那么两个加载器加载出来的类是不同的。看看下面的代码:

       MyObjectobject = (MyObject)

       myClassReloadingFactory.newInstance("com.jenkov.MyObject");

MyObject类在上面那段代码中被引用,它的变量名是object。这就导致了MyObject这个类会被这段代码所在类的类加载器所加载。

如果myClassReloadingFactory工厂对象使用不同的类加载器重载MyObject类,你不能把重载的MyObject类的实例转换(cast)到类型为MyObject的对象变量。一旦MyObject类分别被两个类加载器加载,那么它就会被认为是两个不同的类,尽管它们的类的全名是完全一样的。你如果尝试把这两个类的实例进行转换就会报ClassCastException。

你可以解决这个限制,不过你需要从以下两个方面修改你的代码:

1、标记这个变量类型为一个接口,然后只重载这个接口的实现类。

2、标记这个变量类型为一个超类,然后只重载这个超类的子类。

请看下面这两个例子:

       MyObjectInterfaceobject = (MyObjectInterface)

       myClassReloadingFactory.newInstance("com.jenkov.MyObject");

 

       MyObjectSuperclassobject = (MyObjectSuperclass)

       myClassReloadingFactory.newInstance("com.jenkov.MyObject");

只要保证变量的类型是超类或者接口,这两个方法就可以正常运行,当它们的子类或是实现类被重载的时候超类跟接口是不会被重载的。

为了保证这种方式可以运行你需要手动实现类加载器然后使得这些接口或超类可以被它的父加载器加载。当你的类加载器加载MyObject类时,超类MyObjectSuperclass或者接口MyObjectSuperclass也会被加载,因为它们是MyObject的依赖。你的类加载器必须要代理这些类的加载到同一个类加载器,这个类加载器加载这个包括接口或者超类的类。

 

类加载/重载实例

下面这个例子是一个类加载器的子类。注意在这个类不想被重载的情况下它是如何把对一个类的加载代理到它的父加载器上的。如果一个类被它的父加载器加载,这个类以后将不能被重载。记住,一个类只能被同一个ClassLoader实例加载一次。

就像我之前说的那样,这仅仅是一个简单的例子,通过这个例子会向你展示类加载器的基本行为。这并不是一个可以让你直接用于设计你项目中类加载器的模板。你自己设计的类加载器应该不仅仅只有一个,如果你想用来重载类的话你可能会设计很多加载器。并且你也不会像下面这样将需要加载的类的路径硬编码(hardcore)到你的代码中。

       publicclass MyClassLoader extends ClassLoader{

        

           public MyClassLoader(ClassLoader parent) {

               super(parent);

           }

        

           public Class loadClass(String name) throwsClassNotFoundException {

              if(!"reflection.MyObject".equals(name))

                       return super.loadClass(name);

        

               try {

                   String url ="file:C:/data/projects/tutorials/web/WEB-INF/" +

                                  "classes/reflection/MyObject.class";

                   URL myUrl = new URL(url);

                   URLConnection connection =myUrl.openConnection();

                   InputStream input =connection.getInputStream();

                   ByteArrayOutputStream buffer = newByteArrayOutputStream();

                  int data = input.read();

        

                   while(data != -1){

                       buffer.write(data);

                       data = input.read();

                   }

        

                   input.close();

        

                   byte[] classData =buffer.toByteArray();

        

                   returndefineClass("reflection.MyObject",

                           classData, 0,classData.length);

 

               } catch (MalformedURLException e) {

                   e.printStackTrace();

               } catch (IOException e) {

                   e.printStackTrace();

               }

        

               return null;

           }

        

       }

下面是使用MyClassLoader的例子:

       publicstatic void main(String[] args) throws

           ClassNotFoundException,

           IllegalAccessException,

           InstantiationException {

        

           ClassLoader parentClassLoader = MyClassLoader.class.getClassLoader();

           MyClassLoader classLoader = newMyClassLoader(parentClassLoader);

           Class myObjectClass =classLoader.loadClass("reflection.MyObject");

        

           AnInterface2       object1 =

                   (AnInterface2) myObjectClass.newInstance();

        

           MyObjectSuperClass object2 =

                   (MyObjectSuperClass)myObjectClass.newInstance();

        

           //create new class loader so classes can bereloaded.

           classLoader = newMyClassLoader(parentClassLoader);

           myObjectClass = classLoader.loadClass("reflection.MyObject");

        

           object1 = (AnInterface2)       myObjectClass.newInstance();

           object2 = (MyObjectSuperClass)myObjectClass.newInstance();

        

       }

下面这个就是被加载的reflection.MyObject类。注意它既继承了一个超类并且也实现了一个接口。这样做仅仅是为了通过例子演示这个特性。在你自定义的情况下你可能仅会实现一个类或者继承一两个接口。

       publicclass MyObject extends MyObjectSuperClass implements AnInterface2{

           //... body of class ... override superclassmethods

           //   or implement interface methods

       }

0 0