【Java】反射机制

来源:互联网 发布:win7打印机无usb端口 编辑:程序博客网 时间:2024/06/06 14:15

Java反射机制作用

1.编译期(Compile Time)之外的运行期(Runtime)检查类,接口,变量以及方法的信息。
2.运行期实例化对象,调用方法,通过调用 get/set 方法获取变量的值。(常用)

Java6的反射机制例子

    //得到MyObject所有方法    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)容器之间的适配器

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

0 0
原创粉丝点击