反射
一、什么是反射
-
反射(reflection) 是一种动态获取对象信息和动态调用对象功能称为java语言的反射机制 这种机制 是动态语言的关键
-
在程序的运行时,可以构造任意类的对象、可以了解任意一个类的所有信息(接口、方法、注解、父类,成员变量、方法)
-
并且可以调用 用反射获得的属性、方法
二、类的装载过程
-
类的加载过程指jvm 虚拟机 把.class文件加载到内存 并生成对映的class对象的过程
-
类的加载 装载、链接(验证 准备 解析) 、初始化
2.1、加载
1、通过类的全限定名 获取其自定义的 二进制字节流 (不同来源的类)
2、将这个字节流代表的静态存储结构转化成方法区的动态存储结构
3、在堆内存生成 对应于这个类的Java.lang.class 文件作为方法区数据的访问接口
-
不同来源
- 从本地系统
- 从zip ,jar 等文件
- 从网络下载
- 将Java源文件动态编译为.class (自己编写的类文件)
-
生成class对象
- 在堆内存中生成
- 可以用这个class对象访问 方发区中的类对象
2.2 、链接
1、验证
确保加载类的正确性和安全性
2、准备
类的静态变量在此阶段被分配内存并赋予默认值
- 被分配的内存来自方法区 (类变量的值不属于类的实例对象而是属于类)
- 当变量被final 修饰时在定义时就必须对其进行显示赋值 否则编译不通过
- 对于引用类型 当没有对其显式赋值就使用 会自赋予null值
3、解析
把类中引用变量全部替换为栈内存中的真实地址
2.3、初始化
jvm会调类 构造器() 方法对类进行初始化(方法过程)
-
类构造器()方法
- 该方法是由jvm自动收集类中所有类变量的赋值动作和静态语句块中语句合并产生
- 即该阶段会对类变量执行它的赋值语句和静态块中的语句(它们的执行顺序参考它们的书写顺序)
-
当执行一个类的()方法时会先确保它父类的()方法已经被执行(即它的父类已经被初始化 如果父类没有被初始化 则先初始化父类)
-
进行初始化前提是该类的装载流程已经完成了前两个阶段
三、类的装载时机
3.1、类被主动使用
-
使用new 操作符new 一个类的实例
-
当子类被初始化时
-
public class ClassLoadTest { public static void main(String[] args) { new C2(); } } class C1{ static { System.out.println("c1 被初始化"); } } class C2 extends C1{ static { System.out.println("c2 被初始化"); } }
-
-
类成员或类方法被调用
-
public class ClassLoadTest { public static void main(String[] args) { // int a=C2.c2_; C2.getNull(); } } class C1 { static { System.out.println("c1 被初始化"); } } class C2 extends C1 { static { System.out.println("c2 被初始化"); } static int c2_; //我们不为它赋值 在准备阶段他会被赋予内存和默认值 static void getNull() { } }
-
-
使用反射创建类的实例
-
class C3 { public static void main(String[] args) throws ClassNotFoundException { Class aClass = Class.forName("core.C2"); } //还没 newInstance就加载了 }
-
3.2、类的被动使用
类的被动使用不会加载类
-
访问一个类变量时 只有定义了该类变量的类会被加载 (忽略父类先初始化?)
-
引用常量不会触发类的初始化
-
定义数组不会 只是分配空间
四、Class 类
4.1、Class类是什么
-
它也是一个类 不能通过new 操作符创建 只能 使用 Class.forName() 或者超类的getClass()得到
-
我们在类加载过程中了解到 每个类在装载过程中 jvm会在堆内存为类创建一个该类对应的唯一的Class 对象
-
它可以访问\得到方法区中已经被装载的所有结构(被装载后才可以得到Class对象)
-
java运行时系统始终为所有对象的对象维护一个被称为运行时的类型标识。这个信息跟踪着每个对象所属的类。虚拟机利用运行时类型信息选择相应的方法执行。可以通过一个特定的java类访问这些信息,保存这些信息的类就是Class类
4.2、得到Class对象
- Class.forName()基于类的完全限定类名得到
public static void main(String[] args) throws ClassNotFoundException {
Class aClass = Class.forName("com.lxc.entity.Person");
}
- Object类的getClass()得到
public static void main(String[] args) throws ClassNotFoundException {
Person person = new Person();
Class aClass = person.getClass();
}
- 如果 T 是任意的java 类型 (或void 关键字)T.class 将返回T 在装载时堆内存区所创建的Class 对象
public static void main(String[] args) throws ClassNotFoundException {
Class aClass = Person.class;
}
- 基本类型的包装类可以使用.type 方法得到Class 对象
五、反射常用API
Field、Method、Constructor 分别用于描述类的域 、方法、构造器
它们有一个getName()的共同方法 返回一个描述自己的字符串
Field getType() 返回实例域所属的Class对象
Method getModifiers() 返回类中所有方法 权限修饰符的使用状况 (用某个整数的不同位开关)
5.1、forName
- static Class forName(String className)
- 返回全限定类名为className 类的Class 对象
5.2、newInstance()
- Object newInstance()
- 返回这个类的一个新实例(已弃用)
- getDeclaredConstrucotr().newInstance()替代
5.3、getFields()
-
Fields[ ] getFields()
-
返回包含该类和其父类的所有公共实例域的一个数组
-
Class<?> aClass = Class.forName("core.User"); Field fields[]=aClass.getFields(); int i=0; for (Field field : fields) { System.out.println(field); System.out.println(); }
-
5.4、getDeclaredFields()
- Fields [ ] getDeclaredFields()
- 返回当前类的所有实例域的Fields数组 (无视访问权限)
- 当类中没有实例域时返回长度为0的数组
5.5、getMethods()
- 返回包含继承链和本身所有的公共方法的Method数组
5.6、getDeclareMethods()
-
返回包含本身所有方法的数组
-
Method methods[]=aClass.getMethods(); for (Method method : methods) { System.out.println(method); }
-
5.7、getConstructor()
- Constructors [ ] getConstructors()
- 获得所有公共构造器
5.8、getDecalredConstructors()
- Constructors [ ] getDecalredConstructors()
- 返回所有构造器
5.9、getField(String fieldName)getDecalerdField(String fieldName)
-
getField 返回该类或父类中 实例域名为 fieldName 且访问权限为Public 的实例域对象
-
getDecalerdField 返回该类本身定义 名为 fieldName 的实例域 无关访问权限修饰符
5.10、getMethod(String methodName, Class<?>… parameterTypes)
- 返回该类或父类中方法名为methodName 的公共方法 如果有参数 则需要传入 parameterTypes 这里使用了**…** 可以传数组 也可以传单个参数
5.11、getDecalredMethod(String methodName, Class<?>… parameterTypes)
- 返回该类 中方法名为 methodName 的方法 无视访问权限修饰符 如果有参数 则需要传入 parameterTypes 这里使用了**…** 可以传数组 也可以传单个参数
5.12、getConstructors(Class<?>… parameterTypes)
5.13、getDecalredConstructors(Class<?>… parameterTypes)
- parameterTypes 传参数列表的每个参数的 Class对象
- 之所以传参数是因为 构造函数会有很多重载 传Class 选择需要拿到的构造器
5.14、getParameterTypes()
- Class [ ] getParameterTypes()
- 返回一个 描述 构造器 或 方法 所有参数类型 的Class 数组
5.15、getReturnType()
- Class getReturnType()
- 返回方法的返回值类型
5.16、setAccessible()
-
void setAccessible(boolean flag)
-
void setAccessible(AccessibleObj[ ] array,boolean flag)
-
就算使用getDeclaredFields()拿到了所有实例域的数组 但是被private 修饰的域是不能被get()访问的
-
我们可以使用setAccessible(true) 这样就能访问了
-
启用 或禁用访问安全检查 (非常官方)
-
Class<?> aClass = Class.forName("core.User"); Field [] fields=aClass.getDeclaredFields(); User user=new User("lxc","湖南","23234"); for (Field field : fields) { field.setAccessible(true); System.out.println(field.get(user)); }
-
5.17、get()
- Object get(Object obj)
- field.get(obj) 是这样用的
- 返回obj 中field 的阈值 (当然得拥有访问权限)
5.18、set()
-
void set(Object obj,Object newValue)
- field.set(Object obj,Object newValue)
- 为obj的field域的值覆盖为newValue(需要合理的访问权限)
5.19、invoke()
-
Object invoke(Object obj, Object... args)
-
method.invoke(Object obj, Object… args)
-
使用args 为参数 唤醒 (调用)obj 的 method 方法 (需要合理访问权限)
六、使用反射分析类的能力
6.1、PerSon 类
public class Person {
private String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {
}
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 void sayName() {
System.out.println("myName is" + name);
}
private void SayAge() {
System.out.println(age);
}
}
6.2、Student 类
public class Student extends Person {
public int grade;
private String className;
private Bag schoolBag;
public Student(int grade, String className) {
this.grade = grade;
this.className = className;
}
public Student(String name, int age) {
super(name, age);
}
public int getGrade() {
return grade;
}
// public void setGrade(int grade) {
// this.grade = grade;
// }
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public void sayClass(String className)
{
System.out.println(className);
}
private void sayGrade(String grade)
{
System.out.println(grade);
}
public Student(String name, int age, int grade, String className, Bag schoolBag) {
super(name, age);
this.grade = grade;
this.className = className;
this.schoolBag = schoolBag;
}
}
6.3、Bag 类
package com.lxc.entity;
public class Bag {
public String brand;
//书包的品牌
public Bag(String brand) {
this.brand = brand;
}
}
6.4、测试类
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test_ {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
Class aClass = Class.forName("com.lxc.entity.Student");
//拿到Student 类的Class 对象
Field[] fields = aClass.getDeclaredFields();
//拿到所有自己所有的实例域
Field.setAccessible(fields, true);
//为所有域都设置可访问
for (Field field : fields) {
System.out.println(field);
}
//遍历一下 所有域的描述
Student student = (Student) aClass.getDeclaredConstructor(String.class, int.class, int.class, String.class, Bag.class).newInstance("lxc", 18, 15, "androidPro", new Bag("nike"));
//用反射创建一个实例
for (Field field : fields) {
System.out.println(field.get(student));
}
//查看Student 域中所有的值 这样只能拿到自己的不能拿到父类的
Field[] fields2 = aClass.getFields();
Field.setAccessible(fields2, true);
for (Field field : fields2) {
System.out.println(field.get(student));
}
//拿到父类 和自己的修饰符为public 的域
Field field = aClass.getField("age");
//拿到父类定义的域 年龄
field.set(student, 100);
System.out.println(field.get(student));
//修改年龄为 100
Method[] methods = aClass.getMethods();
//拿到继承链和自己的公共方法
for (Method method : methods) {
System.out.println(method);
}
//输出一下这些方法的描述
Method [] methods2=aClass.getDeclaredMethods();
//拿到自己的所有方法 不包含父类的
for (Method method : methods2) {
if (method.getParameterTypes().length==1) {
method.setAccessible(true);
method.invoke(student, "asd");
}
}
//判断参数为 一个 然后传参执行
}
}
七、反射性能测试
public class PerformanceTest {
static void test01()
{
long start=System.currentTimeMillis();
Student student=new Student();
for (int i = 0; i < 1000000000; i++) {
student.getGrade();
}
long end=System.currentTimeMillis();
long result=end-start;
System.out.println("正常方式10亿次花费时间"+result+"ms");
}
static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
long start=System.currentTimeMillis();
Student student=new Student();
Class aClass=student.getClass();
Method method=aClass.getDeclaredMethod("getGrade");
for (int i = 0; i < 1000000000; i++) {
method.invoke(student,null);
}
long end=System.currentTimeMillis();
long result=end-start;
System.out.println("反射方式10亿次花费时间"+result+"ms");
}
static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
long start=System.currentTimeMillis();
Student student=new Student();
Class aClass=student.getClass();
Method method=aClass.getDeclaredMethod("getGrade");
method.setAccessible(true);
for (int i = 0; i < 1000000000; i++) {
method.invoke(student,null);
}
long end=System.currentTimeMillis();
long result=end-start;
System.out.println("反射关闭访问安全检查10亿次花费时间"+result+"ms");
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
test01();
test02();
test03();
//反射方式效率 跟 正常方式的 比值非常悬殊
//关闭安全访问检查 能提高效率
}
}