反射
正常使用的类全部是被jvm加载以后的,我们运行的程序在编译期的时候就已经知道需要的类已经被加载了。而反射是在编译时不确定那个类被加载了,而是在运行时才加载,探知自审(可以获得类的方法与属性,并获得属性的类型,对应Type对象;还可以获得生命周期为Runtime和Class的注解),使用编译期并不知道的类就是反射。
反射:将类的各个组成部分(成员变量field,构造方法constructor,成员方法method)封装成其他对象,就是反射机制
好处:
- 可以在程序运行阶段,操作上述组成部分对象
- 可以解耦,提高程序的可扩展性
- 反射可以不受访问权限控制
获取对象的三种方式
Class.forName(“包名.类名”); 将字节码文件加载入内存,返回class对象 (源代码阶段获取),多用于配置文件中,将类名定义在配置文件中,读取文件加载类
类名.class; 通过类名的属性class来获取 (类加载后字节码对象获取),多用于参数传递
对象.getClass; 该方法在object类中就被定义了,所以所有对象都具有该方法 (运行时阶段获取),多用于获取字节码对象
结论:同一个字节码文件在一次程序运行过程中,只会被加载一次,通过上述三种方法获取出来的字节码对象都是同一个
Class对象功能:
- 获取成员变量们
- 获取成员方法们
- 获取构造方法们
- 获取类名
获取成员变量实例:
package com.seele;
import com.seele.pojo.Crew;
import java.lang.reflect.Field;
public class Test01 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
Class clsCrew = Crew.class;
Field[] fields = clsCrew.getFields(); //获取public权限的成员变量
for (Field field : fields){
System.out.println(field);
}
System.out.println("------------反射打印所有权限成员变量----------------");
fields = clsCrew.getDeclaredFields(); //获取所有权限的成员变量
for (Field field : fields){
field.setAccessible(true); //暴力反射,忽略访问权限
System.out.println(field);
}
System.out.println("------------反射修改私有变量值----------------");
Field field = clsCrew.getDeclaredField("name");
Crew crew = new Crew();
field.setAccessible(true);
field.set(crew,"Seele");
System.out.println(crew);
System.out.println("------------反射获取私有变量值----------------");
String str = (String)field.get(crew);
System.out.println("获取到的私有变量值为:" + str);
}
}
获取构造方法实例:
package com.seele;
import com.seele.pojo.Crew;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class GetConstructor {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class clsCrew = Crew.class;
//构造器对象就是用来创建对象实例的
//有参构造
Constructor constructor = clsCrew.getConstructor(int.class,String.class);
Crew crew = (Crew) constructor.newInstance(18,"Seele");
System.out.println(crew);
//无参构造可以被简化,直接使用字节码对象创建实例
Crew crew1 = (Crew) clsCrew.newInstance();
System.out.println(crew1);
}
}
获取成员方法实例:
package com.seele;
import com.seele.pojo.Crew;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class GetMethod {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class clsCrew = Crew.class;
Crew crew = new Crew(18,"Seele");
//获取指定名称的无参方法
Method method = clsCrew.getMethod("sayName");
//执行无参方法
method.invoke(crew);
//获取指定名称的有参方法
Method method1 = clsCrew.getMethod("sayName1", String.class);
//执行有参方法
method1.invoke(crew,"希儿");
//获取所有public修饰的方法(控制台打印的不仅仅只有当前Crew类的public方法,还有直接父类Object的public方法)
Method[] methods = clsCrew.getMethods();
for (Method method2 : methods){
System.out.println(method2.getName());
}
}
}
案例:
需求:写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行任意方法
实现:1. 配置文件 2.反射
步骤:
- 将需要创建的对象的全类名和需要执行的方法写在配置文件中
- 在程序中加载读取配置文件
- 使用反射机制加载类文件进内存
- 创建对象
- 执行方法
实体类:
package com.seele.pojo;
public class Crew {
private int age;
private String name;
public Crew(){
}
public Crew(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void sayName(){
System.out.println("我的名字是" + this.name);
}
public void sayName1(String string){
System.out.println("我的名字是" + string);
}
@Override
public String toString() {
return "Crew{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
//-----------------------------------------------------------
package com.seele.pojo;
public class Captain {
public void sleep(){
System.out.println("sleep...");
}
}
入口类:
package com.seele;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
public class ReflectTest {
public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//可以创建任意类对象,可以执行任意方法
//1.1创建properties对象
Properties properties = new Properties();
//1.2加载配置文件,转换为一个集合
//1.2.1 获取class目录下的配置文件
ClassLoader classLoader = ReflectTest.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("pro.properties");
properties.load(is);
//2.获取配置文件当中的数据
String className = properties.getProperty("className");
String methodName = properties.getProperty("methodName");
//3.加载该类进内存
Class cls = Class.forName(className);
//4.创建对象
Object obj = cls.newInstance();
//5.获取方法对象
Method method = cls.getMethod(methodName);
//6.执行方法
method.invoke(obj);
}
}