【Java】反射机制

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值