reflection反射
动态和静态语言
动态语言
-
动态语言就是一类在运行时可以改变其结构的语言,通俗点说就是在运行时代码可以根据某些条件改变自身结构
-
主要动态语言:object-C,C#,JavaScript,PHP,Python等.
静态语言
- 与动态语言相对应,运行时结构不可变的语言就是静态语言。如java,c,c++。
- java不是动态语言,但java可以被称为“准动态语言”。即java有一定的动态性,我们可以利用反射机制获得类似动态语言的特性,java动态性让编程时更加灵活
java反射机制概述
反射概述
- 反射是java被视为动态语言的关键
- 反射机制允许程序在运行期间借助于Reflection API取得任何类的内部信息,
- 反射能直接操作任意对象的内部属性以及方法
- 反射就是我们正常的方式反过来
- 反射可以通过对象反射出类的名称
- 一个类被加载后,类的整个结构都会被封装在Class对象中
狂神 反射 反射概述视频06:29
-
在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息,这个对象就想一面镜子,透过这面镜子看到类的结构,所以我们称之为:反射
狂神 反射 获得反射对象01:29
反射的优缺点
优点
- 可以实现动态创建对象和编译,体现出很大的灵活性
缺点
- 对性能有影响。使用反射基本上是一解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于直接相同操作。
Class类
Class的概述
狂神 反射 得到Class类的几种方式00:04
Class的常用方法
狂神 反射 得到Class类的几种方式01:47
哪些类型有Class对象
狂神 反射 所有类型的Class对象
![哪些类型有Class对象](C:\Users\lyj\Desktop\Learning summary\img\reflection反射\哪些类型有Class对象.png)
Class c1 = Object.class;//类
Class c2 = Comparable.class;//接口
Class c3=String[].class;//一维数组
Class c4=int[][].class;//二维数组
Class c5= Override.class;//注解
Class c6= ElementType.class;//枚举
Class c7=Integer.class;//基本类型
Class c8=void.class;//空
Class c9=Class.class;//Class
java内存分析
狂神类加载内存分析00:23
类的加载过程
狂神类加载内存分析00:39
类的加载和Class Loader的理解
加载
- 编译过后将class文件加载到内存当中,并且将这些静态数据转换成方法区的运行时数据结构,然后生成一个Class对象
链接
- 将java类的二进制代码合并到JVM的运行环境中
验证
- 确保加载的类信息符合JVM规范,没有安全方面的问题
准备
- 正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配
解析
- 虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程
初始化
- 执行类构造器
()方法的过程。类构造器 ()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造器信息的,不是构造该类对象的构造器)。 - 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
- 虚拟机会保证一个类的
()方法在多线程环境中被正确加锁和同步。
public class Test04 {
public static void main(String[] args) {
A a = new A();
System.out.println(A.m);
}
}
class A{
static {
System.out.println("A类静态代码块初始化");
m=300;
}
static int m = 100;
public A() {
System.out.println("A类的无参构造初始化");
}
}
什么时候会发生类初始化
类主动引用(一定会发生类的初始化)
- 当虚拟机启动,先初始化main方法所在的类
- new一个类的对象
- 调用类的静态成员(除了final常量)和静态方法
- 使用java.lang.reflect包的方法对类进行反射调用
- 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类
类的被动引用(不会发生类的初始化)
- 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态常量,不会导致子类初始化
- 通过数组定义类引用,不会触发此类的初始化
- 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)
类加载器的作用
类加载的作用
- 将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
类缓存
- 标准的javaSE类加载器可以按照要求查找类,但一旦某个类被加载到加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。
JVM规范定义了如下类型的加载器
反射的方法
获取类名
System.out.println(c1.getName());//获得包名加类名(由于我直接在src下写的类所有没有包名)
System.out.println(c1.getSimpleName());//获得类名
获取属性
user类内全部属性都为private
//获得类的属性
System.out.println("==============");
Field[] fields = c1.getFields();//只能找到public属性
for (Field field : fields) {
System.out.println(field);
}
Field[] declaredFields = c1.getDeclaredFields();//找到全部的属性
for (Field field : declaredFields) {
System.out.println(field);
}
System.out.println("==================");
System.out.println(c1.getDeclaredField("name"));//获取指定属性不管修饰符是什么
System.out.println(c1.getField("name"));//只能指定获取public属性
因为name是private所有报异常
获得类的全部类
//获取全部类的方法
System.out.println("==================");
Method[] methods = c1.getMethods();//获得本类和父类全部的public方法
for (Method method : methods) {
System.out.println(method);
}
System.out.println("==================");
methods=c1.getDeclaredMethods();//获得本类的所有方法(包括private)
for (Method method : methods) {
System.out.println(method);
}
- 获得本类和父类全部的public方法
- 获得本类的所有方法(包括private)
获得指定类的方法
//获得类的指定方法
//为什么后面要参数,因为java里有重载
System.out.println("==================");
Method getName = c1.getMethod("getName",null);
Method setName = c1.getMethod("setName", String.class);
System.out.println(setName);
System.out.println(getName);
获得构造器
//获得构造器
System.out.println("==================");
Constructor[] constructors = c1.getConstructors();//获得public构造器
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
System.out.println("==================");
constructors = c1.getDeclaredConstructors();//获取全部构造器(后面单列模式构造方法是private)
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
获得指定构造器
//获得指定构造器
System.out.println("==================");
Constructor constructor = c1.getConstructor(String.class, int.class, int.class);//获得public构造器
System.out.println(constructor);
constructor=c1.getDeclaredConstructor(String.class, int.class, int.class);//获取构造器(后面单列模式构造方法是private)
System.out.println(constructor);
有了Class对象,能做什么?
public static void main(String[] args) throws Exception {
//获得CLass对象
Class c1 = Class.forName("User");
//构造一个对象
User user = (User) c1.newInstance();
System.out.println(user);
//通过构造器创建对象
Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
User user2 = (User) constructor.newInstance("青牛", 1, 18);
System.out.println(user2);*/
//通过反射调用普通方法
User user = (User) c1.newInstance();
//通过反射调用方法
Method setName = c1.getDeclaredMethod("setName", String.class);
setName.invoke(user,"青牛");
System.out.println(user.getName());
//通过反射操作属性
User user = (User) c1.newInstance();
Field name = c1.getDeclaredField("name");
//不能直接操作私有属性,我们需要关闭程序的安全检测
name.setAccessible(true);//这个就是关闭检测的,参数写true
name.set(user,"青牛");
System.out.println(user.getName());
}
setAccessible
性能测试
//普通方式调用
public static void test01(){
User user =new User();
long startTime = System.currentTimeMillis();
for (int i = 0; i < 2000000000; i++) {
user.getName();
}
long endTime = System.currentTimeMillis();
System.out.println("普通方式执行时间:"+(endTime-startTime)+"ms");
}
//通过反射调用
public static void test02() throws Exception {
User user =new User();
Class c1 = user.getClass();
Method getName = c1.getMethod("getName", null);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 2000000000; i++) {
getName.invoke(user,null);
}
long endTime = System.currentTimeMillis();
System.out.println("反射方式执行时间:"+(endTime-startTime)+"ms");
}
//通过反射调用 关闭检测
public static void test03() throws Exception {
User user =new User();
Class c1 = user.getClass();
Method getName = c1.getMethod("getName", null);
getName.setAccessible(true);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 2000000000; i++) {
getName.invoke(user,null);
}
long endTime = System.currentTimeMillis();
System.out.println("反射关闭检测方式执行时间:"+(endTime-startTime)+"ms");
}
public static void main(String[] args) throws Exception {
test01();
test02();
test03();
}
- 结果