反射的概念
Java的反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,并且修改部分类型信息。这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
反射的基石
反射的基石是字节码文件对象。
字节码文件
字节码文件的产生过程
触发类加载的方式
(1)new一个对象的时候;
(2)访问一个类静态成员的时候;
(3)调用一个类静态方法的时候;
(4)通过反射的方式创建一个类的字节码文件对象的时候;
(5)创建一个子类对象的时候;
(6)java命令执行一个字节码文件的时候。
在lava中,一切皆对象,当字节码文件加载到JVM中后,会形成一个Class类对象, 即:该类在JVM中变成了一个对象(注意与new T(创建的对象不同)。
字节码文件的内容
字节码文件对象中包含了三部分内容:
● 构造方法–>Constructor对象
● 成员方法–>Method对象
● 成员变量—>Filed对象
注意: Class对象(字节码文件对象)存放在方法区,不在堆里面。
反射就是通过配置文件触发类加载拿到字节码文件对象,然后通过字节码文件对象操作类中的成员。
反射的实现
反射的第一个就是获取Class对象,然后通过Class对象的核心方法达到反射的目的,即:在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,包括修改部分类型信息。
获取字节码文件对象
获取Class对象总共有三种方式:
- 使用Class.forName(“类的全路径名”),该方法是Class中的静态方法,前提时必须知道类的全路径名
- 使用.class方法,仅适合在编译器就已经明确要操作的Class
- 使用对象的getClass()方法
public class Student {
private String name;
private String gender;
private int age;
public Student(){}
public Student(String name, String gender, int age) {
this.name = name;
this.gender = gender;
this.age = age;
}
private Student(String name){
this.name = name;
}
@Override
public String toString() {
return "student[" + name + "," + gender + "," + age + "]";
}
}
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
//1.获取Student类的字节码文件对象(class对象)
public static void test1() throws ClassNotFoundException {
//1.每个类里面实际都包含一个class的静态属性
Class<?> stuClass1 = Student.class;
System.out.println(stuClass1);
//2.通过对象调用getClass()
Student s = new Student("peter", "男", 15);
Class<?> stuClass2 = s.getClass();
System.out.println(stuClass2);
//3.通过Class静态方法forName()
Class<?> stuClass3 = Class.forName("Student");
System.out.println(stuClass3);
System.out.println(stuClass1.equals(stuClass2));//true
System.out.println(stuClass2.equals(stuClass3));//true
System.out.println(stuClass3.equals(stuClass1));//true
}
反射的使用
所有和反射相关的包都在 import java.lang.reflect 包下面。
要实现反射,需要用到Class类中的以下方法:
- 先获取要反射类的Class对象,然后获取该对象的构造器来创建实例
获取类的构造器对象
//2.获取类的构造器对象---前提是:必须先要拿到类的字节码文件对象
public static void test2(){
try {
Class<?> stuClass = Class.forName("Student");
//获取构造器对象
//getConstructor:获取所有共有的构造器-->Constructor[]
Constructor<?>[] stuConstruct1 = stuClass.getConstructors();
System.out.println(stuConstruct1.length);
//注意:getConsrtuctor获取具体某个公有的构造器--》想要获取哪个构造器,该方法的参数与对应构造器的参数对应起来
Constructor<?> stuConstruct2 = stuClass.getConstructor();
System.out.println(stuConstruct2);
Constructor<?> stuConstruct3 = stuClass.getConstructor(String.class, String.class, int.class);
System.out.println(stuConstruct3);
//getDeclaredConstructors:获取所有的构造器对象-->与访问权限无关
Constructor<?>[] stuConstruct4 = stuClass.getDeclaredConstructors();
System.out.println(stuConstruct4.length);
Constructor<?> stuConstruct5 = stuClass.getDeclaredConstructor(String.class);
System.out.println(stuConstruct5);
} catch (Exception e) {
e.printStackTrace();
}
}
创建对象实例
-
如果构造方法是公有的,可以直接调用Class类提供的newInstrance方法来构造对象;
-
如果构造方法是私有的,必须使用Constructor类的setAccessible方法改变构造器权限才可以实例化对象
//3.通过构造器来实例化对象
public static void test3(){
try {
//1.获取字节码文件对象
Class<?> stuClass = Class.forName("Student");
//2.获取构造器
Constructor<?> stuConstructor = stuClass.getDeclaredConstructor(String.class);
//3.实例化对象
//如果构造器是私有的,则不能直接用来实例化对象
stuConstructor.setAccessible(true);//将该构造器的访问权限设置为public
Student s = (Student) stuConstructor.newInstance("Peter");
System.out.println(s);
//反射Student属性
Field[] fields = stuClass.getFields();
System.out.println(fields.length);
Field name = stuClass.getField("name");
System.out.println(name);
} catch (Exception e) {
e.printStackTrace();
}
}
}
反射属性
该系列函数的返回值必须使用Filed或者Filed[]接收。
反射方法