反射和自定义注解教程和结合使用

目录

反射

反射的作用

如何使用反射

获取Class对象的方法

反射的常见API

1)获得变量及方法

2)反射新建实例和访问方法

反射的实际使用

案例一

案例二

注解

Java内置注解

元注解

@Target

@Retention

@Document

@Inherited

自定义注解

注解结合反射使用


反射

        Reflection(反射) 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查。被private封装的资源只能类内部访问,外部是不行的,但反射能直接操作类私有属性。反射可以在运行时获取一个类的所有信息,(包括成员变量,成员方法,构造器等),并且可以操纵类的字段、方法、构造器等部分。

反射的作用

        JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

如何使用反射

要想解剖一个类,必须先要获取到该类的字节码文件对象(Class对象)。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象。

获取Class对象的方法

1)调用某个对象的getClass()方法:Object.getClass()

Person p = new Person();
Class cls = p.getClass();

2)使用类的class属性,即:类名.class

Class clazz = Person.class;

3)使用Class类中的forName()静态方法:Class.forName(“类的全路径”)

Class clazz = Class.forName("类的全路径");

反射的常见API

我这里直接总结一下,我们常常通过反射获得:构造方法、成员变量、成员方法:可以获取公有的、私有的

1)获得变量及方法

构造方法

getConstructor(参数类型列表)//获取公开的构造方法

getConstructors()//获取所有的公开的构造方法

getDeclaredConstructors()//获取所有的构造方法,包括私有 getDeclaredConstructor(int.class,String.class)

成员方法

getMethods()//获取所有可见的方法,包括继承的方法 getMethod(方法名,参数类型列表) getDeclaredMethods()//获取本类定义的的方法,包括私有,不包括继承的方法 getDeclaredMethod(方法名,int.class,String.class)

成员变量

getFields()//获取所有公开的成员变量,包括继承变量

getDeclaredFields()//获取本类定义的成员变量,包括私有,但不包括继承的变量

getField(变量名)

getDeclaredField(变量名)

2)反射新建实例和访问方法

新建实例

clazz.newInstance();//执行无参构造创建对象

clazz.newInstance(222,"韦小宝");//执行有参构造创建对象 clazz.getConstructor(int.class,String.class)//获取构造方法

访问成员变量

clazz.getDeclaredField(变量名);//获取变量

clazz.setAccessible(true);//使私有成员允许访问

f.set(实例,值);//为指定实例的变量赋值

f.get(实例);//访问指定实例变量的值

调用成员方法

Method m = Clazz.getDeclaredMethod(方法名,参数类型列表);

m.setAccessible(true);//使私有方法允许被调用

m.invoke(实例,参数数据);//让指定实例来执行该方法

反射的实际使用

案例一

我们都知道String类的值是没办法改变的,为什么?我们看看String的源码就知道了。

/** The value is used for character storage. */
private final char value[];

String是用字符数组表示字符串的,然而它是被final修饰的,我们可以通过反射来改变他的值。

public class StringReflection {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        //定义一个字符串赋值为test
        String str = "test";
        System.out.println(str);
        //获得String的类对象
        Class<String> cls = String.class;
        //通过API拿到String的私有变量value
        Field value = cls.getDeclaredField("value");
        //设置访问权限为true
        value.setAccessible(true);
        //通过API改变字符串对象str的value值
        value.set(str,"string is changed".toCharArray());
        
        System.out.println(str);
    }
}

输出结果:

test
string is changed

案例二

模拟Spring的包扫描,扫描带有某个注解的类。

首先新建三个测试类,然后给其中一个标注上@Componet注解

public class Test1 {
}
public class Test2 {
}
@Component
public class Test3 {
}

然后开始通过反射完成这一系列操作

public class ScanDemo {
    public static void main(String[] args) throws Exception {
        //获得当前类的路径
        Class<ScanDemo> scanDemoClass = ScanDemo.class;
        String path = scanDemoClass.getResource(".").getPath();
​
        //获取当前类的包名
        String packageName = scanDemoClass.getPackage().getName();
​
        //创建一个路径为path的File对象,并得到所有的子文件
        File[] files = new File(path).listFiles();
​
        //定义被扫描的类的全类名
        String className = "";
        for (File file : files) {
            //获取文件名字,然后将.class删除就得到了类的名字
            String name = file.getName().substring(0, file.getName().indexOf('.'));
            //将包名和类名拼接,就得到了全类名
            className = packageName + "." + name;
​
            //通过全类名生成Class对象
            Class<?> cls = Class.forName(className);
            //判断是否被Component注解标注,如果标注就交给Spring处理
            if (cls.isAnnotationPresent(Component.class)) {
                System.out.println("类"+cls.getName()+"将被spring容器自动管理");
                Object o = cls.newInstance();
                System.out.println("实例出来的对象:"+o);
            }
        }
    }
}

注解

        官方:注解是一种能被添加到java代码中的元数据,类、方法、变量、参数和包都可以用注解来修饰。注解对于它所修饰的代码并没有直接的影响。可以被其他程序(比如:编译器等)读取。

  1. 注解是一种元数据形式。即注解是属于java的一种数据类型,和类、接口、数组、枚举类似。

  2. 注解用来修饰,类、方法、变量、参数、包。

  3. 注解不会对所修饰的代码产生直接的影响,只是一个修饰的东西。

Java内置注解

  • Override

    此注解只使用于修辞方法,表示一个方法声明打算重写超类中的另一个方法声明。

  • Deprecated

    此注解可以用于修辞方法、属性、类、表示不鼓励程序员使用这样的元素,就是平时看到的画斜线的方法,表示不建议使用

  • SuppressWarnings

    用来抑制编译时的警告信息,需要添加一个参数才能正确使用(all、unchecked等参数)

元注解

元注解是组成其他注解的注解,元注解是标记其他注解的注解,例如:注解@A被注解@B注解了,那么@B就是@A的元注解

@Target

指定注解使用的目标范围(类、方法、字段等)

参数有:

  1. ElementType.TYPE :

    用于描述类、接口(包括注解类型) 或enum声明。

  2. ElementType.METHOD :

    用于描述方法。

  3. ElementType.CONSTRUCTOR :

    用于描述构造器。

  4. ElementType.FIELD :

    成员变量、对象、属性(包括enum实例)。

  5. ElementType.LOCAL_VARIABLE:

    用于描述局部变量。

  6. ElementType.PACKAGE :

    用于描述包。

  7. ElementType.PARAMETER :

    用于描述参数。

  8. ElementType.ANNOTATION_TYPE:

    用于描述参数

@Retention

指定注解的生命周期

参数有:

  1. RetentionPolicy.SOURCE

    在编译阶段丢弃。这些注解在编译结束之后就不再有任何意义,所以它们不会写入字节码。

  2. RetentionPolicy.CLASS

    在类加载的时候丢弃。在字节码文件的处理中有用。注解默认使用这种方式。

  3. RetentionPolicy.RUNTIME

    始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。

@Document

指定被标注的注解会包含在javadoc中。

@Inherited

是指定某个自定义注解如果写在了父类的声明部分,那么子类的声明部分也能自动拥有该注解。@Inherited注解只对那些@Target被定义为ElementType.TYPE的自定义注解起作用。

自定义注解

定义的方式:和类、接口等定义方式差不多

使用@interface来定义注解,后面跟注解的名称

public @interface MyAnnotation {
    
}

自定义注解传参

模板是:

[public] 数据类型 参数名字 ( ) [default];

被[]括起来的表示可以省略

public @interface MyAnnotation {
    String name();
    int sort() default 1;
    int[] array();
}

定义参数的时候,有点像接口的抽象方法,实际上不是的;定义参数的时候数据类型前面默认是有public的,只是被省略了;数据类型是使用该注解的时候,传入参数的数据类型;()只是一个语法,没有实际意义;

注意点:

  1. 数据类型只能是:基本数据类型、String、Class、枚举类型、注解类型、数组

  2. 如果只有一个参数,建议将参数名写成value,传参时就可以省略value;

  3. 双括号只是一个语法,没有实际意义;

  4. default代表默认值,默认值要和参数类型一致;如果没有默认值,使用注解时必须传参。

注解结合反射使用

新建一个注解

@Target(ElementType.TYPE)//表示注解在类上
@Retention(RetentionPolicy.RUNTIME)//表示该注解的生命周期是运行时
public @interface TestAnnotation {
    String value();
    String name() default "defaultName";
}

基本的使用

@Test
public void test1(){
    //获取java的类对象
    Class<User> cls = User.class;
​
    //获取所有注解了User类的注解
    Annotation[] annotations = cls.getAnnotations();
    for (Annotation annotation : annotations) {
        System.out.println(annotation);
    }
​
    //判断是否被@TestAnnotation注解
    boolean b = cls.isAnnotationPresent(TestAnnotation.class);
    System.out.println(b);
​
    //获得注解的值
    TestAnnotation annotation = cls.getAnnotation(TestAnnotation.class);
    //获取value属性的值
    String value = annotation.value();
    System.out.println("value = "+value);
    //获取name属性的值
    String name = annotation.name();
    System.out.println("name = "+name);
}

运行结果:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值