一文详解Java中的反射与new创建对象

 更新时间:2024年07月18日 09:50:44   作者:码农研究僧  
Java中的反射(Reflection)和使用new关键字创建对象是两种不同的对象创建方式,各有优缺点和适用场景,本文小编给大家详细介绍了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设计模式之浅谈适配器模式

    java设计模式之浅谈适配器模式

    我们现实生活中的适配器不少.例如,我们使用存储卡适配器连接存储卡和一个计算机,因为计算机仅支持一种类型的存储卡和我们的卡不与计算机兼容,适配器是两种不相容的实体之间的转换器,适配器模式是一种结构模式.本文就带大家了解一下java适配器模式,需要的朋友可以参考下
    2021-06-06
  • Java Iterator迭代器与foreach循环代码解析

    Java Iterator迭代器与foreach循环代码解析

    这篇文章主要介绍了Java-Iterator迭代器与foreach循环,主要包括Iterator迭代器接口的操作方法和foreach 循环语法解析,需要的朋友可以参考下
    2022-04-04
  • Java超详细介绍抽象类与接口的使用

    Java超详细介绍抽象类与接口的使用

    在类中没有包含足够的信息来描绘一个具体的对象,这样的类称为抽象类,接口是Java中最重要的概念之一,它可以被理解为一种特殊的类,不同的是接口的成员没有执行体,是由全局常量和公共的抽象方法所组成,本文给大家介绍Java抽象类和接口,感兴趣的朋友一起看看吧
    2022-05-05
  • spring boot实现图片上传到后台的功能(浏览器可直接访问)

    spring boot实现图片上传到后台的功能(浏览器可直接访问)

    这篇文章主要介绍了spring boot实现图片上传到后台的功能(浏览器可直接访问),需要的朋友可以参考下
    2022-04-04
  • Java Character类的详解

    Java Character类的详解

    本篇文章主要详细介绍了JAVA中 Character类 方法等,需要的朋友可以参考下
    2017-04-04
  • Java实现简单的递归操作方法实例

    Java实现简单的递归操作方法实例

    这篇文章主要给大家介绍了关于Java实现简单的递归操作的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-02-02
  • Nacos后台频繁打印get changedGroupKeys:[]的问题及解决

    Nacos后台频繁打印get changedGroupKeys:[]的问题及解决

    这篇文章主要介绍了Nacos后台频繁打印get changedGroupKeys:[]的问题及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01
  • Spring中@Import的各种用法以及ImportAware接口详解

    Spring中@Import的各种用法以及ImportAware接口详解

    这篇文章主要介绍了Spring中@Import的各种用法以及ImportAware接口详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-10-10
  • 浅谈Java数据结构之稀疏数组知识总结

    浅谈Java数据结构之稀疏数组知识总结

    今天带大家了解一下Java稀疏数组的相关知识,文中有非常详细的介绍及代码示例,对正在学习java的小伙伴们有很好地帮助,需要的朋友可以参考下
    2021-05-05
  • Java中四种线程池的使用示例详解

    Java中四种线程池的使用示例详解

    这篇文章主要给大家介绍了关于Java中四种线程池的使用方法,四种线程池分别包括FixedThreadPool、CachedThreadPool、ScheduledThreadPool以及SingleThreadExecutor,文中给出了详细的示例代码供大家参考,需要的朋友们下面来一起看看吧。
    2017-08-08

最新评论