01、Java特性反射

反射

基本概念

反射机制:将类的各个组成部分封装为其他对象,这就是反射机制。

通俗来讲,反射就是来操作Java类的一种机制,通过反射可以操作Java中的字节码文件。

反射的核心类是Class类,它是类的类模板,即该类是用来描述Java类的。

描述的基本上就是一个类的组成部分:包名、类名、接口、父类、成员变量(Filed)、构造方法(Construct)、普通方法(Method)、注解(Annotation)等等。
所以说,反射的操作基本上就是对这些类的元素进行操作。

Java在计算机中会经历三个阶段,分别如下:

Source源代码阶段:*.java文件会被编译成*.class字节码文件。

Class对象阶段:*.class字节码文件会被类加载器装进内存,并将其封装成Class对象。

RunTime运行阶段:创建对象的过程。

其中,在Class对象阶段,Class对象将原字节码中所有成员变量封装成Field[],将构造函数封装成Construction[],将成员方法封装成Method[]。

获取Class对象

获取Class对象分别对应Java代码在计算机中的三个阶段:

1、Source源代码阶段:多用于配置文件的读取和加载。

Class.forName("全类名")。

2、Class类对象阶段:多用于参数的传递。

**.class

3、Runtime运行阶段:多用于对象的获取字节码的方式

xxx.getClass();

注意:同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,无论通过哪一种方式获取的Class对象都是同一个。

案例分析

下面的案例演示三种Java字节码的获取方式:

public class Main {
    public static void main(String[] args) throws ClassNotFoundException {
        // 方式一:Class.forName("全类名")
        Class clazz1 = Class.forName("com.legend.reflect.Person");
        System.out.println("clazz1 = " + clazz1);

        // 方式二:类名.class
        Class clazz2 = Person.class;
        System.out.println("clazz2 = " + clazz2);

        // 方式三:对象.getClass()
        Class clazz3 = new Person().getClass();
        System.out.println("clazz3 = " + clazz3);

        boolean isEquals = (clazz1 == clazz2) && (clazz2 == clazz3) ? true : false;
        System.out.println("clazz1 == clazz2 == clazz3:" + isEquals);
    }
}

输出结果如下所示:

clazz1 = class com.legend.reflect.Person
clazz2 = class com.legend.reflect.Person
clazz3 = class com.legend.reflect.Person
clazz1 == clazz2 == clazz3:true

Class对象功能

Class对象包含一个类的所有信息,这里只介绍主要的。

(1)获取成员变量

Field[] getFields() :获取所有public修饰的成员变量
Field getField(String name)   获取指定名称的 public修饰的成员变量

Field[] getDeclaredFields()  获取所有的成员变量,不考虑修饰符
Field getDeclaredField(String name) 

(2)获取构造函数

Constructor<?>[] getConstructors()  
Constructor<T> getConstructor(Class<?>... parameterTypes)  

Constructor<?>[] getDeclaredConstructors()  
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 

(3)获取成员方法

Method[] getMethods()  
Method getMethod(String name, Class<?>... parameterTypes)  

Method[] getDeclaredMethods()  
Method getDeclaredMethod(String name, Class<?>... parameterTypes)

(4)获取全类名

String getName() 

(5)获取类加载器

ClassLoader	getClassLoader()

Filed成员变量

下面针对Person对象中的三种修饰符:private、protected和public三种类型的成员变量使用反射赋值。

1、定义Person对象,并提供三种修饰符修饰的属性。

public class Person {
    public String name;
    protected int age;
    private String address;

	...get和set...
}

2、使用获取成员变量的方式进行反射,只能获取到public的成员变量。

public class Main {
    public static void main(String[] args) throws NoSuchFieldException {
        Class personClass = Person.class;
        // 获取所有public修饰的成员变量
        Field[] fields = personClass.getFields();
        for (Field field : fields){
            System.out.println(field);
        }
    }
}

获取所有的变量,必须使用getDeclaredFields()方法来完成。

public class Main {
    public static void main(String[] args) throws NoSuchFieldException {
        Class personClass = Person.class;
        Field[] fields = personClass.getDeclaredFields();
        for (Field field : fields){
            System.out.println(field);
        }
    }
}

注意:如果获取单个private或protected属性的值需要使用暴力反射来完成:setAccessible(true);

Constructor构造函数

通过反射的方式来创建对象,使用到的实体类如下:

public class Person {
    private String name;
    private Integer age;

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
	...get和set...
}

然后通过反射的方式来创建该对象:

public class Main {
    public static void main(String[] args) throws Exception {
        Class personClass = Person.class;
        Constructor[] constructors = personClass.getConstructors();
        for (Constructor constructor : constructors){
            System.out.println(constructor);
        }

        // 获取有参的构造函数来创建对象
        Constructor constructor = personClass.getConstructor(String.class, Integer.class);
        Object legend = constructor.newInstance("Legend", 30);
        System.out.println(legend);
    }
}

如果要获取private修饰的构造函数,需要使用getDeclaredConstructor方法。

Method方法对象

通过反射调用对象中的方法,所用到的实例类如下:

public class Person {
    private String name;
    private Integer age;

    public Person() {
    }
	...get和set...

    public void eat(String food){
        System.out.println("eat..." + food);
    }
}

然后通过反射的方式来执行eat方法:

public class Main {
    public static void main(String[] args) throws Exception {
        Class personClass = Person.class;
        // 获取所有public的方法
        Method[] methods = personClass.getMethods();
        for (Method method : methods){
            System.out.println(method);
        }

        // 执行字节码中的方法
        Object o = personClass.newInstance();
        Method method = personClass.getMethod("eat", String.class);
        method.invoke(o, "apple");
    }
}

反射的大致用法如上所示,还可以通过反射的方式去修改配置类并运行。

原文地址:https://www.cnblogs.com/pengjingya/p/14924718.html