网站首页 > java教程 正文
Hi,大家好,我是抢老婆酸奶的小肥仔。
反射,在我们日常开发中无时无刻,被大量运用在框架代码和工具代码中,反射可以通俗点讲就是一个类的自我剖析,通过反射可以获取到这个类所有信息,包括:属性,属性值,方法等等。
我们今天来初探下java中的反射,希望对大家有所帮助,开撸!
1、反射概念
Java反射机制:即在运行状态中,任意一个类,都能够知道这个类所有属性和方法;任意一个对象,都能够调用他的方法和属性。
1.1 反射流程
在java中,我们编写的类在编译时,会编译成.class文件,然后通过类加载器加载到JVM中,JVM会每个类创建一个Class对象,而Class对象中会保存每个的信息,例如:方法、属性等。JVM底层提供了一些native方法,让Java类获取到Class对象clz,进而获取到类的属性。
在上图中,可以获取类的对象有三种方式:
- 通过Object类中的getClass(),例如:实体先进行构造,然后调用getClass()。
- 通过对象的.class属性,即直接使用类名.class即可
- 通过Class.forName(),这是通过类名来获取对象,会有异常需要处理。
1.2 优缺点
优点:能够很方便的获取当前类的信息,能够创建灵活代码,更易于实现面向对象
缺点:1、反射需消耗一定的系统资源
2、破坏了封装性,可能会导致一些安全问题。
2、反射常用方法
在java.lang.reflect包下面,我们会看到定义的很多类。在日常开发中,大多数我们用不到,常用的也就是那么几个,如:Field(类的域/属性)、Method(类的方法)、Constructor(类的构造器)等,下面我们来看看这些类中定义的哪些方法。
准备工作:
创建两个实体类
java复制代码/**
* @author: jiangjs
* @description:
* @date: 2023/6/30 9:41
**/
@Data
public class Animal {
private String name;
private Integer age;
private String color;
@Data
public static class OtherAnimal{
private String other;
}
}
/**
* @author: jiangjs
* @description:
* @date: 2023/6/30 9:51
**/
@EqualsAndHashCode(callSuper = true)
@Data
public class Dog extends Animal{
/**
* 品种
*/
private String breed;
@Data
public static class OtherDog{
private String other;
}
}
其中Dog继承了Animal,是Animal的子类。
2.1 Class类方法
方法名 | 描述 |
forName(String className); | 获取当前类的对象 |
getClassLoader(); | 获取当前类的加载器 |
getClasses(); | 获取当前类及其父类中的所有公共类和接口的对象 |
getDeclaredClasses(); | 获取当前类中所有类和接口类的对象 |
newInstance(); | 创建类的实例 |
getName(); | 获取当前类的完整路径名称 |
getSimpleName(); | 获取类的名称 |
getPackage(); | 获取当前类的包 |
getSuperclass(); | 获取当前类的父类的对象 |
getInterfaces(); | 获取当前类的实现类或接口对象 |
我们来看下代码实现:
java复制代码public static void main(String[] args) throws IllegalAccessException, InstantiationException {
Class<Animal> clazz = Animal.class;
Class<Dog> dogClazz = Dog.class;
//获取类加载器
ClassLoader classLoader = clazz.getClassLoader();
log.info("类加载器:" + classLoader);
//获取当前类及其父类中的所有公共类和接口的对象
Class<?>[] classes = dogClazz.getClasses();
for (Class<?> aClass : classes) {
log.info("获取的类及父类中的类对象:" + aClass);
}
//获取当前类中所有类和接口类的对象
Class<?>[] declaredClasses = dogClazz.getDeclaredClasses();
for (Class<?> aClass : declaredClasses) {
log.info("获取的类对象:" + aClass);
}
//创建类的实例
Dog dog = dogClazz.newInstance();
dog.setName("橙橙");
log.info("创建类的实例:" + dog.getName());
//获取当前类的完整路径名称
String name = dogClazz.getName();
log.info("获取当前类的完整路径名称:" + name);
//获取类的名称
String simpleName = dogClazz.getSimpleName();
log.info("获取类的名称:" + simpleName);
//获取当前类的包
Package aPackage = dogClazz.getPackage();
log.info("获取当前类的包:" + aPackage);
//获取当前类的父类的对象
Class<? super Dog> superclass = dogClazz.getSuperclass();
log.info("获取当前类的父类的对象:" + superclass);
//获取当前类的实现类或接口
Class<?>[] interfaces = dogClazz.getInterfaces();
for (Class<?> anInterface : interfaces) {
log.info("获取当前类的实现类或接口:" + anInterface);
}
}
输出:
2.2 Field属性方法
很多时候我们都是需要获取属性及属性值来做处理,特别是使用AOP时。
方法名 | 描述 |
getField(String name); | 根据名称获取属性对象,属性值必须是公有的 |
getFields(); | 获取该类所有公有的属性对象 |
getDeclaredField(String name); | 根据名称获取当前类的属性对象 |
getDeclaredFields(); | 获得该类所有属性对象 |
代码实现:
java复制代码//属性
//根据名称获取属性对象
Field breedField = dogClazz.getField("breed");
log.info("根据名称获取属性对象:" + breedField);
//获取该类所有公有的属性对象
Field[] fields = dogClazz.getFields();
for (Field field : fields) {
log.info("获取该类公有的属性对象:" + field);
}
//获取当前类及父类指定的属性对象
Field nameField = clazz.getDeclaredField("name");
log.info("获取当前类及父类指定的属性对象:" + nameField);
//获得该类所有属性对象
Field[] clzFields = clazz.getDeclaredFields();
for (Field field : clzFields) {
log.info("获得该类所有属性对象:" + field);
}
输出:
在属性对象中,也提供了一些方法:
- equals(Object obj) :属性与obj是否相等,相等则返回true,否则返回false。
- get(Object obj) :获取obj中对应的属性值。
- set(Object obj,Object value) :给obj中的属性赋值。
上述提供的属性方法中只能获取到当前类的属性,很多时候我们还需要知道当前类的父类有哪些属性。因此我们可以结合类中的getSuperclass()来获取。代码如下:
java复制代码private static List<Field> getAllFields(Class<?> clazz) {
List<Field> allFields = new ArrayList<>();
while (Objects.nonNull(clazz)){
Field[] declaredFields = clazz.getDeclaredFields();
allFields.addAll(Arrays.asList(declaredFields));
clazz = clazz.getSuperclass();
}
return allFields;
}
测试: 获取Dog实体及父类的属性。
java复制代码List<Field> allFields = getAllFields(dogClazz);
for (Field field : allFields) {
log.info("获取dog当前类及父类属性:" + field);
}
2.3 Method方法
方法名 | 描述 |
getMethod(String name, Class...<?> parameterTypes) | 根据名称获取当前类及父类中的公有方法。name:方法名称,parameterTypes:参数类型,如String.class等,没有参数可以不填 |
getMethods(); | 获取当前类及其父类的所有公有的方法 |
getDeclaredMethod(String name, Class...<?> parameterTypes) | 根据名称获取当前类的方法 |
getDeclaredMethods(); | 获取当前类中所有的方法 |
getConstructor(Class...<?> parameterTypes) | 获取当前类的公有构造方法 |
getConstructors() | 获取当前类所有公有的构造方法 |
getDeclaredConstructor(Class...<?> parameterTypes) | 获取当前类及父类的所有构造方法 |
getDeclaredConstructors() | 获得当前类所有构造方法 |
代码实现:
java复制代码 //method方法
Method setName = dogClazz.getMethod("setName",String.class);
log.info("获取setName方法:" + setName);
Method method = dogClazz.getDeclaredMethod("getBreed");
log.info("获取getBreed方法:" + method);
Method[] declaredMethods = dogClazz.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
log.info("获取Dog所有方法:" + declaredMethod);
}
Constructor<Dog> constructor = dogClazz.getConstructor(String.class);
log.info("获取带参数构造方法:" + constructor);
Constructor<?>[] constructors = dogClazz.getConstructors();
for (Constructor<?> con : constructors) {
log.info("获取Dog下所有构造方法:" + con);
}
Method[] methods = dogClazz.getMethods();
for (Method dogMethod : methods) {
log.info("获取Dog及父类所有方法:" + dogMethod);
}
输出:
在Method方法中,可以使用invoke(Object obj, Object... args) 来指定obj对象中参数相同的方法。
2.4 Annotation注解方法
方法名 | 描述 |
getAnnotation(Class annotationClass) | 获取当前类及其父类与参数类型匹配的公有注解对象 |
getAnnotations(); | 获取当前类所有公有注解对象 |
getDeclaredAnnotation(Class annotationClass) | 获取当前类及其父类与参数类型匹配的所有注解对象 |
getDeclaredAnnotations() | 获取当前类中所有注解对象 |
isAnnotation() | 验证当前类是否为注解类型,是则返回true,否则返回false |
isAnnotationPresent(Class<? extends Annotation> annotationClass) | 属性中指定类型是否为注解类型,是则返回true,否则返回false |
代码实现:
java复制代码 //Annotation注解
NotBlank annotation = dogClazz.getDeclaredField("breed").getAnnotation(NotBlank.class);
log.info("获取Dog中Data注解:" + annotation);
String message = annotation.message();
log.info("获取注解信息:" + message);
boolean isAnnotation = dogClazz.isAnnotation();
boolean notBlank = NotBlank.class.isAnnotation();
log.info("验证是否为注解类型的类:dog," + isAnnotation+ ";notBlank:" + notBlank);
boolean present = dogClazz.getDeclaredField("breed").isAnnotationPresent(NotBlank.class);
log.info("指定NotBlank是否为注解类型:" + present);
输出:
2.5 其他方法
除了上述我们常用的一些方法外,还有些不常用的,我们也整理下,以备不时之需吧。
方法名 | 描述 |
isAnonymousClass() | 当前类是否为匿名类。是则返回true,否则返回false |
isArray() | 是否为数组类型。是则返回true,否则返回false |
isEnum() | 是否为枚举类型。是则返回true,否则返回false |
isInstance(Object obj) | obj是否该类的实例。是则返回true,否则返回false |
isInterface() | 是否为接口。是则返回true,否则返回false |
isMemberClass() | 是否为内部类 |
isLocalClass() | 是否为局部类 |
代码实现:
java复制代码//其他
boolean anonymousClass = dogClazz.isAnonymousClass();
log.info("当前类是否为匿名类:" + anonymousClass);
boolean array = dogClazz.isArray();
log.info("是否为数据类型:" + array);
boolean isEnum = dogClazz.isEnum();
log.info("是否为枚举类型:" + isEnum);
boolean instance = dogClazz.isInstance(dog);
log.info("obj是否该类的实例:" + instance);
boolean anInterface = dogClazz.isInterface();
log.info("是否为接口:" + anInterface);
Class<?>[] clazzs = dogClazz.getDeclaredClasses();
for (Class<?> aClass : clazzs) {
log.info("类名:"+aClass.getSimpleName() + ";是否为内部类:" + aClass.isMemberClass());
log.info("类名:"+aClass.getSimpleName() + ";是否为局部类:" + aClass.isLocalClass());
}
输出:
上述就是我理解的反射及常用的方法。其实只需要多敲敲代码,很多东西都会变得熟练。
一言以蔽之:反射就是通过jdk提供的方法从JVM中获取保存的类的各种属性的操作。
谢谢大家听我唠叨。
作者:抢老婆酸奶的小肥仔
链接:https://juejin.cn/post/7251113487317336121
猜你喜欢
- 2024-09-12 学习java应该如何理解反射?(怎么理解java反射)
- 2024-09-12 Java反射详解(java反射总结)
- 2024-09-12 读懂框架设计的灵魂—Java 反射机制
- 2024-09-12 Java的反射机制(java的反射机制是什么)
- 2024-09-12 java反射机制Java反射机制是什么?原理详解
- 2024-09-12 聊一聊Java当中的反射机制(java的反射机制是什么)
- 2024-09-12 Java反射机制的理解(java反射机制的理解和认识)
- 2024-09-12 聊一聊Java的反射机制?(java的反射机制是什么)
- 2024-09-12 Java学习之二——JAVA反射机制(java 反射机制原理)
- 2024-09-12 实操讲解Java的反射机制,你要是再看不懂,神仙都没救了
你 发表评论:
欢迎- 最近发表
-
- Java常量定义防暴指南:从"杀马特"到"高富帅"的华丽转身
- Java接口设计原则与实践:优雅编程的艺术
- java 包管理、访问修饰符、static/final关键字
- Java工程师的代码规范与最佳实践:优雅代码的艺术
- 编写一个java程序(编写一个Java程序计算并输出1到n的阶乘)
- Mycat的搭建以及配置与启动(mycat部署)
- Weblogic 安装 -“不是有效的 JDK Java 主目录”解决办法
- SpringBoot打包部署解析:jar包的生成和结构
- 《Servlet》第05节:创建第一个Servlet程序(HelloSevlet)
- 你认为最简单的单例模式,东西还挺多
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)