Java反射
Java反射
引言
在已知全类名的情况下,如果不通过new方法,如何创建一个对象并调用其方法? 答:通过Java反射
下面是实现的代码,仅展示一下反射用法,后面会讲如何使用
配置参数
class.path = com.shen.inspection.modules.reflection.DemoEntity
method.name = hello
需要创建实例的类
public class DemoEntity {
public static final String NAME = "demo";
public DemoEntity() {
}
public void hello() {
log.info("hello this is {}",NAME);
}
}
反射实现类的创建与方法调用
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Properties;
@Slf4j
public class ReflectionTestOne {
public static void main(String[] args) throws Exception {
//正常创建对象调用方法
DemoEntity demoEntity = new DemoEntity();
demoEntity.hello();
log.info("========================================================");
//使用反射创建对象调用方法
FileInputStream fileInputStream = new FileInputStream(System.getProperty("user.dir")+ File.separator + "src" + File.separator + "main" + File.separator + "resources" + File.separator + "demo.properties");
Properties properties = new Properties();
//从配置文件中读取配置信息
properties.load(fileInputStream);
String classPath = properties.getProperty("class.path");
String methodName = properties.getProperty("method.name");
//加载类,返回Class类型对象cls
Class cls = Class.forName(classPath);
//通过cls得到加载的类com.shen.inspection.modules.reflection.DemoEntity的对象实例,此方式已废弃不推荐使用
// Object object = cls.newInstance();
//通过cls获取构造器,通过构造器生成对应类的对象实例
Constructor constructor = cls.getConstructor();
Object object = constructor.newInstance();
//通过cls得到加载的类com.shen.inspection.modules.reflection.DemoEntity的方法对象
//在反射中,可以把方法看作对象
Method method = cls.getMethod(methodName);
//反射中是 方法.invoke(对象)来调用方法
method.invoke(object);
}
}
反射机制
什么是反射
Java反射是Java语音的一种特性,它允许程序在执行期间通过反射API取得任何类的内部信息(成员变量、构造器、成员方法等),并能操作对象的属性及方法。
Java程序在计算机的三个阶段
编译阶段、类加载阶段、运行时阶段。三个阶段共同构成了Java程序的整个生命周期,从源代码到字节码,再到程序的允许和结束。
1.编译阶段(Compile Time)
在编译阶段,Java源代码(.java文件)会被Java编译器(javac)编译成字节码(.class文件)。此阶段包括以下几个步骤:
- 词法分析: 源代码被分解成一系列的标记(tokens)。
- 语法分析: 标记被组织成语法结构,如表达式、语句、声明等。
- 语义分析: 编译器检查语法结构是否有意义,是否符合Java语言规范。
- 代码生成: 编译器将Java源代码转换成Java虚拟机(JVM)可以理解的字节码。
2.类加载阶段(Class Loading Time)
类加载阶段,JVM的类加载器子系统会负责加载编译阶段生成的字节码文件。此阶段有以下几个步骤:
-
加载: 类加载器读取.class文件,并将其数据转换成方法区中的数据结构,同时在堆中生成对应的Class对象作为访问方法区数据的入口。
-
链接: 链接阶段包含验证、准备、解析
-
验证: 确保加载的类信息符合JVM规范,没有安全方面的问题。
-
准备: 为类变量(静态变量)分配内存,并设置默认初始值。
-
解析: 将符合引用替换为直接引用,也就是将类、接口、属性和方法的符合引用转换为直接引用。
-
-
初始化: 执行类构造器
()方法,这个方法是由编译器自动收集类中的所有类变量(静态变量)的赋值动作和静态代码块中的语句合并产生的。
3.运行时阶段(Runtime)
在运行时阶段,JVM开始执行Java程序。这个阶段包括以下几个步骤:
-
运行main方法: JVM调用main方法开始执行Java程序的主逻辑。
-
方法调用: 程序中的方法被调用,包括实例方法、静态方法等。
-
执行字节码: JVM的解释器逐条执行字节码指令,或者通过JIT编译器将字节码编译成本地机器码执行。
-
内存管理: JVM管理堆(Heap)和栈(Stack)等内存区别,进行对象的创建、垃圾回收等操作。
-
异常处理: 程序运行过程中可能抛出异常,JVM会处理这些异常。
-
程序结束: 当main方法执行完毕,或者程序遇到System.exit()方法调用时,JVM会退出并释放所有资源。
反射机制原理
当一个类完成加载后,会在堆中生成一个Class类型的对象(一个类只有一个Class对象),这个Class类型的对象包含类的完整结构信息。通过这个对象就可以得到类的结构。这个Class类型对象就像一面镜子,通过这个镜子(Class类对象)可以看到类的结构,所以形象地称之为反射。
反射机制的主要用途
- 动态创建对象: 反射允许你在运行时创建一个类的实例,即使你不知道这个类的名字或是其构造函数的参数类型。
- 访问私有成员: 可以使用反射来访问类的私有字段和方法,在一些框架和工具中非常有用,比如序列化,持久化等。
- 分析类的能力: 反射可以用来检查类的结构,比如它有哪些成员变量、方法、构造器、注解等。
- 方法调用: 反射可以用来调用任意方法,这在实现某些策略模式或者回调机制时非常有用。
- 框架开发: 很多流行的Java框架,如Spring、JUnit等,都大量使用了反射来提供动态配置和运行时增强。
- 代理和AOP: 反射可以用来创建代理对象,通过代理可以拦截方法的调用,这是实现AOP的关键技术。
- 注解处理: 反射可以用来读取和处理Java注解,这通常用于配置管理和依赖注入。
反射相关的主要类
- java.lang.Class: 代表一个类,Class对象表示某个类加载后在Java堆区中的对象
- java.lang.reflect.Method: 代表类的方法,Method对象表示某个类的方法
- java.lang.reflect.Field: 代表类的成员变量, Field对象表示某个类的成员变量
- java.lang.reflect.Constructor: 代表类的构造方法,Constructor对象表示某个类的构造器
反射常用类示例
需要创建实例的类
public class DemoEntity {
public static final String NAME = "demo";
public static final String TIME = "12:00:00";
public String date = "2024";
public DemoEntity(String date) {
this.date = date;
}
public DemoEntity() {
}
public void hello() {
log.info("hello this is {}",NAME);
}
}
反射实现对类的构造器获取并创建、成员变量的获取、方法调用
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@Slf4j
public class ReflectionTestTwo {
public static void main(String[] args) throws Exception{
Class cls = Class.forName("com.shen.inspection.modules.reflection.DemoEntity");
// 可以指定构造器的参数类型,如果没有则为无参构造器
Constructor noConstructor = cls.getConstructor();
log.info("无参构造器: {}",noConstructor);
// String.class就是String类的Class对象
Constructor allConstructor = cls.getConstructor(String.class);
log.info("有参构造器: {}",allConstructor);
Object object = noConstructor.newInstance();
Method method = cls.getMethod("hello");
method.invoke(object);
log.info("=================================================");
// 获取所有公有属性,无法获取私有属性
Field[] field = cls.getFields();
Field oneField = cls.getField("NAME");
log.info("NAME value is {}",oneField.get(object));
}
}
反射的优缺点
优点
- 动态性: 反射机制在允许状态中,对于任意一个类,都能够知道这个类的所有属性和方法。对于任意一个对象,都能够调用它的任意一个方法和属性。
- 增加程序的灵活性: 反射允许程序创建和控制任何类的对象,无需硬编码,增加了程序的灵活性。
- 框架设计的必要工具: 反射是很多框架(spring)的实现核心,它使得框架能够根据配置文件加载不同的对象和类,动态地创建对象实例并进行操作。
缺点
- 性能开销: 反射涉及动态解析,因此其性能比直接的代码执行要低很多。
- 破坏封装性: 反射允许代码执行一些在正常情况下不被允许的操作,比如访问私有成员,这可能会破坏对象的封装性,导致代码脆弱。
反射调用优化-关闭访问检查
反射的常用类中Method、Field、Constructor对象都有setAccessible()方法。
setAccessible方法的作用是启动或禁用访问安全检查的开关,默认是开启安全检查的。
当设置参数为true时,反射的对象在使用时取消访问安全检查,提高反执行的效率。参数值为false则表示反射对象在使用时执行访问安全检查。
反射调用优化代码示例
从实际代码运行上来看,关闭访问检查可以提升反射操作的执行效率
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
/**
* 测试反射的效率与优化后效率
*/
@Slf4j
public class ReflectionTestThree {
public static void main(String[] args) throws Exception {
normalInvoke();
reflectionInvoke();
optimizeReflectionInvoke();
}
public static void normalInvoke() {
DemoEntity demoEntity = new DemoEntity();
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
demoEntity.hello();
}
long end = System.currentTimeMillis();
log.warn("normal invoke method use {} ms", end - start);
}
public static void reflectionInvoke() throws Exception {
Class cls = Class.forName("com.shen.inspection.modules.reflection.DemoEntity");
Constructor noConstructor = cls.getConstructor();
Object object = noConstructor.newInstance();
Method method = cls.getMethod("hello");
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
method.invoke(object);
}
long end = System.currentTimeMillis();
log.warn("reflection invoke method use {} ms", end - start);
}
/**
* 反射调用调优-关闭访问检查
*
* @throws Exception exception
*/
public static void optimizeReflectionInvoke() throws Exception {
Class cls = Class.forName("com.shen.inspection.modules.reflection.DemoEntity");
Constructor noConstructor = cls.getConstructor();
Object object = noConstructor.newInstance();
Method method = cls.getMethod("hello");
//在反射调用方式时,取消访问检查
method.setAccessible(true);
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
method.invoke(object);
}
long end = System.currentTimeMillis();
log.warn("optimize reflection invoke method use {} ms", end - start);
}
}
Class类
概念
Class也是类,其父类也是Object类。
Class类对象是系统(类加载器)创建的,Class类对象存放在Java堆中,并且一个类的Class类对象在内存中只有一份,因为类只加载一次(同一个类的HashCode相同)。
每个类的实例都知道是由哪个Class类对象所生成。通过Class类对象的API可以完整地得到一个类的完整结构。
类的字节码二进制数据(又称为类的元数据),存放方法区。
代码示例
import lombok.extern.slf4j.Slf4j;
/**
* class类对象的创建
*/
@Slf4j
public class ClassDemoOne {
public static void main(String[] args) throws ClassNotFoundException {
//通过类图发现Class也是类,也继承Object类
//Class类对象不是new出来的,而是系统创建的
//常规方式新建一个类对象
/** ClassLoader类构建
* public Class<?> loadClass(String name) throws ClassNotFoundException {
* return loadClass(name, false);
* }
*/
// DemoEntity demoEntityOne = new DemoEntity();
//反射方式创建类对象,同样是通过ClassLoader类创建
Class<?> cls1 = Class.forName("com.shen.inspection.modules.reflection.DemoEntity");
//一个类的对象在内存中只有一份,因为类加载只有一次
Class<?> cls2 = Class.forName("com.shen.inspection.modules.reflection.DemoEntity");
log.info("demoOne hascode is : {}, demoTwo hascode is : {}", cls1.hashCode(), cls2.hashCode());
}
}
Class类的常用方法
public static Class<?> forName(String className)
返回指定类名的Class类对象
@Deprecated(since="9")
public T newInstance()
调用无参构造器,返回一个该类的一个实例,自JDK9开始废弃,推荐获取构造器的方法进行对象创建。
public String getName()
返回此Class对象所表示的实体名称
public native Class<? super T> getSuperclass();
返回当前Class对象的父类的Class对象
public Class<?>[] getInterfaces()
返回当前Class对象的接口
ClassLoader getClassLoader0()
返回该类的类加载器
public Constructor<?>[] getConstructors()
返回所有public修饰的构造器
public Field[] getFields()
返回所有public修饰的属性,包含本类以及父类
public Method getMethod(String name, Class<?>... parameterTypes)
返回一个Method对象,此对象方法名为name,形参类型为parameterTypes
Class类的常用方法使用示例
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
/**
* Class类的常用方法
*/
@Slf4j
public class ClassDemoTwo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
String classPath = "com.shen.inspection.modules.reflection.DemoEntity";
// 获取DemoEntity类对应的Class对象
//<?> 表示不确定的java类型
Class<?> cls = Class.forName(classPath);
log.info("显示cls的对象(具体为哪个类的Class对象):{}", cls);
log.info("显示cls类的类型:{}", cls.getClass());
log.info("显示cls对象的包名:{}", cls.getPackage());
log.info("显示cls对象的全类名:{}", cls.getName());
//通过构造器,创建对象实例
Constructor<?> constructor = cls.getConstructor();
DemoEntity demoEntity = (DemoEntity) constructor.newInstance();
log.info("打印demoEntity对象:{}", demoEntity.toString());
//通过反射获取属性 date
Field field = cls.getField("date");
log.info("打印date属性值:{}", field.get(demoEntity));
//通过反射给属性赋值
field.set(demoEntity, "2020");
log.info("打印date属性值:{}", field.get(demoEntity));
//通过反射获取所有属性
Field[] fields = cls.getFields();
for (Field f : fields) {
log.info("打印属性名称:{}", f.getName());
}
}
}
获取Class类对象的6种方式
- Class.forName("全类名")
在已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取
应用场景: 多用于配置文件,读取类全路径,加载类。
- 类名.class
在已知具体的类,通过类的class方法获取,此种方式安全且程序性能最高
应用场景: 多用于参数传递,比如通过反射得到对应构造器对象。
- 对象.getClass()
在已知某个类的实例,调用该实例的getClass()方法获取
应用场景: 通过创建好的对象,获取Class类对象
- 通过类加载器获取(不推荐使用)
先获取类加载器
ClassLoader cl = 对象.getClass().getClassLoader();
通过调用类加载器的方法传入全类名获取
Class cls = cl.loadClass("全类名")
- 基本数据类型可以通过.class方式
8种基本数据类型int,short,long,char,float,double,boolean,byte
基本类型.class
- 基本数据类型的封装类可以通过.TYPE方式
封装类.TYPE
获取Class类对象的示例
import lombok.extern.slf4j.Slf4j;
/**
* 获取Class类对象的不同方式(6种)
*/
@Slf4j
public class GetClassDemo {
public static void main(String[] args) throws ClassNotFoundException {
//1.Class.forName
String classPath = "com.shen.inspection.modules.reflection.DemoEntity";
Class<?> cls1 = Class.forName(classPath);
//2.类名.class
Class<?> cls2 = DemoEntity.class;
//3.对象.getClass
DemoEntity demoEntity = new DemoEntity();
Class<?> cls3 = demoEntity.getClass();
//4.通过类加载器获取类对象
ClassLoader classLoader = DemoEntity.class.getClassLoader();
Class<?> cls4 = classLoader.loadClass(classPath);
//四种方式创建的类对象是同一个
log.info("cls1: {}, hashcode is {}", cls1, cls1.hashCode());
log.info("cls2: {}, hashcode is {}", cls1, cls2.hashCode());
log.info("cls3: {}, hashcode is {}", cls1, cls3.hashCode());
log.info("cls4: {}, hashcode is {}", cls1, cls4.hashCode());
//5.基本数据类型可以通过类型.class的方式获取类对象
Class<Character> characterClass = char.class;
Class<Short> shortClass = short.class;
Class<Integer> integerClass = int.class;
Class<Long> longClass = long.class;
Class<Byte> byteClass = byte.class;
Class<Boolean> booleanClass = boolean.class;
Class<Float> floatClass = float.class;
Class<Double> doubleClass = double.class;
log.info("int Class is : {}, hascode is {}", integerClass, integerClass.hashCode());
//6.基本数据类型的封装类可以通过.TYPE的方式获取类对象
Class<Integer> type = Integer.TYPE;
log.info("Integer Class is : {}, hascode is {}", type, type.hashCode());
}
}
具有Class类对象的类型
- 外部类,成员内部类,静态内部类,局部内部类,匿名内部类
- 接口(interface)
- 数组
- 枚举(enum)
- 注解(annotation)
- 基本数据类型
- void
不同类型获取Class类对象示例
import lombok.extern.slf4j.Slf4j;
import java.io.Serializable;
/**
* 所有具有Class类对象的类型
*/
@Slf4j
public class AllTypeClassDemo {
public static void main(String[] args) {
//外部类
Class<String> stringClass = String.class;
//接口
Class<Serializable> serializableClass = Serializable.class;
//基本类型
Class<Integer> integerClass = Integer.class;
//基本类型封装类
Class<Double> doubleClass = double.class;
//注解
Class<Deprecated> deprecatedClass = Deprecated.class;
//枚举
Class<Thread.State> stateClass = Thread.State.class;
//一维数组
Class<Integer[]> aClass = Integer[].class;
//二维数组
Class<double[][]> aClass1 = double[][].class;
//void数据类型
Class<Void> voidClass = void.class;
//Class类
Class<Class> classClass = Class.class;
log.info("外部类: {}", stringClass);
log.info("接口: {}", serializableClass);
log.info("基本类型: {}", integerClass);
log.info("基本类型封装类: {}", doubleClass);
log.info("注解: {}", deprecatedClass);
log.info("枚举: {}", stateClass);
log.info("一维数组: {}", aClass);
log.info("二维数组: {}", aClass1);
log.info("void数据类型: {}", voidClass);
log.info("Class类: {}", classClass);
}
}
类加载
动态加载和静态加载
静态加载: 编译时加载相关的类,如果没有则报错,依赖性强。
静态加载的类通常在程序启动时就已经确定需要加载的类,比如在代码中直接使用new关键词实例化对象。
动态加载: 运行时加载相关的类,如果运行时不用该类,即使不存在该类,也不会报错,即根据程序需要,通过反射来加载类,依赖性低。
动态加载运行程序在运行时根据条件或用户输入来决定加载哪些类,提供了更高的灵活性和动态性。
动态加载与静态加载示例
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Scanner;
/**
* 类加载:静态加载和动态加载
*/
@Slf4j
public class ClassLoadDemo {
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException, ClassNotFoundException {
Scanner scanner = new Scanner(System.in);
log.info("请输入字符串:");
String key = scanner.nextLine();
switch (key) {
case "1":
//静态加载-依赖性很强,编译无法通过
Demo demo = new Demo();
demo.test();
break;
case "2":
//反射-动态加载,能够编译,只有运行后动态加载类找不到时会报错
Class<?> cls = Class.forName("com.shen.inspection.modules.reflection.Hello");
Constructor<?> constructor = cls.getConstructor();
Object object = constructor.newInstance();
Method method = cls.getMethod("test");
method.invoke(object);
break;
default:
log.info("非法字符");
break;
}
}
}
//class Demo {
// public void test() {
// System.out.println("this is a test");
// }
//}
//
//class Hello {
// public Hello() {
// }
//
// public void test() {
// System.out.println("hello");
// }
//}
类加载过程
1.加载阶段(loading)
JVM会通过全类名(例如com.example.TestClass)来获取定义此类的二进制字节流。这个字节流可以来自文件系统、网络、Jar文件等。
将这个字节流所代码的静态存储结构转化为方法区中的数据结构,在内存(Java堆)中生成一个Class类对象,作为方法取这个类的各种数据的访问入口。
2.链接-验证(verification)
验证阶段的目的是确保被加载的类的正确性,这个阶段是会完成以下四个验证动作:
1.文字格式验证: 确保字节流符合Class文件格式的规范。
2.元数据验证: 对类的元数据进行语义校验,确保不存在不符合Java语言规范的元数据信息。
3.字节码验证: 通过数据流和控制流分析,确保程序语义是合法的。
4.符合引用验证: 确保解析动作能正确执行。
3.链接-准备(preparation)
准备阶段是正式为类变量分配内存并设置类变量(静态变量)初始值,这些变量所使用的内存都将在方法取中进行分配。
设置静态变量的初始值,通常情况下是数据类型的默认值,例如int类型的默认值为0,boolean类型默认值为false,对象引用类型的默认值为null。
如果是静态常量(static final),则会在一开始赋对应的值,因为静态常量一旦赋值后就不会改变。
4.链接-解析(resolution)
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。
初始化(initialization)
初始化阶段是执行类构造器<clinit>()方法的过程,在此阶段,JVM会做以下事情:
1.执行<clinit>()方法,为类变量(静态变量)赋予正确的初始值。
2.<clinit>()方法是由编译器自动收集类中的所有静态遍历的赋值动作和静态代码块中的语句合并产生的,因此它是由程序园编写的静态初始化代码和编译器生成的代码组成的。
注: JVM会保证一个类的<clinit>() 方法在多线程环境中被正确地加锁、同步。如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。
通过反射获取类的结构信息
Class类的常用方法
public String getName()
获取全类名
public String getSimpleName()
获取简单类名
public Field[] getFields()
获取所有public修饰的属性,包含本类以及父类的
public Field[] getDeclaredFields()
获取本类的所有属性
public Method[] getMethods()
获取所有public修饰的方法,包含本类以及父类的
public Method[] getDeclaredMethods()
获取本类的所有方法
public Constructor<?>[] getConstructors()
获取本类所有public修饰的构造器
public Constructor<?>[] getDeclaredConstructors()
获取本类的所有构造器
public Package getPackage()
以Package形式返回包信息
public native Class<? super T> getSuperclass()
以Class形式返回父类信息
public Class<?>[] getInterfaces()
以Class[]形式返回接口信息
public Annotation[] getAnnotations()
以Annotation[]形式返回注解信息
Field类常用方法
public int getModifiers()
以int形式返回修饰符
默认修饰符是0,public是1,private是2,protected是4,static是8,final是16。
如果存在两个或多个修饰符则相加,比如默认静态常量的修饰符是0+8+16=24
public Class<?> getType()
以Class形式返回类型
public String getName()
返回属性名
Method类常用方法
public int getModifiers()
以int形式返回修饰符
默认修饰符是0,public是1,private是2,protected是4,static是8,final是16。
如果存在两个或多个修饰符则相加,比如默认静态常量的修饰符是0+8+16=24
public Class<?> getReturnType()
以Class形式返回类型
public String getName()
返回方法名
public Class<?>[] getParameterTypes()
以Class[]形式返回参数类型数组
Constructor类常用方法
public int getModifiers()
以int形式返回修饰符
默认修饰符是0,public是1,private是2,protected是4,static是8,final是16。
如果存在两个或多个修饰符则相加,比如默认静态常量的修饰符是0+8+16=24
public String getName()
返回构造器名(全类名)
public Class<?>[] getParameterTypes()
以Class[]形式返回参数类型数组
通过反射获取类结构信息的代码示例
import lombok.extern.slf4j.Slf4j;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* 通过反射类获取类结构信息
*/
@Slf4j
public class ReflectionUtils {
public static void main(String[] args) throws Exception {
log.info("=========================获取类结构信息的常用方法-Class类的方法=====================================");
//得到Class对象
Class<?> cls = Class.forName("com.shen.inspection.modules.reflection.Dog");
//获取全类名
log.info("全类名:{}", cls.getName());
//获取简单类名
log.info("简单类名:{}", cls.getSimpleName());
//获取所有public修饰的属性,包含本类以及父类
Field[] fields = cls.getFields();
for (Field field : fields) {
log.info("获取本类以及父类的公有属性:{}", field.getName());
}
//获取本类的所有属性
Field[] declareFields = cls.getDeclaredFields();
for (Field declareField : declareFields) {
log.info("获取本类的所有属性:{}", declareField.getName());
}
//获取所有public修饰的方法,包含本类以及父类
Method[] methods = cls.getMethods();
for (Method method : methods) {
log.info("获取本类以及父类的公有方法:{}", method.getName());
}
//获取本类的所有方法
Method[] declareMethods = cls.getDeclaredMethods();
for (Method declareMethod : declareMethods) {
log.info("获取本类的所有方法:{}", declareMethod.getName());
}
//获取所有public修饰的构造器,包含本类
Constructor[] constructors = cls.getConstructors();
for (Constructor constructor : constructors) {
log.info("获取本类的公有构造器:{}", constructor.getName());
}
//获取本类的所有构造器
Constructor[] declareConstructors = cls.getDeclaredConstructors();
for (Constructor declareConstructor : declareConstructors) {
log.info("获取本类的所有构造器:{}", declareConstructor.getName());
}
//返回包信息
log.info("包信息:{}", cls.getPackage());
//返回父类信息
log.info("父类信息:{}", cls.getSuperclass());
//返回接口信息
Class<?>[] interfaces = cls.getInterfaces();
for (Class<?> anInterface : interfaces) {
log.info("接口信息:{}", anInterface.getName());
}
//返回注解信息
Annotation[] annotations = cls.getAnnotations();
for (Annotation annotation : annotations) {
log.info("注解信息:{}", annotation);
}
log.info("=================================================================================================");
log.info("============================获取类结构信息的常用方法-Field类的方法====================================");
//默认修饰符为0,public为1,private为2,protected为4,static是8,final是16,组合为两者相加
for (Field declareField : declareFields) {
log.info("获取本类的所有属性:{},对应修饰符值为:{},对应的类型为:{}", declareField.getName(), declareField.getModifiers(), declareField.getType());
}
log.info("=================================================================================================");
log.info("============================获取类结构信息的常用方法-Method类的方法===================================");
for (Method declareMethod : declareMethods) {
log.info("获取本类的所有方法:{},对应修饰符值为:{},对应的返回类型为:{},对应传参数组为:{}", declareMethod.getName(), declareMethod.getModifiers(), declareMethod.getReturnType(), declareMethod.getParameterTypes());
}
log.info("=================================================================================================");
log.info("============================获取类结构信息的常用方法-Constructor类的方法==============================");
for (Constructor declareConstructor : declareConstructors) {
log.info("获取本类的所有构造器:{},对应修饰符值为:{},对应传参数组为:{}", declareConstructor.getName(), declareConstructor.getModifiers(), declareConstructor.getParameterTypes());
}
}
}
class Animal {
public String id;
public Animal() {
}
public void animalTestOne() {
}
private void animalHello() {
}
}
interface Train {
}
interface Feed {
}
@Deprecated
class Dog extends Animal implements Train, Feed {
public static final int NUM_ONE = 1;
public String name;
protected String age;
private String gender;
String birthday;
public Dog() {
}
private Dog(String name) {
this.name = name;
}
public String dogTestOne(String test, int number) {
return test + number;
}
private void dogHello() {
}
}
通过反射创建对象
通过Class类的方法获取构造器,通过构造器创建对象,对于私有构造器的对象创建,需要先通过Constructor类的setAccessible方法进行暴破后才可以使用
代码示例
import java.lang.reflect.Constructor;
/**
* 通过反射创建对象
*/
public class ReflectionCreateInstance {
public static void main(String[] args) throws Exception {
//1.获取User类的Class对象
Class<?> userClass = Class.forName("com.shen.inspection.modules.reflection.User");
//2.通过public的无参构造创建实例
Object user1 = userClass.newInstance();
System.out.println(user1);
///3.通过public的有参构造器创建实例
//创建对应构造器
Constructor<?> constructor1 = userClass.getConstructor(String.class);
//创建实例,传入实参
Object user2 = constructor1.newInstance("小强");
System.out.println(user2);
//4.通过private的有参构造器创建实例
//创建私有构造器
Constructor<?> constructor2 = userClass.getDeclaredConstructor(Integer.TYPE, String.class);
//使用暴破,可以访问私有的构造器
constructor2.setAccessible(true);
Object user3 = constructor2.newInstance(20,"小刚");
System.out.println(user3);
}
}
class User {
private int age = 18;
private String name = "小明";
public User() {
}
public User(String name) {
this.name = name;
}
private User(int age, String name) {
this.age = age;
this.name = name;
}
public String toString() {
return "user age:" + age + ", name:" + name + ";";
}
}
通过反射访问类中的属性
通过Class类的方法获取Field对象,对于私有属性,需要使用Field的setAccessible方法进行暴破才可以使用。
Field类的set,get方法可以操作属性
set(对象,参数值)
get(对象)
注:如果是静态属性,对象参数可以写为null
代码示例
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
/**
* 通过反射访问属性
*/
public class ReflectionAccessProperty {
public static void main(String[] args) throws Exception {
//1.获取Student类的Class对象
Class<?> studentClass = Class.forName("com.shen.inspection.modules.reflection.Student");
//2.通过创建构造器创建对象
Constructor<?> constructor = studentClass.getConstructor();
Object student = constructor.newInstance();
//3.通过反射获取属性
Field age = studentClass.getField("age");
//给student实例的age属性赋值
age.set(student, 20);
//获取student实例的age属性的值
System.out.println(age.get(student));
Field name = studentClass.getDeclaredField("name");
//使用暴破,可以操作私有属性
name.setAccessible(true);
//name.set(student, "小刚");
//由于name属性是静态的,赋值时,具体实例也可以为null
name.set(null, "小明");
//System.out.println(name.get(student));
System.out.println(name.get(null));
System.out.println(student);
}
}
class Student {
public int age;
private static String name;
public Student() {
}
public String toString() {
return "student age:" + age + ", name:" + name + ";";
}
}
通过反射获取类中的方法
通过Class类的方法获取Method对象,对于私有属性,需要使用Method的setAccessible方法进行暴破才可以使用。
Method类的invoke方法可以执行Method对象的方法
invoke(对象,入参列表)
注: 如果是静态方法,对象参数可以写成null
代码示例
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
/**
* 通过反射访问方法
*/
public class ReflectionAccessMethod {
public static void main(String[] args) throws Exception {
//1.获取Student类的Class对象
Class<?> studentClass = Class.forName("com.shen.inspection.modules.reflection.Boss");
//2.通过创建构造器创建对象
Constructor<?> constructor = studentClass.getConstructor();
Object boss = constructor.newInstance();
//3.获取public方法
Method hello = studentClass.getMethod("hello", String.class);
//4.调用hello方法
hello.invoke(boss, "小刚");
//5.获取private方法
Method saySomething = studentClass.getDeclaredMethod("saySomething", int.class, boolean.class, String.class);
//6.暴破,可以使用私有方法
saySomething.setAccessible(true);
//System.out.println(saySomething.invoke(boss, 1, true, "boss"));
//由于saySomething是静态方法,具体实例也可以写null
System.out.println(saySomething.invoke(null, 1, true, "boss"));
//在反射中,如果方法有返回值,统一返回Object,但运行类型和方法定义的返回类型相同
Object realValue = saySomething.invoke(boss, 1, false, "bot");
System.out.println(realValue.getClass());
}
}
class Boss {
public int age;
private static String name;
public Boss() {
}
private static String saySomething(int a, boolean b, String c) {
return a + "" + b + c;
}
public void hello(String name) {
System.out.println("hello my name is : " + name);
}
}
练习
利用Class类方法得到File类的Class类对象,通过获取其构造器,创建File对象,并写入到磁盘中。
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
/**
* 练习:
* 1.利用Class类的forName方法获取File类的Class对象
* 2.打印其所有的构造器
* 3.通过构造器创建File对象,并创建F:\\test.txt文件
*/
@Slf4j
public class ReflectionHomeWork {
public static void main(String[] args) throws Exception {
//1.创建File类的Class类对象
Class<?> fileClass = Class.forName("java.io.File");
//2.获取所有构造器并打印
Constructor[] constructors = fileClass.getDeclaredConstructors();
for (Constructor constructor : constructors) {
log.info(constructor.toString());
}
//3.获取公有的构造器创建一个File实例
Constructor<?> createFileConstructor = fileClass.getConstructor(String.class);
Object file = createFileConstructor.newInstance("F:\\test.txt");
log.info(file.toString());
//4.获取File的创建文件方法,并调用
Method createNawFile = fileClass.getMethod("createNewFile");
createNawFile.invoke(file);
}
}