注解和反射

注解和反射

什么是注解

  • Annotation是从JDK5.0开始引入的新技术;
  • Annotation的作用:
    • 不是程序本身,但可以对程序做出解释;
    • 可以被其他程序读取(比如:编译器读取)。
  • Annotation的格式:
    • @+注解名称,也可以添加一些参数,例如:@SuppressWarnings("all")、@Override
  • Annotation可以用在哪里:
    • 可以附加在package、method、class、Filed等等上,相当于给它们增加了额外的辅助信息,可以通过反射机制编程实现对这些元数据的访问。

内置注解

  • @Override:只作用在方法生,表示重写父类的方法;
  • @Deprecated:可以做用在类、方法、属性上,表示不鼓励程序员使用,通常是它是危险的或者有更改好的选择;
  • @SuppressWarnings:用来抑制编译时的警告信息,这个注解强制需要添加参数;@SuppressWarnings("all")、@SuppressWarning("unchecked")、@SuppressWarnings(value={"unchecked","deprecation"})等等。

元注解(Meta-Annotation)

元注解的作用是用来负责注释其他注解的注解。java中定义了四个元注解类型(@Target、@Retention、@Documented、@Inherited)用来对其他注解类型进行说明。

  • @Target:用于描述注解可以使用的范围,即注解可以用在什么地方(类、方法、字段等等);
  • @Retention:表示在什么级别保存注解信息,用于描述注解的声明周期(runtime(运行时期)>class(编译阶段)>source(代码阶段)),在实际开发中几乎用的都是runtime;
  • @Documented:表示注解包含在javadoc中;
  • @Inherited:子类可以继承父类的注解。
java 复制代码
public class Demo01 extends Object{
    @Override
    public String toString() {
        return super.toString();
    }
}
//@Target表示注解可以做用在什么地方
@Target(value = {ElementType.METHOD,ElementType.TYPE})
//@Retention 表示注解在哪些地方有效
//runtime(运行时)>class(编译成class)>source(代码阶段)
@Retention(RetentionPolicy.RUNTIME)
@Documented //表示生成在javadoc中
@Inherited //表示子类可以继承父类的注解
@interface MyAnnotation{

}

自定义注解

  • 自定义注解的格式:public @interface 注解名{定义内容}
  • 定义内容格式为:返回值类型+参数名();
  • 方法的名称就是参数的名称;
  • 返回类型就是参数的类型,返回值类型的范围:基本数据类型、class、String、enum;
  • 可以通过default声明参数的默认值;
  • 如果只有一个参数时,一般定义参数名为value,这样在使用注解是可以直接写参数,不用写参数名;
  • 注解元素必须要有值,定义注解元素时,常用空字符串、0作为默认值;
  • 当注解内有多个参数时,使使用注解时可以不按照注解参数的顺序进行使用;
java 复制代码
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation02 {

    //定义方式: 类型+参数名();
    String name() default "";
    int age() default 0;
    int id() default -1;//默认 -1 表示不存在

    String[] books();
}

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation03 {

    String value();
}


public class Demo01{
    @MyAnnotation02(books = {"《三国》"},age=18)
    public void test01(){

    }

    //如果只有一个参数时,一般定义参数名为value,这样在使用注解是可以直接写参数,不用写参数名;
    @MyAnnotation03("hello")
    public void test02(){

    }
}

反射

  • java的反射机制
  • 理解Classs类并获取class实例
  • 类的加载与ClassLoader
  • 创建运行时类的对象
  • 获取运行时类的完整结构
  • 调用运行时类的指定结构

静态语言和动态语言

动态语言

  • 在运行时可以改变其结构的语言:是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构。
  • 主要动态语言:Object-C、C#、JavaScript、PHP、Python等。
html 复制代码
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
    function myFunction() {
        var num = "var a=1;var b= 2;alert(a+b)";
        eval(num);//可以让num变成一条可执行的语句,并弹窗显示a+b的结果
    }
</script>

<button onclick="myFunction()">点击我</button>

</body>
</html>

静态语言

  • 运行时结构不可变的语言。java、C、C++;

  • Java不是动态语言,但Java可以称之为"准动态语言"。即Java有一定的动态性,我们可以利用反射机制获得类似动态语言的特性。Java的动态性让编程的时候更加灵活!

    java 复制代码
    Class c = Class.forName("java.lang.String");

Java Reflection

  • Java Reflection允许程序在运行期间,借助Reflection API去的任何类的内部信息,并操作任意对象的属性和方法;

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

    正常方式:引入需要的"包类"名称 --> 通过new实例化 --> 取得实例化对象

    反射方式:实例化对象 --> getClass()方法 --> 得到完整的"包类"名称

  • 反射的效率和安全性会下降。

获取class类实例方式

java 复制代码
public class Demo03 {

    public static void main(String[] args) throws ClassNotFoundException {
        Student student = new Student();
        System.out.println(student.name);

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

        //方式二:通过forName获得
        Class c2 = Class.forName("com.zwb.po.Student");
        System.out.println(c2.hashCode());

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

        //方式四:基本类型的包装类都有一个Type属性
        Class c4 = Integer.TYPE;
        System.out.println(c4);

        //获得父类的类型
        Class c5 = c1.getSuperclass();
        System.out.println(c5);

    }
}
java 复制代码
public class Person {

    public String name;

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }
}
java 复制代码
public class Student extends Person{

    public Student() {
        this.name = "学生";
    }
}

哪些类可以有class对象

  • class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类。
  • interface:接口
  • \]:数组

  • annotation:注解@interface
  • primitive type:
  • 基本数据类型void
java 复制代码
public class Demo04 {

    public static void main(String[] args) {
        Class c1 = Object.class;//类
        Class c2 = Comparable.class;//接口
        Class c3 = String[].class;//一维数组
        Class c4 = int[][].class;//二维数组
        Class c5 = Integer.class;//基本数据类型
        Class c6 = void.class;//void
        Class c7 = Override.class;//注解
        Class c8 = ElementType.class;//枚举
        Class c9 = Class.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);

        int[] ary1 = new int[10];
        int[] ary2 = new int[100];
        //不同数组的class是同一个 ary1和ary2的类是同一个
        System.out.println(ary1.getClass().hashCode());
        System.out.println(ary2.getClass().hashCode());
    }
}

类的加载内存分析

类的加载与ClassLoader的理解
  • 加载:类加载器(classLoader)将字节码文件(.class文件、网络文件等)加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,生成一个可以代表这个类的java.lang.Class对象。
  • 链接:将java类的二进制代码合并到jvm中的过程;
    • 验证:确认加载的类信息是否符合jvm规范,是否安全;
    • 准备:为static变量初始化默认值和分配内存空间;final修饰的变量在编译时期就放在了常量池中;
    • 解析:将常量池中的符号引用(常量名)替换为直接引用(地址)的过程。
  • 初始化:
    • 执行类构造器()方法的过程。类构造器()合并类中所有变量的赋值动作和静态代码块的语句合并;
    • 初始化类时,发现对应的父类没有进行初始化,先初始化父类;
    • 虚拟机会确保在多线程环境下一个类的()方法会被正确的同步和加锁。
java 复制代码
public class Demo05 {

    static{
        System.out.println("Main类被初始化");
    }

    public static void main(String[] args) {
        A a = new A();
        System.out.println(a.num);
    }
}

class A{
    static{
        System.out.println("A的静态代码块被初始化");
        num = 1;
    }

    static int num = 100;

    public A(){
        System.out.println("A的无参构造器被初始化");
    }
}
/*
输出:
Main类被初始化
A的静态代码块被初始化
A的无参构造器被初始化
100
*/
什么时候类会发生初始化
  • 类的主动引用(一定会发生初始化)
    • 当虚拟机启动时,先初始化main方法所在的类;
    • new一个对象;
    • 调用静态成员(非final修饰的常量)和静态方法;
    • 使用java.lang.reflect包的方法对类进行反射调用;
    • 先初始化一个类,如过父类没有进行初始化,会先初始化父类;
  • 类的被动引用(不会发生类的初始化)
    • 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:子类访问父类的静态变量,子类不会被初始化;
    • 通过数组定义类引用,不会触发此类的初始化;
    • 引用常量不会触发此类的初始化(常量在编译阶段就被放入了常量池中)。
java 复制代码
public class Demo06 {

    static {
        System.out.println("Main被加载");
    }

    public static void main(String[] args) throws ClassNotFoundException {
        /**************类的主动引用*********************/
        //方式一:new方式会加载类
//        S s = new S();
        /*
        Main被加载
        父类被初始化
        子类被初始化
         */

        //方式二:通过反射
//        Class.forName("com.zwb.anntation.S");
        /*
        Main被加载
        父类被初始化
        子类被初始化
         */
        //方式三:调用静态成员
//        System.out.println(S.num2);
        /*
        Main被加载
        父类被初始化
        子类被初始化
        101
         */

        /**************类的被动引用*********************/
        //方式一:子类访问父类的静态变量
//        System.out.println(S.num1);
        /*
        Main被加载
        父类被初始化
        100
         */

        //方式二:数组定义引用
//        S[] ary = new S[6];//Main被加载

        //方式三:应用常量
        System.out.println(S.str);
        /*
        Main被加载
        这是一个常量
         */

    }

}

class F{
    static int num1 = 100;

    final static String str = "这是一个常量";

    static{
        System.out.println("父类被初始化");
    }
}

class S extends F{

    static{
        System.out.println("子类被初始化");
        num2 = 102;
    }

    static int num2 = 101;
}

类加载器

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

  • 类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。

  • 类加载器作用是用来把类(class)装载进内存的。JVM规范定义了如下类型的类的加载器。

    • 引导类加载器:用C++编写的,是JVM自带的类加载器,负责Java平台核心库(rt.jar),用来装载核心类库。该加载器无法直接获取。

    • 扩展类加载器:负责jre/lib/ext目录下的jar包或-java.ext.dirs指定目录下的jar包装入工作库.

    • 系统类加载器:负责java -classpath或-Djava.class.path所指的目录下的类与jar包装入工作,是最常用的加载器。

  • 说明:为什么不能定义类似java.lang.String的类,因为java类加载时会一层一层往上找,确保不会重复加载导致出错。(**双亲委派机制是 Java 虚拟机中类加载的核心规则,要求类加载请求优先交给上级处理,确保核心类库安全且不被重复加载。**‌ 这一机制通过层级化的类加载器结构,保证了 Java 程序运行时的稳定性和安全性 。‌‌)

java 复制代码
public class Demo07 {

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

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

        //获取扩展类加载器的父类加载器--根加载器
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1);

        //获取当前测试类的类加载器
        ClassLoader classLoader = Class.forName("com.zwb.anntation.Demo07").getClassLoader();
        System.out.println(classLoader);

        //获取JDK内置类的类加载器
        ClassLoader classLoader1 = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(classLoader1);

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

    }
}

获取类运行时的完整结构

通过反射获取运行时类的完整结构Field, Method, Constructor、 Superclass, Interface, Annotation

实现的全部接口

所继承的父类

全部的构造器

全部的方法

全部的Field

注解

...

java 复制代码
public class Person {

    public String name;
    private int age;

    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;
    }

    //私有方法
    private void  privateMethod(){}

    //公有方法
    public void publicMethod(){}

    public Person(){}

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    private Person(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }
}
java 复制代码
public class Demo08 {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        Class c = Class.forName("com.zwb.po.Person");

        System.out.println(c.getName());//获取类的名字 包名+类名
        System.out.println(c.getSimpleName());//获取类的简单名字  类名

        System.out.println("=====================================");

        //获取类的属性
        Field[] fields = c.getFields();//获取类的所有public属性
        for (Field field : fields) {
            System.out.println("类的所有public属性:"+field);
        }

        Field[] declaredFields = c.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println("类的所有属性:"+declaredField);
        }

        Field name = c.getDeclaredField("name");
        System.out.println("获取类的指定属性:"+name);
        System.out.println("=====================================");

        //获取类的方法
        Method[] methods = c.getMethods();//获取类的public方法,包括父类的方法
        for (Method method : methods) {
            System.out.println("获取类的public方法:"+method);
        }

        Method[] declaredMethods = c.getDeclaredMethods();//获取类的所有方法,仅限本类中声明的方法
        for (Method declaredMethod : declaredMethods) {
            System.out.println("获取类的所有方法:"+declaredMethod);
        }

        Method privateMethod = c.getDeclaredMethod("privateMethod", null);
        System.out.println("获取类中的指定方法(无参):"+privateMethod);

        Method privateMethod2 = c.getDeclaredMethod("setName", String.class);
        System.out.println("获取类中的指定方法(有参):"+privateMethod2);
        System.out.println("=====================================");

        //获取构造器
        Constructor[] constructors = c.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println("获取类中public的构造器:"+constructor);
        }

        Constructor[] declaredConstructors = c.getDeclaredConstructors();
        for (Constructor declaredConstructor : declaredConstructors) {
            System.out.println("获取类中所有的构造器:"+declaredConstructor);
        }

        Constructor declaredConstructor = c.getDeclaredConstructor(String.class, int.class);
        System.out.println("获取指定构造器:"+declaredConstructor);
    }
}

动态创建对象执行方法

  • 通过Class对象创建类的对象

    • 方式一: 通过无参构造器创建:Class对象.newInstance()

    • **方式二:**通过有参构造器创建:Class对象.getDeclaredConstructor(params...).newInstance(params);

  • 调用指定的方法

    通过Class类的getMethod(String name,Class...parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型;

    之后使用Object invoke(Object obj, Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。

  • 设置属性

    通过Class类的getDeclaredField(属性名)来获取属性;之后使用Object invoke(Object obj, 属性名称)来设置值。

  • **说明:**要调用的属性和方法是private时,需要setAccessible(true);才能使用。

    • Method和Field、Constructor对象都有setAccessible()方法。setAccessible作用是启动和禁用访问安全检查的开关。

    • 参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。

    • 提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true。使得原本无法访问的私有成员也可以访问。

    • 参数值为false则指示反射的对象应该实施Java语言访问检查。

    • 滥用会破坏封装性、引发安全风险(如篡改敏感状态)

java 复制代码
public class User {

    private String name;
    private int age;

    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;
    }

    public User() {
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
    Class c = Class.forName("com.zwb.po.User");
    User u = (User)c.newInstance();//调用无参构造器创建User对象
    System.out.println(u);
    System.out.println("==================================");

    //通过有参构造器创建对象
    Constructor declaredConstructor = c.getDeclaredConstructor(String.class, int.class);
    User u1 = (User)declaredConstructor.newInstance("DaiV", 18);
    System.out.println(u1);
    System.out.println("==================================");

    //通过反射调用普通方法
    Method setName = c.getMethod("setName", String.class);
    setName.invoke(u1,"猪猪包");// 方法名.invoke(方法的对象,方法的值)
    System.out.println(u1.getName());
    System.out.println("==================================");

    //通过对象操作属性
    User u2 = (User)c.newInstance();
    Field age = c.getDeclaredField("age");
    //不能直接操作私有属性,通过设置这个属性,可以修改私有属性
    age.setAccessible(true);//关闭安全检测,可以操作私有属性
    age.set(u2,18);
    System.out.println(u2.getAge());

}

反射和正常调用的性能分析

java 复制代码
public class Demo10 {

    //正常方式调用方法
    public static void test01(){
        User user = new User();

        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
            user.getName();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("正常方式调用方法耗时:"+(endTime - startTime) + "ms");
    }

    //反射方式调用方法--开启安全检查
    public static void test02() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Class<?> c = Class.forName("com.zwb.po.User");
        User user = (User)c.newInstance();
        Method getName = c.getDeclaredMethod("getName");

        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
            getName.invoke(user);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("反射方式调用方法--开启安全检查耗时:"+(endTime - startTime) + "ms");
    }

    //反射方式调用方法--开启安全检查
    public static void test03() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Class<?> c = Class.forName("com.zwb.po.User");
        User user = (User)c.newInstance();
        Method getName = c.getDeclaredMethod("getName");
        getName.setAccessible(true);

        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
            getName.invoke(user);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("反射方式调用方法--开启安全检查耗时:"+(endTime - startTime) + "ms");
    }

    public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {
        test01();
        test02();
        test03();
    }

}

获取泛型信息

  • Java采用泛型擦除的机制来引入泛型,Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题,但是,一旦编译完成,所有和泛型有关的类型全部擦除。****(数组在运行时没有泛型擦除)
  • 为了通过反射操作这些类型,Java新增了ParameterizedType , GenericArrayType ,TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型。
  • ParameterizedType:表示一种参数化类型,比如Collection
  • GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型TypeVariable:是各种类型变量的公共父接口
  • WVildcardType:代表一种通配符类型表达式
java 复制代码
public class Demo11 {

    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 c = Demo11.class;

        //获取方法参数
        Method test01 = c.getDeclaredMethod("test01", Map.class, List.class);
        Type[] genericParameterTypes = test01.getGenericParameterTypes();//获取当前方法的参数类型
        for (Type genericParameterType : genericParameterTypes) {
            System.out.println("方法参数:"+genericParameterType);
            if(genericParameterType instanceof ParameterizedType){//判断当前方法的参数是否是参数化类型
                Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println("方法的参数话类型:"+actualTypeArgument);
                }
            }
        }

        System.out.println("==========================================");
        //获取方法返回值
        Method test02 = c.getDeclaredMethod("test02", null);
        Type genericReturnType = test02.getGenericReturnType();
        System.out.println("方法返回值的参数类型:"+genericReturnType);
        if(genericReturnType instanceof  ParameterizedType){
            Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println("方法返回值的参数化类型:"+actualTypeArgument);
            }
        }


    }
}

获取注解信息

java 复制代码
getAnnotations()
getAnnotation()

**示例:**通过注解,获取实体类和数据库表之间的映射关系。

java 复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableAnntation {
    String value();
}
java 复制代码
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldAnntation {
    String column();//列名
    String type();//类型
    int length();//长度
}
java 复制代码
@TableAnntation("db_people")
public class People {
    @FieldAnntation(column = "db_id",type = "varchar",length = 32)
    private String id;
    @FieldAnntation(column = "db_name",type = "varchar",length = 256)
    private String name;
    @FieldAnntation(column = "db_age",type = "number",length = 3)
    private int age;

    public String getId() {
        return id;
    }

    public void setId(String 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;
    }
}
java 复制代码
public class Demo12 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class c = Class.forName("com.zwb.po.People");

        //获取类上的所有注解
        Annotation[] annotations = c.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println("类上的注解:"+annotation);
        }

        System.out.println("============================");
        //获取字段上的注解
        Field id = c.getDeclaredField("id");
        FieldAnntation annotation = id.getAnnotation(FieldAnntation.class);
        System.out.println(annotation.column());
        System.out.println(annotation.type());
        System.out.println(annotation.length());

    }
}

return age;

}

复制代码
public void setAge(int age) {
    this.age = age;
}

}

复制代码
```java
public class Demo12 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class c = Class.forName("com.zwb.po.People");

        //获取类上的所有注解
        Annotation[] annotations = c.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println("类上的注解:"+annotation);
        }

        System.out.println("============================");
        //获取字段上的注解
        Field id = c.getDeclaredField("id");
        FieldAnntation annotation = id.getAnnotation(FieldAnntation.class);
        System.out.println(annotation.column());
        System.out.println(annotation.type());
        System.out.println(annotation.length());

    }
}
相关推荐
涤生大数据10 小时前
大数据面试高频题:row_number() 数据倾斜到底怎么解决?
java·大数据·面试
摇滚侠10 小时前
HashMap 源码解析 底层原理 面试如何回答
java·面试·职场和发展
এ慕ོ冬℘゜10 小时前
JS 前端基础高频面试题
开发语言·前端·javascript
凯瑟琳.奥古斯特10 小时前
常见加密算法及应用
java·开发语言·网络·网络协议·职场和发展
devilnumber10 小时前
java的lambda妙用举例
java·lambda
Dxy123931021611 小时前
JS列表获取指定范围值的 N 种方法
开发语言·javascript·ecmascript
froginwe1111 小时前
Memcached CAS 命令详解
开发语言
invicinble11 小时前
springboot提供的机制大全
java·spring boot·后端
Han_han91911 小时前
题⽬ 4:订单商品统计:
java