前言
对于Java的基本知识推荐阅读:
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!");
}
}
简单分析其效率对比 以及 使用的场景
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();
}
}
}