Java反射

本文深入解析Java反射机制,介绍Class类及其获取实例的方法,并详细阐述Class类提供的多种实用方法,如获取字段、方法、构造器等信息。同时,文章还探讨了Field、Method和Constructor类的主要用途。

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

(一)认识java.lang.Class类
1)Class类的实例不能通过new来创建。Class类只有一个私有的构造方法。

    private Class(ClassLoader loader) {
        classLoader = loader;
    }

2)既然不能通过new来获取Class类的实例,那么我们如何获取Class类的实例呢?
有3种方式:
方式一:类名.class
方式二:对象.getClass()
方式三:调用Class类的静态方法,Class.forName(全类名)

        String str=”abc”;
        Class clazz1=String.class;
        Class clazz2=str.getClass();//所有对象都有getClass()方法
        Class clazz3=Class.forName("java.lang.String");

        //clazz1,clazz2,clazz3是完全相等的,因为他们都指向同一份字节码
        System.out.println(clazz1==clazz2);//输出true
        System.out.println(clazz2==clazz3);//输出true

3)所有的类和接口都有对应的Class类的实例。8种基本数据类型、void以及数组也有对应的Class类的实例
①基本数据类型有对应的Class类的实例

Class clazz1=int.class;
Class clazz2=boolean.class;
System.out.println(clazz1);//输出int
System.out.println(clazz2);//输出boolean

②void有对应的Class类的实例

Class clazz1=void.class;
System.out.println(clazz1);//输出void

③数组有对应的Class类的实例

        Class clazz4=int[].class;
        Class clazz5=String[].class;
        Class clazz6=int[][].class;
        Class clazz7=String[][][].class;

        System.out.println(clazz4);
        System.out.println(clazz5);
        System.out.println(clazz6);
        System.out.println(clazz7);

输出结果:
class [I
class [Ljava.lang.String;
class [[I
class [[[Ljava.lang.String;

不难发现,数组的Class类的实例,取决于2个因素:数据类型和维度。
(二)认识java.lang.Class类的一些方法
1)getName():以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称
2)toString():重写了父类Object的toString()方法

    public String toString() {
        return (isInterface() ? "interface " : (isPrimitive() ? "" : "class "))
            + getName();
    }

可见:接口的前缀是“interface”;基本类型的前缀是空字符串;其他(例如:数组)均是以“class”为前缀的。
3)newInstance():返回Object或者泛型类型。注意:会调用public的无参构造函数来创建实例对象。如果没有公有的无参构造器,则会抛出异常。
4)获取字段信息

public Field[] getFields();//获取所有的public的字段,包括从父类继承过来的
public Field getField(String name);//根据字段名获取public的字段
public Field[] getDeclaredFields();//获取所有声明的字段,可以是private字段,但是不包括从父类继承过来的字段
public Field getDeclaredField(String name);//根据字段名获取声明的字段

5)获取方法信息

public Method[] getMethods();
public Method getMethod(String name, Class<?>... parameterTypes);
public Method[] getDeclaredMethods();
public Method getDeclaredMethod(String name, Class<?>... parameterTypes);

6)获取构造方法信息

public Constructor<?>[] getConstructors();
public Constructor<T> getConstructor(Class<?>... parameterTypes);
public Constructor<?>[] getDeclaredConstructors();
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes);

7)获取内部类信息

public Class<?>[] getClasses();//获取所有public的内部类
public Class<?>[] getDeclaredClasses();//获取所有声明的内部类

8)获取该类所在的外部类
public Class<?> getDeclaringClass();//获取声明该类的外部类
如果该类为外部类,则返回null。如果该类为内部类,则返回该内部类的外部类。
9)返回类型为boolean的一些简单方法

public native boolean isInstance(Object obj);//判断某个对象是否是它的实例
public native boolean isInterface();//是否是接口
public native boolean isArray();//是否是数组类型
public native boolean isPrimitive();//是否是基本数据类型
public boolean isAnnotation();//是否是注解

10)获取该类的父类和实现的接口

public native Class<? super T> getSuperclass();//获取该类的父类
public Class<?>[] getInterfaces();//获取该类实现的所有接口
public Type getGenericSuperclass();
public Type[] getGenericInterfaces();

11)获取修饰符

public native int getModifiers();//返回值是int类型,返回值代表的不是修饰符的个数

特定的int代表特定的某些修饰符。例如:17代表public final; 1代表public
(三)认识java.lang.reflect.Field类的一些方法
1)构造方法:

    Field(Class<?> declaringClass,
          String name,
          Class<?> type,
          int modifiers,
          int slot,
          String signature,
          byte[] annotations)
    {
        this.clazz = declaringClass;
        this.name = name;
        this.type = type;
        this.modifiers = modifiers;
        this.slot = slot;
        this.signature = signature;
        this.annotations = annotations;
    }

2)public String toString();//返回一个描述此 Field 的字符串
3)获取字段的值
public Object get(Object obj);//返回指定对象上该字段的值
问题来了:字段有静态字段和实例字段之分,该如何获取这2种字段的值呢?
如果是实例字段,那么传过去的obj对象必须是declaringClass的实例。
如果是静态变量,那么没必要传具体的实例对象过去,只要把类对应的Class实例传过去就好了,即传的是declaringClass。示例如下:

    /*
     * 获取非静态字段的值
     */
    public static Object getValue(Object obj,String fieldName) throws Exception{
        Class clazz=obj.getClass();

        //获取public的字段,如果fieldName对应的字段不是public,则找不到该字段
        Field field = clazz.getField(fieldName);

        //非静态属性,需要传具体的实例对象过去
        return field.get(obj);
    }

    /*
     * 获取静态字段的值
     */
    public static Object getStaticValue(String className,String fieldName) throws Exception{
        Class clazz=Class.forName(className);

        //获取public的字段,如果fieldName对应的字段不是public,则找不到该字段
        Field field = clazz.getField(fieldName);

        //由于是静态属性,静态属性是类的,不是某个对象的,把对应的Class实例传过去即可
        return field.get(clazz);
    }

注意:获取字段的值不一定会成功,因为字段有访问权限。例如:对于private的字段,只有在类内部可以取到它的值。异常信息如下:java.lang.IllegalAccessException: Class com.Demo3 can not access a member of class com.Test with modifiers “private static”。
②public xxx getXxx(Object obj);// 涉及到8种数据类型。如果该字段是int类型,可以采用getInt(Object obj)方法获取指定对象上该字段的值。
4)设置字段的值
public void set(Object obj, Object value);//为字段赋值。obj代表要操作的对象,value代表要赋的值
②public void setXxx(Object obj, xxx z);//涉及到8种数据类型
(四)认识java.lang.reflect.Field类的一些方法
1)构造函数

    Method(Class<?> declaringClass,
           String name,
           Class<?>[] parameterTypes,
           Class<?> returnType,
           Class<?>[] checkedExceptions,
           int modifiers,
           int slot,
           String signature,
           byte[] annotations,
           byte[] parameterAnnotations,
           byte[] annotationDefault) 
     {
        this.clazz = declaringClass;
        this.name = name;
        this.parameterTypes = parameterTypes;
        this.returnType = returnType;
        this.exceptionTypes = checkedExceptions;
        this.modifiers = modifiers;
        this.slot = slot;
        this.signature = signature;
        this.annotations = annotations;
        this.parameterAnnotations = parameterAnnotations;
        this.annotationDefault = annotationDefault;
    }

2)最为重要的一个方法
public Object invoke(Object obj, Object… args); //调用指定对象的该方法,args是传递给该方法的参数。
注意点:
①如果是静态方法,那么传过去的obj对象为declaringClass(declaringClass是一个Class类的实例)。
②如果是非静态方法,那么传过去的obj对象必须是declaringClass的实例。如果不是实例,会抛出如下异常:java.lang.IllegalArgumentException: object is not an instance of declaring class
③该invoke方法的返回值就是该方法执行后的返回值。如果方法的返回值为void,那么该invoke方法的输出结果为null。如果方法的返回值类型为int,那么invoke方法的结果类型为Integer(因为会自动包装成Object对象)。
(五)认识java.lang.reflect.Constructor<T>类的一些方法
1)构造器

    Constructor(Class<T> declaringClass,
                Class<?>[] parameterTypes,
                Class<?>[] checkedExceptions,
                int modifiers,
                int slot,
                String signature,
                byte[] annotations,
                byte[] parameterAnnotations) {
        this.clazz = declaringClass;
        this.parameterTypes = parameterTypes;
        this.exceptionTypes = checkedExceptions;
        this.modifiers = modifiers;
        this.slot = slot;
        this.signature = signature;
        this.annotations = annotations;
        this.parameterAnnotations = parameterAnnotations;
    }

2)最为重要的一个方法

public T newInstance(Object ... initargs);//通过构造方法创建实例

最后一点:public Field[] getFields();和public Field[] getDeclaredFields();
public Method[] getMethods();和public Method[] getDeclaredMethods();
一般使用getFields();和getMethods();较多,获取公有的字段和方法才是有意义的,因为公有的字段和方法在任何地方都可以访问和调用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值