1、反射
1.1、介绍
- Reflection(反射)是Java语言的特征之一
- 允许运行中的Java程序对自身进行检查
- 反射能够直接操作类私有属性
- 反射能够在运行的时候获取一个类中的所有信息(成员变量、方法、构造器等)。
要想剖析一个类,必须先要获取该类的字节码对象(class对象),而剖析就是使用Class类中的方法。即,反射就是把Java类中的各个成分(变量、方法等等)映射成一个个的Java对象,
// 常用方法
//获取包名、类名
clazz.getPackage().getName()//包名
clazz.getSimpleName()//类名
clazz.getName()//完整类名
//获取成员变量定义信息
getFields()//获取所有公开的成员变量,包括继承变量
getDeclaredFields()//获取本类定义的成员变量,包括私有,但不包括继承的变量
getField(变量名)
getDeclaredField(变量名)
//获取构造方法定义信息
getConstructor(参数类型列表)//获取公开的构造方法
getConstructors()//获取所有的公开的构造方法
getDeclaredConstructors()//获取所有的构造方法,包括私有
getDeclaredConstructor(int.class,String.class)
//获取方法定义信息
getMethods()//获取所有可见的方法,包括继承的方法
getMethod(方法名,参数类型列表)
getDeclaredMethods()//获取本类定义的的方法,包括私有,不包括继承的方法
getDeclaredMethod(方法名,int.class,String.class)
//反射新建实例
clazz.newInstance();//执行无参构造创建对象
clazz.newInstance(222,"韦小宝");//执行有参构造创建对象
clazz.getConstructor(int.class,String.class)//获取构造方法
//反射调用成员变量
clazz.getDeclaredField(变量名);//获取变量
clazz.setAccessible(true);//使私有成员允许访问
f.set(实例,值);//为指定实例的变量赋值,静态变量,第一参数给null
f.get(实例);//访问指定实例变量的值,静态变量,第一参数给null
//反射调用成员方法
Method m = Clazz.getDeclaredMethod(方法名,参数类型列表);
m.setAccessible(true);//使私有方法允许被调用
m.invoke(实例,参数数据);//让指定实例来执行该方法
测试:
public class UserTest {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
Class<?> c1 = Class.forName("com.psy.fanshe.User");
// 生成无参的对象
Object user = c1.newInstance();
System.out.println(user);
/**
* 获取class的三种方式(一个Class只有一个class对象)
* 以下最常用的是第三种,第一种对象都有了,就不需要反射了。第二种需要导入包,不导入包就无法使用,不够灵活。第三种是最安全的,性能最好的
* 1、对象.getClass()
* 2、类.class
* 3、Class.forName("全类名")
*/
// 1、对象.getClass()
User user1 = new User();
Class c2 = user1.getClass();
// 2、类.class
Class c3 = User.class;
// 查看是否是同一个hashcode用来判断是否是同一个对象
System.out.println(c1.hashCode()+" "+c2.hashCode()+" "+c3.hashCode());
}
}
1.2、类加载时候的内存分析
Java内存:
- 堆
- 存放new的对象和数组
- 可以被所有的线程共享,不会存在别的对象引用。
- 一些要执行的代码
- 栈
- 存放基本变量类型
- 由方法区中的class信息创建的java.lang.Class对象
- 方法区(特殊的堆)
- 可以被所有的线程共享
- 包含了所有的class的信息和static变量
类加载时候的内存分析:
当那个程序主动使用某个类时,如果该类还未加载到内存中,则系统会通过如下三个步骤对该类进行初始化。
- 类的加载
将类的class文件读入内存。此过程由类的加载器完成,并将静态数据装换成方法区的运行时数据,并为之创建一个java.lang.Class对象
- 类的链接
将类的二进制数据合并到JRE中,为类变量(static)分配内存,并设置默认的初始值,内存在方法区中进行分配
- 类的初始化
JVM负责对类进行初始化
什么时候会发生类的初始化:
- 类的主动引用
- 当虚拟机被启动,先初始化main方法所在的类
- new一个类的对象
- 调用类的静态成员和静态方法
- 使用java.lang.reflect包对类进行反射调用
- 初始化一个类,其父类没有被初始化的时候,首先初始化其父类
- 类的被动引用(不会发生类的初始化)