注解与反射

一 注解机制

1.1 内置注解

@Override:此注解只适用于修饰方法,表示一个方法声明打算重写父类中的另一个方法声明

@Deprecated:此注解可以修饰方法、属性、类,表示不鼓励程序员使用这样的元素,通常因为它很危险或存在更好的选择

@SuppressWarning:用来镇压编译时的警告信息

  • @SuppressWarning("all")
  • @SuppressWarning("unchencked")
  • @SuppressWarning(values={"unchecked", "deprecation"})
@SuppressWarnings("all")
public void test02(){
    // 定义了但没有使用,默认是会出现警告的,但加了注解之后,警告消失(被镇压)
	LinkedList list = new LinkedList();
}

1.2 元注解

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

  • @Target:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
  • @Retention:表示需要在什么级别保存该注解,用于描述注解的生命周期
    • SOURCE < CLASS < RUNTIME(一般使用这个)
  • @Document:说明该注解将被包含在javadoc中
  • @Inherited:说明子类可以继承父类中的注解
package zr.annotation;

import java.lang.annotation.*;

/**
 * 测试元注解
 */
public class Test01 extends Object{
    @MyAnnotation
    public void test(){

    }
}

// 定义一个注解
@Inherited
// Documented 表示是否将我们的注解生成在Javadoc中
@Documented
// Recumented 表示注解的生命周期
@Retention(value = RetentionPolicy.RUNTIME)
// Target 表示注解的使用范围
@Target(value = {ElementType.METHOD, ElementType.TYPE})
@interface MyAnnotation{
}

1.3 自定义注解

使用@interface自动以注解时,自动继承了java.lang.annotation.Annotation接口

  • @interface用来声明一个注解,格式:public @interface 注解名
  • 其中的每一个方法实际上是声明了一个配置参数
  • 方法的名称就是参数的名称
  • 返回值类型就是参数的类型(返回值只能是基本类型、Class、String、enum)
  • 可以通过default来声明参数的默认值
  • 如果只有一个参数成员,一般参数名为value,在使用的时候可以把value省略掉
  • 注解元素必须要有值,我们定义注解元素时,经常使用空字符串、0作为默认值

二 反射机制

2.1 反射概念

Refelection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API获取任何类的内部信息,并能直接操作任意对象的内部属性及方法。

Class clazz = Class.forName("java.lang.String");

类加载完之后,在堆内存中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息,我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以我们形象地称之为:反射。

创建对象的方式:

  • 正常方式:引入需要的“包类”名称—>通过new关键字实例化—>取得实例化对象
  • 反射方式:实例化对象—>getClass()方法—>得到完整的“包类”名称

2.2 反射相关的API

  • java.lang.Class:代表一个类
  • java.lang.reflect.Method:代表类的方法
  • java.lang.reflect.Field:代表类的成员变量
  • java.lang.reflect.Constructor:代表类的构造器
  • ......

2.3 获取Class类的实例

  • 若已知具体的类,通过类的class属性获取,该方法最为可靠,程序性能最高

    Class clazz = Person.class;

  • 已知某个类的实例,调用该实例的getClass()方法获取Class对象

    Class clazz = person.getClass();

  • 已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取

    Class clazz = Class.forName("zr.reflection.User");

  • 内置基本数据类型可以直接用类名.Type(比如Integer.Type返回的就是int)

  • 利用ClassLoader

package zr.reflection;
public class Test01 {
    public static void main(String[] args) throws ClassNotFoundException {
        Person person = new Student();
        System.out.println("这个人是: " + person.name);

        // 方式一:通过对象获得
        Class c1 = person.getClass();
        System.out.println(c1.hashCode());

        // 方式二:forName获得
        Class c2 = Class.forName("zr.reflection.Student");
        System.out.println(c2.hashCode());

        // 方式三:通过类名.class获得
        Class c3 = Student.class;
        System.out.println(c3.hashCode());

        // 获取父类类型
        Class c4 = c1.getSuperclass();
        System.out.println(c4);
    }
}
class Person{
    public String name;
    public Person(){}
    public Person(String name) {
        this.name = name;
    }
}
class Student extends Person{
    public Student(){
        this.name = "学生";
    }
}
package zr.reflection;
import java.lang.annotation.ElementType;
// 所有的类型的Class对象
public class Test02 {
    public static void main(String[] args) {
        // 类
        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;

        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
        System.out.println(c4);
        System.out.println(c5);
        System.out.println(c6);
        System.out.println(c7);
        System.out.println(c8);
        System.out.println(c9);

        // 只要元素类型与维度一致,就是同一个Class对象
        int[] a = new int[10];
        int[] b = new int[100];
        System.out.println(a.getClass().hashCode());
        System.out.println(b.getClass().hashCode());
    }
}
// 输出结果:
class java.lang.Object
interface java.lang.Comparable
class [Ljava.lang.String;
class [[I
interface java.lang.Override
class java.lang.annotation.ElementType
class java.lang.Integer
void
class java.lang.Class
356573597
356573597

2.4 什么时候会发生类初始化

类的加载过程:加载—>链接—>初始化

2.4.1 类的主动引用

  • 当虚拟机启动,先初始化main方法中的所有声明的类
  • new一个类的对象
  • 调用类的静态成员(除了final static静态常量)和静态方法
  • 使用java.lang.reflection包的方法对类进行反射调用
  • 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类

2.4.2 类的被动调用(不会发生类的初始化)

  • 当访问一个静态域,只有真正声明这个域的类才会被初始化。如:子类引用父类的静态变量,不会导致子类的初始化
  • 通过数组定义类引用
  • 引用常量(因为常量在链接阶段就已经存入调用类的常量池中了)

2.5 类加载器

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

双亲委派模型:从下到上检测类是否被加载,从上到下尝试加载类

package zr.reflection;

public class Test03 {
    public static void main(String[] args) throws ClassNotFoundException {
        // 获取系统类的加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);

        // 获取系统类加载器的父类加载器——>扩展类加载器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent);

        // 获取扩展类加载器的父类——>根加载器(C/C++写的,所以一般获得null)
        ClassLoader boot = parent.getParent();
        System.out.println(boot);

        // 测试自定义类是由谁加载的(由系统类加载器加载)
        ClassLoader loader = Class.forName("zr.reflection.Test01").getClassLoader();
        System.out.println(loader);

        // 测试JDK内部类是由谁加载的(由boot根加载器加载)
        loader = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(loader);

        // 查看系统类加载器可以加载的路径
        System.out.println(System.getProperty("java.class.path"));
        /*
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\charsets.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\deploy.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\access-bridge-64.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\cldrdata.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\dnsns.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\jaccess.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\jfxrt.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\localedata.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\nashorn.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\sunec.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\sunjce_provider.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\sunmscapi.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\sunpkcs11.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\zipfs.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\javaws.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\jce.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\jfr.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\jfxswt.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\jsse.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\management-agent.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\plugin.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\resources.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\rt.jar;
        E:\idea\out\production\idea;
        E:\idea\lib\mysql-connector-java-8.0.21.jar;
        D:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2020.2.2\lib\idea_rt.jar
        */
    }
}

// 输出结果
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@1540e19d
null
sun.misc.Launcher$AppClassLoader@18b4aac2
null

2.6 获得类的运行时结构

package zr.reflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

// 获得类的信息
public class Test04 {
    public static void main(String[] args) throws ClassNotFoundException {
        Class clazz = Class.forName("zr.reflection.Student");

        // 获得类的名字
        System.out.println(clazz.getName());       // 获得包名 + 类名
        System.out.println(clazz.getSimpleName()); // 获得类名

        // 获得类的属性
        Field[] fields = clazz.getFields();  // 只能找到public属性
        fields = clazz.getDeclaredFields();  // 能获得所有声明的属性
        for(Field field : fields)
            System.out.println(field);

        // 获得类的方法
        Method[] methods = clazz.getDeclaredMethods();  // 获取本类的所有方法(私有的也行)
        for(Method method : methods)
            System.out.println();

        // 获得类的构造器
        Constructor[] constructors = clazz.getConstructors();
        for(Constructor constructor : constructors)
            System.out.println(constructor);
    }
}

2.7 获得Class对象后的操作

package zr.reflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

// 获得类的信息
public class Test04 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        // 获得Class对象
        Class clazz = Class.forName("zr.reflection.User");
        // 构造一个对象
        User user = (User)clazz.newInstance();   // 本质上是调用了类的无参构造器
        System.out.println(user);

        // 通过构造器创建对象,这样就可以间接地去使用User的有参构造器
        Constructor constructor =  clazz.getDeclaredConstructor(String.class, int.class, int.class);
        User xiaozeng = (User)constructor.newInstance("曾润", 24, 99);
        System.out.println(xiaozeng);

        // 通过反射调用普通方法
        Method setName = clazz.getDeclaredMethod("setName", String.class);
        setName.invoke(xiaozeng, "范春春");
        System.out.println(xiaozeng);

        // 通过反射操作属性
        Field name = clazz.getDeclaredField("name");
        // 首先需要设置权限,防止private修饰的变量不能修改
        name.setAccessible(true);
        name.set(xiaozeng, "曾益达");  // 相当于赋值语句: name = "曾益达";
        System.out.println(xiaozeng);
    }
}
// 输出
User{name='null', age=0, grade=0}
User{name='曾润', age=24, grade=99}
User{name='范春春', age=24, grade=99}
User{name='曾益达', age=24, grade=99}

2.8 通过反射操作泛型

package zr.reflection;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

public class Test05 {
    public static void test01(Map<String, User> map, List<User> list){
        System.out.println("test01");
    }
    public static Map<String, User> test02(){
        System.out.println("test02");
        return null;
    }
    public static void main(String[] args) throws NoSuchMethodException {
        Class clazz = Test05.class;
        Method method = clazz.getDeclaredMethod("test01", Map.class, List.class);
        // 获得泛型的参数信息
        Type[] genericParameterTypes = method.getGenericParameterTypes();
        for(Type genericParameterType : genericParameterTypes){
            System.out.println("# " + genericParameterType);
            Type[] actualyTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
            for(Type actualyTypeArgument : actualyTypeArguments)
                System.out.println(actualyTypeArgument);
        }
    }
}
// 输出结果
# java.util.Map<java.lang.String, zr.reflection.User>
class java.lang.String
class zr.reflection.User
# java.util.List<zr.reflection.User>
class zr.reflection.User

2.9 通过反射操作注解

package zr.reflection;

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

public class Test06{
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class clazz = Class.forName("zr.reflection.Student1");
        // 通过反射获取类的注解
        Annotation[] annotations = clazz.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
        // 获得注解的value值
        Tablerun tablerun = (Tablerun) clazz.getAnnotation(Tablerun.class);
        String value = tablerun.value();
        System.out.println(value);

        // 获得类指定的注解
        Field name = clazz.getDeclaredField("name");
        Fieldrun annotation = name.getAnnotation(Fieldrun.class);
        System.out.println(annotation.columnName());
        System.out.println(annotation.type());
        System.out.println(annotation.length());
    }
}

@Tablerun("db_student")
class Student1{
    @Fieldrun(columnName = "db_id", type = "int", length = 10)
    private int id;
    @Fieldrun(columnName = "db_age", type = "int", length = 10)
    private int age;
    @Fieldrun(columnName = "db_name", type = "varchar", length = 3)
    private String name;
    public Student1(){}
    public Student1(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 "Student1{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

// 类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Tablerun{
    String value();
}

// 属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Fieldrun{
    String columnName();
    String type();
    int length();
}

// 输出结果
@zr.reflection.Tablerun(value=db_student)
db_student
db_name
varchar
3
posted @   程序员曾奈斯  阅读(212)  评论(1编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· 字符编码:从基础到乱码解决
· Open-Sora 2.0 重磅开源!
点击右上角即可分享
微信分享提示

目录导航