Java基础增强-反射机制

1 反射机制简介

Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。

反射这一概念最早由编程开发人员Smith在1982年提出,主要指应用程序访问、检测、修改自身状态与行为的能力。这一概念的提出立刻吸引了编程界的极大关注,各种研究工作随之展开,随之而来引发编程革命,出现了多种支持反射机制的面向对象语言。

在计算机科学领域,反射是指一类能够自我描述和自控制的应用。在Java编程语言中,反射是一种强有力的工具,是面向抽象编程一种实现方式,它能使代码语句更加灵活,极大提高代码的运行时装配能力。

2 Class类

对于一个字节码文件.class,虽然表面上我们对该字节码文件一无所知,但该文件本身却记录了许多信息。Java在将.class字节码文件载入时,JVM将产生一个java.lang.Class对象代表该.class字节码文件,从该Class对象中可以获得类的许多基本信息,这就是反射机制。所以要想完成反射操作,就必须首先认识Class类。 [1]

反射机制所需的类主要有java.lang包中的Class类和java.lang.reflet包中的Constructor类、Field类、Method类和Parameter类。Class类是一个比较特殊的类,它是反射机制的基础,Class类的对象表示正在运行的Java程序中的类或接口,也就是任何一个类被加载时,即将类的.class文件(字节码文件)读入内存的同时,都自动为之创建一个java.lang.Class对象。Class类没有公共构造方法,其对象是JVM在加载类时通过调用类加载器中的defineClass()方法创建的,因此不能显式地创建一个Class对象。通过这个Class对象,才可以获得该对象的其他信息

3 反射思维导图

4 反射的具体体现

4.1 准备工作

1、新建@InheritedAnnotation注解

@Target({ElementType.FIELD, ElementType.TYPE, ElementType.CONSTRUCTOR})
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface InheritedAnnotation {
    String value();
}

2、新建@MyAnnotation注解

@Target({ElementType.FIELD, ElementType.TYPE, ElementType.CONSTRUCTOR})
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value();
}

3、新建一个Person类

@InheritedAnnotation("Inherited")
public class Person {

    private Integer id;

    public Person() {
    }

    public void say() {
        System.out.println("Hello Person");
    }
}

4、新建一个User类继承Person类

@MyAnnotation("myAnnotation")
public class User extends Person {

    @MyAnnotation("name")
    public String name;

    @MyAnnotation("sex")
    private String sex;

    protected int age;

    public User() {
    }

    @MyAnnotation("User(name, sex, age)")
    public User(String name, String sex, int age) {
        this.name = name;
        this.sex = sex;
        this.age = age;
    }

    @MyAnnotation("name")
    private User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", age=" + age +
                '}';
    }

    private void sayHello() {
        System.out.println("Hello World");
    }
}

4.2 获取Class类的三种方式

	/**
     * 三种获取Class对象的方式
     * @param obj
     * @return
     * @throws ClassNotFoundException
     */
    public static Class getClazz(Object obj) throws ClassNotFoundException {
        //第一种方式:通过对象的全路径名获取Class对象
        Class clazz = Class.forName("com.base.domain.User");
        System.out.println("1、通过对象的路径获取Class对象:" + clazz);
        //第二种方式:通过对象的getClass方法获取Class对象
        Class clazz1 = obj.getClass();
        System.out.println("2、通过对象实例的getClass方法获取Class对象:" + clazz1);
        //第三种方式:通过对象的.class方法获取Class对象
        Class clazz2 = User.class;
        System.out.println("3、通过对象.class方法获取Class对象:" + clazz2);
        return clazz;
    }

执行:

public class ReflectTest {
    public static void main(String[] args) throws Exception {
        User user = new User();
        Class clazz = ReflectUtils.getClazz(user);
    }
}

执行结果:

1、通过对象的路径获取Class对象:class com.base.domain.User
2、通过对象实例的getClass方法获取Class对象:class com.base.domain.User
3、通过对象.class方法获取Class对象:class com.base.domain.User

4.3 获取类的所有属性

	/**
     * 获取类的所有属性
     * @param clazz
     * @throws NoSuchFieldException
     */
    public static void getAllFields(Class clazz) throws NoSuchFieldException {
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            System.out.println("获取类中的所有属性:" + field);
        }
        Field field = clazz.getDeclaredField("name");
        System.out.println("通过属性名获取类的属性:" + field);
    }

执行结果:

获取类中的所有属性:public java.lang.String com.base.domain.User.name
获取类中的所有属性:private java.lang.String com.base.domain.User.sex
获取类中的所有属性:protected int com.base.domain.User.age
通过属性名获取类的属性:public java.lang.String com.base.domain.User.name

4.4 获取类的公开属性

	/**
     * 获取类中的公开属性
     * @param clazz
     * @throws NoSuchFieldException
     */
    public static void getPublicFields(Class clazz) throws NoSuchFieldException {
        Field[] fields = clazz.getFields();
        for (Field field : fields) {
            System.out.println("获取类中的共开属性:" + field);
        }

        //clazz.getField(String name)方式不能获取私有的属性,会抛出NoSuchFieldException异常
        Field field1 = clazz.getField("name");
        System.out.println("通过属性名获取类的公开属性:" + field1);
    }

执行结果:

获取类中的共开属性:public java.lang.String com.base.domain.User.name
通过属性名获取类的公开属性:public java.lang.String com.base.domain.User.name

4.5 获取本类的所有方法

	/**
     * 获取本类中的所有方法
     * @param clazz
     */
    public static void getAllMethods(Class clazz) throws NoSuchMethodException {
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println("获取本类的所有方法:" + method);
        }

        Method method = clazz.getDeclaredMethod("setSex", String.class);
        System.out.println("通过方法名获取setSex方法" + method);
    }

执行结果:

获取本类的所有方法:public java.lang.String com.base.domain.User.toString()
获取本类的所有方法:public java.lang.String com.base.domain.User.getName()
获取本类的所有方法:public void com.base.domain.User.setName(java.lang.String)
获取本类的所有方法:public java.lang.String com.base.domain.User.getSex()
获取本类的所有方法:public void com.base.domain.User.setSex(java.lang.String)
获取本类的所有方法:public int com.base.domain.User.getAge()
获取本类的所有方法:public void com.base.domain.User.setAge(int)
获取本类的所有方法:private void com.base.domain.User.sayHello()
通过方法名获取setSex方法public void com.base.domain.User.setSex(java.lang.String)

4.6 获取本类及父类的公开方法

	/**
     * 获取本类及父类的所有公开方法,私有方法不会被获取
     * @param clazz
     */
    public static void getPublicMethods(Class clazz) throws NoSuchMethodException {
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            System.out.println("获取类的所有公开方法,包括父类的方法:" + method);
        }

        Method method = clazz.getMethod("getSex");
        System.out.println("通过方法名获取getSex方法" + method);
    }

执行结果:

获取类的所有公开方法,包括父类的方法:public java.lang.String com.base.domain.User.toString()
获取类的所有公开方法,包括父类的方法:public java.lang.String com.base.domain.User.getName()
获取类的所有公开方法,包括父类的方法:public void com.base.domain.User.setName(java.lang.String)
获取类的所有公开方法,包括父类的方法:public java.lang.String com.base.domain.User.getSex()
获取类的所有公开方法,包括父类的方法:public void com.base.domain.User.setSex(java.lang.String)
获取类的所有公开方法,包括父类的方法:public int com.base.domain.User.getAge()
获取类的所有公开方法,包括父类的方法:public void com.base.domain.User.setAge(int)
获取类的所有公开方法,包括父类的方法:public void com.base.domain.Person.say()
获取类的所有公开方法,包括父类的方法:public final void java.lang.Object.wait() throws java.lang.InterruptedException
获取类的所有公开方法,包括父类的方法:public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
获取类的所有公开方法,包括父类的方法:public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
获取类的所有公开方法,包括父类的方法:public boolean java.lang.Object.equals(java.lang.Object)
获取类的所有公开方法,包括父类的方法:public native int java.lang.Object.hashCode()
获取类的所有公开方法,包括父类的方法:public final native java.lang.Class java.lang.Object.getClass()
获取类的所有公开方法,包括父类的方法:public final native void java.lang.Object.notify()
获取类的所有公开方法,包括父类的方法:public final native void java.lang.Object.notifyAll()
通过方法名获取getSex方法public java.lang.String com.base.domain.User.getSex()

4.7 获取类的所有构造方法

**
     * 获取类的所有构造方法
     * @param clazz
     */
    public static void getAllConstructors(Class clazz) throws NoSuchMethodException {
        Constructor[] constructors = clazz.getDeclaredConstructors();
        for (Constructor constructor : constructors) {
            System.out.println("获取类的所有构造方法:" + constructor);
        }

        Constructor constructor = clazz.getDeclaredConstructor(String.class);
        System.out.println("通过参数个数及类型获取构造方法:" + constructor);
    }

执行结果:

获取类的所有构造方法:private com.base.domain.User(java.lang.String)
获取类的所有构造方法:public com.base.domain.User(java.lang.String,java.lang.String,int)
获取类的所有构造方法:public com.base.domain.User()
通过参数个数及类型获取构造方法:private com.base.domain.User(java.lang.String)

4.8 获取类的公开构造方法

	/**
     * 获取类的公开构造方法
     * @param clazz
     */
    public static void getPublicConstructors(Class clazz) throws NoSuchMethodException {
        Constructor[] constructors = clazz.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println("获取类的公开构造方法:" + constructor);
        }
        Constructor constructor = clazz.getConstructor(String.class, String.class, int.class);
        System.out.println("通过参数个数及类型获取公开的构造方法:" + constructor);
    }

执行结果:

获取类的公开构造方法:public com.base.domain.User(java.lang.String,java.lang.String,int)
获取类的公开构造方法:public com.base.domain.User()
通过参数个数及类型获取公开的构造方法:public com.base.domain.User(java.lang.String,java.lang.String,int)

4.9 获取本类上的注解

	/**
     * 获取类上的注解
     * @param clazz
     */
    public static void getClassAnnotations(Class clazz) {
        Annotation[] annotations = clazz.getDeclaredAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println("获取类上的注解" + annotation);
        }

        Annotation annotation = clazz.getDeclaredAnnotation(MyAnnotation.class);
        System.out.println("通过注解类获取注解:" + annotation);
    }

执行结果:

获取类上的注解@com.base.annotation.MyAnnotation(value=myAnnotation)
通过注解类获取注解:@com.base.annotation.MyAnnotation(value=myAnnotation)

4.10 获取本类及父类的注解

/**
 * 获取类上的注解,会将父类中用@Inherited修饰的注解获取到
 * @param clazz
 */
public static void getAnnotations(Class clazz) {
    Annotation[] annotations = clazz.getAnnotations();
    for (Annotation annotation : annotations) {
        System.out.println("获取父类中可继承的及本类中的类注解" + annotation);
    }
    Annotation annotation = clazz.getAnnotation(InheritedAnnotation.class);
    System.out.println("通过注解类获取注解:" + annotation);
}

执行结果:

获取父类中可继承的及本类中的类注解@com.base.annotation.InheritedAnnotation(value=Inherited)
获取父类中可继承的及本类中的类注解@com.base.annotation.MyAnnotation(value=myAnnotation)
通过注解类获取注解:@com.base.annotation.InheritedAnnotation(value=Inherited)

4.11 通过反射实例化对象

	/**
     * 通过反射实例化对象
     * @param clazz
     * @return
     * @throws Exception
     */
    public static  Object newInstanceObject(Class clazz) throws Exception {
        User user = (User)clazz.newInstance();
        System.out.println("通过newInstance方式实例化对象:" + user.toString());

        Constructor constructor = clazz.getDeclaredConstructor(String.class, String.class, int.class);
        user = (User) constructor.newInstance("张三", "男", 18);
        System.out.println("通过公开的构造方法方式实例化对象:" + user.toString());

        constructor = clazz.getDeclaredConstructor(String.class);
        constructor.setAccessible(true);//访问私有的方法及属性需要将Accessible设置为true
        user = (User) constructor.newInstance("李四");
        System.out.println("通过私有的构造方法方式实例化对象:" + user.toString());

        return user;
    }

执行结果:

通过newInstance方式实例化对象:User{name='null', sex='null', age=0}
通过公开的构造方法方式实例化对象:User{name='张三', sex='男', age=18}
通过私有的构造方法方式实例化对象:User{name='李四', sex='null', age=0}

4.12 通过反射调用方法

/**
     * 通过反射调用方法
     * @param clazz
     * @throws Exception
     */
    public static void  invokeMethod(Class clazz) throws Exception {
        Method method = clazz.getDeclaredMethod("setAge", int.class);
        User user = (User) newInstanceObject(clazz);
        method.invoke(user, 28);
        System.out.println("通过反射调用setAge方法:" + user.toString());
    }

执行结果:

通过反射调用setAge方法:User{name='李四', sex='null', age=28}

4.13 通过反射修改属性

	/**
     * 通过反射修改属性值
     * @param clazz
     * @throws Exception
     */
    public static void invokeUpdateField(Class clazz) throws Exception {
        Field field = clazz.getDeclaredField("sex");
        User user = (User) newInstanceObject(clazz);
        field.setAccessible(true);//如果是公开的属性,则不需要setAccessible为true
        field.set(user, "女");
        System.out.println("通过反射修改对象的私有属性值:" + user.toString());
    }

执行结果:

通过反射修改对象的私有属性值:User{name='李四', sex='女', age=0}

5 反射意义

首先,反射机制极大的提高了程序的灵活性和扩展性,降低模块的耦合性,提高自身的适应能力。

其次,通过反射机制可以让程序创建和控制任何类的对象,无需提前硬编码目标类。

再次,使用反射机制能够在运行时构造一个类的对象、判断一个类所具有的成员变量和方法、调用一个对象的方法。

最后,反射机制是构建框架技术的基础所在,使用反射可以避免将代码写死在框架中。

正是反射有以上的特征,所以它能动态编译和创建对象,极大的激发了编程语言的灵活性,强化了多态的特性,进一步提升了面向对象编程的抽象能力,因而受到编程界的青睐。

6 反射特点

尽管反射机制带来了极大的灵活性及方便性,但反射也有缺点。反射机制的功能非常强大,但不能滥用。在能不使用反射完成时,尽量不要使用,原因有以下几点:

1、性能问题。

Java反射机制中包含了一些动态类型,所以Java虚拟机不能够对这些动态代码进行优化。因此,反射操作的效率要比正常操作效率低很多。我们应该避免在对性能要求很高的程序或经常被执行的代码中使用反射。而且,如何使用反射决定了性能的高低。如果它作为程序中较少运行的部分,性能将不会成为一个问题。

2、安全限制。

使用反射通常需要程序的运行没有安全方面的限制。如果一个程序对安全性提出要求,则最好不要使用反射。

3、程序健壮性。

反射允许代码执行一些通常不被允许的操作,所以使用反射有可能会导致意想不到的后果。反射代码破坏了Java程序结构的抽象性,所以当程序运行的平台发生变化的时候,由于抽象的逻辑结构不能被识别,代码产生的效果与之前会产生差异。

7 总结

1、Calss.getAnnotations();方法获取注解,父类的注解必须含有元注解@Inherited才能被获取;

2、通过私有的构造函数实例化对象或者修改私有属性时需要设置setAccessible为true,才能正常执行;

3、本文列举了反射机制使用过程中常用的、重要的一些类及其方法,其他用法请自行查看官方文档。

posted @ 2020-04-02 17:23  天意斩三生  阅读(442)  评论(0编辑  收藏  举报