什么是反射
反射是指在Java运行状态中
给定一个类对象(Class对象),通过反射获取这个类对象
(Class对象)的所有成员结构;
给定一个具体的对象,能够动态地调用它的方法及对任意
属性值进行获取和赋值
这种动态获取类的内容、创建对象、以及动态调用对象方法及操作属性的机制,就叫做Java的反射机制
反射能做什么?
在运行时判断一个类所具有的成员变量和方法
在运行时判断任意一个对象所属的类
在运行时构造一个类的对象
在运行时调用任意一个对象的方法
优缺点
优点:
增加程序的灵活性,避免将固有的逻辑程序写死到代码里
代码简洁,可读性强,可提高代码的复用率
缺点
:
相较直接调用在创建对象比较多的情景下反射性能下降
内部暴露和安全隐患(破坏单例)
反射性能慢的原因
寻找类Class字节码的过程,比如通过ClassName找到对应的字
节码Class,然后进行加载、解析,也会比较慢,而new的方式
则无需寻找,因为在Linking的解析阶段已经将符号引用转为
了直接引用
安全管理机制的权限验证等等
若需要调用native方法调用时JNI接口的使用
入参校验
具体详细原因参考:
https://www.jianshu.com/p/4e2b49fa8ba1
class对象的组成
反射的基本操作
获取Class文件的方式
四种方式:
1、类名.class
2、Class.forName //反射
3、对象.getClass()
4、通过类加载器.loadClass()
//1、类名.class获取class文件,这种方式不会对类初始化
Class<Person> clazz01 = Person.class;
System.out.println(clazz01);
//2、会进行类的初始化操作,所以咱们在加载jdbc的MySQL驱动的时候都是用这种方式去加载的
Class<?> clazz02 = Class.forName("com.nx.vip.javacore.reflection.pojo.Person");
System.out.println(clazz02);
//3、已经创建出来的实例对象,可以直接用这种方式,
//既然实例对象都有了,为什么还要获取实例对象所对应的类对象?
//既然实例对象都有了,说明这个类已经被加载到jvm虚拟机中了,可以直接调用getClass从虚拟机中获取到类所定义的类对象
Class<? extends Person> clazz03 = new Person().getClass();
System.out.println(clazz03);
//4、通过类加载器.loadClass()
Class<?> clazz04 = CoreMain.class.getClassLoader().loadClass("com.nx.vip.javacore.reflection.pojo.Person");
System.out.println(clazz04);
class.forName和classLoader的区别?
class.forName:
- 将类的.class文件加载到jvm中
- 对类进行解释,执行类中的static块
classLoader:
- 只干一件事情:将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。
Class.forName(className):内部调用的方法是 Class.forName(className,true,classloader);
第2个boolean参数表示类是否需要初始化, Class.forName(className)默认是需要初始化。
一旦初始化,就会触发目标对象的static块代码执行,static参数也也会被再次初始化。
ClassLoader.loadClass(className):
内部调用的方法是ClassLoader.loadClass(className,false);
第2个 boolean参数,表示目标对象是否进行链接,false表示不进行链接,不进行链接意味着不进行包括初始化等一些列步骤,那么静态块和静态对象就不会得到执行
基本信息操作
int modifier = clazz.getModifiers(); // 获取类修饰符
Package aPackage = clazz.getPackage(); // 获取类包名
String fullClassName = clazz.getName(); // 获取类的全路径名称
String simpleName = clazz.getSimpleName(); // 获取类的简单名称
ClassLoader classLoader = clazz.getClassLoader(); // 获取类加载器
Class[] interfaces = clazz.getInterfaces(); // 获取类实现的接口列表
Class superclass = clazz.getSuperclass(); // 获取类的父类
Annotation[] annotations = clazz.getAnnotations(); // 获取类的注解信息
类的属性操作
Person person = (Person) clazz.newInstance();
// 获取类中所有的共有字段 包含继承的字段
Field[] fields = clazz.getFields();
// 获取类中定义的字段 内部
Field[] declaredFields = clazz.getDeclaredFields();
// 获取指定名称的类中定义的字段
Field nameField = clazz.getDeclaredField("name");
// 获取字段的修饰符
int modifiers = nameField.getModifiers();
// 指定字段强制访问
nameField.setAccessible(true);
// 修改字段你的值
nameField.set(person,“samuel");
// 静态字段赋值
nameField.set(null,"静态字段赋值");
类的方法操作
// 获取类中的所有的共有的方法 继承
Method[] methods = clazz.getMethods();
// 获取类中的定义的方法
Method[] declaredMethods = clazz.getDeclaredMethods();
// 获取类中指定名称和参数的公有方法
Method say = clazz.getMethod("say", String.class);
// 获取类中定义的指定名称和参数的方法
Method say1 = clazz.getDeclaredMethod("say");
// 获取方法的修饰符
int modifiers1 = say.getModifiers();
// 指定对象进行成员方法的调用
Object samuel666 = say.invoke(person, “samuel666");
say.setAccessible(true);// 指定方法的强制执行
// 静态方法调用
say.invoke(null);
类的构造器操作
Constructor[] cons = clazz.getConstructors(); //获取类中所有的公有构造器
Constructor[] cons1 = clazz.getDeclaredConstructors(); //获取类中所有的构造器
Constructor conNoParam= clazz.getDeclaredConstructor(); //获取类中无参的构造器
Constructor con= clazz.getDeclaredConstructor(String.class,String.class); //获取类中有参构造
int modifers = con.getModifiers(); //获取构造器的修饰符
conNoParam.newInstance(); //构造器实例对象
con.setAccessible(true); //指定方法的强制访问
con.newInstance("abc","bbb"); //有参构造调用
Person.class.newInstance(); //class直接调用默认无参构造