Java 反射机制学习笔记

Java反射机制作用

        1.编译期(Compile Time)之外的运行期(Runtime)检查类,接口,变量以及方法的信息。

        2.运行期实例化对象,调用方法,通过调用 get/set 方法获取变量的值。(常用)


Java6的反射机制例子

        Method[] methods = MyObject.class.getMethods();

        for(Method method : methods){    

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

        }


Java反射可以获取类的如下内容

①   编译期获得对象(已知类名):MyObject.class //获取MyObject对象

      运行期获得对象(编译期未知类名,运行期获得类名字符串):Class.forName("packageName.className");   

②   cls.getName()  返回类的全限定类名(包含包名)

      cls.getSimpleName()  仅仅返回类的名称(不包含包名)

③   cls.getModifiers()    获得修饰符(public...)

④   cls.getPackage()  获取包名

⑤   cls.getSuperclass()  获取父类

⑥   cls.getInterfaces()  获取实现接口集合

⑦   cls.getConstructors()   获取所有构造方法

⑧   cls.getMethods()  获取一个类的所有方法

⑨   cls.getFields()  获取一个类的所有成员变量

⑩   cls..getAnnotations()  获取一个类的所有注解


Java构造器

①Constructor constructor =  cls.getConstructor(new Class[]{String.class}); //获取指定构造器

②Class[] constructor.getParameterTypes()  //获取指定构造方法的方法参数

 利用 Constructor 对象实例化一个类

    constructor.newInstance()方法的方法参数是一个可变参数列表,但是当你调用构造方法的时候你必须提供精确的参数,即形参与实参必须一一对应。

    Constructor constructor = MyObject.class.getConstructor(String.class);

    MyObject myObject = (MyObject) constructor.newInstance("args1");


Java变量

① Field[]  cls.getFields();    //获取所有成员变量

② Field cls.getField("someField")  //获取指定成员变量

③ field.getName() //得到变量名称

④ field.getType() //获取变量类型

⑤ Class  aClass = MyObject.class

    Field field = aClass.getField("someField");

    MyObject objectInstance = new MyObject();

    Object value = field.get(objectInstance);

    field.set(objetInstance, value);

传入 Field.get()/Field.set()方法的参数 objetInstance 应该是拥有指定变量的类的实例。在上述的例子中传入的参数是 MyObjec t类的实例,是因为 someField 是 MyObject 类的实例。 如果变量是静态变量的话(public static)那么在调用 Field.get()/Field.set()方法的时候传入 null 做为参数而不用传递拥有该变量的类的实例。


Java方法

  cls.getMethods(); //获取所有方法集合

②  cls.getMethod("doFunction", new Class[]{String.class}); //获取制定方法,并且参数是String类型,若没有参数设置为null

③ Class[] method.getParameterTypes();  //获取指定方法的参数

④ Class method.getReturnType();  //获取指定方法的返回类型

⑤ 通过Method调用方法

    //获取一个方法名为doSomesthing,参数类型为String的方法

   Method method = MyObject.class.getMethod("doSomething", String.class);

   Object returnValue = method.invoke(null, "parameter-value1");


Java访问器

Getter

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

Setter

    Setter 方法的名字以 set 开头,有一个方法参数。

    setters 方法有可能会有返回值也有可能没有,一些 Setter 方法返回 void,一些用来设置值,有一些对象的 setter 方法在方法链中被调用(译者注:这类的 setter 方法必须要有返回值),因此你不应该妄自假设 setter 方法的返回值,一切应该视情况而定。

//判断getter/setter方法

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;

}


Java泛型

两个典型的使用泛型的场景:

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

2、使用一个参数化类。

① 泛型方法返回类型

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

Type returnType = method.getGenericReturnType(); 

if(returnType instanceof ParameterizedType){   

         ParameterizedType type = (ParameterizedType) returnType;    

         Type[] typeArguments = type.getActualTypeArguments();   

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

         for(Type typeArgument : typeArguments){       

                 Class typeArgClass = (Class) typeArgument;     

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

        }

}


②泛型方法参数类型

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

         }    

    }

}


③ 泛型变量类型

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

     }

}


Java 动态代理

常见用例

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

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

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

    4. 类似 AOP 的方法拦截器

利用Java反射机制你可以在运行期动态的创建接口的实现。 java.lang.reflect.Proxy 类就可以实现这一功能。


①创建代理

Proxy.newProxyInstance()方法创建动态代理

newProxyInstance()方法有三个参数:

InvocationHandler handler = new MyInvocationHandler();

MyInterface proxy = (MyInterface) Proxy.newProxyInstance(                            

    MyInterface.class.getClassLoader(), //1、类加载器(ClassLoader)用来加载动态代理类。       new Class[] { MyInterface.class }, //2、一个要实现的接口的数组。                          

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


InvocationHandler 接口

public interface InvocationHandler{  

    Object invoke(Object proxy, Method method, Object[] args)  throws Throwable;

}

实现类

public class MyInvocationHandler implements InvocationHandler{  

     Object invoke(Object proxy, Method method, Object[] args)  throws Throwable { }

}


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

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

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


Java 动态类加载与重载

①类加载器

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

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

②类加载体系

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

③类加载

    类加载器加载类的顺序如下: 1、检查这个类是否已经被加载。 2、如果没有被加载,则首先调用父加载器加载。 3、如果父加载器不能加载这个类,则尝试加载这个类。


动态类加载

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 实例来重载这个类。





    


  

    


评论

© dzxlovelar | Powered by LOFTER