【Java】Reflection 反射机制 01概述

Reflection 反射机制

反射允许程序在执行期间借助ReflectionAPI获取任何类的内部信息,直接操作任意对象的内部属性和方法

 

加载完类之后,堆内存的方法区产生了一个Class 类类型的对象

要注意!一个类只能有它对应的唯一的一个类类型实例

这个对象封装了类的完整的结构信息,可以通过这个类对象看到类的结构

 

动态语言和静态语言之分:

动态语言

在运行时可以改变结构的语言,新的函数、对象、代码、可以在运行时加入

已有的函数可以被删除或者修改、,运行时代码可以根据一定条件改变结构

主要语言: Object-C、C#、JavaScript、PHP、Python、Eriang

静态语言

运行时不可以改变代码的编程语言即是静态语言:Java、C、C++

虽然Java是静态语言,但是反射机制可以让Java看起来是动态的!!!

 

反射机制提供的功能:

- 判断对象所属类

- 运行时创建类的实例

- 判断一个类所有的成员变量和方法

- 获取泛型信息

- 调用任何实例的成员变量和方法

- 运行时处理注解

- 动态代理!!!

 

API

- java.lang.Class 类类型类

- java.lang.reflect.Method 方法类

- java.lang.reflect.Field 字段类

- java.lang.reflect.Construtor 构造器类

 

我们声明一个自定义类

public class Person {
    private String name;
    private int age;
    private boolean gender;

    public Person() {
    }

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

    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 boolean isGender() {
        return gender;
    }

    public void setGender(boolean gender) {
        this.gender = gender;
    }

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

 

通过反射实现对类的访问和实例的操作

public class ReflectTest {

    @Test
    public void reflect() throws Exception {
        // class属性可返回这个类的类对象
        Class<Person> personClass = Person.class;

        // 获取这个类的构造器类的对象,这里获取的是全参构造器
        Constructor<Person> personClassConstructor = personClass.getConstructor(String.class, int.class, boolean.class);

        // 调用构造器,创建Person对象
        Person person = personClassConstructor.newInstance("杰哥", 28, true);

        // 调用Person对象的toString();
        System.out.println(person.toString());

        // 通过反射,调用对象指定的属性,方法
        Field age = personClass.getDeclaredField("age");
        
        // 解开访问权限 要在访问前设置,否则无意义
        age.setAccessible(true);

        // 通过类的字段类实例,调用参数实例age字段,修改值
        // 发现异常报错 private无法访问
        // Class cn.dai.Reflection.ReflectTest can not access a member of class cn.dai.Reflection.Person with modifiers "private"
        age.set(person,10);
        System.out.println(person);
    }
}

例如方法,构造器,这些基本都是获取对应的封装类,得到封装类的实例,再调用方法即可,

如果是private修饰的访问权限,使用前先对封装实例设置可访问,再执行调用

 

类的加载过程:

- 字节码经过java.exe之后,会根据源码编写的内容生成一个或者多个字节码文件

- java.exe会根据某个字节码文件进行解释运行 == 字节码加载进内存

- 上面的这一步被称为类的加载

- 加载到内存的类,称为运行时类,作为Class类类的一个实例存在

- 反之,Class的对象就是一个在JVM种运行的字节码类

 

四种获取Class实例的方式:

public class GetClassStyle {

    @Test
    public void getClass4Way() throws Exception {
        // 第一种  类.class
        Class<Person> personClass1 = Person.class;
        System.out.println(personClass1);

        // 第二种  类的实例.getClass();
        Person person = new Person();
        Class<? extends Person> personClass2 = person.getClass();
        System.out.println(personClass2);

        // 第三种 Class.forName(类的完整限定名)
        Class<?> personClass3 = Class.forName("cn.dai.Reflection.Person");
        System.out.println(personClass3);

        // 第四种 当前类.class.getClassLoader().loadClass(类的完全限定名");
        Class<?> personClass4 = GetClassStyle.class.getClassLoader().loadClass("cn.dai.Reflection.Person");
        
        // 所有调用的方法出来的实例都是同一个对象
    }
}

 

使用ClassLoader加载配置文件

public class Loader {

    @Test
    public void loader() throws Exception {
        Properties properties = new Properties();
        // 流对象读取只能读取在当前源码工程的目录下 如果要读取,路径要写成"src\\jdbc.properties"
        FileInputStream inputStream = new FileInputStream("jdbc.properties");
        properties.load(inputStream);
        String driver = properties.getProperty("driver");
        String url = properties.getProperty("url");
        String username = properties.getProperty("username");
        String password = properties.getProperty("password");
        System.out.println(driver);
        System.out.println(url);
        System.out.println(username);
        System.out.println(password);
        inputStream.close();
    }

    @Test
    public void loader2() throws Exception {
        // 创建配置实例
        Properties properties = new Properties();
        // 类加载器读取 文件的位置默认是在当前Module或者项目的src包下
        InputStream inputStream = Loader.class.getClassLoader().getResourceAsStream("jdbc.properties");
        // 加载
        properties.load(inputStream);

        // 读取信息
        String driver = properties.getProperty("driver");
        String url = properties.getProperty("url");
        String username = properties.getProperty("username");
        String password = properties.getProperty("password");

        System.out.println(driver);
        System.out.println(url);
        System.out.println(username);
        System.out.println(password);
        
        inputStream.close();
    }

    @Test
    public void loader3() throws Exception {
        // 创建配置实例
        Properties properties = new Properties();
        // 返回URL的编码 %20  类加载器读取 文件的位置默认是在当前Module或者项目的src包下
        String path = Loader.class.getClassLoader().getResource("jdbc.properties").getFile();
        // 需要解码
        String decode = URLDecoder.decode(path, "UTF-8");
        // 创建流对象
        InputStream inputStream = new FileInputStream(decode);
        // 加载配置
        properties.load(inputStream);

        // 读取信息
        String driver = properties.getProperty("driver");
        String url = properties.getProperty("url");
        String username = properties.getProperty("username");
        String password = properties.getProperty("password");

        System.out.println(driver);
        System.out.println(url);
        System.out.println(username);
        System.out.println(password);
        
        inputStream.close();
    }
}

 

 创建运行时类的对象

    @Test
    public void getInstanceByReflect() throws Exception {
        Class<Person> personClass = Person.class;

        // 直接用类的类实例创建对象 在JDK9+ 以后不推荐使用此方法创建实例了
        // .newInstance(); 内部调用运行时的空参构造器,没有空参构造,调用异常
        // 运行时类必须提供空参构造器,访问权限不得低于默认
        
        // javaBean 要求提供一个空参构造器的原因:
        // 通过这个反射调用构造器创建Bean的实例
        // 子类继承运行时类时,调用super()保证父类也有此构造器
        
        Person person = personClass.newInstance();
        System.out.println(person);
    }

 

反射的动态性,动态生成实例

    @Test
    public void dynamic() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        for (int i = 0; i < 100; i++) {
            int random = new Random().nextInt(3);
            String classPath = null;
            switch (random){
                case 0:
                    classPath = "java.util.Date";
                    break;
                case 1:
                    classPath = "java.lang.Object";
                    break;
                case 2:
                    classPath = "cn.dai.Reflection.Person";
            }

            Class<?> name = Class.forName(classPath);
            Object instance = name.newInstance();
            System.out.println(instance);
        }
    }

 

posted @ 2020-04-23 09:44  emdzz  阅读(141)  评论(0编辑  收藏  举报