Java反射
什么是反射:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意一个方法和属性;
这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
类加载机制
在java中我们要想编译执行java程序那么我们就要进行编译,那么这个编译过后.java文件就会被编译成.class文件最后在终端显示执行结果
-
装载(重点)
- 通过类的属性和类名在定义类的二进制字节流
- 将字节流中的静态存储结构转换为方法去中的数据结构
- 在java堆中生成一个类的java.lang.Class对象,作为方法区这些数据的访问入口
- 在此阶段完成之后,jvm外的二进制字节流会按照jvm要求的格式的二进制字节流进入jvm中的方法区中
-
验证
验证阶段主要是检查方法区中的class文件字节流符合jvm的格式要求
-
准备
此阶段正式为类中的变量分配内存和设置类变量的初始值
-
解析
jvm常量池内的符号引用(常量名)替换为直接引用(地址)过程
-
初始化
类的初始化阶段是类加载过程的最后一步,在准备阶段,类变量已赋过一次系统要求的初始值,而在初始化阶段,则是根据程序员通过程序制定的主观计划去初始化类变量和其他资源,或者可以从另外一个角度来表达:初始化阶段是执行类构造器()方法的过程
为什么有反射
为什么要有反射,首先我们知道我们本来基础的实例化类创建对象时使用的格式是:
Classname Objectname = new Classname();
而我们通过反射获取类对象的格式是:
Class 类对象名称 = Class.forName("包名.类名");
在我们实际工作中我们的代码是跑在服务器上的,而我们的服务在日常工作中会面对各种各样的请求,但是对应的class并没有在jvm中那我们不可能停止服务器,然后在代码中new出一个对象来。
此时反射的动态编译的优点就体现出来了,反射可以当你的服务器需要该类时生成一个类对象,虽然运行效率不可能和new代码相比,但是大大提高了程序的灵活性。
反射优缺点
优点
-
增加了程序的灵活性
定义了一个接口,实现这个接口的类有20个,程序里用到了这个实现类的地方有好多地方,如果不使用配置文件手写的话,代码的改动量很大,因为每个地方都要改而且不容易定位,如果你在编写之前先将接口与实现类的写在配置文件里,下次只需改配置文件,利用反射(java API已经封装好了,直接用就可以用 Class.newInstance())就可完成。
-
代码简洁,复用性高,外部调用方便
-
对于任意一个类都能知道这个类中的属性和方法;对于任意一个类对象都能调用该方法
缺点
-
性能问题:
反射是解释型操作,而正常使用是直接执行。
-
使用反射会模糊程序内部的逻辑结构
程序员都喜欢从源代码中了解程序内部的逻辑,但是反射操作等于绕过了源代码,会带来维护上的难度。
-
安全限制
对于任意一个类都能知道该类的中的属性和方法这个特性,所以反射只能在没有安全限制的条件下实现
利用反射操作创建类对象
有两种操作可以直接创建class对象
//方式一:通过反射获取类的class对象
Class c1 = Class.forName("reflection.user");
//方式2: 类加载的另一种机制
Class c2 = user.class;
//方式3:
Class c3 = c1.getClass();
小知识点:类对象是唯一的,可以打印输出c1,c2,c3的hashcodeSystem.out.println(c1.hashCode());输出的值都相同;
但是通过new出的对象都是实例化对象创建出的对象的hashCode不相同;
通过类对象来调用类中的方法和值
通过方法获取及使用类的构造方法
//获取构造方法
Constructor[] constructors = c1.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
constructors = c1.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
//通过构造器创建对象
Constructor constructor = c1.getConstructor(int.class,String.class,boolean.class);//参数的顺序要对应
user u1 = (user) constructor.newInstance(1,"a",true);
System.out.println(u1);
通过方法获取及使用类的值
//获取属性
Field[] fields = c1.getFields();//只能找到public属性
for (Field field : fields) {
System.out.println(field);
}
fields = c1.getDeclaredFields();//能获取所有属性名
for (Field field : fields) {
System.out.println(field);
}
System.out.println("************************************");
Field id = c1.getDeclaredField("id");//获取特定属性
System.out.println(id);
System.out.println("************************************");
//通过反射操作属性
user u2 = (user) c1.newInstance();
Field name = c1.getDeclaredField("username");
name.setAccessible(true);//关闭安全锁
name.set(u2,"hh");
System.out.println(u2.getUsername());
通过方法获取及使用类的方法
//获取方法
Method[] methods = c1.getMethods();//获取所有父类方法和本类的public方法
for (Method method : methods) {
System.out.println(method);
}
System.out.println("************************************************");
methods = c1.getDeclaredMethods();//获取本类方法
for (Method method : methods) {
System.out.println(method);
}
System.out.println("************************************************");
Method declaredMethod = c1.getDeclaredMethod("getUsername",null);
System.out.println(declaredMethod);
Method setUsername = c1.getMethod("setUsername", String.class);//参数是必要的因为重载会导致方法名相同
//通过反射获取方法
Method setId = c1.getDeclaredMethod("setId", int.class);
//invoke方法激活的意思
//(对象,方法的值)
user u = (user)c1.newInstance();
setId.invoke(u,33);
System.out.println(u.getId());
通过反射的性能及其优化
添加 实例化对象.setAccessible(true);