一、java的反射机制是什么?有什么作用
1、什么是java反射
Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法,这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制;
2、反射提供的功能:
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法;
- 在运行时调用任意一个对象的方法;
3、反射的使用场景
- Java编码时知道类和对象的具体信息,此时直接对类和对象进行操作即可,无需反射;
- 如果编码时不知道类或者对象的具体信息,此时应该使用反射来实现;比如阿里的路由框架、插件化框架、启动优化框架、eventbus等等;
二、反射涉及到的知识点
整个反射图

1、简单介绍类加载器
类加载器负责将.class文件加载到内存中,并为之生成对应的Class对象。类加载器负责加载所有的类,系统为所有加载到内存中的类生成一个java.lang.Class 的实例

类加载器的组成:
- Bootstrap ClassLoader 根类加载器 :也被称为引导类加载器,负责Java核心类的加载,比如System类,在JDK中JRE的lib目录下rt.jar文件中的类;
- Extension ClassLoader 扩展类加载器 :负责JRE的扩展目录中jar包的加载,在JDK中JRE的lib目录下ext目录;
- System ClassLoader 系统类加载器 :负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径,主要是我们开发者自己写的类;
2、Class
Java文件被编译后,生成了.class文件,JVM此时就要去解读.class文件。当程序主动去使用某个类时,JVM会通过前面提到的三个步骤:加载、连接和初始化三个步骤对类进行初始化。被编译后的Java文件.class也被JVM解析为一个对象,这个对象就是java.lang.Class。这样当程序在运行时,每个java文件就最终变成了Class类对象的一个实例;
每个类被加载之后,系统都会为该类生成一个对应的Class对象,通过Class对象就可以访问到JVM中该类的信息,一旦类被加载到JVM中,同一个类将不会被再次载入。被载入JVM的类都有一个唯一标识就是该类的全名,即包括包名和类名。
获取到class类的方式:
- 使用Class类的静态方法forName(String className),其中参数className表示所需类的全名。如“Class cObj=Class.forName(”java.lang.String”);”。另外,forName()方法声明抛出ClassNotFoundException异常,因此调用该方法时必须捕获或抛出该异常;
- 用类名调用该类的class属性来获得该类对应的Class对象,即“类名.class”。如,语句“ClasscObj=Cylinder.class;”将返回Cylinder类所对应的Class对象赋给cObj变量;
- 用对象调用getClass()方法来获得该类对应的Class对象,即“对象.getClass()”。该方法是Object类中的一个方法,因此所有对象调用该方法都可以返回所属类对应的Class对象。如例8.8中的语句“Person per=new Person(“张三”);”可以通过以下语句返回该类的Class对象:Class cObj=per.getClass();
3、Field类
即字段类,我们可以通过一个类的Class对象获取其Field类的对象,然后java当中提供了这个Field类来提供反射获取字段的相关信息,以及进行一些操作,比如set一个字段的值等功能;
4、Method类
即方法类,我们可以通过一个类的Class对象获取其Method类的一个实例对象,并且使用获得的Method对象去获取这个方法的相关信息,以及调用这个方法的功能;
二、反射的实际用法
1、通过一个对象获得完整的包名和类名
/**
* 获得完整的包名和类名
*/
public class Reflect1 {
public static void main(String[] args) {
Reflect1 reflect1 = new Reflect1();
System.out.println("包名: " + reflect1.getClass().getPackage());
System.out.println("类名: " + reflect1.getClass().getName());
System.out.println("简单名: " + reflect1.getClass().getSimpleName());
System.out.println("规范名: " + reflect1.getClass().getCanonicalName());
System.out.println("类型名: " + reflect1.getClass().getTypeName());
/**
* 包名: package com.test
* 类名: com.test.Reflect1
* 简单名: Reflect1
* 规范名: com.test.Reflect1
* 类型名: com.test.Reflect1
*/
}
}
2、获取Class类对象
/**
* 实例化Class类对象
*/
public class Reflect2 {
public static void main(String[] args) throws ClassNotFoundException {
Class<?> class1 = null;
Class<?> class2 = null;
Class<?> class3 = null;
class1 = Class.forName("com.test.Reflect2");
class2 = new Reflect2().getClass();
class3 = Reflect2.class;
System.out.println("类名: " + class1.getName());
System.out.println("类名: " + class2.getName());
System.out.println("类名: " + class3.getName());
}
}
3、方法调用
public class Main {
public static void main(String[] args) throws Exception{
//返回A的构造方法
Constructor c = A.class.getConstructor();
//返回A类的所有为public 声明的构造方法
Constructor[] cons = A.class.getConstructors();
//返回A类所有的构造方法,包括private
Constructor[] cons2 = A.class.getDeclaredConstructors();
//返回A类的第一个public 方法
Method m = A.class.getMethod("say");
//执行
m.invoke(A.class.newInstance(), null);
//返回A类所有的public 方法
Method[] ms = A.class.getMethods();
//返回A类所有的方法,包括private
Method[] allMs = A.class.getDeclaredMethods();
//返回A类的public字段
Field field = A.class.getField("i");
System.out.println(field.get(A.class.newInstance()));
//返回A类的static 字段
System.out.println(field.get(null));
}
}
class A{
public int i = 1;
public static int b = 2;
public A(){
System.out.println("无参构造");
}
private A(String s){
System.out.println("有参构造"+s);
}
public void say(){
System.out.println("say");
}
}
4、属性的调用
/**
* 反射机制更改属性值
*/
public class Reflect implements Serializable{
private static final long serialVersionUID = -2862585049955236662L;
private String proprety = null;
public static void main(String[] args) throws Exception{
// 获取类
Class<?> clazz = Class.forName("com.test.Reflect");
// 得到当前类的实例
Reflect instance = (Reflect) clazz.newInstance();
// 获取要更改的属性
Field proprety = clazz.getDeclaredField("proprety");
// 设置可访问
proprety.setAccessible(true);
// 设置属性
proprety.set(instance, "属性");
System.out.println(proprety.get(instance));
/**
* 属性
*/
}
}
5、反射机制的动态代理
/**
* 反射机制的动态代理
* 在java中有三种类类加载器。
*/
public class Reflect {
public static void main(String[] args) throws Exception{
MyInvocationHandler invocationHandler = new MyInvocationHandler();
Subject sub = (Subject) invocationHandler.bind(new RealSubject());
String info = sub.say("test", 23);
System.out.println(info);
}
}
class MyInvocationHandler implements InvocationHandler{
private Object obj = null;
public Object bind(Object obj) {
this.obj = obj;
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object temp = method.invoke(obj, args);
return temp;
}
}
class RealSubject implements Subject{
@Override
public String say(String name, int age) {
return name + ", " + age;
}
}
interface Subject{
String say(String name, int age);
}
三、项目中经验总结封装Reflect类
Reflect类是个反射帮助类

实例说明:
public class ReflectTest {
public ReflectTest() {
}
public void test(){
System.out.println("testtest dddddd");
}
}
1、获取ReflectTest对象
ReflectTest reflectTest = Reflect.on("ReflectTest").create().get();
ReflectTest reflectTest1 = Reflect.on(ReflectTest.class).create().get();
2、属性
Reflect.on("ReflectTest").create().field("names");
Reflect.on("ReflectTest").create().set("names","12");
3、方法操作
Reflect.on("com.force.vpn.app.ui.activity.ReflectTest").create().call("test");
Reflect.on("com.force.vpn.app.ui.activity.ReflectTest").create().call("test1","89");
用了Reflect类是不是很简单,那想要获取反射工具类Reflect,在公众号里输入【反射】即可得到;
总结反射
1、性能问题
Java反射机制中包含了一些动态类型,所以Java虚拟机不能够对这些动态代码进行优化,因此,反射操作的效率要比正常操作效率低很多。我们应该避免在对性能要求很高的程序或经常被执行的代码中使用反射,而且,如何使用反射决定了性能的高低。如果它作为程序中较少运行的部分,性能将不会成为一个问题;
2、安全限制
使用反射通常需要程序的运行没有安全方面的限制。如果一个程序对安全性提出要求,则最好不要使用反射;
3、程序健壮性
反射允许代码执行一些通常不被允许的操作,所以使用反射有可能会导致意想不到的后果。反射代码破坏了Java程序结构的抽象性,所以当程序运行的平台发生变化的时候,由于抽象的逻辑结构不能被识别,代码产生的效果与之前会产生差异。
阅读原文