java注解和反射

java 注解和反射

注解

Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。
Java 语言中的类、方法、变量、参数和包等都可以被标注。和 Javadoc 不同,Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。 当然它也支持自定义 Java 标注。

注解也是一种类的类型,他使用的修饰符为 @interface

例如:

public @interface MyTestAnnotation {
}
内置注解

共有 7 个,3 个在 java.lang 中,剩下 4 个在 java.lang.annotation 中。

作用在代码的注解是

  • @Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
  • @Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
  • @SuppressWarnings - 指示编译器去忽略注解中声明的警告。

内置注解包括元注解

元注解:(元注解就是自定义注解时,对自定义注解的一个定义)

  • @Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。

    //@Retention是需要入参的
    //源码
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    public @interface Retention {
        //定义在注解内的都是使用注解时所需要传入的参数,如果为一个,就命名为value;
        RetentionPolicy value();
    }
    //RetentionPolicy是声明好的枚举类型
    public enum RetentionPolicy {
        /**
         * Annotations are to be discarded by the compiler.
         */
        //此注解类型的信息只会记录在源文件中,编译时将被编译器丢弃,也就是说
        //不会保存在编译好的类信息中
        SOURCE,
    
        /**
         * Annotations are to be recorded in the class file by the compiler
         * but need not be retained by the VM at run time.  This is the default
         * behavior.
         */
         //编译器将注解记录在类文件中,但不会加载到JVM中。如果一个注解声明没指定范围,则系统
        //默认值就是Class
        CLASS,
    
        /**
         * Annotations are to be recorded in the class file by the compiler and
         * retained by the VM at run time, so they may be read reflectively.
         *
         * @see java.lang.reflect.AnnotatedElement
         */
         //注解信息会保留在源文件、类文件中,在执行的时也加载到Java的JVM中,因此可以反射性的读取。
        RUNTIME
    }
    
    //也就是说SOURCE只存在于.java文件,.class文件中就不存在;大部分声明为RUNTIME在jvm中同样生效
    
  • @Documented - 标记这些注解是否包含在用户文档中。

  • @Target - 标记这个注解应该是哪种 Java 成员。

    //@Target同样需要入参的;
    //源码分析
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    public @interface Target {
        //定义在注解内的都是注解所需要的参数,如果为一个,就命名为value;
        ElementType[] value();
    }
    
    
    //可以看到是一个枚举类型的入参,这个枚举类型的入参规定了自定义注解的作用在什么地方;
    //比如TYPE就说明,该注解只能在类、接口(包括注释类型)或枚举中使用
    public enum ElementType {
        /** Class, interface (including annotation type), or enum declaration */
        //类、接口(包括注释类型)或枚举声明  
        TYPE,
    
        /** Field declaration (includes enum constants) */
        //字段声明(包括枚举常量)
        FIELD,
    
        /** Method declaration */
        //方法声明
        METHOD,
    
        /** Formal parameter declaration */
        //正式的参数声明
        PARAMETER,
    
        /** Constructor declaration */
        //构造函数声明
        CONSTRUCTOR,
    
        /** Local variable declaration */
        //局部变量声明
        LOCAL_VARIABLE,
    
        /** Annotation type declaration */
        //注释类型声明
        ANNOTATION_TYPE,
    
        /** Package declaration */
        //包装声明
        PACKAGE,
    
        /**
         * Type parameter declaration
         *
         * @since 1.8
         */
        //类型参数声明
        TYPE_PARAMETER,
    
        /**
         * Use of a type
         *
         * @since 1.8
         */
        //类型的使用
        TYPE_USE
    }
    
    
    
    
    //举个例子
    @My
    public class Testzhujie {
    
        @My//会报错(写上就会直接报:'@My' not applicable to method)
        public int hashCode() {
            return super.hashCode();
        }
    }
    
    @Target(ElementType.TYPE)
    @interface My{
    
    }
    
  • @Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)

从 Java 7 开始,额外添加了 3 个注解:

  • @SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
  • @FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。
  • @Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。

反射

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;

对于任意一个对象,都能够调用它的任意一个方法和属性;

这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

也正是反射机制,java也可以称为**“准动态语言”**

静态语言和动态语言
//动态语言
/*
动态语言就是可以在运行时改变其结构的语言;
就是说在运行时代码可以根据某些条件改变自身结构
*/

//静态语言
/*
与动态语言相反
*/
//要想了解一个类,必须先要获取到该类的字节码文件对象. 

//在Java中,每一个字节码文件,被夹在到内存后,都存在一个对应的Class类型的对象 

//白话,反射就是通过操作class类型的对象(从字节码文件)获得类的所有属性和方法


得到Class的几种方式

要想了解一个类,必须先要获取到该类的字节码文件对象.

在Java中,每一个字节码文件,被夹在到内存后,都存在一个对应的Class类型的对象

1. 如果在编写代码时, 知道类的名称, 且类已经存在, 可以通过 

包名.类名.class 得到一个类的 类对象 

2. 如果拥有类的对象, 可以通过 

Class 对象.getClass() 得到一个类的 类对象 

3. 如果在编写代码时, 知道类的名称 , 可以通过 

Class.forName(包名+类名): 得到一个类的 类对象 

上述的三种方式, 在调用时, 如果类在内存中不存在, 则会加载到内存 ! 如果类已经在内存中存在, 不

会重复加载, 而是重复利用 !

通过class对象 获取一个类的构造方法Constructor

		//1、将person类加载到内存(已存在的实体类)
        Class<?> aClass = Class.forName("com.kfd.test002.Person");
        //通过类对象获得无参构造方法
        Constructor<?> constructor = aClass.getConstructor();
        //通过无参构造方法创建对象
        Object o = constructor.newInstance();
        System.out.println(o);

		//2、通过有参构造方法创建对象
        Constructor<?> constructor1 = aClass.getConstructor(String.class, int.class);
		//通过有参构造方法创建对象时,需要知道入参类型;
        Object instance = constructor1.newInstance("张三", 19);
		//打印该对象
        System.out.println(instance);

//结果:
Person{name='null', age=0, phoneNumber='null'}
Person{name='张三', age=19, phoneNumber='null'}
//但是通过getConstructor()方法获得的构造方法是非私有的;

		//3、通过反射使用私有权限的构造方法创建对象
        //获取所有权限的单个构造方法getDeclaredConstructor
        //获取所有权限的构造方法数组getDeclaredConstructors
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class);
        //设置:构造方法(忽略权限检查)必须设置忽略权限检查,否则会因为权限不够而报错
        declaredConstructor.setAccessible(true);

        Object instance1 = declaredConstructor.newInstance("老夏");
        System.out.println(instance1);

//实体类:
//私有构造方法
		    private Person(String name) {
        		this.name = name;
        		this.age = 99;
    		}
//结果:
Person{name='老夏', age=99, phoneNumber='null'}

通过Constructor创建对象

​ 常用方法:

​ newInstance(Object… para)

​ 调用这个构造方法, 把对应的对象创建出来

参数: 是一个Object类型可变参数, 传递的参数顺序 必须匹配构造方法中形式参数列表的顺

序!

​ setAccessible(boolean flag)

如果flag为true 则表示忽略访问权限检查 !(可以访问任何权限的方法)

上面只是通过反射Class类对象获得实体类的无参构造方法和有参构造方法

下面通过Class获得对象中的方法

1.getMethod(String methodName , class.. clss) 
	根据参数列表的类型和方法名, 得到一个方法(public修饰的)
2. getMethods(); 
    得到一个类的所有方法 (public修饰的) 
3. getDeclaredMethod(String methodName , class.. clss) 
	根据参数列表的类型和方法名, 得到一个方法(除继承以外所有的:包含私有, 共有, 保护, 默认) 
4. getDeclaredMethods(); 
	得到一个类的所有方法 (除继承以外所有的:包含私有, 共有, 保护, 默认)
//练习
public static void main(String[] args) throws Exception {
        //1、将类加载到内存
        Class<?> aClass = Class.forName("com.kfd.test002.Person");
        //2、获取无参构造方法
        Constructor<?> constructor = aClass.getConstructor();
        //3、利用无参构造方法创建对象
        Object instance = constructor.newInstance();

        //4、获取类的单个方法(需要传递方法名,和入参类型)
        Method method = aClass.getMethod("setName", String.class);
        //5、执行这个方法(需要传递:执行方法的类对象,和参数)
        method.invoke(instance, "李四");

        //看结果
        System.out.println(instance);


        //6、调用私有的setAge方法(使用getDeclaredMethod方法)
        Method setAge = aClass.getDeclaredMethod("setAge", int.class);
        //7、别忘了设置忽略权限
        setAge.setAccessible(true);
        setAge.invoke(instance,10);
        System.out.println(instance);
    }
//setname是共有方法
//setage是私有方法
//结果:
Person{name='李四', age=0, phoneNumber='null'}
Person{name='李四', age=10, phoneNumber='null'}

下面通过Class获得对象的属性

1. getDeclaredField(String filedName) 
	根据属性的名称, 获取一个属性对象 (所有属性) 
2. getDeclaredFields() 
	获取所有属性 
3. getField(String filedName) 
	根据属性的名称, 获取一个属性对象 (public属性) 
4. getFields() 
	获取所有属性 (public)
	


获得属性后的常用方法
常用方法: 
1. get(Object o ); 
	参数: 要获取属性的对象 获取指定对象的此属性值
2. set(Object o , Object value);
	参数1. 要设置属性值的 对象 
	参数2. 要设置的值 设置指定对象的属性的值
3. getName() 
	获取属性的名称 
4. setAccessible(boolean flag) 
	如果flag为true 则表示忽略访问权限检查 !(可以访问任何权限的属性)	
//练习
public static void main(String[] args) throws Exception {
        Class<?> aClass = Class.forName("com.kfd.test002.Person");
        Constructor<?> constructor = aClass.getConstructor();

        Object instance = constructor.newInstance();

        //获得对象的属性
        Field phoneNumber = aClass.getField("phoneNumber");
		//可以通过属性对象对其赋值,需要传入,是为那个对象的属性赋值,第一参数是对象,第二个参数是属性值
        phoneNumber.set(instance,"1526475892");

        System.out.println(instance);

    }
//结果:
Person{name='null', age=0, phoneNumber='1526475892'}

获取注解信息

//	获取类/属性/方法的全部注解对象.注意替换
Annotation[] annotations01 = Class/Field/Method.getAnnotations(); for (Annotation annotation : annotations01) { System.out.println(annotation); }
//练习
public static void main(String[] args) throws Exception {
        Class aClass = Class.forName("com.kfd.test003.Book");
        Constructor constructor = aClass.getConstructor();
    	//通过无参构造方法创建对象
        Object instance = constructor.newInstance();
		//获得类的table_name注解
        table_name annotation = (table_name) aClass.getAnnotation(table_name.class);
    	//打印
        String value = annotation.value();
        System.out.println(value);

		//获得类的所有属性
        Field[] fields = aClass.getDeclaredFields();
		//循环获得每个属性的注解
        for (Field field: fields) {
            column_name annotation1 = field.getAnnotation(column_name.class);
            System.out.println(annotation1.name()+"类型是:"+annotation1.type()+"长度是:"+annotation1.length());
        }
   //实体类:
    @table_name("book")
public class Book {
    @column_name(name = "id",type = "int",length = "11")
    private int id;
    @column_name(name = "name",type = "varchar",length = "50")
    private String name;
    @column_name(name = "info",type = "varchar",length = "1000")
    private String info;
}
    
    
//执行结果
book
id类型是:int长度是:11
name类型是:varchar长度是:50
info类型是:varchar长度是:1000
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值