反射
反射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/catchgetDeclaredFields返回一个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);
}
反射在项目中的应用
反射总结