Java
day18_2023.9.1
反射
反射 :Java中的反射,用来将类中的方法或者属性、构造方法直接获取到,完成它们的调用
反射是框架设计的灵魂
学习反射之前,了解两个概念 : 编译期 和 运行期
编译期 : 指的是将源码,交给编译器,编译成计算机可以执行的文件的过程。在Java中就是把java文件编译成.class文件的过程。编译期就是做了一些翻译的功能,还没有将代码在内存中运行。
运行期 : 把编译后的文件交给计算机,直到程序结束。其实就是把在磁盘中的代码加载到内存中,执行起来
反射机制: 在运行期,对于任意的类,都能够获取这个类的所有属性和方法,这种动态获取信息以及动态调用方法的功能,称为Java的反射机制
Java代码在计算机中运行的三个阶段
反射的好处 :
1,可以在程序的运行过程中,获取到Class类对象中封装的对象,可以操作这些对象
2,可以 解耦,提高程序的可扩展性
获取Class类对象的方式
方式1: 在第一个阶段,还没有加载的时候
Class.forName(“全类名地址”) : 将字节码文件直接加载进内存,返回Class对象,一般将来用于配置文件,读取文件,加载类
**方式2:**通过类名的属性 class获取
类名.class的方式获取到这个类的类对象
方式3: 通过实例化后的对象获取
对象.getClass() : 获取到这个类的类对象
public class TestPerson {
public static void main(String[] args) throws ClassNotFoundException {
Person person = new Person();
//方式1 : 通过Class.forName()
Class personClass1 = Class.forName("com.iweb.airui369.test.Person");
//方式2 : 通过类名.class
Class personClass2 = Person.class;
//方式3: 通过对象.getClass()
Class personClass3 = person.getClass();
//比较3个获取的类对象,值都是相同的,表示指向内存中的同一个地址
//其实就是同一个类对象
//得出结论 :
//同一个字节码文件(xxx.class)在同一个程序的运行过程中,
//只会被加载一次,可以通过不同的方式去找到这个类对象,
//不管通过哪种方式,获取到的类对象都是同一个
System.out.println(personClass1 == personClass2);
System.out.println(personClass2 == personClass3);
}
}
public class Person {
//属性
public int sid;
private String name;
private int age;
//构造函数
public Person() {
}
//成员方法
public void eat(){}
}
Class类对象中,封装的其他几个对象
Field类 :Field类提供有关类或接口的单个字段的信息和动态访问。 反射的字段可以是类(静态)字段或实例字段。
Constructor类 :Constructor提供了一个类的单个构造函数的信息和访问。
Class类对象的常用方法(功能)
获取成员变量们
Field getField(String name)
返回一个 Field对象,它反映此表示的类或接口的指定公共成员字段 类对象。
Field[] getFields()
返回包含一个数组 Field对象反射由此表示的类或接口的所有可访问的公共字段 类对象。
Field getDeclaredField(String name)
返回一个 Field对象,它反映此表示的类或接口的指定已声明字段 类对象。
Field[] getDeclaredFields()
返回的数组 Field对象反映此表示的类或接口声明的所有字段 类对象。
//获取成员变量相关的方法
//Field getField(String name) 获取指定的public的属性
Field sid = personClass1.getField("sid");
System.out.println(sid);
//Field[] getFields() 获取所有public的属性
Field[] fields = personClass1.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("----------------");
//Field getDeclaredField(String name) 获取所有(包含私有化)的指定的属性
Field name = personClass1.getDeclaredField("name");
System.out.println(name);
System.out.println("----------------");
//Field[] getDeclaredFields() 获取所有的属性
Field[] fields1 = personClass1.getDeclaredFields();
for (Field field1 : fields1) {
System.out.println(field1);
}
//通过Field对象,完成对象属性值的获取和赋值
System.out.println("----------------");
Person p = new Person();
p.sid = 1001;
//获取p对象的sid属性值
Object o = sid.get(p);
System.out.println(o);
//通过sid属性对象,完成person对象的sid属性赋值
sid.set(p,1002);
System.out.println(p.sid); //1002
通过暴力反射,完成私有属性的赋值
//通过Field对象,结合setAccessible()方法对私有属性的赋值或者获取值
System.out.println("----------------");
name.setAccessible(true); //暴力反射
name.set(p,"jack"); //反射给属性赋值
Object o1 = name.get(p); //反射获取属性值
System.out.println(o1);
获取构造方法们
Constructor getConstructor(类<?>… parameterTypes)
返回一个 Constructor对象,该对象反映 Constructor对象表示的类的指定的公共 类函数。
Constructor<?>[] getConstructors()
返回包含一个数组 Constructor对象反射由此表示的类的所有公共构造 类对象。
Constructor getDeclaredConstructor(类<?>… parameterTypes)
返回一个 Constructor对象,该对象反映 Constructor对象表示的类或接口的指定 类函数。
Constructor<?>[] getDeclaredConstructors()
返回一个反映 Constructor对象表示的类声明的所有 Constructor对象的数组 类 。
public class ConstructorDemo {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//获取类对象
Class<Person> personClass = Person.class;
//获取构造函数对象
//Constructor<T> getConstructor(T<?>... parameterTypes)
//传入构造函数的参数的类型,获取指定类对象的无参构造
Constructor<Person> constructor = personClass.getConstructor();
Person person = constructor.newInstance();
System.out.println(person);
//获取指定类对象的有参构造
Constructor<Person> constructor1 = personClass.getConstructor(String.class, int.class);
Person person1 = constructor1.newInstance("jack",20);
System.out.println(person1);
//Constructor<?>[] getConstructors() 获取所有的public的构造函数
Constructor<?>[] constructors = personClass.getConstructors();
for (Constructor<?> constructor2 : constructors) {
System.out.println(constructor2);
}
//Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
Constructor<Person> constructor3 = personClass.getDeclaredConstructor(String.class);
System.out.println(constructor3);
//可以通过暴力反射,完成私有构造方法的调用,并且可以完成属性赋值
constructor3.setAccessible(true);
Person person2 = constructor3.newInstance("tom");
System.out.println(person2);
//类对象可以通过newInstance()方法,直接完成对象的创建
Person person3 = personClass.newInstance();
System.out.println(person3);
}
}
使用反射完成对象创建和属性赋值
//创建一个Student对象,有3个私有属性,学号、姓名、班级,有一个toString()方法
//利用反射,创建1个无参Student对象,利用反射,给这个Student对象的属性赋值
//最后,将这个Student对象输出
public class Student {
private int sid;
private String name;
private String className;
@Override
public String toString() {
return "Student{" +
"sid=" + sid +
", name='" + name + '\'' +
", className='" + className + '\'' +
'}';
}
}
public class StudentTest {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchFieldException {
//获取类对象
Class<Student> studentClass = Student.class;
//通过类对象调用方法
Student s1 = studentClass.newInstance();
//通过类对象获取属性
Field sid = studentClass.getDeclaredField("sid");
sid.setAccessible(true);
Field name = studentClass.getDeclaredField("name");
name.setAccessible(true);
Field className = studentClass.getDeclaredField("className");
className.setAccessible(true);
//给指定的对象属性赋值
sid.set(s1,1001);
name.set(s1,"jack");
className.set(s1,"1班");
System.out.println(s1);
//创建另一个对象
Student s2 = studentClass.newInstance();
sid.set(s2,1002);
name.set(s2,"tom");
className.set(s2,"2班");
System.out.println(s2);
}
}
获取成员方法们
Method getDeclaredMethod(String name, 类<?>… parameterTypes)
返回一个 方法对象,它反映此表示的类或接口的指定声明的方法 类对象。
Method[] getDeclaredMethods()
返回包含一个数组 方法对象反射的类或接口的所有声明的方法,通过此表示 类对象,包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。
Method getMethod(String name, 类<?>… parameterTypes)
返回一个 方法对象,它反映此表示的类或接口的指定公共成员方法 类对象。
Method[] getMethods()
返回包含一个数组 方法对象反射由此表示的类或接口的所有公共方法 类对象,包括那些由类或接口和那些从超类和超接口继承的声明
public class MethodDemo {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
//Method getMethod(String name, Class<?>... parameterTypes)
//返回一个 方法对象,它反映此表示的类或接口的指定公共成员方法 类对象。
//name参数表示 :方法名
//Class<?>... parameterTypes :参数类.class
//... 表示可变长的参数列表
//创建类对象
Class<Student> studentClass = Student.class;
//通过类对象获取无参数方法
Method sleep = studentClass.getMethod("sleep");
//创建对象
Student s1 = studentClass.newInstance();
//通过Method类中的方法,完成对象对方法的调用
sleep.invoke(s1);
//通过类对象获取有参数方法
Method eat = studentClass.getMethod("eat", String.class);
eat.invoke(s1,"鸡腿");
Method add = studentClass.getMethod("add", int.class, int.class);
add.invoke(s1,10,20);
}
}
public class Student {
private int sid;
private String name;
private String className;
public void sleep(){
System.out.println("学生睡觉!");
}
public void eat(String food){
System.out.println("学生在吃" + food);
}
public void add(int a,int b){
System.out.println(a + b);
}
@Override
public String toString() {
return "Student{" +
"sid=" + sid +
", name='" + name + '\'' +
", className='" + className + '\'' +
'}';
}
}
获取类名
String className = studentClass.getName();
System.out.println(className);
总结
获取类对象 :类名 .class
类对象常用方法
获取私有属性对象Field :Field getDeclaredField(String name)
获取构造方法对象Constructor :Constructor getConstructor(Class<?>… parameterTypes)
获取普通方法对象Method :Method getMethod(String name, Class<?>… parameterTypes)
几个对象的常用方法:
Filed类 :
setAccessible(true) 开启暴力反射,可以操作私有属性
get() 获取属性值
set(Object obj, Object value) 设置属性值
Constructor类:
newInstance(Object… initargs) 用来构建对象
如果是调用无参构造来创建,直接通过类对象调用 newInstance() 方法
Method类
invoke(Object obj, Object… args) 执行方法
getName() 获取方法名
模拟框架案例
需求 :写一个框架,将来再不改变任何代码的情况下,可以帮助我们创建任意类的对象,并且能够执行其中的任意方法。
properties配置文件
className=com.iweb.airui369.reflecttest2.Student
methodName=sleep
public class Person {
public void eat(){
System.out.println("eat.....");
}
}
public class Student {
public void sleep(){
System.out.println("sleep.....");
}
}
public class TestReflect {
public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//Person person = new Person();
//person.eat();
//Student student = new Student();
//student.sleep();
//需要在不改变任何代码的情况下,可以创建对象,并执行方法
//把类和方法的信息,配置在一个配置文件中,通过代码读取到配置文件中的信息
//完成加载和调用
//1,在src目录下,创建一个info.properties 配置文件,在文件中写入需要加载的
//类的信息和方法的信息
//完成配置文件加载
Properties properties = new Properties();
//获取src目录下的配置文件,使用inputStream完成文件加载
InputStream is =
TestReflect.class.getClassLoader().
getResourceAsStream("info.properties");
//properties对象加载is流对象中的数据
properties.load(is);
//去获取到properties对象中的配置数据信息
String className = properties.getProperty("className");
String methodName = properties.getProperty("methodName");
//获取类对象
Class cls = Class.forName(className);
//创建一个对象
Object o = cls.newInstance();
//获取方法对象
Method method = cls.getMethod(methodName);
//执行方法
method.invoke(o);
}
}