反射的概念
- java类的执行过程
编译:java文件通过编译生成一个.class文件。
加载:通过类的权限定名获取定义此类的二进制流;将这个字节流所代表的静态存储结构转换成方法区的运行时数据结构;在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个累的各种数据的访问入口。
连接--------------------------------------------
验证:文件格式的验证;元数据验证;字节码验证;符号引用验证。验证字节流是否符合class文件规范,描述的信息是否符合java语言规范的要求。是否会对java虚拟机产生危害等。
准备:为类变量分配初始内存并设置初始值。
解析:将虚拟机中的常量池的符号引用转换为直接引用。
连接--------------------------------------------
初始化:开始真正执行类中定义的java程序代码。 - 什么是反射
反射就是程序在运行时,可以根据类的全限定名,动态的架加载该类,创建对象,并调用该对象的任意属性和方法。
java的反射就是利用加载到jvm中的.class文件进行操作。 - 反射实现过程 vs 正常实现过程
正常顺序:引入需要的包类名称 —— 通过new实例化——获取实例化对象;
反射的顺序:实例化对象——getClass()方法——得到完整的包类名称。
反射相关API
反射机制的实现借助于4个类:
- java.lang.Class : 代表一个类
- java.lang.reflect.Method : 代表类的方法
- java.lang.reflect.Field : 代表类的属性
- java.lang.reflect.Constructor : 代表类的构造器
获取Class
- 已知一个类的权限定类名,可以通过Class类的静态方法forName获取一个class.
- 已知一个具体的类,可以通过该类的class属性获取。
- 已知某个类的实例,可以通过该实例getClass方法获取
Class clazz = Class.forName("reflecttest.Person");
System.out.println(clazz);
Class clazz2 = Person.class;
System.out.println(clazz2);
Person person = new Person();
Class clazz3 = person.getClass();
System.out.println(clazz3);
获取运行时类的属性
获取运行时类的属性有两个方法:
Field[] getFields()
Field[] getDeclaredFields()
public class Person {
//私有属性
private String name = "Jack";
//公有属性
public int age = 17;
...
}
-----------------------------------------------
Field[] fields = clazz.getFields();
for (Field f :fields){
System.out.println(f);
}
System.out.println("-----");
Field[] declaredFields = clazz.getDeclaredFields();
for (Field d : declaredFields){
System.out.println(d);
}
输出结果:
有此可见,
getFields方法将返回一个包含Filed对象的数组,这个对象记录这个类或者=其父类的public属性;
getDeclaredFields方法返回一个包含Filed对象的数组,这个对象记录这个类的全部属性。
获取运行时类的方法
Method[] getMethods()
Method[] getDeclaredMethods()
public class Person {
//私有方法
private void eat(){
System.out.println("吃饭");
}
//公有方法
public void study(){
System.out.println("学习!");
}
}
--------------------
Method[] methods = clazz.getMethods();
for (Method m1:methods){
System.out.println(m1);
}
System.out.println("~~~~~~~~");
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method m2:declaredMethods){
System.out.println(m2);
}
System.out.println("~~~~~~~~");
运行结果:
getMethods:记录这个类或其父类的public方法
getDeclaredMethods:记录这个类或接口的全部方法。
获取运行时类的构造方法
Constructor[] getConstructors()
Constructor[] getDeclaredConstructors()
public class Person {
//构造方法
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
----------------------------
Constructor[] constructors = clazz.getConstructors();
for (Constructor c1:constructors){
System.out.println(c1);
}
System.out.println("----------");
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
for (Constructor c2:declaredConstructors){
System.out.println(c2);
}
运行结果:
getConstructors:记录这个类或其父类的public公有构造器
getDeclaredConstructors:记录这个类所有构造器。
动态创建类的对象
Class clazz = Class.forName("reflecttest.Person");
//通过无参构造器构造一个对象
Person person = (Person) clazz.newInstance();
System.out.println(person);
//通过构造器构造一个对象
Constructor cons = clazz.getDeclaredConstructor(String.class, int.class);
Person tom = (Person) cons.newInstance("Tom", 15);
System.out.println(tom);
//可以直接调用public方法
person.study();
//调用private 方法
Method eat = clazz.getDeclaredMethod("eat");
//能直接操作私有属性,我们需要关闭程序的安全监测,属性或者方法的setAccessible(true)
name.setAccessible(true);
eat.setAccessible(true);
eat.invoke(person);
//属性同理
person.age = 18;
System.out.println(person.age);
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
name.set(person,"Ammy");
System.out.println(person.getName());
创建一个实例的方法:newInstance()