一文详解Java中的反射与new创建对象
1. 基本知识
Java中的反射(Reflection)和使用new关键字创建对象是两种不同的对象创建方式,各有优缺点和适用场景
第一:使用new关键字创建对象
优点
- 编译时类型检查:在编译时就可以检查类型是否正确
- 性能高:不涉及额外的解析和方法调用,直接创建对象
缺点
- 灵活性差:不能在运行时动态决定要创建的对象类型
基本的测试代码如下:
public class Main { public static void main(String[] args) { // 使用new关键字创建对象 MyClass obj = new MyClass(); obj.sayHello(); } } class MyClass { public void sayHello() { System.out.println("Hello, World!"); } }
二、使用反射创建对象
优点
- 灵活性高:可以在运行时动态决定要创建的对象类型,适用于框架和工具类
缺点
- 性能较低:由于需要解析类名和方法名,性能比直接使用new关键字低
- 缺乏编译时类型检查:可能会在运行时抛出异常
示例代码如下:
import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class Main { public static void main(String[] args) { try { // 使用反射创建对象 Class<?> clazz = Class.forName("MyClass"); Constructor<?> constructor = clazz.getConstructor(); MyClass obj = (MyClass) constructor.newInstance(); obj.sayHello(); } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } } } class MyClass { public void sayHello() { System.out.println("Hello, World!"); } }
简单分析其效率对比 以及 使用的场景
方面 | 使用new关键字 | 使用反射 |
---|---|---|
创建对象的效率 | 高效:编译后生成的字节码直接创建对象,无需额外解析和方法调用 | 较低:需要解析类名和方法名、获取构造器等操作,额外增加了开销 |
方法调用的效率 | 高效:直接调用对象方法,无额外开销 | 较低:需通过反射API调用方法,效率较低 |
使用场景 | 确定的场景下,直接创建对象,例如日常开发中的大部分情况 | 需要在运行时动态决定要创建的对象类型,例如框架开发(如Spring框架中的依赖注入)、工具类开发(如ORM框架) |
2. 效率对比
public class PerformanceTest { public static void main(String[] args) { final int iterations = 1000000; // 测试new关键字 long startNew = System.nanoTime(); for (int i = 0; i < iterations; i++) { MyClass obj = new MyClass(); } long endNew = System.nanoTime(); System.out.println("Using new keyword: " + (endNew - startNew) + " ns"); // 测试反射 long startReflection = System.nanoTime(); try { for (int i = 0; i < iterations; i++) { Class<?> clazz = Class.forName("MyClass"); Constructor<?> constructor = clazz.getConstructor(); MyClass obj = (MyClass) constructor.newInstance(); } } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } long endReflection = System.nanoTime(); System.out.println("Using reflection: " + (endReflection - startReflection) + " ns"); } } class MyClass { public void sayHello() { System.out.println("Hello, World!"); } }
截图如下:
具体时间因机器性能和JVM状态不同而异
3. 反射补充知识
反射创建对象的三种主要方式是通过类字面常量、Class.forName()方法和对象实例的getClass()方法
3.1 类字面场量
在编译时就知道类的类型,适用于类名已知且在同一编译单元中的情况
public class ReflectDemo { public void sayHello() { System.out.println("Hello, World!"); } public static void main(String[] args) { // 方式1:使用类字面常量 Class<ReflectDemo> reflectDemoClass = ReflectDemo.class; try { ReflectDemo reflectDemo = reflectDemoClass.newInstance(); reflectDemo.sayHello(); } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); } } }
3.2 Class.forName()
适用于类名在运行时才知道的情况,常用于从配置文件或输入中读取类名,然后动态加载类
public class ReflectDemo { public void sayHello() { System.out.println("Hello, World!"); } public static void main(String[] args) { try { // 方式2:使用Class.forName() Class<?> reflectDemoClass = Class.forName("ReflectDemo"); ReflectDemo reflectDemo = (ReflectDemo) reflectDemoClass.newInstance(); reflectDemo.sayHello(); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { e.printStackTrace(); } } }
3.3 对象实例的 getClass()
适用于已有实例对象时,通过该对象获取其类信息,然后创建新的实例
public class ReflectDemo { public void sayHello() { System.out.println("Hello, World!"); } public static void main(String[] args) { ReflectDemo existingInstance = new ReflectDemo(); // 方式3:使用对象实例的getClass() Class<? extends ReflectDemo> reflectDemoClass = existingInstance.getClass(); try { ReflectDemo newReflectDemo = reflectDemoClass.newInstance(); newReflectDemo.sayHello(); } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); } } }
3.4 总结
方式 | 优点 | 缺点 |
---|---|---|
使用类字面常量 (ClassName.class) | - 编译时类型安全 - 性能最佳 | - 类名在编译时必须已知 - 灵活性较差 |
使用 Class.forName() | - 可以在运行时根据类名动态加载类 - 灵活性高 | - 类名必须为字符串形式 - 可能会引发ClassNotFoundException - 性能略低于类字面常量 |
使用对象实例的 getClass() | - 可以通过已有实例获取类信息 - 动态创建相同类型的新实例 | - 需要先有一个实例对象 - 性能略低于类字面常量,但高于Class.forName() |
以上就是一文详解Java中的反射与new创建对象的详细内容,更多关于Java反射与new创建对象的资料请关注脚本之家其它相关文章!
相关文章
Java Iterator迭代器与foreach循环代码解析
这篇文章主要介绍了Java-Iterator迭代器与foreach循环,主要包括Iterator迭代器接口的操作方法和foreach 循环语法解析,需要的朋友可以参考下2022-04-04spring boot实现图片上传到后台的功能(浏览器可直接访问)
这篇文章主要介绍了spring boot实现图片上传到后台的功能(浏览器可直接访问),需要的朋友可以参考下2022-04-04Nacos后台频繁打印get changedGroupKeys:[]的问题及解决
这篇文章主要介绍了Nacos后台频繁打印get changedGroupKeys:[]的问题及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教2024-01-01Spring中@Import的各种用法以及ImportAware接口详解
这篇文章主要介绍了Spring中@Import的各种用法以及ImportAware接口详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧2019-10-10
最新评论