05- 反射

反射基础

概念

​ JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;

​ 这种动态获取的信息以及动态调用对象方法的功能称为java语言的反射机制。

反射机制是构建框架技术的基础

无反射 不框架

IDE Eclipse IEDA 代码提示功能

反射的原理

.java文件 =编译器=> .class 文件 =虚拟机=> 运行 .class文件

反射把该过程 逆向推理

正在运行的.class 文件 =编译器=> ?

反射就是把java类中的各种成分映射成一个个的Java对象

例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。

(其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述)

电脑本身是一个对象

解剖成为 一个一个的组成部分 CPU 硬盘 GPU 内存 风扇 网卡 …

Class类

  • Class类的表示正在运行的Java应用程序中的类和接口。

Object 中 有一个方法 getClass() 返回值 是 Class 类型 表示 正在运行的类的实际类型

所有的类都有该方法

三种Class 对象的获取方式

第一种/ 根据对象获取

Student stu1 = new Student();
Student stu2 = new Student();
System.out.println(stu1 == stu2);

Class cla1 = stu1.getClass();
Class cla2 = stu2.getClass();  
System.out.println(cla1 == cla2);

程序运行开始 , 在创建对象的时候, 如果同一个类创建多个对象 ,实际运行的类型只有一个

第二种 / 根据类获取

类名.class 属性 
Class cla = Student.class;

第三种 / 根据字符串获取

// 通过一个类的 全路径 获取该类的字节码文件对象
Class cla = Class.forName("s0731.ref.Student");

后期使用反射推荐使用第三种, 只依赖于一个字符串 Class类常用方法

属性类 Field

获取属性的方法

getField(属性名)  只能获取公共(public)的属性
getDeclaredField(属性名)  获取任意修饰符的属性
getFields() 获取所有公共(public)的属性  返回值为Field 数组
getDeclaredFields()  获取任意修饰符的属性集合

属性类 Field

构造类 Constructor

获取构造的方法

getConstructor(参数列表类型 类<?>... parameterTypes)  
// 方法重载 
getDeclaredConstructor(参数列表类型 类<?>... parameterTypes)
getConstructors()  
getDeclaredConstructors() 

Constructor 构造包装类的方法

newInstance(Object... initargs) 
使用此 Constructor对象表示的构造函数,使用指定的初始化参数来创建和初始化构造函数的声明类的新实例。 

方法类 Method

invoke(Object obj, Object... args) 
在具有指定参数的 方法对象上调用此 方法对象表示的底层方法。 

获取方法的方法

getMethod(String name, 类<?>... parameterTypes) 
getDeclaredMethod(String name, 类<?>... parameterTypes) 
getMethods() 
getDeclaredMethods() 

反射应用

基础数据

public class Student {
    public String name;
    int age;
    private String address;

    public Student(){}

    public void study(){
        System.out.println("正在学习!!");
    }

    private void play(){
        System.out.println("随便玩");
    }
    private void play(String thing){
        System.out.println("玩"+ thing);
    }

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

案例1 公共属性

获取对象的公共属性并且赋值

// 获取到了 该类的字节码文件 .class 文件
Class stuCla = Class.forName("s0731.ref.Student");
// 获取该类的构造方法
Constructor constructor = stuCla.getConstructor();
// 获取的就是 public Student(){} 的包装对象
Object obj1 = constructor.newInstance();
Object obj2 = constructor.newInstance();
// 获取属性
Field nameField = stuCla.getField("name");
nameField.set(obj1,"张三");
nameField.set(obj2,"李四");
System.out.println(obj1);
System.out.println(obj2);

案例2 私有属性

获取对象的私有属性并且赋值

public static void main(String[] args) throws Exception{
    Class stuCla = Class.forName("s0731.ref.Student");
    Constructor constructor = stuCla.getConstructor();
    Object obj = constructor.newInstance();
    // 获取私有属性的包装类
    Field addressField = stuCla.getDeclaredField("address");
    // 虽然能够获取到 private 修饰的属性
    // private 不能被外界访问和修改
    // 设置 取消 private 的 限制
    addressField.setAccessible(true);
    addressField.set(obj,"青岛");
    
    Field nameField = stuCla.getField("name");
    nameField.set(obj,"张三");
    
    Field ageField = stuCla.getDeclaredField("age");
    ageField.set(obj,20);
    System.out.println(obj);
}

案例3 方法

获取公共的方法并调用

Class stuCla = Class.forName("s0731.ref.Student");
Constructor constructor = stuCla.getConstructor();
Object obj = constructor.newInstance();
/ 获取公共方法 方法名为 study 的无参方法
Method study = stuCla.getMethod("study");
study.invoke(obj);

获取私有的无参方法

// 获取私有方法 方法名为 play 的无参方法
 Method play = stuCla.getDeclaredMethod("play");
 play.setAccessible(true);
 play.invoke(obj);

获取私有的有参方法

// 获取私有方法 方法名为 play 的有参方法
Method play = stuCla.getDeclaredMethod("play",String.class);
play.setAccessible(true);
play.invoke(obj,"篮球");

获取方法的返回值

如果play 方法有返回值 
Object Parma = play.invoke(obj,"篮球");

实际应用

避开list 泛型检查

public static void main(String[] args) throws Exception{
    List<String> list = new ArrayList<String>();
    list.add("张三");
    list.add("李四");
    // list.add(123);
    // 任务 向 泛型为 String 的 list 集合中 成功添加一个 123 的整数类型
    /*  步骤
    1- 获取list 的运行时 字节码对象
    2- 获取 list 的 add 方法封装对象
    3, 使用 封装对象 调用 add 方法添加内容
    泛型实际上仅仅是在编译期生效
    */
    Class<?> clazz = Class.forName("java.util.ArrayList");
    Method add = clazz.getMethod("add", Object.class);
    add.invoke(list,123);
    System.out.println(list);
}

properties

基本使用

新建文件

abc.properties

name=于老板
age=50

通过程序加载

public static void main(String[] args) throws Exception {
    // 1- 创建 properties 对象
    // 2- 对象 通过 输入流 加载 本地文件
    // 3- 分别通过键 获取值
    Properties pro = new Properties();
    FileInputStream fis = new FileInputStream("src\\s0731\\abc.properties");
    pro.load(fis);
    fis.close();
    
    String name = pro.getProperty("name");
    String age = pro.getProperty("age");
    System.out.println(name + "\t"+age);

通过配置文件自定义执行内容

public class Demo1 {
    public static void main(String[] args) throws Exception {
        //        Test1 t1 = new Test1();
        //        t1.m1();
        Properties pro = new Properties();
        FileInputStream fis = new FileInputStream("C:\\Users\\Administrator\\abc.properties");
        pro.load(fis);
        fis.close();

        String className = pro.getProperty("class");
        String methodName = pro.getProperty("method");

        Class<?> clazz = Class.forName(className);
        Object obj = clazz.newInstance();
        Method method = clazz.getMethod(methodName);
        method.invoke(obj);
    }
}

需要把整个项目打成一个可执行 jar 包

可以通过修改配置文件 , 来动态的指定我们需要运行的程序

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章