Skip to content

注解

Annotation是从jdk5.0开始引入的新技术

使用注解的时候只需要在代码中添加 @注解名 即可使用。注解和注释不同,注解有检查和约束的作用他是能够影响程序的执行的

内置注解

  • @Override:重写方法注解
  • @Deprecated:表示该方法已经废弃
  • @SuppressWarnings:忽略警告
  • @FunctionalInterface:表示该接口是一个函数式接口
  • ......

元注解

作用:负责解释其他注解的注解,Java定义了四个标准

@Target注解

用于描述注解的作用范围,可以指定注解应用的地方,如类、方法、字段等

常见取值包括:

常量作用
ElementType.TYPE类、接口(包括注解类型)、枚举声明
ElementType.FIELD字段声明(包括枚举常量)
ElementType.METHOD方法声明
ElementType.PARAMETER参数声明
ElementType.CONSTRUCTOR构造方法声明
ElementType.LOCAL_VARIABLE局部变量声明
ElementType.ANNOTATION_TYPE注解类型声明
ElementType.PACKAGE包声明

@Retention注解

用于描述注解的生命周期,即在什么级别保存注解信息

有三种策略:

常量作用
RetentionPolicy.RUNTIME注解会被保留到运行时,可以通过反射机制读取
RetentionPolicy.SOURCE注解仅存在于源代码中,在编译时会丢弃
RetentionPolicy.CLASS注解会被保留到编译进行时,但不会被加载到 JVM 中

@Documented注解

用于说明该注解将被包含在生成的 JavaDoc 文档中,这样可以让开发人员了解该注解的使用方法和含义

@Inherited注解

用于说明该注解是否会被子类继承。如果一个注解被 @Inherited 修饰,那么该注解将被子类继承,否则不会

自定义注解

注解定义的时候需要使用@interface修饰,然后在内部定义属性内容

java
import java.lang.annotation.*;

@Target(value = {ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
public @interface InitExample {
    String value() default "";
}

以上就是定义了一个初始化的注解,可以看到里面定义了一个value属性,默认值为""

然后在类中方法上使用该注解

java
public class TestClazz {

    @InitExample("INIT")
    public void test1() {
        System.out.println("test-1");
    }

    public void test2() {
        System.out.println("test-2");
    }
}

这就算是使用了,但是还是需要通过反射机制来读取编写这个注解的逻辑。没有使用反射的话这个注解什么用都没有

一般都是会扫描所有类,然后找找这个类里面有没有使用该注解的地方,然后在这个地方执行该注解的逻辑

以下是一个简单的例子

java
public static void main(String[] args) throws Exception {
    // 这里通过类的路径获取class,然后通过反射获取该类的所有方法
    Class<?> aClass = Class.forName("top.xmln.annotation.TestClazz");
    Method[] methods = aClass.getMethods();
    // 如果不为空遍历所有方法
    if (methods != null) {
        for (Method method : methods) {
            // 判断方法上是否有InitExample注解
            boolean present = method.isAnnotationPresent(InitExample.class);
            // 如果有就执行以下逻辑
            if (present) {
                // 下面两行代码是获取注解上的值
                InitExample annotation = method.getAnnotation(InitExample.class);
                System.out.println(annotation.value());

                // 下面这行代码是执行方法
                method.invoke(aClass.getConstructor(null).newInstance(null));
            }
        }
    }
}

反射

反射是动态获取某一个class对象的全部信息

获取反射对象:Class xxx = Class.forName("对象路径")

一个类在内存中只有一个class对象,在加载后,类的整个结构都会被封装在class对象中,每个class对象都对应着一个加载到jvm中的.class文件

常用方法

方法名功能
forName(String)返回指定的类名的class对象
newInstance()调用缺省构造参数,返回class对象的一个实例
getName()返回此Class对象所表示的实体(类、接口、数组···)的名称
getSuperClass()返回当前class对象的父类的class对象
getInterfaces()返回当前class对象的接口
getClassLoader()返回该类的加载器
getConstructors()返回一个包含某些Constructors对象的数组
getMothed(String,Class·· T)返回一个Method对象,此对象的形参为paramType
getDaeclaredFields()返回Field对象的数组

获取类的实例

已至具体类:类名.class

已知类的实例:实例名.getClass()

已知类的全类名(包括路径):Class.forName("全类名")

基本数据类型的包装类都有一个TYPE属性

类加载器

类加载的流程:将字节码文件内容加载到内存中,然后把静态数据转换成方法区的运行时入口,最后在堆中生成一个代表这个类的 java.lang.Class 对象作为方法区数据访问的入口

类缓存:某个类被加载到类加载器中,它将维持加载(缓存)一段时间,JVM垃圾回收机制可以回收这些Class对象

扩展(双亲委派机制):这是一种保护机制,他会在类加载的时候逐层往上查找,如:定义一个类,在加载的时候会一层一层往上寻找直到最顶端,如果找到了这个类,那么就会使用上层的这个类而不会去使用你自己定义的这个类,如果在最顶端没有找到这个类,那么他会自上而下开始寻找,加载找到的第一个类

反射获取运行时类的完整结构

User类:

java
public class User {
    public String id;
    private String name;
    private String age;

    // 有参、无参

    // get、set

    // toString
}

反射:

java
Class aClass = Class.forName("com.wang.User");

// 说明:返回类的全部名称
// 结果:com.wang.User
String name = aClass.getName();

// 说明:返回类的简单名称
// 结果:User
String simpleName = aClass.getSimpleName();

// ------------------------------------------------

// 说明:返回全部属性
// 权限:public
// 某个结果:public java.lang.String User.id
Field[] fields = aClass.getFields();

// 说明:返回某个属性
// 权限:public
// 异常:防止没有该属性(NoSuchFieldException)
// 结果:public java.lang.String User.id
Field id = aClass.getField("id");

// ------------------------------------------------

// 说明:返回全部属性
// 权限:所有权限
// 异常:防止没有该属性(NoSuchFieldException)
// 某个结果:private java.lang.String User.name
Field[] declaredFields = aClass.getDeclaredFields();

// 说明:返回某个属性
// 权限:所有权限
// 异常:防止没有该属性(NoSuchFieldException)
// 结果:private java.lang.String User.name
Field declaredField = aClass.getDeclaredField("name");

// ------------------------------------------------

// 说明:返回全部类的方法,包含继承的类
// 权限:public
// 异常:防止没有该方法(NoSuchMethodException)
// 某个结果:public java.lang.String User.getId() ...
Method[] methods = aClass.getMethods();

// 说明:返回类的某个方法,包含继承的类
// 权限:public
// 异常:防止没有该方法(NoSuchMethodException)
// 值的属性:get方法null就行,set方法填入参数的类型String.class
// 结果:public java.lang.String User.getId() ...
Method method = aClass.getMethod("getId", null);

// ------------------------------------------------

// 说明:返回类的全部方法,不包含继承的类
// 权限:所有权限
// 异常:防止没有该方法(NoSuchMethodException)
// 某个结果:public java.lang.String User.getId() ...
Method[] declaredMethods = aClass.getDeclaredMethods();

// 说明:返回类的某个方法,不包含继承的类
// 权限:所有权限
// 异常:防止没有该方法(NoSuchMethodException)
// 值的属性:get方法null就行,set方法填入参数的类型String.class
// 某个结果:public java.lang.String User.getId() ...
Method declaredMethod = aClass.getDeclaredMethod("setId", String.class);

// ------------------------------------------------

// 说明:获取所有构造方法
// 权限:public
// 某个结果:public User()
Constructor[] constructors = aClass.getConstructors();

// 说明:获取指定构造方法
// 权限:public
// 参数:构造方法中传入的类型
// 某个结果:public User(java.lang.String,java.lang.String,java.lang.String)
Constructor constructor = aClass.getConstructor(String.class, String.class, String.class);

// ------------------------------------------------

// 说明:获取所有构造方法
// 权限:所有权限
// 某个结果:public User()
Constructor[] declaredConstructors = aClass.getDeclaredConstructors();

// 说明:获取指定构造方法
// 权限:所有权限
// 参数:构造方法中传入的类型
// 某个结果:public User(java.lang.String,java.lang.String,java.lang.String)
Constructor declaredConstructor = aClass.getDeclaredConstructor(String.class, String.class, String.class);

反射实例化对象

private修饰的不能直接使用,除非关闭权限检查(xxx.setAccessible(true)

无参

java
User user = (User) Class.forName("com.wang.User").newInstance();
user.setId("1");
System.out.println(user.getId());

可以通过newInstance来实例化对象,本质上是调用了类的无参构造器

有参

java
Class aClass = Class.forName("com.wang.User");
Constructor constructor = aClass.getDeclaredConstructor(String.class, String.class, String.class);
User user = (User) constructor.newInstance("1", "张三", "18");
System.out.println(user.getId());

先获取指定构造方法,然后传值,最后实例化

方法

java
User user = new User();
Class<?> aClass = Class.forName("User");
aClass.getDeclaredMethod("setName", String.class).invoke(user, "张三");
System.out.println(user.getName());

反射获取class对象,然后调用getDeclaredMethod()并传入数据,调用invoke()方法激活执行一下