前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java注解和反射

java注解和反射

作者头像
不期而遇丨
发布2022-09-09 18:01:10
3620
发布2022-09-09 18:01:10
举报
文章被收录于专栏:用户8637799的专栏

java 注解和反射

注解

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

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

例如:

代码语言:javascript
复制
public @interface MyTestAnnotation {
}
内置注解

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

作用在代码的注解是

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

内置注解包括元注解

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

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

代码语言:javascript
复制
//@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 成员。

代码语言:javascript
复制
//@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也可以称为**“准动态语言”**

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

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

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

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

得到Class的几种方式

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

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

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

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

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

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

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

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

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

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

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

代码语言:javascript
复制
		//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获得对象中的方法

代码语言:javascript
复制
1.getMethod(String methodName , class.. clss) 
	根据参数列表的类型和方法名, 得到一个方法(public修饰的)
2. getMethods(); 
    得到一个类的所有方法 (public修饰的) 
3. getDeclaredMethod(String methodName , class.. clss) 
	根据参数列表的类型和方法名, 得到一个方法(除继承以外所有的:包含私有, 共有, 保护, 默认) 
4. getDeclaredMethods(); 
	得到一个类的所有方法 (除继承以外所有的:包含私有, 共有, 保护, 默认)
代码语言:javascript
复制
//练习
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获得对象的属性

代码语言:javascript
复制
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 则表示忽略访问权限检查 !(可以访问任何权限的属性)	
代码语言:javascript
复制
//练习
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'}

获取注解信息

代码语言:javascript
复制
//	获取类/属性/方法的全部注解对象.注意替换
Annotation[] annotations01 = Class/Field/Method.getAnnotations(); for (Annotation annotation : annotations01) { System.out.println(annotation); }
代码语言:javascript
复制
//练习
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
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-06-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • java 注解和反射
    • 注解
      • 反射
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档