10-javase-注解反射-笔记
10-javase-注解&反射-ydl学习笔记
文章目录
- 10-javase-注解&反射-ydl学习笔记
- 一、注解
- 1. 注解(Annotation)的定义
- 1.1 定义注解并使用
- 1.2 添加默认值
- 1.3 方法名字定义为value()
- 2. 注解(Annotation)组成部分
- 2.1 Annotation.java
- 2.2 ElementType.java
- 2.3 RetentionPolicy.java
- 3. java自带的 注解(Annotation)
- 3.1 内置的注解
- 3.2 常用的注解
- 4. 注解(Annotation)的作用
- 二、反射
- 1.反射入门
- 2. 获取类对象的方法
- 2.1 获取方式
- 2.2 对类对象操作
- 3. 对成员变量的操作
- 3.1 获取成员变量
- 3.2 获取对象属性
- 3.3 设置对象d1属性
- 4. 对方法的操作
- 4.1 获取方法
- 4.2 调用方法
- 5. 对构造器的操作
- 5.1 获取构造器对象
- 5.2 获取无参构造器
- 5.3获取有参构造器并且实力化对象
- 6. 对注解的操作
- 7. 利用类加载器获取资源
- 三、案例
一、注解
概念:
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.classLast modified 2022-3-9; size 260 bytesMD5 checksum 113ec4cabaa9715f2aaba823edd7bb69Compiled from "MyAnnotation.java"// 我们发现字节码中注解其实也是一个接口统一继承字java.lang.annotatoin.Annotation
public interface com.it.Annotation.MyAnnotation extends java.lang.annotation.Annotationminor version: 0major version: 52flags: 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_ABSTRACTAnnotationDefault:default_value: s#7public abstract int value();descriptor: ()Iflags: 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 requiredreturn getInterfaces0();} else {Class<?>[] interfaces = rd.interfaces;if (interfaces == null) {interfaces = getInterfaces0();rd.interfaces = interfaces;}// defensively copy before handing over to user codereturn interfaces.clone();}}private native Class<?>[] getInterfaces0(); // 获得方法@CallerSensitivepublic Method[] getMethods() throws SecurityException {checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);return copyMethods(privateGetPublicMethods());}// 获得他的构造器@CallerSensitivepublic Constructor<?>[] getConstructors() throws SecurityException {checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);return copyConstructors(privateGetDeclaredConstructors(true));}// 获得他的属性@CallerSensitivepublic 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;@Beforepublic void before(){dogClass = Dog.class;}

代码如下(示例):
@Testpublic 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 {@Testpublic 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);}
}


三、案例
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!






