整体介绍
◆介绍反射及作用 ◆讲解反射四个核心类 ◆反射在项目中的应用
介绍反射及作用
什么是反射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();
}
}
}
选中需要捕获异常的代码块,选择菜单栏的code→suround with→trycatch即可快速捕获异常
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