反射

反射Reflect
反射是在运行时动态访问类与对象的技术
反射是jdk1.2版本后的高级特性,隶属于java.lang.reflect
大多数Java框架都基于反射实现参数配置、动态注入等特性

反射根本的目的就是把我们创建对象的时机,从程序编译的时候延迟到程序运行时。

Class.forName()j加载指定的类。之后.newInstance()创建对象
看下列代码,区别传统创建对象和反射有什么不同
package com.imooc.reflect;

import java.util.Scanner;

/**
 * 初识反射的作用
 */
public class ReflectSample {
    /**
     * 传统的创建对象方式
     */
    public static void case1() {
        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入计算类名:");
        String op = scanner.next();
        System.out.print("请输入a:");
        int a = scanner.nextInt();
        System.out.print("请输入b:");
        int b = scanner.nextInt();
        MathOperation mathOperation = null;
        if (op.equals("Addition")) {
            mathOperation = new Addition();
        } else if (op.equals("Subtraction")) {
            mathOperation = new Subtraction();
        } else if (op.equals("Multiplication")) {
            mathOperation = new Multiplication();
        } else {
            System.out.println("无效的计算类");
            return;
        }
        float result = mathOperation.operate(a, b);
        System.out.println(result);
    }

    /**
     * 利用反射创建对象更加灵活
     */
    public static void case2() {
        System.out.println("请输入计算类名:");
        Scanner sc = new Scanner(System.in);
        String op = sc.next();
        System.out.println("请输入a:");
        int a = sc.nextInt();
        System.out.println("请输入b:");
        int b = sc.nextInt();
        MathOperation mathOperation = null;
        try {
            //newInstance返回的是object对象所以要强制转换为MathOperation
            mathOperation = (MathOperation) Class.forName("com.imooc.reflect." + op).newInstance();
        } catch (Exception e) {
            System.out.println("无效计算类名");
            return;
        }
        float result = mathOperation.operate(a, b);
        System.out.println(result);
    }
    public static void main(String[] args) {
        ReflectSample.case2();
    }
}
反射优点添加功能不用修改源代码

反射的四个核心类
Class类
Constructor构造方法类
Method方法类
Field成员变量类
Class类
Class是JVM中代表“类和接口”的类
Class对象具体包含了某个特定类的结构信息
通过Class对象可获取对应类的构造方法/方法/成员变量
Class.forName()职责非常单纯,就是将指定的字节码文件class加入到JVM中。只有被加入以后我们才能基于类进行实例化的工作

通过反射创建对象
package com.imooc.reflect;

import com.imooc.reflect.entity.Employee;

public class ClassSample {
    public static void main(String[] args) {
        try {
            //Class.for Name()方法将指定的类加载到JVM,并返回对应的Class对象
            Class employeeClass = Class.forName("com.imooc.reflect.entity.Employee");
            System.out.println("Employee已被加载到JVM");
            //newInstance通过默认的构造方法创建新的对象
            Employee emp = (Employee)employeeClass.newInstance();
            System.out.println(emp);
        } catch (ClassNotFoundException e) {
            //类名与类路径书写错误时抛出"类无法找到"异常
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            //非法访问异常
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }
}

package com.imooc.reflect.entity;

public class Employee {

    private Integer eno;
    private String ename;
    private Float salary;
    private String dname;

    public Employee(){
        System.out.println("Employee默认构造方法已被执行");
    }
    //类被加载完以后要经过初始化的工作,类的初始化是通过静态块来完成的
    {
        System.out.println("Employee已被加载到JVM并已初始化");
    }
    public Integer getEno() {
        return eno;
    }

    public void setEno(Integer eno) {
        this.eno = eno;
    }

    public String getEname() {
        return ename;
    }

    public void setEname(String ename) {
        this.ename = ename;
    }

    public Float getSalary() {
        return salary;
    }

    public void setSalary(Float salary) {
        this.salary = salary;
    }

    public String getDname() {
        return dname;
    }

    public void setDname(String dname) {
        this.dname = dname;
    }
}
三个异常
ClassNotFoundException 类名与类路径书写错误时抛出"类无法找到异常"
InstantiationException 非法访问异常,当在作用域外访问对象方法或成员变量时抛出。
IllegalAccessException 实例化异常i,对象无法被实例化。如抽象类
Constructor构造方法类
Constructor类是对Java类中的构造方法的抽象
Constructor对象包含了具体类的某个具体构造方法的声明
通过Constructor对象调用带参构造方法创建对象
classObj.newInstance()是调用的默认构造方法创建对象。要想使用带有参数的构造方法创建对象,就要先获取到对应的构造方法对象,再通过调用构造方法对象的newInstance,来创建与之对应的对象。
所有反射在操作前必须要获得相应的类对象。方式Class.forName()
Constructor捕获的异常是NoSuchMethodException 没有找到与之对应格式的方法。例,constructor.newInstance时少传了一个参数,就会抛出这个异常
InvocationTargetException 目标调用异常。当被调用的方法的内部抛出了异常而没有被捕获的时候
Method方法类
Method对象指代某个类中的方法的描述
Method对象使用classObj.getMethod()方法获取
通过Method对象调用指定对象的对应方法
 
package com.imooc.reflect;

import com.imooc.reflect.entity.Employee;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class MethodSample {
    public static void main(String[] args) {
        try {
            // 获取employee的类对象
            Class employeeClass=Class.forName("com.imooc.reflect.entity.Employee");
            // 通过调用带参构造方法来实例化employee对象
            Constructor constructor = employeeClass.getConstructor(new Class[]{
                    Integer.class,String.class,Float.class,String.class
            });
            Employee employee = (Employee) constructor. newInstance(new Object[]{
                    100, "解来景", 3000f, "研发部"
            });

            Method updateSalaryMethod = employeeClass.getMethod("updateSalary", new Class[]{
                    Float.class
            });
            Employee employee1 = (Employee) updateSalaryMethod.invoke(employee,new Object[]{
                    1000f
            });
            System.out.println(employee1);

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}
package com.imooc.reflect.entity;

public class Employee {

    private Integer eno;
    private String ename;
    private Float salary;
    private String dname;

    public Employee() {
        System.out.println("Employee默认构造方法已被执行");
    }

    //类被加载完以后要经过初始化的工作,类的初始化是通过静态块来完成的
    {
        System.out.println("Employee已被加载到JVM并已初始化");
    }

    public Employee(Integer eno, String ename, Float salary, String dname) {
        this.setEno(eno);
        this.setEname(ename);
        this.setSalary(salary);
        this.setDname(dname);
        System.out.println("Employee带参构造方法已被执行");

    }

    public Integer getEno() {
        return eno;
    }

    public void setEno(Integer eno) {
        this.eno = eno;
    }

    public String getEname() {
        return ename;
    }

    public void setEname(String ename) {
        this.ename = ename;
    }

    public Float getSalary() {
        return salary;
    }


    public void setSalary(Float salary) {
        this.salary = salary;
    }

    public String getDname() {
        return dname;
    }

    public void setDname(String dname) {
        this.dname = dname;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "eno=" + eno +
                ", ename='" + ename + '\'' +
                ", salary=" + salary +
                ", dname='" + dname + '\'' +
                '}';
    }

    // 为当前员工进行调薪
    public Employee updateSalary(Float val) {

        this.salary=this.salary+val;
        System.out.println(this.ename + "调薪至" +this.salary +"元");
        return this;
    }
}
调用getMethod时需要传入两个参数,第一个是方法的名字,第二个是参数的类型列表。        
Method updateSalaryMethod = employeeClass.getMethod("updateSalary", new Class[]{
                    Float.class
            });
获取到方法对象以后,我们可以通过invoke来执行对应的方法,invoke需要传入两个参数,第一个执行哪个对象的方法就传入哪个对象,第二个使用对象数组传入刚才方法声明时,所匹配的参数数量和参数类型。作为invoke如果目标方法是有返回值的,需要通过类型强制转换得到方法执行后的返回值。
Employee employee1 = (Employee) updateSalaryMethod.invoke(employee,new Object[]{
                    1000f
            });
Field成员变量类
Field对应某个具体类中的成员变量的声明
Field对象使用classObj.getField()方法获取
通过Field对象可为某对象成员变量赋值/取值
成员变量、类的属性、类的字段是一个东西
getField只有一个参数,"成员变量的名称"。
getConstructor(new Class[]{})
(强制转换对象)newInstance(new Object[]{})
getMethod("方法名",new Class[]{})
(强制转换对象).invoke(要操作的对象名,new Object[]{})
(强制转化数据类型)fieldObj.get(对象名)
fieldObj.set(对象名,"新的成员变量名")
getDeclared访问私有数据
getDeclaredConstructor(s)|Method(s)|Field(s) 
加s获取当前类所有构造方法|方法|成员变量
getDeclared获取到的对象可能是私有的,当对私有的构造方法、方法、成员变量访问时,会抛出无法访问异常
getConstructor(s)|Method(s)|Field(s) 只能获取public修饰的
Field抛出的异常
NoSuchFieldException 没有找到对应成员变量时抛出
快速捕获异常:选中要捕获异常的代码,点击上面菜单栏中的code->surround with->try/catch
getDeclaredFields返回一个Field数组,循环之后用getName方法即可获取各个成员变量
 Field[] fields = employeeClass.getDeclaredFields();
            for(Field fields1:fields){
                System.out.print(fields1.getName() +" ");
            }
将成员变量区分对待
getModifiers包含了当前成员变量的修饰符,该方法返回值是一个整数==1时public修饰==2时private  
获取当前类所有的成员变量getDeclaredField
getModidiers获取访问修饰符类型==1为public==2为private
 public直接get(对象名)
private用get方法来进行调用
package com.imooc.reflect;

import com.imooc.reflect.entity.Employee;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class getDeclaredSample {
    public static void main(String[] args) {
        Class<?> employeeClass = null;
        try {
            employeeClass = Class.forName("com.imooc.reflect.entity.Employee");
            Constructor<?> constructor = employeeClass.getConstructor(new Class[]{
                    Integer.class, String.class, Float.class, String.class
            });
            Employee employee = (Employee)constructor.newInstance(new Object[]{
                    1011,"解来景",3000f,"研发部"
            });
            Field[] fields = employeeClass.getDeclaredFields();
            for(Field fields1:fields){
//                System.out.print(fields1.getName() +" ");
                if(fields1.getModifiers()==1) {
                    //public修饰
                    Object val = fields1.get(employee);
                    System.out.println(fields1.getName() + ":" + val);
                }else if(fields1.getModifiers()==2){
                    //private修饰
                    String methodName = "get" + fields1.getName().substring(0,1).toUpperCase()
                            + fields1.getName().substring(1);
                    Method getMethod = employeeClass.getMethod(methodName);
                    // 不能进行强制转换 访问不同的成员变量的时候,不同的成员变量类型是不同的,get方法返回的类型也是不一样的
                    Object ret = getMethod.invoke(employee);
                    System.out.println(fields1.getName() + ":" + ret);
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }
}
Field[] fields = employeeClass.getDeclaredFields();
				if(fields1.getModifiers()==1) {
                    //public修饰
                    Object val = fields1.get(employee);
                    System.out.println(fields1.getName() + ":" + val);
                }
				if(fields1.getModifiers()==2){
                    //private修饰
                    String methodName = "get" + fields1.getName().substring(0,1).toUpperCase()
                            + fields1.getName().substring(1);
                    Method getMethod = employeeClass.getMethod(methodName);
                    // 不能进行强制转换 访问不同的成员变量的时候,不同的成员变量类型是不同的,get方法返回的类型也是不一样的
                    Object ret = getMethod.invoke(employee);
                    System.out.println(fields1.getName() + ":" + ret);
                }
反射在项目中的应用
反射总结
 
 




posted @ 2020-09-15 16:58  看清楚了吗  阅读(97)  评论(0编辑  收藏  举报