注解和反射

本文详细介绍了Java中的注解、反射技术,包括注解的定义、作用、格式,以及元注解的使用。涵盖了内置注解如@Override、@Deprecated和@SuppressWarnings,自定义注解的创建,以及如何通过反射读取注解、动态创建对象和操作注解。同时讲解了类加载器的工作原理和使用方法。
摘要由CSDN通过智能技术生成

注解和反射

注解Annotation

1. 什么是注解

注解是从JDK5.0开始引入的新技术,注解和注释是有一定区别的,可以把注解理解为代码中的特殊标记。

注解的作用

  • 注解不是程序本身,可以对程序作出解释
  • 注解可以在程序编译,类加载,运行时被读取,并且执行相应的处理

注解的格式

  • 注解是以@注释名在代码中存在的,还可以添加一些参数值,例如:@(SuppersWarnings("all"))

注解的使用场景?

  • 可以在package,class,method,filed等上面,相当于给它们添加了额外的辅助信息,我们可以通过反射机制实现对这些元数据的访问

2. 常用内置注解

  • @Override

    定义在java.lang.Override中,此注解可以确保子类确实重写了父类的方法,避免出现低级错误

  • @Deprecated

    定义在java.lang.Deprecated中,此注解可以用来修饰方法,属性,类。

    表示不鼓励程序员使用这样的元素,表明被修饰的元素类,方法等已经过时。

  • @SuupressWarnings

    定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息。

3. 元注解

元注解的作用就是负责注解其他注解,Java定义了4个标准的元注解类型,他们被用来提供对其他注解类型作说明

  1. @Target:用于描述注解的使用范围

    • ElementType.TYPE:类或接口
    • ElementTYpe.FIELD:字段
    • ElementTYpe.METHOD:方法
    • ElementTYpe.CONSTRUCTOR:构造方法
    • ElementTYpe.PARAMETER:方法参数
  2. @Retention:表示需要什么场景下生效该注解,也就是注解的生命周期

    runtime > class > sources

  3. @Document:说明该注解将包含在javadoc

  4. @Iherited:定义子类是否可以继承父类定义Annotation

    @Inherited仅针对 @Target(ElementType.TYPE)类型的Annotation有效,并且仅针对

    class的继承,对interface的继承无效

@MyAnnotation
public class Test01 {

    @MyAnnotation
    public void method1() {

    }
}

/**
 * 定义一个注解MyAnotation,再给注解加上几个元注解	
 * 
 * Target 表示这个注解可以用在什么地方
 * Retention 表示这个注解在什么时候生效 (runtime > class > sources)
 * Documented 说明该注解将包含在javadoc中
 * Inherited 说明子类可以继承父类中的该注解
 */
@Target(value = {ElementType.METHOD, ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
@Inherited
@interface MyAnnotation {

}

4. 自定义注解

  • 使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口
  • 分析:
    • @interface来声明一个注解,格式:public @interface 注解名{定义内容}
    • 其中的每个方法实际就是声明的一个配置参数
    • 方法的名称就是参数的名称
    • 返回值类型就是参数的类型
    • 可以通过default来声明参数的默认值
    • 如果只有一个参数成员,一般参数名为value
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

public class Test02 {
    @MyAnnotation(id = 1)
    public void method1() {

    }
}

@Target(value = ElementType.METHOD)
@interface MyAnnotation {
    // 参数类型+参数名()
    String name() default "root";

    int age() default 18;

    int id();
}

5. 反射读取注解

反射Reflection

1. 反射和反射机制

反射

Java的反射是指程序在运行期间可以拿到一个对象的所有信息。

反射的优缺点

  • 优点:可以实现动态创建对象和编译,灵活性大
  • 缺点:对性能有影响,反射操作对比直接执行相同操作性能较差

反射机制

Java的反射机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用,操作任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言(在程序运行的时候可以改变其结构)的关键。

2. 动态语言和静态语言

动态语言

  • 是一类在运行的时候可以改变其结构的语言
  • 主要动态语言:JavascriptPHPPython

静态语言

  • 与动态语言相对,运行时结构不可变的语言就是静态语言。如JavaCC++
  • Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性,我们可以利用 反射机制获得类似动态语言的特性。Java的动态性让编程的时候更加灵活!

3. Class类

java.lang.reflect.Class类,实现反射的核心类

  • Class类只能由系统建立对象
  • 一个加载的类在内存(JVM)中只有一个Class对象
  • 一个类被加载后,类的整个结构都会被封装在Class对象中
  • 每个类的实例都会记得自己是由哪个Class实例所生成
  • Class类是反射的根源,针对任何你想动态加载,运行的类,唯有获得相应的Class对象

4. 获得Class类的五种方式

// 1. 调用Class类的静态方法
Class<?> c1 = Class.forName("com.entity.User");

// 2. 通过类的实例对象调用该实例的getClass方法
User user = new User();
Class<? extends User> c2 = user.getClass();

// 3. 已知具体类,通过类的class属性获取,该方法最安全可靠,性能最高
Class<User> c3 = User.class;

// 4. 通过内置类型的包装类的TYPE属性获得Class实例
Class<Integer> c4 = Integer.TYPE;

// 5. 通过当前子类的Class对象获得父类的Class对象
Class<?> c5 = c1.getSuperclass();

// 因为一个类只有一个Class对象,所有它们的hashCode相同
System.out.println(c1.hashCode() + " |" +
        c2.hashCode() + " | " +
        c3.hashCode());

5. 哪些类型可以有Class对象?

// 类
    Class c1 = Object.class;
    // 接口
    Class c2 = Comparable.class;
    // 一维数组
    Class c3 = String[].class;
    // 二维数组
    Class c4 = int[][].class;
    // 注解
    Class c5 = Override.class;
    // 枚举
    Class c6 = ElementType.class;
    // 基本数据类型
    Class c7 = Integer.class;
    // void
    Class c8 = void.class;
    // Class
    Class c9 = Class.class;

6. 类加载器

类加载器:完成类的加载

类加载器的作用:将class字节码文件内容加载到内存中,并将这些数据装换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口

JVM三种预定义类型类加载器,当JVM启动的时候,Java开始使用如下三种类型的类加载器:

  • 根/启动(Bootstrap)类加载器:根类加载器是用本地代码实现的类加载器,它负责将JAVA_HOME/lib下面的核心类库或-Xbootclasspath选项指定的jar包等虚拟机识别的类库加载到内存中。由于根类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到根类加载器的引用。
  • 扩展(Extension)类加载器:扩展类加载器是由Sun的ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现的,它负责将JAVA_HOME /lib/ext或者由系统变量-Djava.ext.dir指定位置中的类库加载到内存中。开发者可以直接使用标准扩展类加载器。
  • 系统(System)类加载器:系统类加载器是由 Sun 的 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的,它负责将用户类路径(java -classpathDjava.class.path变量所指的目录,即当前类所在路径及其引用的第三方类库的路径,如第四节中的问题6所述)下的类库加载到内存中。开发者可以直接使用系统类加载器。

类加载三种机制

  • 全盘负责机制:就是当一个类加载器负责加载某个Class时,该Class所依赖和引用其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入。
  • 双亲委派机制:所谓的双亲委派,则是先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类。通俗的讲,就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父加载器,依次递归,如果父加载器可以完成类加载任务,就成功返回;只有父加载器无法完成此加载任务时,才自己去加载。
  • 缓存机制机制:缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区中搜寻该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓冲区中。这就是为很么修改了Class后,必须重新启动JVM,程序所做的修改才会生效的原因。

获取三种预定义类型类加载器:

//获取系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2

//获取系统类加载器的父类加载器-->扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);//sun.misc.Launcher$AppClassLoader@18b4aac2

//获取扩展类加载器的父类加载器-->根类加载器
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);//null
//测试当前类是由哪个类加载器加载的
ClassLoader classLoader = Class.forName("com.cheng.annotation.Test01").getClassLoader();
System.out.println(classLoader);
//sun.misc.Launcher$AppClassLoader@18b4aac2 统类加载器

//测试JDK内置类是由哪个类加载器加载的
ClassLoader classLoader1 = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader1);//null 根类加载器

//获得系统类加载器可以加载的路径
String property = System.getProperty("java.class.path");
System.out.println(property);

7. 通过反射动态的创建对象

通过构造器创建对象

// 通过构造器创建对象
Class<?> userClass = Class.forName("com.entity.User");
Constructor<?> userConstructor = userClass.getDeclaredConstructor(String.class, String.class);
User u1 = (User) userConstructor.newInstance("root", "root");

System.out.println(u1);  // User{username='root', password='root'}

调用指定方法

Class c1 = Class.forName("com.entity.User");
User user = (User) c1.newInstance();
Method setName = c1.getDeclaredMethod("setUsername", String.class);
//invoke激活
setName.invoke(user, "root");
System.out.println(user);  // User{username='root', password='null'}

8. 通过反射操作注解

import java.lang.annotation.*;
import java.lang.reflect.Field;

//练习反射操作注解
public class Test {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class c1 = Class.forName("com.cheng.reflection.student2");

        //1.通过反射获得注解
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }

        //2.获得注解的value值
        Cheng cheng = (cheng) c1.getAnnotation(cheng.class);
        System.out.println(cheng.value());

        //3.获得类指定字段的注解
        Field f = c1.getDeclaredField("id");
        Fieldhaha annotation = f.getAnnotation(Fieldcheng.class);
        System.out.println(annotation.columnName());
        System.out.println(annotation.length());
        System.out.println(annotation.type());
    }
}
class Student(){
    public student2() {
    }
    public student2(int id, int age, String name) {
        this.id = id;
        this.age = age;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "student2{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值