Java注解与反射
1. 注解
可以在包package,类class,方法method,属性field上使用,相当于添加了额外的辅助信息。可以通过反射机制实现对这些元数据的访问
1.1 内置注解
在java.lang包中包含了很多的注解,其中常用的三个如下:
- Override,只修饰方法,声明一个方法重写超类的方法。
- Deprecated,修饰方法、属性、类,表示它废弃不建议使用
- SupperessWarnings,都可以修饰,需要添加一个参数才可以使用。作用是抑制警告,参数定义好了。"all"所有警告,"unchecked"未检查类型转换的警告。这些参数可以单独使用,也可以组合。
由于只定义了一个参数,且参数名为value,则在传入参数时可以省略 "参数="
1.2 元注解
作用是注解其他的注解,,在Java中定义了四个标准的meta-annotation类型,用来对其他annotation作说明。
- @Target:用于描述使用范围,参数传入ElementType.(TYPE,FIELD,METHOD...),可组合。
- @Retention:指定被注解的注解如何保留,描述声明周期。SOURCE<CLASS<RUNTIME
- RetentionPolicy.SOURECE,保留在源代码,编译后的字节码不包含。
- RetentionPolicy.CLASS,保留在编译后的字节码中,但在运行时不可访问。
- RetentionPolicy.RUNTIME,保留在编译后的字节码,并在运行中可用过反射访问。
1.3 自定义注解
使用@interface声明即可,自动继承java.lang.annotation.Annotation接口。分析如下:
- @interface用来声明一个注解
- 其中的每一个方法实际上是声明了一个配置参数,方法的名词就是参数的名称
- 返回值类型就是参数的类型(基本类型,Class,String,已定义的枚举类型,注解类型,以上的一维数组)
- 使用default声明参数的默认值
- 如果只有一个参数成员,一般参数名为value,这样传参可省略名字。
- 定义注解的元素时,如果不指定默认值,那么这些元素在使用注解时是必须要提供值的。
@Myinnotation(hk=4)
public class test {
}
@Target({ElementType.FIELD,ElementType.TYPE})
@interface Myinnotation{
String[] value() default {"hello","hk"};
int hk();
}
2.反射
在加载完类后,在堆内存产生一个CLASS对象,通过这个对象可以看到类的结构,故称为反射。
反射机制是框架设计的灵魂。可以在运行时动态地获取类的信息并操作类或对象。
优点是灵活,缺点是性能差。通过反射可以实现如下功能:
- 动态创建对象,通过反射无需在编译时知道类的类型。
- 动态调用方法,反射可以调用方法包括私有方法。
- 获取和设置字段值,也包括私有
- 调用私有构造函数,从而创建实例
- 获取类的注解和注解信息。
2.1 常用API
- java.lang.Class,反射的核心类,代表一个类或接口的运行时信息。提供了许多方法来获取类的信息。类的名称、父类、实现的接口、字段、方法、构造函数等。
- java.lang.reflect.Field,表示类的字段(成员变量),提供了方法来获取和设置字段的值,获取字段的名称、类型、修饰符等。
- java.lang.reflect.Method,表示类的方法,提供了方法来调用类的方法,以及获取方法的名称、参数类型、返回类型等。
- java.lang.reflect.Constructor,表示类的构造方法,提供了方法创建类的实例,以及获取构造函数的参数类型,修饰符等。
2.2 五种class对象获取方法
- 通过new对象的.getClass方法
- 通过Class静态方法.forName()路径获得
- 通过类名.class方法获取
- 基本类型的包装类有一个TYPE属性可以通过该属性获得class对象
- 通过子类class对象的getSuperClass获取父类的对象。
public class FiveGetClass {
public static void main(String[] args) throws ClassNotFoundException {
Person person = new Person();
//1. 通过对象的getClass
System.out.println(person.getClass().hashCode());
//2. 通过路径forName
System.out.println(Class.forName("org.example.annotationandreflection.Person").hashCode());
//3. 通过子类的class对象getSuperClass
Class student = Class.forName("org.example.annotationandreflection.Student");
System.out.println(student.getSuperclass().hashCode());
//4. 通过类名.class
System.out.println(Person.class.hashCode());
//5. 包装类的Type
Class type = Integer.TYPE;
System.out.println(type);
}
}
class Person{ }
class Student extends Person{ }
2.3 Class类的常用方法
- getName():获取类的全限定名。
- getSimpleName():获取类的简单名。
| 方法名 | 作用 |
|---|---|
| Class getSuperClass() | |
| Class[] getInterfaces() | |
| ClassLoader getClassLoader() | |
| Constructor[] getConstructors() | |
| Method getMethod(String name,Class...) | 获取指定的方法,有参数的话传入int.class,String.class等,之后使用method.invoke进行调用。 |
| Field[] getDeclaredFields() | 获取本类的所有字段包括private |
| Method[] getDeclaredMethods() | 获取奔雷的所有方法。 |
| newInstance() | 通过默认构造函数创建类的实例,该方法要求类要有一个可访问的无参构造函数 |
| boolean isInterface/Array/Enum | 是否是什么类型 |
2.4 类加载内存分析

加载过程
- Load类的加载:将类的class文件读入内存,将静态数据转换成方法区的运行时数据结构,然后创建一个代表这个类的java.lang.Class对象。
- Link类的链接:验证类信息没有错误;准备,正式为类变量(static)分配内存并设置初始值。
- Initialize类的初始化:执行类的构造器方法(clinit),并执行静态变量赋值和静态代码块。
- 虚拟机保证多线程的同步
- 初始化一个类时,如果其父类还没有初始化,则会先触发父类的初始化。
2.5 什么时候发生类的初始化?
- 类的主动引用(一定发生类的初始化)
- 虚拟机启动先初始化 main所在的类
- new 一个类的对象
- 调用类的静态成员(除了final常量)和静态方法
- java.lang.reflect包的方法对类进行反射调用
- 初始化一个类,父类没有初始化,先初始化父类
- 类的被动引用(不发生初始化)
- 当访问静态域时,只有真正声明这个域的类会初始化。例如子类引用父类的静态变量,子类不会初始化。
- 通过数组定义类引用,不会触发类的初始化
- 引用常量不会触发类的初始化。(常量在链接阶段存入)
//1.主动引用
Son son=new Son();
//2.反射也会产生主动引用
Class.forName("com.kuang.reflection.Son");
//不会产生类的引用的方法 ,子类继承父类的
System.out.println(Son.b);
Son[] array=new Son[5];
2.6 类加载器的作用
负责将类的字节码加载到内存,并转化为可执行的运行时对象。 采用双亲委派机制,当需要加载一个类时,类加载器首先会检查自身是否已加载过该类,如果没有,则委托给父类加载器进行加载。这种层次结构可以实现类的隔离和版本控制,防止如果有人想替换系统级别的类。
- 统类加载器(System Class Loader):也称为应用程序类加载器(Application Class Loader),是负责加载应用程序类路径(Classpath)上的类的加载器。它是 JVM 默认的类加载器,负责加载用户自定义的类和第三方类库等
- 扩展类加载器(Extension Class Loader):扩展类加载器是负责加载 Java 扩展目录(jre/lib/ext)下的类的加载器。它用于加载扩展类库,通常由 Java 扩展机制使用。
- 启动类加载器(Bootstrap Class Loader):启动类加载器是 JVM 的一部分,它负责加载核心 Java 类库(如 java.lang 包中的类)。它是 JVM 的内部组件,通常由本地代码实现,无法直接在 Java 代码中引用。
2.7 获取类的运行时结构
有一些方法或属性设置了private,则需要取消安全检测。调用setAccessible(true);
package com.kuang.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
//获得类的信息
public class Test08 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class c1 = Class.forName("com.kuang.reflection.User");
//获得类的名字
System.out.println(c1.getName());//获得包名+类名
System.out.println(c1.getSimpleName());//获得类名
//获得类的属性
System.out.println("======================");
Field[] fields = c1.getFields();//只能找到public属性
fields = c1.getDeclaredFields();//找到全部的属性
for (Field field : fields) {
System.out.println(field);
}
Field name = c1.getDeclaredField("name");
System.out.println(name);
//获得类的方法
System.out.println("======================");
Method[] methods = c1.getMethods();//获得本类及其父类的全部public方法
for (Method method : methods) {
System.out.println("正常的:" + method);
}
methods = c1.getDeclaredMethods();//获得本类的所有方法
for (Method method : methods) {
System.out.println("getDeclaredMethods" + method);
}
//获得指定方法
//重载
Method getName = c1.getMethod("getName", null);
Method setName = c1.getMethod("setName", String.class);
System.out.println(getName);
System.out.println(setName);
//获得指定的构造器
System.out.println("======================");
Constructor[] constructors = c1.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
constructors = c1.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println("#" + constructor);
}
//获得指定的构造器
Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
System.out.println("指定:" + declaredConstructor);
}
}
2.8 通过反射创建对象,并操作
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
// 动态的创建,通过反射
public class Demo2 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
Class c1 = Class.forName("GetClassStructure.User");
// 构造一个对象 newInstance() 必须有无参构造,也可以通过构造器
User user1 = (User) c1.newInstance();
System.out.println(user1); //user{name='null', id=0, age=0}
// 通过构造器创建对象
Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
User user = (User) declaredConstructor.newInstance("姓名", 1, 2);
System.out.println(user); //user{name='姓名', id=1, age=2}
// 通过反射调用方法
User user3 = (User) c1.newInstance();
// 通过反射获取方法
Method setName = c1.getDeclaredMethod("setName", String.class);
// invoke激活 (对象,方法的值)
setName.invoke(user3,"invoke名字");
System.out.println(user3.getName()); //invoke名字
// 通过反射操作属性
User user4 = (User) c1.newInstance();
Field name = c1.getDeclaredField("name");
// 私有属性不能被赋值,需要关闭Java语言检测
name.setAccessible(true);
name.set(user4,"set名字");
System.out.println(user4.getName()); //set名字
}
}
2.9 获取注解信息
在ORM框架中
| java | 数据库 |
|---|---|
| 类 | 表结构 |
| 属性 | 字段 |
| 对象 | 一行记录 |
package com.text;
import java.lang.annotation.*;
//反射操作注解
public class test38 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
//创建Class对象
Class<?> c1 = Class.forName("com.text.Student2");
//通过Class对象获得类注解
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//获得指定注解值
Table annotation = c1.getAnnotation(Table.class);
System.out.println(annotation.value());
//获得类中的指定注解
java.lang.reflect.Field id = c1.getDeclaredField("id");//指定字段
Field annotation1 = id.getAnnotation(Field.class);//指定注解
System.out.println(annotation1.listName());
System.out.println(annotation1.type());
System.out.println(annotation1.length());
}
}
@Table("staff_table")
class Student2 {
@Field(listName = "id", type = "int", length = 10)
private int id;
@Field(listName = "name", type = "String", length = 3)
private String name;
@Field(listName = "age", type = "varchar", length = 3)
private int age;
public Student2() {
}
public Student2(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
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;
}
}
//修饰表
@Target(ElementType.TYPE) //表示作用于类
@Retention(RetentionPolicy.RUNTIME) //表示它会被加载进入到JVM中
@interface Table {
String value();//如果只有一个可以用value来命名
}
//修饰字段
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Field {
String listName();
String type();
int length();
}

浙公网安备 33010602011771号