Java&Spring基础技术Java学习之路Java 杂谈

java反射机制(精讲)

2017-05-13  本文已影响325人  CoderZS

一、概述

Java反射机制定义

Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类中的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。枚举除外

** 反射机制的功能**

  • 在运行时判断任意一个对象所属的类。

通过反射来获取某一个类的构造器

需求:通过反射来获取某一个类的构造器:
1):获取该类的字节码对象.
2)从该字节码对象中去找需要获取的构造器.
Class类获取构造器方法:
Constructor类:表示类中构造器的类型,Constructor的实例就是某一个类中的某一个构造器
public Constructor<?>[] getConstructors():该方法只能获取当前Class所表示类的public修饰的构造器
public Constructor<?>[] getDeclaredConstructors():获取当前Class所表示类的所有的构造器,和访问权限无关
public Constructor<T> getConstructor(Class<?>... parameterTypes)

注意获取当前Class所表示类中指定的一个public的构造器
参数:parameterTypes表示:构造器参数的Class类型
如:
public User(String name)
Constructor c = clz.getConstructor(String.class);
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
获取当前Class所表示类中指定的一个的构造
访问私有的成员: 必须先设置可访问的 对象setAccessible(true);

import java.lang.reflect.Constructor;
class User1 {
String name;
int age;
// create constructor
public User1() {
    System.out.println("无参构造器");
}
public User1(String name) {
    this.name = name;
    System.out.println("构造器" + name);
}

private User1(String name, int age) {
    this.name = name;
    this.age = age;
    System.out.println("构造器" + name + age);
  }
}

public class ReflactDemo_03 {
public static void main(String[] args) throws Exception {
// 传统的方式创建对象
// new User1();
// new User1("CoderZS");
// 获取类的字节码
Class<User1> clz = User1.class;
// 获取public User1() 注意有public 修饰的 一定与方法相匹配
Constructor<User1> con = clz.getConstructor();
// 用构造器的newInstance方法来创建对象 会调用构造器的方法
con.newInstance();
System.out.println(con);

// 调用public User1(String name)
con = clz.getConstructor(String.class);
con.newInstance("CoderZS");

// 调用 private User1(String name,int age)!!!!!!!!!!!!!!
con = clz.getDeclaredConstructor(String.class, int.class);
// 设置当前构造器可以访问
// 访问私有的成员:必须先设置可访问的对象
// setAccessible(true);
con.setAccessible(true);
con.newInstance("CoderZS", 18);
  }
}
使用反射获取方法
 import java.lang.reflect.Method;
/**
 * 需求:使用反射获取类中的方法: 1):获取方法所在类的字节码对象.  2):获取方法.

 * Class类中常用方法: public Method[] getMethods():获取包括自身和继承过来的所有的public方法

*  public Method[] getDeclaredMethods():获取自身类中所有的方法(不包括继承的,和访问权限无关) 

 * public Method getMethod(String methodName, Class<?>...parameterTypes)
 * :表示调用指定的一个公共的方法(包括继承的) 参数: methodName: 表示被调用方法的名字
 * parameterTypes:表示被调用方法的参数的Class类型如String.class

* public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
 * :表示调用指定的一个本类中的方法(不包括继承的) 参数: methodName: 表示被调用方法的名字
 * parameterTypes:表示被调用方法的参数的Class类型如String.class
 */
class User2 {
public void doWork() {
}
public static void doWork(String name) {
}

private String doNotWork(String name, int age) {

    return name + "有参有返回值" + age;
}

// 获取User2类中指定的一个方法
static void getOne1() throws Exception {
    // 获取类的字节码
    Class<User2> clz = User2.class;
    
// 需求1:获取doWork()
    Method m = clz.getMethod("doWork");
    System.out.println(m);
    
// 需求:获取private String doNotWork(String name,int age)
    m = clz.getDeclaredMethod("doNotWork", String.class, int.class);
    System.out.println(m);
}

static void getAll() {
    // 获取类的字节码
    Class<User2> clz = User2.class;
    // 需求1:获取doWork()
    Method[] ms = clz.getMethods();
    
    System.out.println(ms);
    System.out.println(ms.length);
    for (Method m : ms) {
        System.out.println(m);
    }
  }
}

// 使用反射获取类中方法
public class ReflactDemo_04 {
public static void main(String[] args) throws Exception {
    // User2.getOne1();
    User2.getAll();
  }
}
使用反射调用私有静态方法(public-private--static)
import java.lang.reflect.Method;
/**
 * 使用反射调用方法: 1):获取方法所在类的字节码对象. 2):获取方法对象. 3):使用反射调用方法. 
 * 如何使用反射调用一个方法:
 * 在Method类中有方法: public Object invoke(Object obj,Object...args):表示调用当前Method所表示的方法 
 * 参数: obj: 表示被调用方法底层所属对象
 *  Method m =clz.getMethod("doWork",String.class);
 * args:表示调用方法是传递的实际参数 返回: 底层方法的返回结果
 * 调用私有方法: 在调用私有方法之前:应该设置该方法为可访问的 又因为Method是AccessibleObject子类,所以Method中具有该方法.
 * doWorkMethod.setAccessible(true);
*/
class User3 {
public void doWork() {
    System.out.println("使用反射调用--无参--方法--doWork");
}

public void doWork(String name) {

    System.out.println("使用反射调用--有参--方法---doWork");
}

private String doWork(String name, int age) {

    return name + "使用反射调用--私有--方法" + age;
}

public static String doWork(String name, String sex, int age) {

    return name + "使用反射调用--静态--方法" + sex + age;
}
}

public class ReflactDemo_05 {
public static void main(String[] args) throws Exception {
    // TODO Auto-generated method stub
    // 使用反射调用方法
    // 需求1:调用public void doWork()
    // 获取类的字节码
    Class<User3> clz = User3.class;
    // 获取指定方法
    Method m = clz.getMethod("doWork");
    // 创建对象
    Object result = m.invoke(clz.newInstance());
    System.out.println(result);// null

    // 需求2:public static void doWork(String name)
    m = clz.getMethod("doWork", String.class);
    result = m.invoke(clz.newInstance(), "CoderZS");
    
    // 需求3:调用private String doNotWork(String name, int age)
    m = clz.getDeclaredMethod("doWork", String.class, int.class);
    m.setAccessible(true);
    result = m.invoke(clz.newInstance(), "CoderZS", 18);
    System.out.println(result);

    // 需求4,使用反射调用静态方法: 静态方法不属于任何对象,静态方法属于类本身.此时把invoke方法的第一个参数设置为null即可.
    m = clz.getDeclaredMethod("doWork", String.class, String.class, int.class);
    m.setAccessible(true);
    result = m.invoke(null, "CoderZS", "boy", 18);
    System.out.println(result);
  }
}
概括

获取class对象的其他信息

boolean isPrimitive = class1.isPrimitive();//判断是否是基础类型
boolean isArray = class1.isArray();//判断是否是集合类
boolean isAnnotation = class1.isAnnotation();//判断是否是注解类
boolean isInterface = class1.isInterface();//判断是否是接口类
boolean isEnum = class1.isEnum();//判断是否是枚举类
boolean isAnonymousClass = class1.isAnonymousClass();//判断是否是匿名内部类
boolean isAnnotationPresent = class1.isAnnotationPresent(Deprecated.class);//判断是否被某个注解类修饰
String className = class1.getName();//获取class名字 包含包名路径
Package aPackage = class1.getPackage();//获取class的包信息
String simpleName = class1.getSimpleName();//获取class类名
int modifiers = class1.getModifiers();//获取class访问权限
Class<?>[] declaredClasses = class1.getDeclaredClasses();//内部类
Class<?> declaringClass = class1.getDeclaringClass();//外部类

动态代理是指在运行时动态生成代理类。即,代理类的字节码将在运行时生成并载入当前代理的 ClassLoader。与静态处理类相比,动态类有诸多好处。

动态代理涉及的主要类

主要涉及两个类,这两个类都是java.lang.reflect包下的类,内部主要通过反射来实现的。
java.lang.reflect.Proxy: 这是生成代理类的主类,通过 Proxy 类生成的代理类都继承了 Proxy 类。
Proxy提供了用户创建动态代理类和代理对象的静态方法,它是所有动态代理类的父类。
java.lang.reflect.InvocationHandler: 这里称他为"调用处理器",它是一个接口。当调用动态代理类中的方法时,将会直接转接到执行自定义的InvocationHandler中的invoke()方法。即我们动态生成的代理类需要完成的具体内容需要自己定义一个类,而这个类必须实现 InvocationHandler 接口,通过重写invoke()方法来执行具体内容。

Proxy提供了如下两个方法来创建动态代理类和动态代理实例。

static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) 返回代理类的java.lang.Class对象。第一个参数是类加载器对象(即哪个类加载器来加载这个代理类到 JVM 的方法区),第二个参数是接口(表明你这个代理类需要实现哪些接口),第三个参数是调用处理器类实例(指定代理类中具体要干什么),该代理类将实现interfaces所指定的所有接口,执行代理对象的每个方法时都会被替换执行InvocationHandler对象的invoke方法。

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 返回代理类实例。参数与上述方法一致。

对应上述两种方法创建动态代理对象的方式:

    //创建一个InvocationHandler对象
    InvocationHandler handler = new MyInvocationHandler(.args..);
    //使用Proxy生成一个动态代理类
    Class proxyClass = Proxy.getProxyClass(RealSubject.class.getClassLoader(),RealSubject.class.getInterfaces(), handler);
    //获取proxyClass类中一个带InvocationHandler参数的构造器
    Constructor constructor = proxyClass.getConstructor(InvocationHandler.class);
    //调用constructor的newInstance方法来创建动态实例
    RealSubject real = (RealSubject)constructor.newInstance(handler);

    //创建一个InvocationHandler对象
    InvocationHandler handler = new MyInvocationHandler(.args..);
    //使用Proxy直接生成一个动态代理对象
    RealSubject real =Proxy.newProxyInstance(RealSubject.class.getClassLoader(),RealSubject.class.getInterfaces(), handler);

newProxyInstance这个方法实际上做了两件事:第一,创建了一个新的类【代理类】,这个类实现了Class[] interfaces中的所有接口,并通过你指定的ClassLoader将生成的类的字节码加载到JVM中,创建Class对象;第二,以你传入的InvocationHandler作为参数创建一个代理类的实例并返回。
Proxy 类还有一些静态方法,比如:
InvocationHandler getInvocationHandler(Object proxy):获得代理对象对应的调用处理器对象。
Class getProxyClass(ClassLoader loader, Class[] interfaces):根据类加载器和实现的接口获得代理类。
InvocationHandler 接口中有方法:
invoke(Object proxy, Method method, Object[] args)
这个函数是在代理对象调用任何一个方法时都会调用的,方法不同会导致第二个参数method不同,第一个参数是代理对象(表示哪个代理对象调用了method方法),第二个参数是 Method 对象(表示哪个方法被调用了),第三个参数是指定调用方法的参数。

动态代理模式的简单实现

public class DynamicProxyDemo {
public static void main(String[] args) {
    //1.创建目标对象
    RealSubject realSubject = new RealSubject();    
    //2.创建调用处理器对象
    ProxyHandler handler = new ProxyHandler(realSubject);    
   //3.动态生成代理对象
    Subject proxySubject = (Subject)Proxy.newProxyInstance(RealSubject.class.getClassLoader(),                                                    
RealSubject.class.getInterfaces(), handler);   
    //4.通过代理对象调用方法   
    proxySubject.request();    
   }
}

/**
 * 主题接口
 */
interface Subject{
void request();
}

/**
 * 目标对象类
 */
class RealSubject implements Subject{
public void request(){
    System.out.println("====RealSubject Request====");
}
}
/**
 * 代理类的调用处理器
 */
class ProxyHandler implements InvocationHandler{
private Subject subject;
public ProxyHandler(Subject subject){
    this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable {
    //定义预处理的工作,当然你也可以根据 method 的不同进行不同的预处理工作
    System.out.println("====before====");
   //调用RealSubject中的方法
    Object result = method.invoke(subject, args);
    System.out.println("====after====");
    return result;
}
}

可以看到,我们通过newProxyInstance就产生了一个Subject 的实例,即代理类的实例,然后就可以通过Subject .request(),就会调用InvocationHandler中的invoke()方法,传入方法Method对象,以及调用方法的参数,通过Method.invoke调用RealSubject中的方法的request()方法。同时可以在InvocationHandler中的invoke()方法加入其他执行逻辑。

泛型和Class类

从JDK 1.5 后,Java中引入泛型机制,Class类也增加了泛型功能,从而允许使用泛型来限制Class类,例如:String.class的类型实际上是Class<String>。如果Class对应的类暂时未知,则使用Class<?>(?是通配符)。通过反射中使用泛型,可以避免使用反射生成的对象需要强制类型转换。
泛型的好处众多,最主要的一点就是避免类型转换,防止出现ClassCastException,即类型转换异常。以下面程序为例:

public class ObjectFactory {
public static Object getInstance(String name){
    try {
        //创建指定类对应的Class对象
        Class cls = Class.forName(name);
        //返回使用该Class对象创建的实例
        return cls.newInstance();
    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
        e.printStackTrace();
        return null;
    }
  }
}

上面程序是个工厂类,通过指定的字符串创建Class对象并创建一个类的实例对象返回。但是这个对象的类型是Object对象,取出实例后需要强制类型转换。
如下例:

Date date = (Date) ObjectFactory.getInstance("java.util.Date");

String string = (String) ObjectFactory.getInstance("java.util.Date");

上面代码在编译时不会有任何问题,但是运行时将抛出ClassCastException异常,因为程序试图将一个Date对象转换成String对象。
但是泛型的出现后,就可以避免这种情况。

public class ObjectFactory {
public static <T> T getInstance(Class<T> cls) {
    try {
        // 返回使用该Class对象创建的实例
        return cls.newInstance();
    } catch (InstantiationException | IllegalAccessException e) {
        e.printStackTrace();
        return null;
    }
  }
}

在上面程序的getInstance()方法中传入一个Class<T>参数,这是一个泛型化的Class对象,调用该Class对象的newInstance()方法将返回一个T对象。

String instance = ObjectFactory.getInstance(String.class);

通过传入String.class便知道T代表String,所以返回的对象是String类型的,避免强制类型转换。

当然Class类引入泛型的好处不止这一点,在以后的实际应用中会更加能体会到。

使用反射来获取泛型信息

通过指定类对应的 Class 对象,可以获得该类里包含的所有 Field,不管该 Field 是使用 private 修饰,还是使用 public 修饰。获得了 Field 对象后,就可以很容易地获得该 Field 的数据类型,即使用如下代码即可获得指定 Field 的类型。
// 获取 Field 对象 f 的类型
Class<?> a = f.getType();
但这种方式只对普通类型的 Field 有效。如果该 Field 的类型是有泛型限制的类型,如 Map<String, Integer> 类型,则不能准确地得到该 Field 的泛型参数。
为了获得指定 Field 的泛型类型,应先使用如下方法来获取指定 Field 的类型。
// 获得 Field 实例的泛型类型
Type type = f.getGenericType();
然后将 Type 对象强制类型转换为 ParameterizedType 对象,ParameterizedType 代表被参数化的类型,也就是增加了泛型限制的类型。ParameterizedType 类提供了如下两个方法。
getRawType():返回没有泛型信息的原始类型。
getActualTypeArguments():返回泛型参数的类型。

下面是一个获取泛型类型的完整程序。
public class GenericTest
{
private Map<String , Integer> score;
public static void main(String[] args)
    throws Exception
{
    Class<GenericTest> clazz = GenericTest.class;
    Field f = clazz.getDeclaredField("score");
    // 直接使用getType()取出Field类型只对普通类型的Field有效
    Class<?> a = f.getType();
    // 下面将看到仅输出java.util.Map
    System.out.println("score的类型是:" + a);
    // 获得Field实例f的泛型类型
    Type gType = f.getGenericType();
    // 如果gType类型是ParameterizedType对象
    if(gType instanceof ParameterizedType)
    {
        // 强制类型转换
        ParameterizedType pType = (ParameterizedType)gType;
        // 获取原始类型
        Type rType = pType.getRawType();
        System.out.println("原始类型是:" + rType);
        // 取得泛型类型的泛型参数
        Type[] tArgs = pType.getActualTypeArguments();
        System.out.println("泛型类型是:");
        for (int i = 0; i < tArgs.length; i++) 
        {
            System.out.println("第" + i + "个泛型类型是:" + tArgs[i]);
        }
    }
    else
    {
        System.out.println("获取泛型类型出错!");
    }
  }
}

输出结果:

score 的类型是: interface java.util.Map
原始类型是: interface java.util.Map
泛型类型是:
第 0 个泛型类型是: class java.lang.String
第 1 个泛型类型是:class java.lang.Integer

从上面的运行结果可以看出,直接使用 Field 的 getType() 方法只能获取普通类型的 Field 的数据类型:对于增加了泛型参数的类型的 Field,应该使用 getGenericType() 方法来取得其类型。
Type 也是 java.lang.reflect 包下的一个接口,该接口代表所有类型的公共高级接口,Class 是 Type 接口的实现类。Type 包括原始类型、参数化类型、数组类型、类型变量和基本类型等。

上一篇下一篇

猜你喜欢

热点阅读