Java反射机制详解及示例

本文介绍了Java反射机制的概念、用途,如操作私有属性和方法、实现自定义注解、动态加载类以解决Android方法数限制问题。详细阐述了反射的工作原理,包括获取类信息的三种方式,以及如何通过反射创建实例、调用方法和修改属性。此外,还讨论了反射对性能的影响,指出在性能敏感的场景中应谨慎使用。
摘要由CSDN通过智能技术生成

Java反射机制详解及示例

面试一家中小型公司 问到反射 居然回答不上来,不能忍 下来好好的整理了一下反射相关的东东,希望能帮到大家。

什么是反射?

反射是一种能够在程序运行时动态访问、修改某个类中任意属性(状态)和方法(行为)的机制(包括private实例和方法),Java反射机制提供了以下几个功能:

  • 在运行时判断任意一个对象所属的类;
  • 在运行时构造任意一个类的对象;
  • 在运行时判断任意一个类所具有的成员变量和方法;
  • 在运行时调用任意一个对象的方法。

反射涉及到四个核心类:

  • java.lang.Class.java:类对象;
  • java.lang.reflect.Constructor.java:类的构造器对象;
  • java.lang.reflect.Method.java:类的方法对象;
  • java.lang.reflect.Field.java:类的属性对象;

反射的用途

反射可以用于以下几种情况:

1. 操作因访问权限限制的属性和方法

如果某个类中的属性或方法被设置为private,则该属性或方法在类外部是无法访问的,但是通过反射可以绕过访问权限限制,实现对其进行访问或修改。

2. 实现自定义注解

通过反射,可以获取并解析类中的注解信息,进而实现自定义注解的功能。

3. 动态加载第三方jar包,解决Android开发中方法数不能超过65536个的问题

当一个应用中方法数超过一定数量时,可能会出现java.lang.IllegalArgumentException: method ID not in [0, 0xffff]:的错误,通过反射可以动态加载第三方jar包,解决这个问题。

4. 按需加载类,节省编译和初始化APK的时间

通过反射,可以在运行时动态地加载和卸载类,从而实现按需加载类的功能,节省编译和初始化APK的时间。

反射工作原理

当我们编写完一个Java项目之后,每个java文件都会被编译成一个.class文件,这些Class对象承载了这个类的所有信息,包括父类、接口、构造函数、方法、属性等,这些class文件在程序运行时会被ClassLoader加载到虚拟机中。当一个类被加载以后,Java虚拟机就会在内存中自动产生一个Class对象。我们通过new的形式创建对象实际上就是通过这些Class来创建,只是这个过程对于我们是不透明的而已。

反射的工作原理就是借助Class.javaConstructor.javaMethod.javaField.java这四个类在程序运行时动态访问和修改任何类的行为和状态。当一个类被加载以后,Java虚拟机会在内存中自动产生一个Class对象。我们可以通过这个Class对象获取该类的构造器、方法和属性等信息,并对它们进行访问和修改。

其中,Class对象是反射的核心,它代表了一个类的类型信息。我们可以通过以下方式获取Class对象:

  1. 调用对象的getClass()方法获取Class对象
  2. 调用类的.class属性获取Class对象
  3. 使用Class.forName()方法获取Class对象

一旦获取了Class对象,我们就可以通过它来创建类的实例、调用方法和修改属性等操作。例如,我们可以通过Class对象调用newInstance()方法来创建一个类的实例:

Class clazz = Class.forName("com.demo.Student");
Object obj = clazz.newInstance();

除了创建实例外,我们还可以通过Class对象获取类的构造器、方法和属性等信息。例如,我们可以通过Class对象获取指定名称和参数类型的构造器:

Class clazz = Class.forName("com.demo.Student");
Constructor constructor = clazz.getConstructor(String.class, int.class);

通过获取类的方法和属性等信息,我们可以在运行时动态地访问和修改类的行为和状态。这为我们提供了更大的灵活性和扩展性,使得我们可以编写更加智能和灵活的程序

其中,Class类是反射的核心类,它代表了一个类的信息,包括类的名字、包名、父类、实现的接口、构造器、方法和属性等。Constructor类代表类的构造方法,Method类代表类的方法,Field类代表类的属性。

通过这些反射类,我们可以在程序运行时动态地获取并操作类的信息,从而实现一些非常有用的功能。下面将介绍如何通过反射来实现以下8个功能。

1. 获取类信息的三种方式

Java中有三种方式可以获取一个类的Class对象:

  1. 通过类名获取Class对象:使用Class.forName(“className”)方法,需要指定类的全限定名,例如:

    Class<?> clazz = Class.forName("java.lang.String");
    
  2. 通过类的实例获取Class对象:使用Object.getClass()方法,例如:

    String str = "Hello, World!";
    Class<?> clazz = str.getClass();
    
  3. 直接获取类的Class对象:使用类名.class语法,例如:

    Class<?> clazz = String.class;
    

2. 获取当前类的所有方法和获取当前类及其父类的所有方法

通过Class类的getDeclaredMethods()方法可以获取当前类声明的所有方法,不包括父类的方法。例如:

Class<?> clazz = MyClass.class;
Method[] declaredMethods = clazz.getDeclaredMethods();

通过Class类的getMethods()方法可以获取当前类及其父类所有的公共方法。例如:

Class<?> clazz = MyClass.class;
Method[] methods = clazz.getMethods();

3. 获取当前类的所有实例和获取当前类及其父类的所有实例

通过Class类的newInstance()方法可以创建当前类的一个实例,例如:

Class<?> clazz = MyClass.class;
MyClass instance = (MyClass) clazz.newInstance();

通过Class类的getDeclaredConstructors()方法可以获取当前类声明的所有构造方法,例如:

Class<?> clazz = MyClass.class;
Constructor<?>[] constructors = clazz.getDeclaredConstructors();

通过Constructor类的newInstance()方法可以调用指定的构造方法来创建当前类的实例,例如:

Class<?> clazz = MyClass.class;
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class);
MyClass instance = (MyClass) constructor.newInstance("Hello, World!");

4. 获取父类信息

通过Class类的getSuperclass()方法可以获取当前类的父类,例如:

Class<?> clazz = MyClass.class;
Class<?> superClass = clazz.getSuperclass();

5. 获取接口信息

通过Class类的getInterfaces()方法可以获取当前类实现的所有接口,例如:

Class<?> clazz = MyClass.class;
Class<?>[] interfaces = clazz.getInterfaces();

6. 操作因访问权限限制的属性和方法

反射机制可以绕过Java语言的访问控制,可以访问和修改被private、protected修饰的属性和方法。例如:

public class MyClass {
    private void testMethod() {
        System.out.println("privateMethod is called");
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        MyClass obj = new MyClass();
        Method method = MyClass.class.getDeclaredMethod("testMethod");
        method.setAccessible(true);//将testMethod方法的访问权限设为可访问
        method.invoke(obj);//方法调用了obj对象中的privateMethod方法
    }
}

7.获取父类信息

可以通过 Class 对象的 getSuperclass() 方法获取当前类的父类信息,示例代码如下:

Class<?> superClass = clazz.getClass().getSuperclass();
System.out.println("Superclass: " + superClass.getName());

8.获取接口信息

可以通过 Class 对象的 getInterfaces() 方法获取当前类实现的所有接口信息,示例代码如下:

Class<?>[] interfaces = clazz.getClass().getInterfaces();
for (Class<?> interfaceClass : interfaces) {
    System.out.println("Interface: " + interfaceClass.getName());
}

9.反射方法和实例性能比较

反射机制相比于直接调用方法和访问属性会有一定的性能损失,因为它需要动态解析并调用方法,而直接调用方法则是静态编译过程中已经确定了的。以下是反射方法和实例性能比较的示例代码:

public class Person {
    public  void sayHello(String str) {
    }
}
 public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
        Person person = new Person();
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 10000000; i++) {
            person.sayHello("world");
        }
        long endTime = System.currentTimeMillis();
        System.out.println("Direct call: " + (endTime - startTime) + " ms");

        // 通过反射调用方法
        startTime = System.currentTimeMillis();
        Class personRe = Person.class;
        Method sayHelloMethod = personRe.getMethod("sayHello", String.class);
        for (int i = 0; i < 10000000; i++) {
            sayHelloMethod.setAccessible(true);
            /**
             *   sayHelloMethod.invoke(personRe, "world"); 我刚开始是直接将personRe字节码放入
             *    会报错 :
             *   object is not an instance of declaring class
             *   通过查看文章 除非你的对象是static 的  否则 你需要通过newInstance来调用该方法
             */
            sayHelloMethod.invoke(personRe.newInstance(), "world");
        }
        endTime = System.currentTimeMillis();
        System.out.println("Reflect call: " + (endTime - startTime) + " ms");
    }

耗时对比

可以看到,直接调用方法的性能比反射调用方法要高得多。因此,在性能要求较高的场景下,应该尽量避免使用反射机制。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值