目录
一、定义
什么是反射?反射有什么用?怎么用反射?
面对以上3个问题,可能很多初学JAVA的开发人员都摸不到头脑,今天来简单分享一下JAVA语言当中的反射机制,也当是一种复习。
从问题出发,
什么是反射?
JAVA反射是指程序运行时,动态的获取类的信息并操作类的属性和方法。通过反射,可以在运行时获取类的方法,属性,构造方法等信息,从而在运行时动态地创建对象,调用方法,修改属性等。反射机制可以让程序在运行时动态地加载,探测和使用类,使得程序具有更大的灵活性和可扩展性。JAVA反射机制是JAVA语言的一个重要特性,被广泛应用于JAVA里的工具,框架,应用程序。
反射有什么用呢?
1:动态获取类的信息,比如类名,修饰符,父类,接口,属性,方法等
2:动态创建对象,运行时动态地创建对象,而不需要编译时确定对象类型
3:动态修改属性,运行时动态修改属性值,而不需要编译时确定属性名称和类型
4:动态调用方法,运行时动态调用方法,而不需要编译时确定方法名和类型
5:动态加载类,运行时动态加载类,实现对类的扩展性和更新
6:实现框架和工具,反射是许多框架和工具的基础,比如Spring框架,热修复等
反射有什么缺点呢?
1:安全性:举个例子,比如类的访问由修饰符决定,而反射的出现一定程度上忽视了类的修饰符,由此带来的不同程度的可访问,可修改风险
2:可读性:通过反射后,由于部分代码在运行时被修改,因此逻辑性和完整性都会收到影响
3:系统性能:频繁使用反射,由于在运行时动态确定类型,因此会对系统性能有所影响
二、用一张图来认识反射
注意:并不是所有的类都会加载到JVM中,某些不怎么用的,或者用的少的,只有在调用时,才会加载进JVM
正常对象的创建过程即:当类经过类检查,类加载,进入到JVM虚拟机中,经过堆内存的分配,初始化,执行,才会创建出对象,从而获取到类的相关信息。
而反射机制的作用,在于还没有进入JVM,在类加载前,就已经获取到类的信息,从而可以进行修改,替换等操作。
三、反射的使用
1:反射获取对象的4种方式
(1):对象.getClass()
Person person = new Person();
Class<?> p = person.getClass();
(2):类.class
Class<?> p1 = Person.class;
(3):Class.forName()
try {
Class<?> p2 = Class.forName("com.example.myapplication.Person");
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
(4):ClassLoader类加载器
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class<?> mClazz = null;
try {
mClazz = loader.loadClass("com.tiancity.dom.myapplication.bean.CarBean");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
2:获取成员变量
(1):getDeclaredFields():获取本类的所有属性集合,与修饰符无关
(2):getFields():获取本类及父类public属性集合
(3):getDeclaredField(name):同理,只能获取本类的属性名为name的属性,与修饰符无关
(4):getField(name):获取本类及父类属性名为name的属性,属性修饰符必须为public
3:获取构造方法
(1):getConstructors():获取本类public修饰的构造方法集合
(2):getDeclaredConstructors():获取本类所有的构造方法集合,与修饰符无关
(3):getConstructor(parameterTypes):获取本类public修饰parameterTypes类型的构造方法
(4):getDeclaredConstructor(parameterTypes):获取本类所有parameterTypes类型的构造方法,与修饰符无关
4:获取成员方法
(1):getDeclaredMethods():获取本类所有的成员方法集合
(2):getMethods():获取本类及父类所有public的成员方法集合,包括Object类的toString(),wait(),equals(),hashCode(),notifyAll(),notify()方法
(3):getDeclaredMethod(String name, Class<?>... parameterTypes):获取本类方法名为name的成员方法
(4): getMethod(String name, Class<?>... parameterTypes):获取本类或父类或Object类的方法名为name的public成员方法
5:获取类修饰符,名称,包名
(1):包名(含package)
Package p = p2.getPackage();
// package com.example.myapplication
(2):类名
String name = p2.getSimpleName();
// Person
(3):完整类名
String n = p2.getName();
// com.example.myapplication.Person
(4):getCanonicalName和getName区别
外部类没区别,主要差异在内部类,getCanonicalName返回的是JAVA语言标准规范输出
而getName在内部类返回的是实体类型名称
String p = Person.class.getCanonicalName();
// com.example.myapplication.Person
String p1 = Person.class.getName();
// com.example.myapplication.Person
String p = Person.Son.class.getCanonicalName();
// com.example.myapplication.Person.Son
String p1 = Person.Son.class.getName();
// com.example.myapplication.Person$Son
(5):获取类的修饰符
int p = p2.getModifiers();
getModifiers返回值为int类型,类的修饰符包含,public,private,protected,static,abstract,final等,
拿到返回值后,需要用Modifier解码
PUBLIC: 1
PRIVATE: 2
PROTECTED: 4
STATIC: 8
FINAL: 16
SYNCHRONIZED: 32
VOLATILE: 64
TRANSIENT: 128
NATIVE: 256
INTERFACE: 512
ABSTRACT: 1024
STRICT: 2048
6:获取该类继承的父类
所有类的默认父类是Object类
Class p = p2.getSuperclass();
7:获取该类实现的接口类
Class<?>[] p = p2.getInterfaces();
// interface com.example.myapplication.IfaceWork
8:获取该类的内部类
Class[] p = p2.getDeclaredClasses()
9:获取该类的外部类
Class p = p2.getDeclaringClass()
10:反射给setName赋值,通过getName取值
Class<?> p2 = Class.forName("com.example.myapplication.Person");
Method method = p2.getMethod("setName", String.class);
//保证是同一个对象实例
Object obj = p2.newInstance();
method.invoke(obj,"zhangsan");
Method m = p2.getMethod("getName");
String name = (String) m.invoke(obj);
System.out.printf("name:"+name);
11:暴力反射
当修饰类或者方法,属性为private或protected时,想要修改值,则需要暴力反射
//因为方法都是私有的,因此需要设置暴力反射
method.setAccessible(true);
12:跳过泛型检查
我们都知道,定义一个数据结构,给他规定泛型,就能约束其添加的元素类型,比如在String的数组里添加字符串,在Integer的ArrayList里添加数字
ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.add(1);
arrayList.add(2);
Class<?> list = arrayList.getClass();
Method method = list.getMethod("add", Object.class);
method.invoke(arrayList,"zhangsan");
System.out.println("arrayList value:"+arrayList);
//arrayList value:[1, 2, zhangsan]
四:总结
反射是一种编程语言的特性,可以在运行时获取和操作一个对象的信息。下面是反射的总结:
- 反射允许程序在运行时获取对象的类型信息、成员变量和方法信息,并可以执行方法或设置变量的值;
- 反射可以让程序动态地创建对象、调用方法等,这往往需要在代码中不确定对象类型或者对象类型可能会变化时使用;
- 反射是一种强大而灵活的工具,但也有一定的缺点,例如反射的效率较低,因此应该尽量避免在性能敏感的场景中使用它;
- 反射要求程序员具备较高的技术水平,因为它提供了更多的控制权和自由度,但同时也增加了出错的风险和复杂度;
- 对于不同的编程语言,反射的实现方式和支持程度都有所不同,因此在使用反射时需要仔细查阅相关文档和参考资料。
总之,反射是一种重要而有用的编程工具,尤其适用于动态构建对象、编写通用代码、框架和库等场景。对于开发人员来说,熟练掌握反射技术可以提高代码的灵活性、可扩展性和维护性。