整体介绍

◆介绍反射及作用 ◆讲解反射四个核心类 ◆反射在项目中的应用

介绍反射及作用

什么是反射Reflect

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

初始反射技术

实现四则运算:

package com.imooc.reflect;
 
/**
 * 四则运算接口
 */
public interface MathOperation {
    public float operate(int a , int b);
}

加法实现接口: Addition.java

package com.imooc.reflect;
 
/**
 * 加法
 */
public class Addition implements MathOperation {
    @Override
    public float operate(int a , int b) {
        System.out.println("执行加法运算");
        return a + b;
    }
}

传统的创建对象方式:

/**
     * 传统的创建对象方式
     */
    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);
    }

如果增加乘法运算需要在com.imooc.reflect新建Multiplication.java文件后,还要在源代码文件中添加

if(op.equals("Multiplication")){
mathOperation = new Multiplication();

进行创建对象,然后重新打包部署非常麻烦 而使用反射技术不必修改源代码,在运行时动态创建新增的对象

反射创建对象更加灵活:

/**
     * 利用反射创建对象更加灵活
     */
    public static void case2(){
        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;
        try {
            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();
    }

mathOperation = (MathOperation) Class.forName("com.imooc.reflect." + op).newInstance();即为使用反射技术动态创建对象,object强制类型转换为MathOperation对象不必在修改源代码

object是所有类的父类

正是因为JAVA提供了反射技术,才有mybatis和Spring等高级框架

反射四个核心类

◆Class类 ◆Constructor构造方法类 ◆Method方法类 ◆Field成员变量类 一个类里刚好包含这几种

Class类

◆Class,是VM中代表”类和接口”的类 ◆Class对象具体包含了某个特定类的结构信息 ◆通过Class对象可获取对应类的构造方法/方法/成员变量

Class核心方法

Class.forName需要捕捉异常

public class ClassSample {
    public static void main(String[] args) {
        try {
            //Class.forName()方法将指定的类加载到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();
        }
    }
}

如何使用带参构造方法实例化对象?

Method方法类

◆Method对象指代某个类中的方法的描述 ◆Method对象使用classObj..getMethod()方法获取 ◆通过Method对象调用指定对象的对应方法

Method类核心方法

static {
System.out.println("Employee类已被加载到jvm,并已初始化");
}
 
public Employee(Integer eno,String ename,Float salary , String dname){
this.eno = eno;
this.ename = ename;
this.salary = salary;
this.dname = dname;
System.out.println("Employee带参构造方法已被执行");
}
//需要调用的方法
public Employee updateSalary(Float val){
this.salary = this.salary + val;
System.out.println(this.ename + "调薪至" + this.salary + "元");
return this;
}

新建methodSample.java

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;
 
/**
 * 利用Method方法类调用
 */
public class MethodSample {
    public static void main(String[] args) {
        try {
            Class 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[]{
                    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();
        }
    }
}

调用带参构造方法

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});

当只有updateSalary方法名是不行的,因为重载函数可以方法名相同,参数不同而调用不同方法,所以还要写上参数类型new Class[]{Float.class} 得到方法后需要使用invoke()方法执行,employee表示执行对象,new Object[]{1000f}为对象数组,表示上调1000元,float类型,返回的是object对象,需要强制转换为Employee对象

Field成员变量类

◆Field对应某个具体类中的成员变量的声明 ◆Field对象使用classObj.getFieldO方法获取 ◆通过Field对象可为某对象成员变量赋值/取值

Field类核心方法

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

新建Field.java文件

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;
 
/**
 * 利用Field对成员变量赋值/取值
 */
public class FieldSample {
    public static void main(String[] args) {
        try {
            Class 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[]{
                    100,"李磊",3000f,"研发部"
            });
            Field enameField = employeeClass.getField("ename");
            enameField.set(employee,"李雷");
            String ename = (String)enameField.get(employee);
            System.out.println("ename:" + ename);
        } 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();
        } catch (NoSuchFieldException e) {
            //没有找到对应成员变量时抛出的异常
            e.printStackTrace();
        }
    }
}

选中需要捕获异常的代码块,选择菜单栏的codesuround withtrycatch即可快速捕获异常

Field enameField = employeeClass.getField("ename");
        enameField.set(employee,"李雷");
        String ename = (String)enameField.get(employee);
        System.out.println("ename:" + ename);

employee表示get获取或者set要设置指定的类

如何访问private修饰的私有变量?

getDeclared系列方法说明

◆getDeclaredConstructor(s) Method(s) Field(s)获取对应对象 ◆getConstructor(s) Method(s) Field(s)只能获取public对象 ◆访问非作用域内构造方法、方法、成员变量,会抛出异常

新建getDeclaredSample.java文件

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) {
        try {
            Class 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[]{
                    100, "李磊", 3000f, "研发部"
            });
            //获取当前类所有成员变量
            Field[] fields = employeeClass.getDeclaredFields();
            for(Field field : fields){
//                System.out.println(field.getName());
                if(field.getModifiers() == 1){ //pubilc修饰
                    Object val = field.get(employee);
                    System.out.println(field.getName() + ":" + val);
                }else if(field.getModifiers() == 2){ //private修饰
                    String methodName = "get" + field.getName().substring(0,1).toUpperCase()
                                        + field.getName().substring(1);
                    Method getMethod = employeeClass.getMethod(methodName);
                    Object ret = getMethod.invoke(employee);
                    System.out.println(field.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();获取成员变量数组,使用field.getModifiers()获取是public修饰还是private修饰,public返回1,private返回2

函数方法:

private Integer eno;
public String ename;
private Float salary;
private String dname;
 
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;
}

使用下列代码获取上述方法:

String methodName = "get" + field.getName().substring(0,1).toUpperCase() + field.getName().substring(1);

每个函数都有get开头,然后后一个字母为大写,field.getName().substring(0,1).toUpperCase()即转换为大写,substring(0,1)表示截取一个字符,而substring(1)表示从第二个字符开始截取后面所有

Object ret = getMethod.invoke(employee);只需传入employee对象即可,因为执行的函数都是无参的,而且此处不能强制类型转换,因为执行的方法的类型都不一样,有String、Float、void类型,所以使用object父类即可

反射在项目中的应用案例

利用反射技术实现i8n 在com.imooc.i18n中新建I18N.Java接口文件

package com.imooc.i18n;
 
public interface I18N {
    public String say();
}

继续新建Zhcn.java文件

package com.imooc.i18n;
 
public class Zhcn implements I18N {
    @Override
    public String say() {
        return "生命不息奋斗不止";
    }
}

继续新建EN.java文件

package com.imooc.i18n;
 
public class En implements I18N {
    @Override
    public String say() {
        return "Cease to the struggle and cease to the life";
    }
}

我们需要进行灵活配置,不能写死在java文件中, 在src文件夹下新建config.properties文件,用来保存配置信息通过修改配置文件来切换语言

language=com.imooc.i18n.Zhcn

创建入口类Application.java

package com.imooc.i18n;
 
import java.io.FileInputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Properties;
 
public class Application {
    public static void say(){
        Properties properties = new Properties();
        String configPath = Application.class.getResource("/config.properties").getPath();
        try {
            configPath = new URLDecoder().decode(configPath,"UTF-8");
            properties.load(new FileInputStream(configPath));
            String language = properties.getProperty("language");
            I18N i18n = (I18N)Class.forName(language).newInstance();
            System.out.println(i18n.say());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
 
    public static void main(String[] args) {
        Application.say();
    }
}

Application.class.getResource("/config.properties").getPath();获取配置文件详细路径 configPath = new URLDecoder().decode(configPath,"UTF-8");得到不包含任何特殊编码的url路径 properties.load(new FileInputStream(configPath));加载文件

String language = properties.getProperty("language");得到language配置选项 I18N i18n = (I18N)Class.forName(language).newInstance();加载并实例化对象 因为实现的都是I18N接口,可以强制转换为I18N类型

对于不同地区只需添加不同地区的语言java文件即可,然后在配置文件中进行修改即可 如添加了英语版本,只需将language=com.imooc.i18n.Zhcn修改为language=com.imooc.i18n.EN