一篇文章全面了解Java反射机制【珍藏】

简介: 一篇文章全面了解Java反射机制【珍藏】

Java的反射机制在实践中可谓无处不在,如果你已经工作几年,还对Java的反射机制一知半解,那么这篇文章绝对值得你读一读。


什么是反射

反射 (Reflection) 是Java的特征之一,它允许运行中的Java程序获取自身的信息,并且可以操作类或对象的内部属性。


通俗的来讲就是:通过反射机制,可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。


注意这里的重点是:运行时,而不是编译时。我们常规情况下写的对象类型都是在编译期就确定下来的。而Java反射机制可以动态地创建对象并调用其属性,这样创建对象的方式便异常灵活了。


虽然通过反射可以动态的创建对象,增加了灵活性,但也不是什么地方都可用,还要考虑性能、编码量、安全、面向对象性等。


我们知道Java是面向对象的,如果通过反射机制去操作对象里面的属性或方法,一定程度上破坏了面向对象的特性。同时,通过反射机制还可以修改私有变量,也存在一定的安全性问题。


但这并不影响反射在实践中的应用,几乎各大框架多多少少都在使用Java反射机制。特别是主流的Spring框架。


功能及用途

Java反射主要提供以下功能:


在运行时判断任意一个对象所属的类;

在运行时构造任意一个类的对象;

在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);

在运行时调用任意一个对象的方法

反射最重要的用途之一就是开发各类通用框架。以Spring为例,当基于XML进行配置Bean时,我们通常写如下代码:


<bean class="com.choupangxia.UserServiceImpl">

</bean>

1

2

Spring在启动的时候便会利用反射机制去加载对应的UserServiceImpl类,然后进行实例化。如果不存在该类则会抛出异常,通常异常中还会出现invoke方法调用的堆栈信息。


当Spring基于注解去实例化对象时,同样利用的是反射机制。下面通过一个简单demo示例,演示一下如何通过反射获得注解信息:


static void initUser(User user) throws IllegalAccessException {
  // 获取User类中所有的属性(getFields无法获得private属性)
  Field[] fields = User.class.getDeclaredFields();
  // 遍历所有属性
  for (Field field : fields) {
    // 如果属性上有此注解,则进行赋值操作
    if (field.isAnnotationPresent(InitSex.class)) {
      InitSex init = field.getAnnotation(InitSex.class);
      field.setAccessible(true);
      // 设置属性的性别值
      field.set(user, init.sex().toString());
      System.out.println("完成属性值的修改,修改值为:" + init.sex().toString());
    }
  }
}

上述代码是之前写《一篇文章,全面了解Java自定义注解》中的示例,相关文章可关注公众号“程序新视界”,回复“注解”获得全文。


更多关于Java反射的例子我们就不多说了。上面的示例现在看不懂也没关系,下面我们就来详细介绍一下Java反射机制的具体使用。


简单示例

我们通过一个简单的示例对比,来了解一下Java反射机制。首先来看正常情况下创建对象并使用对象的示例:


User user = new User();

user.setUsername("公众号:程序新视界");

user.setAge(3);


那么,当基于反射机制来达到统一效果该怎么做呢?看下面的具体实现:


@Test
public void testCreateReflect() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException,
    InvocationTargetException, InstantiationException {
  // 获取User所对应的Class对象
  Class clz = Class.forName("com.choupangxia.reflect.User");
  // 获取User类无参数的构造器
  Constructor constructor = clz.getConstructor();
  // 通过构造器创建User对象
  User user = (User) constructor.newInstance();
  user.setUsername("公众号:程序新视界");
  user.setAge(3);
  System.out.println("username=" + user.getUsername());
  System.out.println("age=" + user.getAge());
}

在上述过程中通过Class.forName获得User所对应的Class对象,获得构造器Constructor,通过构造器创建出来一个User对象,然后调用对应的方法。


当然,后面的步骤中也可以完全不出现User类,直接通过Class对象获得对应的Method进行调用。示例如下:

Method setUsernameMethod = clz.getMethod("setUsername", String.class);
Method setAgeMethod = clz.getMethod("setAge", int.class);
setUsernameMethod.invoke(obj,"公众号:程序新视界");
setAgeMethod.invoke(obj,3);

关于get方法也是如此操作,就不再赘述。


经过上面的实例我们已经能够正常创建对象,并使用对象了。下面就看看反射常用的API,通过这些API我们可以实现更多的更复杂的功能。


反射常用API

获取Class对象的三种方法

第一种方法:当你知道类的全路径名时,可使用Class.forName静态方法来获得Class对象。上面的示例就是通过这种方法获得:


Class clz = Class.forName("com.choupangxia.reflect.User");


第二种方法:通过“.class”获得。前提条件是在编译前就能够拿到对应的类。


Class clz = User.class;


第三种:使用类对象的getClass()方法。


User user = new User();

Class clz = user.getClass();


创建对象的两种方法

可以通过Class对象的newInstance()方法和通过Constructor 对象的newInstance()方法创建类的实例对象。


第一种:通过Class对象的newInstance()方法。


Class clz = User.class;

User user = (User) clz.newInstance();


第二种:通过Constructor对象的newInstance()方法。


Class clz = Class.forName("com.choupangxia.reflect.User");

Constructor constructor = clz.getConstructor();

User user = (User) constructor.newInstance();


其中第二种方法创建类对象可以选择特定构造方法,而通过 Class对象则只能使用默认的无参数构造方法。


Class clz = User.class;

Constructor constructor = clz.getConstructor(String.class);

User user = (User) constructor.newInstance("公众号:程序新视界");


获取类属性、方法、构造器

通过Class对象的getFields()方法获取非私有属性。


Field[] fields = clz.getFields();

for(Field field : fields){

System.out.println(field.getName());

}


上述实例中的User对象属性都是private,无法直接通过上述方法获取,可将其中一个属性改为public,即可获取。


通过Class对象的getDeclaredFields()方法获取所有属性。


Field[] fields = clz.getDeclaredFields();

for(Field field : fields){

System.out.println(field.getName());

}


执行打印结果:


username

age


当然针对属性的其他值也是可以获取的,针对私有属性的修改需要先调用field.setAccessible(true)方法,然后再进行赋值。关于具体应用,回头看我们最开始关于注解的实例中的使用。


获取方法的示例如下:


Method[] methods = clz.getMethods();

for(Method method : methods){

System.out.println(method.getName());

}


打印结果:


setUsername
setAge
getUsername
getAge
wait
wait
wait
equals
toString
hashCode
getClass
notify
notifyAll

可以看到,不仅获取到了当前类的方法,还获取到了该类父类Object类中定义的方法。

关于获取构造器的方法上面已经讲到了,就不再赘述。而上述的这些方法在Class中都有相应的重载的方法,可根据具体情况进行灵活使用。

利用反射创建数组

最后,我们再看一个通过反射创建数组的实例。

@Test
public void createArray() throws ClassNotFoundException {
  Class<?> cls = Class.forName("java.lang.String");
  Object array = Array.newInstance(cls,5);
  // 向数组添加内容
  Array.set(array,0,"Hello");
  Array.set(array,1,"公众号");
  Array.set(array,2,"程序新视界");
  Array.set(array,3,"二师兄");
  Array.set(array,4,"Java");
  // 获取数组中指定位置的内容
  System.out.println(Array.get(array,2));
}

小结

想必经过上述的学习,对Java反射机制有了更进一步的了解,在最开始我们已经说了反射机制也是有不足的。因此,如果可能尽量使用正统的写法,但如果你在开发通用框架,则可考虑使用。

目录
相关文章
|
8天前
|
安全 Java 测试技术
🌟Java零基础-反射:从入门到精通
【10月更文挑战第4天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
14 2
|
1月前
|
安全 Java 索引
Java——反射&枚举
本文介绍了Java反射机制及其应用,包括获取Class对象、构造方法、成员变量和成员方法。反射允许在运行时动态操作类和对象,例如创建对象、调用方法和访问字段。文章详细解释了不同方法的使用方式及其注意事项,并展示了如何通过反射获取类的各种信息。此外,还介绍了枚举类型的特点和使用方法,包括枚举的构造方法及其在反射中的特殊处理。
50 9
Java——反射&枚举
|
1月前
|
安全 Java API
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
String常量池、String、StringBuffer、Stringbuilder有什么区别、List与Set的区别、ArrayList和LinkedList的区别、HashMap底层原理、ConcurrentHashMap、HashMap和Hashtable的区别、泛型擦除、ABA问题、IO多路复用、BIO、NIO、O、异常处理机制、反射
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
|
6天前
|
IDE Java 编译器
java的反射与注解
java的反射与注解
7 0
|
1月前
|
Java 程序员 编译器
Java的反射技术reflect
Java的反射技术允许程序在运行时动态加载和操作类,基于字节码文件构建中间语言代码,进而生成机器码在JVM上执行,实现了“一次编译,到处运行”。此技术虽需更多运行时间,但广泛应用于Spring框架的持续集成、动态配置及三大特性(IOC、DI、AOP)中,支持企业级应用的迭代升级和灵活配置管理,适用于集群部署与数据同步场景。
|
1月前
|
存储 安全 Java
扫盲java基础-反射(一)
扫盲java基础-反射(一)
|
1月前
|
Java
扫盲java基础-反射(二)
扫盲java基础-反射(二)
|
3月前
|
安全 Java 测试技术
day26:Java零基础 - 反射
【7月更文挑战第26天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
33 5
|
2月前
|
缓存 安全 Java
【Java 第十篇章】反射
Java 反射技术让程序能在运行时动态获取类信息并操作对象,极大提升了灵活性与扩展性。本文将介绍反射的基本概念、原理及应用,包括如何使用 `Class`、`Field`、`Method` 和 `Constructor` 类进行动态操作。此外,还将探讨反射在动态加载、框架开发与代码测试中的应用场景,并提醒开发者注意性能与安全方面的问题,帮助你更合理地运用这一强大工具。
24 0
|
3月前
|
IDE Java 测试技术
Java进阶之反射
【7月更文挑战第14天】Java反射机制允许在运行时动态获取类信息、创建对象及调用其方法。它基于`Class`类,让我们能访问类的属性、方法、构造器。例如,通过`Class.forName()`加载类,`Class.newInstance()`创建对象,`Method.invoke()`执行方法。反射广泛应用于动态代理、单元测试、序列化及框架中,提供灵活性但牺牲了性能,且可破坏封装性。IDE的代码补全也是反射的应用之一。在使用时需谨慎,避免对私有成员的不当访问。
36 1