反射
一说到反射,我们通常想到的可能会是,镜面反射啊、条件反射之类的,但是我们今天要说的反射跟那个可没有任何的关系。
我们说 在java中一切皆对象
那么,在我们java程序运行时,会把我们的 .class 文件加载到 方法区 中,会使用一系列的类对我们类中的构造方法、成员方法、成员变量进行描述,而这个 class 文件java也使用了一个类来对它进行描述,这个类就是Class类,;我们之前学过 class 关键字,而Class这个是一个类。
获取 Class 对象的三种方法:
Employee类:
package cn.it.test.bean;
public class Employee {
private String name;
private int age;
public double height;
public double salary;
protected double weight;
// 一般工作中反射方法,对成员变量进行取值/设置
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
public Employee() {
}
private Employee(String name) {
this.name = name;
this.age = age;
}
protected Employee(int age) {
this.name = name;
this.age = age;
}
public Employee(String name, int age) {
this.name = name;
this.age = age;
}
public int sleep(int hour) {
System.out.println(name + "睡了" + hour);
return hour + 2;
}
private void eat() {
System.out.println(name + "吃饭了");
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
", height=" + height +
", salary=" + salary +
", weight=" + weight +
'}';
}
}
public class Demo4 {
public static void main(String[] args) throws ClassNotFoundException {
// 第一种:通过 类名.class
//Class cls = Employee.class;
//System.out.println("cls = " + cls);
// 第二种:通过 对象.getClass
//Employee e = new Employee();
//Class cls = e.getClass();
//System.out.println("cls = " + cls);
// 第三种:也是推荐使用的;通过 Class.forName(类全名)
Class cls = Class.forName("cn.it.test.bean.Employee");
System.out.println("cls = " + cls);
}
}
拿到 Employee 类的Class对象之后我们就可以获取这个类的一些信息了:类名啊、对应的构造方法、成员方法、成员变量之类的。
#Class对象相关方法
- String getSimpleName(); 获得简单类名,只是类名,没有包
- String getName(); 获取完整类名,包含包名+类名
- T newInstance() ;创建此 Class 对象所表示的类的一个新实例。要求:类必须有public的无参数构造方法
Class cls = Class.forName("cn.it.test.bean.Employee");
// 获取类全名
String name = cls.getName();
System.out.println("name = " + name);
// 获取类名
String simpleName = cls.getSimpleName();
System.out.println("simpleName = " + simpleName);
#Constructor类
Constructor是构造方法类,类中的每一个构造方法都是Constructor的对象,通过Constructor对象可以实例化对象。
##Class类中与Constructor相关方法
- Constructor getConstructor(Class… parameterTypes) 根据参数类型获取构造方法对象,只能获得public修饰的构造方法。如果不存在对应的构造方法,则会抛出 java.lang.NoSuchMethodException 异常。
- Constructor getDeclaredConstructor(Class… parameterTypes) 根据参数类型获取构造方法对象,包括private修饰的构造方法。如果不存在对应的构造方法,则会抛出 java.lang.NoSuchMethodException 异常。
- Constructor[] getConstructors() 获取所有的public修饰的构造方法
- Constructor[] getDeclaredConstructors() 获取所有构造方法,包括privat修饰的
##Constructor类中常用方法
- T newInstance(Object… initargs) 根据指定参数创建对象。
- void setAccessible(true) 暴力反射,设置为可以直接访问私有类型的构造方法。
public class Demo6 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
// 获取所有 public 修饰的构造方法
Class cls = Class.forName("cn.it.t.bean.Employee");
/*Constructor[] constructors = cls.getConstructors();
for (Constructor c : constructors) {
System.out.println("c = " + c);
}*/
// 获取 一个 public修饰的构造方法
// Constructor c = cls.getConstructor(String.class,int.class);
// System.out.println("c = " + c);
// 获取 所有声明的构造方法
// Constructor[] declaredConstructors = cls.getDeclaredConstructors();
// for (Constructor c : declaredConstructors) {
// System.out.println("c = " + c);
// }
// 获取 一个声明的构造方法
// Constructor c = cls.getDeclaredConstructor(String.class);
// System.out.println("c = " + c);
// 练习 Class 的 newInstance
// Constructor c = cls.getConstructor();
// Employee e = (Employee) c.newInstance();
// System.out.println("e = " + e);
// 练习 Class 的 newInstance;要求 类必须有 无参构造方法
Employee ee = (Employee) cls.newInstance();
System.out.println("e = " + ee);
}
}
#Method类
Method是方法类,类中的每一个方法都是Method的对象,通过Method对象可以调用方法。
##Class类中与Method相关方法
- Method getMethod(“方法名”, 方法的参数类型… 类型)
根据方法名和参数类型获得一个方法对象,只能是获取public修饰的 - Method getDeclaredMethod(“方法名”, 方法的参数类型… 类型)
根据方法名和参数类型获得一个方法对象,包括private修饰的 - Method[] getMethods()
获取所有的public修饰的成员方法,包括父类中。 - Method[] getDeclaredMethods()
获取当前类中所有的方法,包含私有的,不包括父类中。
##Method类中常用方法
- Object invoke(Object obj, Object… args)
根据参数args调用对象obj的该成员方法
如果obj=null,则表示该方法是静态方法 - void setAccessible(boolean flag)
暴力反射,设置为可以直接调用私有修饰的成员方法
// 获取 Class 对象所表示类的成员方法
public class Demo7 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
// 获取所有 public 修饰的成员方法;包括父类的
Class cls = Class.forName("cn.it.test.bean.Employee");
// Method[] methods = cls.getMethods();
// for (Method m : methods) {
// System.out.println("m = " + m);
// }
// 获取 一个 public 修饰的成员方法
Method method = cls.getMethod("sleep", int.class);
System.out.println("method = " + method);
// 调用方法
Constructor c = cls.getDeclaredConstructor(String.class);
c.setAccessible(true); // 这里就是暴力反射,c是私有的构造方法,外人不可使用,现在我们暴力反
//射,使得我们可以通过构造方法c创建一个对象
Employee e = (Employee) c.newInstance("胡歌");
method.invoke(e,2);
// 获取该类所有声明的成员方法;不包括父类
// Method[] declaredMethods = cls.getDeclaredMethods();
// for (Method m : declaredMethods) {
// System.out.println("m = " + m);
// }
// 获取 一个声明的方法
// Method method = cls.getDeclaredMethod("eat");
// System.out.println("method = " + method);
}
}
#Field类、与Filed相关的方法
public class Demo71 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 获取所有 public 修饰的 成员变量
Class cls = Class.forName("cn.it.test.bean.Employee");
// Field[] fields = cls.getFields();
// for (Field f : fields) {
// System.out.println("f = " + f);
// }
// 获取一个 public 修饰的成员变量
// Field f = cls.getField("salary");
// System.out.println("f = " + f);
// 获取所有声明的成员变量
// Field[] declaredFields = cls.getDeclaredFields();
// for (Field f : declaredFields) {
// System.out.println("f = " + f);
// }
// 获取一个 声明的 成员变量
// Field f = cls.getDeclaredField("weight");
// System.out.println("f = " + f);
// 下面我们来对变量进行一下 操作
Constructor c = cls.getDeclaredConstructor(String.class);
c.setAccessible(true);
Employee e = (Employee) c.newInstance("李易峰");
// Field f = cls.getDeclaredField("name");
// f.setAccessible(true); // 暴力反射
// f.set(e,"黄渤");
Field ff = cls.getField("height");
ff.setDouble(e,180.0);
// 获取变量的值
// String name = e.getName();
// System.out.println("name = " + name);
double height = ff.getDouble(e);
System.out.println("height = " + height);
System.out.println("e = " + e);
}
}
反射案例
需求:写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法
实现:
- 配置文件
- 反射
步骤:
3. 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
4. 在程序中加载读取配置文件
5. 使用反射技术来加载类文件进内存
6. 创建对象
7. 执行方法
pro.properties
className=com.it.domain.Student
methodName=sleep
Student.java
package com.it.domain;
public class Student {
public void sleep(){
System.out.println("sleep...");
}
}
RefectTest.java
public class ReflectTest {
@Test
public void test() throws Exception {
//可以创建任意类的对象,可以执行任意方法
//前提:不能改变该类的任何代码。可以创建任意类的对象,可以执行任意方法
//1.加载配置文件
//1.1创建Properties对象
Properties pro = new Properties();
//1.2加载配置文件,转换为一个集合
//1.2.1获取class目录下的配置文件
ClassLoader classLoader = ReflectTest.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("pro.properties");
pro.load(is);
//2.获取配置文件中定义的数据
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
//3.加载该类进内存
Class cls = Class.forName(className);
//4.创建对象
Object obj = cls.newInstance();
//5.获取方法对象
Method method = cls.getMethod(methodName);
//6.执行方法
method.invoke(obj);
}
}