Java:反射机制

反射的理解

/*
Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法
框架 = 反射 + 注解 + 设计模式
*/

反射机制能提供的功能

/*
1. 在运行时判断任意一个对象所属的类
2. 在运行时构造任意一个类的对象
3. 在运行时判断任意一个类所具有的成员变量和方法
4. 在运行时获取泛型信息
5. 在运行时调用任意一个对象的成员变量和方法
6. 在运行时处理注解
7. 生成动态代理
*/

反射前对Person类的操作

Person.java

package Reflection;

/**
 * ClassName: Person
 * Date:      2020/3/11 12:25
 * author:    Oh_MyBug
 * version:   V1.0
 */
public class Person {
    private String name;
    public int age;

    public Person() {
        System.out.println("这是空参构造器:Person()");
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    private Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public void show(){
        System.out.println("你好,我是一个人");
    }

    private String showNation(String nation){
        System.out.println("我的国籍是:" + nation);
        return nation;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
@Test
public void test1(){
    // 1. 创建Person类的对象
    Person p1 = new Person("Tom",12);
    // 2. 通过对象,调用其内部的属性、方法
    p1.age = 10;
    System.out.println(p1);
    p1.show();
    // 在Person类外部,不可以通过Person类的对象调用其内部私有的结构
    // 比如:name、showNation()以及私有的构造器
    /*
     输出:
         Person{name='Tom', age=10}
         你好,我是一个人
     */
}

反射后对Person类的操作

@Test
public void test2() throws Exception {
    Class clazz = Person.class;
    // 1. 通过反射,创建Person类的对象
    Constructor cons = clazz.getConstructor(String.class, int.class);
    Object obj = cons.newInstance("Tom", 12);
    System.out.println(obj);
    Person p = (Person) obj;
    System.out.println(p);
    // 2. 通过反射,调用对象指定的属性和指定的方法
    // 调用属性
    Field age = clazz.getDeclaredField("age");
    age.set(p, 10);
    System.out.println(p);
    // 调用方法
    Method show = clazz.getDeclaredMethod("show");
    show.invoke(p);
    System.out.println("*****************************");
    // 通过反射可以调用Person类的私有结构。比如:私有的构造器、方法、属性
    // 调用私有构造器
    Constructor cons1 = clazz.getDeclaredConstructor(String.class);
    cons1.setAccessible(true);
    Person p1 = (Person) cons1.newInstance("Jerry");
    System.out.println(p1);
    // 调用私有属性
    Field name = clazz.getDeclaredField("name");
    name.setAccessible(true);
    name.set(p1,"HanMeiMei");
    System.out.println(p1);
    // 调用私有方法
    Method showNation = clazz.getDeclaredMethod("showNation", String.class);
    showNation.setAccessible(true);
    String nation = (String) showNation.invoke(p1,"中国");// 相当于String nation = p1.showNation("中国");
    System.out.println(nation);
    /*
     输出:
         Person{name='Tom', age=12}
         Pe{name='Tom', age=12}
         Person{name='Tom', age=10}
         你好,我是一个人
         *****************************
         Person{name='Jerry', age=0}
         Person{name='HanMeiMei', age=0}
         我的国籍是:中国
         中国
      */
}

对Class类的理解

/*
关于Java.lang.Class类的理解
1. 类的加载过程
程序经过javac.exe命令后,会生成一个或多个字节码文件(.class结尾)。
接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个
字节码文件加载到内存中。此过程就成为类的加载。加载到内存中的类,我
们就称为运行时类,此运行时类,就作为Class的一个实例。
2. 换句话说,Class的实例就对应着一个运行时类。
3. 加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可
    以通过不同的方式来获取此运行时类。
*/

获取Class实例的四种常见方式

@Test
public void test3() throws ClassNotFoundException {
    // 方式一:调用运行时类的属性
    Class clazz1 = Person.class;
    System.out.println(clazz1); // 输出:class Reflection.Person
    // 方式二:通过运行时类的对象,调用getClass()
    Person p1 = new Person();
    Class clazz2 = p1.getClass();
    System.out.println(clazz2); // 输出:class Reflection.Person
    // 方式三:调用Class的静态方法:forName(String classPath)
    Class clazz3 = Class.forName("Reflection.Person");
    System.out.println(clazz3); // 输出:class Reflection.Person
    //        clazz3 = Class.forName("java.lang.String");
    //        System.out.println(clazz3); // 输出:class java.lang.String
    System.out.println(clazz1 == clazz2); // 输出:true
    System.out.println(clazz1 == clazz3); // 输出:true
    // 方式四:使用类的加载器:ClassLoader
    ClassLoader classLoader = ReflectionTest.class.getClassLoader();
    Class clazz4 = classLoader.loadClass("Reflection.Person");
    System.out.println(clazz4); // 输出:class java.lang.String
    System.out.println(clazz1 == clazz4); // 输出:true
}

Class实例可以是哪些结构的说明

@Test
public void test4(){
    Class c1 = Object.class;
    Class c2 = Comparable.class;
    Class c3 = String.class;
    Class c4 = int[][].class;
    Class c5 = ElementType.class;
    Class c6 = Override.class;
    Class c7 = int.class;
    Class c8 = void.class;
    Class c9 = Class.class;

    int[] a = new int[9];
    int[] b = new int[100];
    Class c10 = a.getClass();
    Class c11 = b.getClass();
    // 只要元素类型与维度一样,就是同一个Class
    System.out.println(c10 == c11); // 输出:true
}

创建Class对应运行时类的通用方法

@Test
public void test1() throws Exception {
    Class clazz = Person.class;
    /*
    newInstance():调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参的构造器
    要想此方法正常的创建运行时类的对象,要求:
    1. 运行时类必须提供空参构造器
    2. 空参的构造器的访问权限得够。通常设置为public。

    在javabean中要求提供一个public的空参构造器。原因:
    1. 便于通过反射,创建运行时类的对象
    2. 便于子类继承此运行时类时,默认调用super()时,保证服了由此构造器
    */
    Person obj = (Person) clazz.getConstructor().newInstance();
    System.out.println(obj);  // 输出:这是空参构造器:Person() \n Person{name='null', age=0}
}

反射的动态性

@Test
public void test2(){
    int num = new Random().nextInt(3);// 0,1,2
    String classPath = ";";
    switch (num){
        case 0:
            classPath = "java.util.Date";
            break;
        case 1:
            classPath = "java.lang.Object";
            break;
        case 2:
            classPath = "Reflection.Person";
            break;
    }
    try {
        Object obj = getInstance(classPath);
        System.out.println(obj);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

通过反射获取运行时类的完整结构

需要用到的类、接口、注解

package Reflection;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "Hello";
}
package Reflection;

public interface MyInterface {
    void info();
}
package Reflection;

/**
 * ClassName: People
 * Date:      2020/3/11 15:32
 * author:    Oh_MyBug
 * version:   V1.0
 */
@MyAnnotation(value = "OhMyBug-Class")
public class People extends Creature<String> implements Comparable<String>,MyInterface{
    private String name;
    int age;
    public int id;

    public People() {
    }
    @MyAnnotation(value = "OhMyBug-Construstor")
    private People(String name) {
        this.name = name;
    }

    People(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @MyAnnotation(value = "OhMyBug-Method")
    private String show(String nation){
        System.out.println("我的国籍是:" + nation);
        return nation;
    }
    public String display(String interest, int age) throws NullPointerException, ClassNotFoundException{
        return interest + age;
    }

    @Override
    public void info() {
        System.out.println("我是一个人");
    }

    @Override
    public int compareTo(String o) {
        return 0;
    }

    private static void showDesc(){
        System.out.println("我是一个可爱的人");
    }

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

获取当前运行时类的属性结构

/**
 * ClassName: FieldTest
 * Date:      2020/3/11 15:41
 * author:    Oh_MyBug
 * version:   V1.0
 *
 * 获取当前运行时类的属性结构
 */
public class FieldTest {
    @Test
    public void test1(){
        Class clazz = People.class;
        // 获取属性结构
        // getFields():获取当前运行时类及其所有父类中声明为public访问权限的属性
        Field[] fields = clazz.getFields();
        for (Field f: fields){
            System.out.println(f);
        }
        System.out.println();
        // getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field f: declaredFields){
            System.out.println(f);
        }
        /*
        输出:
            public int Reflection.People.id
            public double Reflection.Creature.weight

            private java.lang.String Reflection.People.name
            int Reflection.People.age
            public int Reflection.People.id
         */
    }

    // 权限修饰符 数据类型 变量名
    @Test
    public void test2(){
        Class clazz = People.class;
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field f: declaredFields){
            // 1. 权限修饰符
            int modifiers = f.getModifiers();
            System.out.print(Modifier.toString(modifiers) + "\t");
            // 2. 数据类型
            Class type = f.getType();
            System.out.print(type.getName() + "\t");
            // 3. 变量名
            String name = f.getName();
            System.out.print(name);
            System.out.println();
        }
        /*
        输出:
            private	java.lang.String	name
                int	age
            public	int	id
         */
    }
}

获取运行时类的方法结构

package Reflection;

import org.junit.Test;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * ClassName: MethodTest
 * Date:      2020/3/11 15:58
 * author:    Oh_MyBug
 * version:   V1.0
 *
 * 获取运行时类的方法结构
 */
public class MethodTest {
    @Test
    public void test1(){
        Class clazz = People.class;
        // getMethods():获取当前运行时类及其所有父类中声明为public权限的方法
        Method[] methods = clazz.getMethods();
        for (Method m: methods){
            System.out.println(m);
        }
        System.out.println();
        // getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method m: declaredMethods){
            System.out.println(m);
        }
        /*
        输出:
            public int Reflection.People.compareTo(java.lang.String)
            public int Reflection.People.compareTo(java.lang.Object)
            public void Reflection.People.info()
            public java.lang.String Reflection.People.display(java.lang.String)
            public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
            public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
            public final void java.lang.Object.wait() throws java.lang.InterruptedException
            public boolean java.lang.Object.equals(java.lang.Object)
            public java.lang.String java.lang.Object.toString()
            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()

            public int Reflection.People.compareTo(java.lang.String)
            public int Reflection.People.compareTo(java.lang.Object)
            public void Reflection.People.info()
            public java.lang.String Reflection.People.display(java.lang.String)
            private java.lang.String Reflection.People.show(java.lang.String)
         */
    }

    /*
    @Xxxx
    权限修饰符 返回值类型 方法名(参数类型1 形参名1,....) throws XxxException{}
     */
    @Test
    public void test2(){
        Class clazz = People.class;
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method m: declaredMethods){
            // 1. 获取方法声明的注解
            Annotation[] annotations = m.getAnnotations();
            for (Annotation a: annotations){
                System.out.println(a);
            }
            // 2. 权限修饰符
            int modifiers = m.getModifiers();
            System.out.print(Modifier.toString(modifiers) + '\t');
            // 3. 返回值类型
            Class returnType = m.getReturnType();
            System.out.print(returnType.getName() + "\t");
            // 4. 方法名
            System.out.print(m.getName() + "(");
            // 5. 形参列表
            Class[] parameterTypes = m.getParameterTypes();
            if (!(parameterTypes == null || parameterTypes.length == 0)){
                for (int i = 0; i < parameterTypes.length; i++) {
                    if (i == parameterTypes.length - 1){
                        System.out.print(parameterTypes[i] + " args_" + i);
                        break;
                    }
                    System.out.print(parameterTypes[i] + " args_" + i + ",");
                }
            }
            System.out.print(")");
            // 6. 抛出的异常
            Class[] exceptionTypes = m.getExceptionTypes();
            if (exceptionTypes.length > 0){
                System.out.print("throws ");
                for (int i = 0; i < exceptionTypes.length; i++) {
                    if (i == exceptionTypes.length - 1){
                        System.out.print(exceptionTypes[i].getName());
                        break;
                    }
                    System.out.print(exceptionTypes[i].getName() + ",");
                }
            }
            System.out.println();
        }
        /*
        输出:
            public	int	compareTo(class java.lang.String args_0)
            public volatile	int	compareTo(class java.lang.Object args_0)
            public	void	info()
            public	java.lang.String	display(class java.lang.String args_0,int args_1)throws java.lang.NullPointerException,java.lang.ClassNotFoundException
            @Reflection.MyAnnotation(value="OhMyBug-Method")
            private	java.lang.String	show(class java.lang.String args_0)
         */
    }
}

调用运行时类的指定结构:属性

/*
	不理想,对属性的要求太高
*/
@Test
public void test1() throws Exception {
    Class clazz = People.class;
    // 创建运行时类的对象
    People p = (People) clazz.getConstructor().newInstance();
    // 获取指定的属性:要求运行时类中属性声明为public
    // 通常不采用此方式
    Field id = clazz.getField("id");
    /*
	设置当前属性的值
	set():参数1:指明设置哪个对象的属性  参数2:将此属性值设置为多少
	*/
    id.set(p, 10086);

    /*
	获取当前属性的值
    get():参数1:获取哪个对象的当前属性值
	*/
    int pid = (int) id.get(p);
    System.out.println(pid);    // 输出:10086
}

/*
如何操作运行时类中指定的属性
*/
@Test
public void test2() throws Exception {
    Class clazz = People.class;
    // 创建运行时类的对象
    People p = (People) clazz.getConstructor().newInstance();
    // 1. getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
    Field name = clazz.getDeclaredField("name");
    // 2. setAccessible(boolean flag):保证当前属性是可访问的
    name.setAccessible(true);
    // 3. 获取、设置指定对象的此属性值
    name.set(p, "Tom");
    System.out.println(name.get(p)); // 输出:Tom
}

调用运行时类的指定结构:方法

/*
如何操作运行时类中的指定的方法
*/
@Test
public void test3() throws Exception {
    Class clazz = People.class;
    // 创建运行时类的对象
    People p = (People) clazz.getConstructor().newInstance();
    /*
	1. 获取指定的方法
    getDeclaredMethod():参数1:指明获取方法的名称   参数2:指明获取的方法的形参列表
	*/
    Method show = clazz.getDeclaredMethod("show", String.class);
    show.setAccessible(true);
    /*
    invoke():参数1:方法的调用者   参数2:给方法形参赋值的实参
    invoke()的返回值即为对应类中调用的方法的返回值
	*/
    Object returnValue = show.invoke(p, "CHN");
    System.out.println(returnValue);

    System.out.println("*************如何调用静态方法*************");
    Method showDesc = clazz.getDeclaredMethod("showDesc");
    showDesc.setAccessible(true);
    // 如果调用的运行时类中的方法没有返回值,则此invoke()返回null
    Object returnVal = showDesc.invoke(People.class);
    System.out.println(returnVal);
    /*
	输出:
    	我的国籍是:CHN
        CHN
        *************如何调用静态方法*************
        我是一个可爱的人
        null
	*/
}

调用运行时类的指定结构:构造

/*
如何调用运行时类中的指定的构造器
*/
@Test
public void test4() throws Exception{
    Class clazz = People.class;
    // private Person(String name)
    /*
    1. 获取指定的构造器
    getDeclaredConstructor():参数:指明构造器的参数列表
    */
    Constructor constructor = clazz.getDeclaredConstructor(String.class);

    // 2. 保证此构造器是可访问的
    constructor.setAccessible(true);
    // 3. 调用此构造器创建运行时类的对象
    People per = (People) constructor.newInstance("Tom");
    System.out.println(per); // 输出:People{name='Tom', age=0, id=0}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章