10-javase-注解&反射-笔记

10-javase-注解&反射-ydl学习笔记



一、注解

概念:
Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种机制。Java语言中的类、方法、变量、参数和包等都可以被标注。

1. 注解(Annotation)的定义

1.1 定义注解并使用

定义的格式是:String name();

在这里插入图片描述

代码如下(示例):

public @interface MyAnnotation {
    String name();
}

在这里插入图片描述

代码如下:

public class Dog {
    private String name;
    private int age;

    @MyAnnotation(name = "小黄")
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

1.2 添加默认值

可以有默认值,也可以没有,如果没有默认值在使用的时候必须填写对应的值。默认值使用default添加。

在这里插入图片描述
在这里插入图片描述

1.3 方法名字定义为value()

如果想在使用的时候不指定具体的名字,方法名字定义为value() 即可。
注意:如果有多个参数时必须指定具体名字
在这里插入图片描述
在这里插入图片描述

2. 注解(Annotation)组成部分

我们使用javap查看生成的注解类:
在这里插入图片描述
在这里插入图片描述

F:\develop\SSM\Spring\donglijiedian_spring\spring\d3-ioc-homework\target\classes\com\it\Annotation>javap -v MyAnnotation.class
Classfile /F:/develop/SSM/Spring/donglijiedian_spring/spring/d3-ioc-homework/target/classes/com/it/Annotation/MyAnnotation.class
  Last modified 2022-3-9; size 260 bytes
  MD5 checksum 113ec4cabaa9715f2aaba823edd7bb69
  Compiled from "MyAnnotation.java"
  // 我们发现字节码中注解其实也是一个接口统一继承字java.lang.annotatoin.Annotation
public interface com.it.Annotation.MyAnnotation extends java.lang.annotation.Annotation
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
Constant pool:
   #1 = Class              #12            // com/it/Annotation/MyAnnotation
   #2 = Class              #13            // java/lang/Object
   #3 = Class              #14            // java/lang/annotation/Annotation
   #4 = Utf8               name
   #5 = Utf8               ()Ljava/lang/String;
   #6 = Utf8               AnnotationDefault
   #7 = Utf8               小黑
   #8 = Utf8               value
   #9 = Utf8               ()I
  #10 = Utf8               SourceFile
  #11 = Utf8               MyAnnotation.java
  #12 = Utf8               com/it/Annotation/MyAnnotation
  #13 = Utf8               java/lang/Object
  #14 = Utf8               java/lang/annotation/Annotation
{

// 生成了两个抽象方法
  public abstract java.lang.String name();
    descriptor: ()Ljava/lang/String;
    flags: ACC_PUBLIC, ACC_ABSTRACT
    AnnotationDefault:
      default_value: s#7
  public abstract int value();
    descriptor: ()I
    flags: ACC_PUBLIC, ACC_ABSTRACT
}
SourceFile: "MyAnnotation.java"

F:\develop\SSM\Spring\donglijiedian_spring\spring\d3-ioc-homework\target\classes\com\it\Annotation>

java Annotation 的组成中,有 3 个非常重要的主干类。它们分别是:

2.1 Annotation.java

package java.lang.annotation;
public interface Annotation {

    boolean equals(Object obj);

    int hashCode();

    String toString();

    Class<? extends Annotation> annotationType();
}

2.2 ElementType.java

在这里插入图片描述

ElementType 是 Enum 枚举类型,它用来指定 Annotation 的类型。大白话就是,说明了我的注解将来要放在哪里。

package java.lang.annotation;

public enum ElementType {
    // 类、接口(包括注释类型)或枚举声明
    TYPE,          
    //  字段声明(包括枚举常量
    FIELD,       
    //  方法声明
    METHOD,       
    //  参数声明
    PARAMETER,      
    //  构造方法声明
    CONSTRUCTOR,     
    //  局部变量声明
    LOCAL_VARIABLE,  
    //   注释类型声明
    ANNOTATION_TYPE,   
    //  包声明
    PACKAGE      
}

2.3 RetentionPolicy.java

在这里插入图片描述

RetentionPolicy 是 Enum 枚举类型,它用来指定 Annotation 的策略。通俗点说,就是不同 RetentionPolicy 类型的 Annotation 的作用域不同。

1.若 Annotation 的类型为 SOURCE,则意味着:Annotation 仅存在于编译器处理期间,编译器处理完之后,该 Annotation 就没用了。 例如," @Override" 标志就是一个 Annotation。当它修饰一个方法的时候,就意味着该方法覆盖父类的方法;并且在编译期间会进行语法检查!编译器处理完后,“@Override” 就没有任何作用了。

2. 若 Annotation 的类型为 CLASS,则意味着:编译器将 Annotation 存储于类对应的 .class 文件中,它是 Annotation 的默认行为。

3. 若 Annotation 的类型为 RUNTIME,则意味着:编译器将 Annotation 存储于 class 文件中,并且可由JVM读入。

package java.lang.annotation;
public enum RetentionPolicy {
    //Annotation信息仅存在于编译器处理期间,编译器处理完之后就没有该Annotation信息了
    SOURCE,       
    //编译器将Annotation存储于类对应的.class文件中。但不会加载到JVM中。默认行为 
    CLASS,       
    // 编译器将Annotation存储于class文件中,并且可由JVM读入,因此运行时我们可以获取。
    RUNTIME       
}

3. java自带的 注解(Annotation)

Annotation 实现类的语法定义:

3.1 内置的注解

Java 定义了一套注解,共有10 个,6个在 java.lang 中,剩下 4 个在 java.lang.annotation 中。

(1)作用在代码的注解是
@Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
@Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
@SuppressWarnings - 指示编译器去忽略注解中声明的警告。
@SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
@FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。
@Repeatable - Java8 开始支持,标识某注解可以在同一个声明上使用多次。

(2)作用在其他注解的注解(或者说 元注解)是:
@Retention -标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
@Documented -标记这些注解是否包含在用户文档中。
@Target - 标记这个注解可以修饰哪些 Java 成员。
@Inherited -如果一个类用上了 @Inherited修饰的注解,那么其子类也会继承这个注解

3.2 常用的注解

通过上面的示例,我们能理解:@interface 用来声明 Annotation,@Documented 用来表示该 Annotation 是否会出现在 javadoc 中, @Target 用来指定 Annotation 的类型,@Retention 用来指定 Annotation 的策略。

@Documented 标记这些注解是否包含在用户文档中。

@Inherited

@Inherited 的定义如下:加有该注解的注解会被子类继承,注意,仅针对类,成员属性、方法并不受此注释的影响。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

@Deprecated

@Deprecated 的定义如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Deprecated {
}

说明:@Deprecated 所标注内容,不再被建议使用。

在这里插入图片描述
加上这个注解在使用或者重写时会有警告:
在这里插入图片描述

@SuppressWarnings

@SuppressWarnings 的定义如下:

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}

说明:
​ SuppressWarnings 的作用是,让编译器对"它所标注的内容"的某些警告保持静默,用于抑制编译器产生警告信息。。例如,“@SuppressWarnings(value={“deprecation”, “unchecked”})” 表示对"它所标注的内容"中的 "SuppressWarnings 不再建议使用警告"和"未检查的转换时的警告"保持沉默

在这里插入图片描述

在这里插入图片描述

关键字用途
all抑制所有警告
boxing抑制装箱、拆箱操作时候的警告
fallthrough抑制在switch中缺失breaks的警告
finally抑制finally模块没有返回的警告
rawtypes使用generics时忽略没有指定相应的类型
serial忽略在serializable类中没有声明serialVersionUID变量
unchecked抑制没有进行类型检查操作的警告
unused抑制没被使用过的代码的警告

4. 注解(Annotation)的作用

(1)Annotation 具有"让编译器进行编译检查的作用“,这个讲了很多了。

(2)利用反射,和反射配合使用能产生奇妙的化学反应。

二、反射

1.反射入门

我们都知道光是可以反射的,我们无法直接接触方法区中一个类的方法、属性、注解等,那就可以通过一面镜子观察它的全貌,这个镜子就是JDK给我们提供的Class类。
在这里插入图片描述

​ 首先我们看一下Class这个类,初步简单的分析一下。我们发现这个类并没有什么成员变量,仅仅存在许多的方法,还有不少是本地方法。通过这些方法的名字我们大致能猜出,这个类能帮我们获取方法、构造器、属性、注解等。

代码如下(示例):

public final class Class<T>  {

    // 获得他实现的接口
    public Class<?>[] getInterfaces() {
        ReflectionData<T> rd = reflectionData();
        if (rd == null) {
            // no cloning required
            return getInterfaces0();
        } else {
            Class<?>[] interfaces = rd.interfaces;
            if (interfaces == null) {
                interfaces = getInterfaces0();
                rd.interfaces = interfaces;
            }
            // defensively copy before handing over to user code
            return interfaces.clone();
        }
    }

    private native Class<?>[] getInterfaces0();   

    // 获得方法
    @CallerSensitive
    public Method[] getMethods() throws SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        return copyMethods(privateGetPublicMethods());
    }

    // 获得他的构造器
    @CallerSensitive
    public Constructor<?>[] getConstructors() throws SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        return copyConstructors(privateGetDeclaredConstructors(true));
    }

    // 获得他的属性
    @CallerSensitive
    public Field getField(String name)
        throws NoSuchFieldException, SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        Field field = getField0(name);
        if (field == null) {
            throw new NoSuchFieldException(name);
        }
        return field;
    }
}

我们已经学过了类的加载过程,这里我们要介绍的是,每一个类加载完成后会在方法区生成一个Class类型的对象,辅助我们访问这个的方法、构造器、字段等。这个对象是Class的子类,每个类【有且仅有】一个Class类,也叫类对象。
在这里插入图片描述

2. 获取类对象的方法

2.1 获取方式

在这里插入图片描述
在这里插入图片描述
代码如下(示例):

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        // 1、使用类名
        Class<Dog> dogClass1 = Dog.class;

        // 2、使用对象
        Dog dog = new Dog();
        Class<? extends Dog> dogClass2 = dog.getClass();

        // 3、formName使用全限定名
        Class<?> dogClass3 = Class.forName("com.it.Annotation.Dog");

        System.out.println(dogClass1 == dogClass2);
        System.out.println(dogClass2 == dogClass3);
    }
}

2.2 对类对象操作

在这里插入图片描述

代码如下(示例):

import com.it.Annotation.Dog;
import org.junit.Before;

public class ReflectTest {
    Class<Dog> dogClass = null;

    @Before
    public void before(){
        dogClass = Dog.class;
    }

在这里插入图片描述
代码如下(示例):

@Test
    public void createTest() throws InstantiationException, IllegalAccessException {
        //获取类名字
        String name = dogClass.getName();
        
        //获取类加载器
        ClassLoader classLoader = dogClass.getClassLoader();
        
        //获取资源
        URL resource = dogClass.getResource("");
        
        //得到父类
        Class superclass = dogClass.getSuperclass();
        
         //判断一个类是不是接口,数组等等
        boolean array = dogClass.isArray();
        boolean anInterface = dogClass.isInterface();

        //重点,使用class对象实例化一个对象
        Object instance = dogClass.newInstance();
    }

3. 对成员变量的操作

在java中万物皆对象成员变量也是对象,他拥有操作一个对象的成员变量的能力。

3.1 获取成员变量

getFields只能获取被public修饰的成员变量,当然反射很牛,我们依然可以使用getDeclaredFields方法获取所有的成员变量。

代码如下(示例):

// 这个方法只能拿到public修饰的成员变量
        Field[] fields = dogClass.getFields();
        for (Field field : fields) {
            System.out.println(field.getName());
        }

        // 这个方法可以获取所有的成员变量
        Field[] fields1 = dogClass.getDeclaredFields();
        for (Field field : fields1) {
            System.out.println(field.getName());
        }

3.2 获取对象属性

代码如下(示例):

// 这个方法可以获取所有的成员变量
        Field[] fields1 = dogClass.getDeclaredFields();
        for (Field field : fields1) {
            System.out.println(field.getName());
        }

当然你要是明确类型你还能用以下方法:
代码如下(示例):

Int i = age.getInt(dog);
xxx.getDouble(dog);
xxx.getFloat(dog);
xxx.getBoolean(dog);
xxx.getChar(dog);
//每一种基本类型都有对应方法

3.3 设置对象d1属性

代码如下(示例):

// 成员变量的本质就是赋值和取值,现在的问题是给谁赋值
        Dog dog = new Dog();
        Field name = dogClass.getDeclaredField("name");
        // 直接设置值,不能是private
        // 暴力注入
        name.setAccessible(true); // 打开权限
        name.set(dog, "xiaohei");
        System.out.println(name.get(dog));

当然如果你知道对应的类型,我们可以这样:

xxx.setBoolean(dog,true);
xxx.getDouble(dog,1.2);
xxx.getFloat(dog,1.2F);
xxx.getChar(dog,'A');
//每一种基本类型包装类都有对应方法

4. 对方法的操作

4.1 获取方法

在这里插入图片描述
代码如下(示例):

        // 获取所有的方法
        Method[] methods = dogClass.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method.getName());
        }

        // 获取某一个方法
        Method getName = dogClass.getDeclaredMethod("getName");
        Method getNam2 = dogClass.getDeclaredMethod("main", String[].class);

4.2 调用方法

在这里插入图片描述
在这里插入图片描述
代码如下(示例):

// 方法而言只有一个核心方法,就是调用
        Method eat = dogClass.getDeclaredMethod("eat", String.class);
        Dog dog = new Dog();
        eat.setAccessible(true);
        eat.invoke(dog,"骨头😝");

5. 对构造器的操作

5.1 获取构造器对象

在这里插入图片描述
代码如下(示例):

// 获取构造器对象
        Constructor<Dog> constructor = dogClass.getDeclaredConstructor();
        System.out.println(constructor.getName());

5.2 获取无参构造器

代码如下(示例):

        // 获取无参构造器对象并且构造对象
        Constructor<Dog> constructor = dogClass.getDeclaredConstructor();
        Dog dog = constructor.newInstance(); // 这行代码与 new Dog(); 是一样的

5.3获取有参构造器并且实力化对象

在这里插入图片描述
代码如下(示例):

        // 获取有参构造器并且实例化对象
        Constructor<Dog> constructor1 = dogClass.getDeclaredConstructor(String.class);
        Dog dog1 = constructor1.newInstance("🐕二哈");
        System.out.println(dog1.getName());

6. 对注解的操作

在这里插入图片描述
在这里插入图片描述
代码如下(示例):

		// 元注解 要加上runtime
        // 类上
        MyAnnotation declaredAnnotation = dogClass.getDeclaredAnnotation(MyAnnotation.class);
        String name1 = declaredAnnotation.name();
        System.out.println("name1 = " + name1);
        Annotation[] annotation = dogClass.getAnnotations();

        // 字段上
        Field name = dogClass.getDeclaredField("name");
        Annotation[] annotation1 = name.getAnnotations();

        // 方法上
        Method eat = dogClass.getDeclaredMethod("eat", String.class);
        Annotation[] annotation2 = eat.getAnnotations();

代码如下(示例):

7. 利用类加载器获取资源

在这里插入图片描述

public class ClassLoderTest {

    @Test
    public void test1() throws IOException {
        // ①:创建properties实列
        Properties properties = new Properties();
        // ②:通过反射获取Class --> 在获取类加载器 --> 通过类加载器获取资源IO流
        InputStream stream =
                ClassLoderTest.class // 通过类名进行反射获取大Class
                .getClassLoader()    // getClassLoader() 获取这个类的加载器
                .getResourceAsStream("com/it/jdbc.properties"); // 以IO流的形式获取资源 返回的就是一个流
        // ③:使用properties 的 load() 方法加载 IO流
        properties.load(stream);  // properties 通过 load() 方法加载,刚好需要一个流
        // ④:通过key获取值
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");

        System.out.println("user = " + user);
        System.out.println("password = " + password);
    }
}

在这里插入图片描述

在这里插入图片描述

三、案例

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

七@归七

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值