java反射与注释

本文详细介绍了Java中的反射机制,包括Class、Field、Method和Constructor的使用,以及如何通过反射读取类的注解。同时,文章还阐述了Java注解的基本概念,包括定义、内置注解和元注解,并探讨了注解的反射使用及其作用。

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

  • 反射

    1. 反射机制

    反射机制的相关类除了一个java.lang.Class,其余都在java.lang.reflect包下。
    反射机制用于读取class字节码文件,需要注意,JVM加载字节码到内存中时都只会保存一份,多次读取class文件时不用担心也会加载多次。
    反射机制相关的常用类:

    java.lang.Class:代表整个类的字节码,表示一个类型。
    java.lang.reflect.Method:代表字节码中的方法字节码,表示一个方法。
    java.lang.reflect.Constructor:代表字节码中的构造方法字节码,表示一个构造方法。
    java.lang.reflect.Field:代表字节码中的属性字节码,表示一个属性。
     

    2. 反射类字节码Class(类/类型)

    获取类的字节码(java.lang.Class类)有三种方式:

    第一种方式:通过Class类的静态方法forName,例如Class c1 = Class.forName("java.lang.String");就表示获取到了String这个类的class字节码,注意,这是Class类下的一个静态方法,参数需要是完整的包名。另外,Class.forName方法的使用会导致类的加载,也就是说如果希望只是执行一个类的静态代码块,并不执行其他的代码,就可以使用这个方法来进行类的加载,此时,自然就会去执行对应的静态代码了。
    第二种方式:通过Object类的getClass方法,例如String s = "abc"; Class c2 = s.getClass();,即通过任何类对象的getClass方法就可以拿到对应了类字节码了,并且因为JVM只会在内存中加载一份相同类的字节码,所以这个例子的c2和第一种方式的c1使用双等号判断返回结果是true。
    第三种方式:通过类的class属性,例如Class c3 = String.class;,java中任何类型都有class属性。
    Class中常用的方法:

    String getName():返回类的完整类名(包含包路径)。
    String getsimpleName():返回类的简类名(类定义名称)。
    Field[] getFields():获取Class中所有public类型的Field对象(属性)。
    Field[] getDeclaredFields():获取Class中所有的Field对象(属性)。
    Field getDeclaredField(String name):获取指定名称的Field对象(属性)。
    Method[] getDeclaredMethods():获取所有的Method对象(方法)。
    Method getDeclaredMethod(String name,Class<?>... parameterTypes):根据方法名称和参数类型列表获取Method对象(方法),例如“userClass.getDeclaredMethod("login",String.class,int.class);”。
    Constructor<?>[] getDeclaredConstructors():获取所有的Constructor对象(构造方法)。
    Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes):获取指定参数类型列表的Constructor对象(构造方法),例如“userClass.getDeclaredConstructor(String.class,int.class);”。
    Class<? super T> getSuperclass():获取类的父类。
    Class<?>[] getInterfaces():获取所有类实现的接口。
     

    3. 反射属性字节码Field(字段/属性)

    获取Field需要先获取到对应的类字节码Class,然后才能从类中获取到对应的Field(java.lang.reflect.Field)。
    Field常用方法:

    Class<?> getType():返回属性的数据类型。
    String getName():返回属性的名称。
    int getModifiers():返回修饰符列表的代号,此代号可以使用java.lang.reflect.Modifier的静态方法toString方法传入代号获取到具体的修饰符名称列表。
    void set(Object obj,Object value):给指定对象的Field对象赋予值value。
    Object get(Object obj):获取指定对象的Field值(属性值)。
    void setAccessible(boolean flag):设置为true时表示打破封装,如果不调用这个方法设置为true的话就没办法获取对象的私有属性了,调用这个方法之后就可以获取到所有的属性了,包括私有属性。
     

    4. 反射方法字节码Method(方法)

    获取Method需要先获取到对应的类字节码Class,然后才能从类中获取到对应的Method(java.lang.reflect.Method)。

    Method中的常用方法:

    Class<?> getReturnType():获取返回值的数据类型。
    Class<?>[] getParameterTypes():获取方法的参数类型列表。
    int getModifiers():返回修饰符列表的代号,此代号可以使用java.lang.reflect.Modifier的静态方法toString方法传入代号获取到具体的修饰符名称列表。
    Object invoke(Object obj,Object... args):调用指定对象的方法。
     

    5. 反射构造方法Constructor(构造方法)

    获取Constructor需要先获取到对应的类字节码Class,然后才能从类中获取到对应的Method(java.lang.reflect.Constructor)。

    Constructor中的常用方法:

    int getModifiers():返回修饰符列表的代号,此代号可以使用java.lang.reflect.Modifier的静态方法toString方法传入代号获取到具体的修饰符名称列表。
    T newInstance(Object... initargs):使用newInstance方法创建一个实例对象。
     

    二、注解

    1. 定义注解(Annotation)

    注解,或者称之为注释,也是一种引用数据类型,编译之后也会生成class文件,具体用法见示例:

    [修饰符列表] @interface 注解类型名{
        // 属性定义
    }
     

    注解定义示例:

     无属性的注解定义
    public @ MyAnnotation{
         这里面什么都不写,表示没有属性
    }
     有属性的注解定义
     MyAnnotation2{
         定义一个没有默认值的属性,在使用这个注解的时候就必须给这个属性传值
         注意,注解的属性定义是有小括号的,但它不是方法,就只是属性
        String name();
        
         使用default给属性指定默认值,有默认值的属性在使用时就可以不用给这个属性传值了
        int id() default 2333;
    }
     属性只有一个,且为value时,使用时可以不用指定属性名称
     MyAnnotation3{
        String value();
    }
     

    注解使用示例:

     使用:直接在类、方法、属性、形参、注解等上面使用形如”@注解类型名“的格式即可。
     注解的使用其实就像修饰符一样在定义的前面加上就可以,但是通常的使用习惯是在定义上一行进行添加
    public class AnnotationTest {
        static void main(String[] args) {
        }
         相当于:@MyAnnotation private int id;
        @MyAnnotation
        private int id;
         注解只有一个属性,且属性名为value时,可以不用指定属性名
        @MyAnnotation3("hello")
        public AnnotationTest() {
        }
         定义了没有默认值的属性的注解,就必须给这个属性传值,有默认值的属性可以传,也可以不传
        @MyAnnotation2(name = "zhangsan" func() {
            @MyAnnotation2(name = "lisi",id = 666)
            int i = 10;
        }
         相当于:@MyAnnotation public void func2(@MyAnnotation String name)
     func2(@MyAnnotation String name) {
            System.out.println(name);
        }
    }
     

    注解属性类型:定义注解的属性时,属性的类型可以是byte、short、int、long、float、double、boolean、char、String、Class、枚举类型,以及这几种类型的数组形式,不能是其他的类型。有一个小技巧,属性如果是数组,并且使用时传入的数组元素只有一个的话,定义数组的大括号是可以不写的。

     

    2. Java内置注解

    内置注解在java.lang包下,常用的有:

    @Override:这个注解只能注解方法,并且只是给编译器在编译阶段做参考用的,和运行阶段的代码没有关系,编译器在编译时会检查这个方法是否是重写的父类方法,如果不是则会报错。
    @Deprecated:表示被标注的类、方法等元素已经过时了,不建议使用。在IDEA中,被标注的方法等会出现一条横线,提示你这个方法已过时。
     

    3. 元注解

    用来标注“注解类型”的注解,即注解的注解,称之为元注解,在java.lang.annotation包下。常用的元注解有:

    @Target(ANNOTATION_TYPE):用来指定被标注的注解可以出现在哪些位置上,参数为枚举类型ElementType的数组,具体有哪些枚举值可以参考帮助文档,如“@Target(ElementType.METHOD)”表示被标注的注解只能出现在方法上。
    @Retention(RUNTIME):用来指定被标注的注解最终保存在哪里,参数是一个枚举类型RetentionPolicy,有三个枚举值,“@Retention(RetentionPolicy.SOURCE)”表示被标注的注解保存在java源文件中,并不会出现在编译之后的class文件中,“@Retention(RetentionPolicy.CLASS)”表示被标注的注解保存在class文件中,“@Retention(RetentionPolicy.RUNTIME)”表示被标注的注解保存在class文件中,并且可以被反射机制读取出来。
     

    4. 反射注解

    以类的注解为例,首先获取到类的字节码对象后,使用Class对象的方法进行反射,常用的方法有:

    boolean isAnnotationPresent(MyAnnotation.class):判断一个类是否有指定的注解,这里需要传入一个指定注解的类字节码对象。
    getAnnotation(MyAnnotation.class):获取类的指定注解对象,这里需要传入一个指定注解的类字节码对象。
    属性获取:通过反射拿到注解对象之后,就可以通过调用方法(其实是属性)的形式获取属性值,因为注解定义属性时,本身就自带小括号,所以看起来就是在调用方法了,如“String name = annotationObj.name();”
    注:方法、属性等的注解获取也是和上面的方法一样,而且调用的方法等大多也都是一样的。

     
    5. 注解的作用

    注解通常是通过反射机制去检查被注解的类、方法等是否满足要求,比如@Override就是检查被注解的方法是否是重写父类的方法,如果不是就会编译报错。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值