Java中的反射

这篇博客总结了Java反射机制,包括java.lang.Class的使用,获取构造器、字段和方法,反射中的Annotation,泛型处理,以及java.lang.reflect.Array和动态代理的应用。详细介绍了如何通过反射进行类、接口、构造器、方法和字段的访问与操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


最近在学习java反射机制,读到了一篇很棒的博客,我的这篇文章是对原文的概括性笔记,总结了这部分知识的学习体系。

1. java.lang.Class


获取Class对象

  • 知道类名时,编译期加载:Class myObjectClass = MyObject.class
  • 通过读取字符串,运行时加载:Class class = Class.forName("com.jenkov.myapp.MyObject");

获取类名

  • String getname():完全包名
  • String getSimpleName():仅类名

获取修饰符

  • int getModifiers():获取修饰符的int标记

然后使用is方法判断:

    Modifier.isAbstract(int modifiers)
    Modifier.isFinal(int modifiers)
    Modifier.isInterface(int modifiers)
    Modifier.isNative(int modifiers)
    Modifier.isPrivate(int modifiers)
    Modifier.isProtected(int modifiers)
    Modifier.isPublic(int modifiers)
    Modifier.isStatic(int modifiers)
    Modifier.isStrict(int modifiers)
    Modifier.isSynchronized(int modifiers)
    Modifier.isTransient(int modifiers)
    Modifier.isVolatile(int modifiers)

包信息

  • Package getPackage():获取包对象java.lang.Package

父类信息

  • Class getSuperclass():得到父类的对象Class

实现的接口

  • Class[] getInterfaces():只返回此类显式实现的接口,不包括父类的

构造器

  • Constructor[] getConstructors()
  • getConstructor(Class[]):根据参数类,获得指定的构造器,NoSuchMethodException

方法

  • Method[] getMethods()
  • Method getMethod(String, Class[]):根据方法名和参数类(无参时传null),获得指定方法对象,NoSuchMethodException

字段

获取public字段(包括父类的):

  • Field[] getFields()
  • Filed getField(String):根据字段名,获取指定字段,NoSuchFieldException

还可以获取private/protected/package字段(不包括父类的):

  • Field[] getDeclaredFields()
  • Field getDeclaredField(String name)

类注解Annotation

  • Annotation[] getAnnotations()
  • A getAnnotation(Class<A> annotationClass):根据注解类,获取指定注解

2. java.lang.reflect.Constructor


得到构造参数

  • Class[] getParameterTypes()

用构造器对象实例化对象

MyObject myObject = (MyObject) constructor.newInstance("constructor-arg1");


3. java.lang.reflect.Field


字段名

  • String getName()

字段类型

  • Class getType()

Getting 和 Setting字段的值

  • Object get(Object obj):获取类实例的字段的值
  • set(Object obj, Object value):设置类实例的字段的值

静态字段可以传入null实例。

访问private/protected/package字段

访问私有字段的值前需要取消访问检查:

  • setAccessible(true)


4. java.lang.reflect.Method


方法参数和返回类型

  • Class[] getParameterTypes()
  • Class getReturnType()

调用类实例的方法

  • Object invoke(Object obj, Object... args):静态方法时,obj可为null

判断方法是否是getter或setter方法

    public static boolean isGetter(Method method){
      if(!method.getName().startsWith("get"))      return false;
      if(method.getParameterTypes().length != 0)   return false;  
      if(void.class.equals(method.getReturnType()) return false;
      return true;
    }

    public static boolean isSetter(Method method){
      if(!method.getName().startsWith("set")) return false;
      if(method.getParameterTypes().length != 1) return false;
      return true;
    }


5. 反射中的Annotation


类的注解
- Annotation[] getAnnotations()
- A getAnnotation(Class<A> annotationClass):根据注解类,获取指定注解

获取方法的注解

  • T Method.getAnnotation(Class<T>)
  • Annotation[] Method.getDeclaredAnnotations()

参数的注解

  • Annotation[][] Method.getParameterAnnotations()

字段注解

  • Annotation[] Field.getDeclaredAnnotations()
  • T Field.getAnnotation(Class<T>)


6. 反射中的泛型


方法的泛型返回值

  • Type method.getGenericReturnType()
  • Type[] typeArguments = type.getActualTypeArguments();:获取泛型中的每个类型
    public class MyClass {

      protected List<String> stringList = ...;

      public List<String> getStringList(){
        return this.stringList;
      }
    }
    Method method = MyClass.class.getMethod("getStringList", null);

    Type returnType = method.getGenericReturnType();

    if(returnType instanceof ParameterizedType){
        ParameterizedType type = (ParameterizedType) returnType;
        Type[] typeArguments = type.getActualTypeArguments();
        for(Type typeArgument : typeArguments){
            Class typeArgClass = (Class) typeArgument;
            System.out.println("typeArgClass = " + typeArgClass);
        }
    }

方法的泛型参数

  • Type[] method.getGenericParameterTypes()

泛型字段

  • Type field.getGenericType()


7. java.lang.reflect.Array


反射获取数组

int[] intArray = (int[]) Array.newInstance(int.class, 3);

获取数组类

非反射方式:
Class stringArrayClass = String[].class;

反射:
Class intArray = Class.forName("[I");
Class stringArrayClass = Class.forName("[Ljava.lang.String;");

  • "[I":表示基本数据类型数组,I,S,C,F,D,J(long),B(byte),Z(boolean)
  • "[L类名;":表示对象数组

数组类的组件类型

  • Class arrayClass.getComponentType()

  • 基本数据类型的数组,组件类型为基本数据类型的类,如int

  • 对象数组,组件类型为对象的类,如java.lang.String


8. 动态代理


java.lang.reflect.Proxy

用于运行时动态实现接口。

创建

  • Object newProxyInstance(ClassLoader, Class<?>[],InvocationHandler)
    InvocationHandler handler = new MyInvocationHandler();
    MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
                                MyInterface.class.getClassLoader(),
                                new Class[] { MyInterface.class },
                                handler);

InvocationHandler

    public class MyInvocationHandler implements InvocationHandler{

      public Object invoke(Object proxy, Method method, Object[] args)
      throws Throwable {
        //do something "dynamic"
      }
    }
  • proxy:用此Handler创建的代理,通常都用不到。
  • method:代理调用的接口的方法。
  • args:代理调用的接口的方法的参数,基本数据类型会变成包装类。

类加载器

创建一个新的标准Java类加载器是,必须提供它的双亲类加载器。这和类加载的顺序有关:

  1. 检查这个类是否已经加载过了。
  2. 如果没有加载过,就让加载器的双亲加载器来加载这个类。
  3. 如果双亲加载器不能加载这个类,就由自己来加载。

而且一个加载器只会加载一个类一次。

应用场景

  • 数据库连接,事务处理
  • 单元测试的动态模拟对象
  • Adaptation of DI Container to Custom Factory Interfaces
  • AOP-like Method Interception

示例

public class Test {
    public static void main(String[] args) {
        HelloImpl hello = new HelloImpl();
        MyInvocationHandler handler = new MyInvocationHandler(hello);
        // 构建代码实例
        Hello proxyHello = (Hello) Proxy.newProxyInstance(HelloImpl.class.getClassLoader(),
                HelloImpl.class.getInterfaces(), handler);
        // 调用代理方法
        proxyHello.sayHello();
    }


}

interface Hello {
    void sayHello();
}

class HelloImpl implements Hello {

    @Override
    public void sayHello() {
        System.out.println("Hello World");
    }
}

class MyInvocationHandler implements InvocationHandler {
    private Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Invoking sayHello");
        return method.invoke(target, args);
    }
}

上一篇:RadioGroup中显示radiobutton之外的其他组件
下一篇:Java中类的实例化过程变量的初始化顺序,以及常见笔试程序阅读题分析

### Java反射机制的原理与用法 #### 一、Java反射机制的基本原理 Java反射机制允许程序在运行时动态地访问类的信息,包括类的字段、方法和构造函数等。这种能力的核心在于`Class`类[^1]。每当一个类被加载到JVM中时,都会为其创建一个对应的`Class`对象,该对象包含了这个类的所有元信息。 获取`Class`对象的方式主要有三种: 1. 使用`.class`语法:例如 `String.class`。 2. 调用对象的`getClass()`方法:例如 `obj.getClass()`。 3. 利用`Class.forName(String className)`静态方法:例如 `Class.forName("java.lang.String")`[^3]。 一旦获得了`Class`对象,就可以通过它来获取类的各种组件,比如字段(`Field`)、方法(`Method`)和构造器(`Constructor`)。 --- #### 二、Java反射机制的主要功能 ##### 1. 动态创建对象 通过反射可以不依赖于传统的`new`关键字,在运行时动态创建对象。以下是实现方式的一个例子: ```java // 获取Class对象 Class<?> clazz = Class.forName("com.example.MyClass"); // 创建实例 Object instance = clazz.getDeclaredConstructor().newInstance(); ``` 这种方式特别适用于需要降低耦合度的场景,例如框架设计中的简单工厂模式优化[^3]。 ##### 2. 访问私有成员 即使某些字段或方法声明为`private`,也可以通过反射绕过访问控制修饰符进行访问。不过需要注意的是,默认情况下这些私有成员是不可见的,因此需要显式调用`setAccessible(true)`来改变其可访问状态[^2]。 下面是一个简单的演示代码片段: ```java import java.lang.reflect.Field; public class ReflectionExample { private String secret = "This is a secret"; public static void main(String[] args) throws Exception { ReflectionExample obj = new ReflectionExample(); Field field = obj.getClass().getDeclaredField("secret"); field.setAccessible(true); // 设置为可访问 System.out.println(field.get(obj)); // 输出秘密值 } } ``` ##### 3. 调用任意方法 无论是公有还是私有的方法都可以借助反射技术完成调用操作。这里给出一段示例代码用于说明这一过程: ```java import java.lang.reflect.Method; public class MethodReflection { private void sayHello(String name) { System.out.println("Hello, " + name); } public static void main(String[] args) throws Exception { MethodReflection mr = new MethodReflection(); Method method = mr.getClass().getDeclaredMethod("sayHello", String.class); method.setAccessible(true); method.invoke(mr, "World"); // 执行方法 } } ``` --- #### 三、性能考量 尽管反射非常强大,但它也伴随着一定的代价——主要是执行效率上的损失。这是因为反射打破了编译期间已知类型的约束,增加了额外的安全性和一致性检查工作量[^1]。所以在实际项目里应谨慎权衡利弊后再决定是否采用此手段解决问题。 --- #### 四、典型应用场景 - **框架开发**:像Spring这样的流行框架广泛运用了反射机制来进行IoC容器初始化等工作; - **调试工具**:许多现代IDE内置的功能(如断点跟踪)背后也是基于反射实现的; - **动态代理**:AOP编程模型下常用来增强目标业务逻辑的行为特性等等[^3]。 --- ### 结论 综上所述,虽然存在些许局限性,但毫无疑问Java反射是一项极其重要的技能点之一,掌握好它的理论基础及实践技巧无疑会对提升个人技术水平大有助益。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值