Java的第十六篇文章——枚举、反射和注解(后期再学一遍)

news2024/9/30 15:25:16

目录

1. 枚举

1.1 学习目标

1.2 内容讲解

1.2.1 枚举的概述

1.2.2 为什么要使用枚举

1.2.3 作用

1.2.4 格式

2. 反射

2.1 学习目标

2.2 内容讲解

2.2.1 类加载(了解)

2.2.2 类的加载过程

2.2.3 类的初始化

2.2.4 类的加载器

2.2.5 Java系统类加载器的双亲委托模式

2.2.6 java.lang.Class类

2.2.7 获取Class对象的四种方式

2.2.8 反射的概念

2.2.9 反射的应用场景

2.3 Type接口的介绍(了解)

2.3.1 Type的分类

2.4 动态创建和操作任何类型的数组

3. 注解

3.1 学习目标

3.2 内容讲解

3.2.1 什么是注解

3.2.2 注解的作用

3.2.3 JDK提供的三个基本注解

3.2.4 自定义注解的语法

3.2.5 常用的元注解


1. 枚举

1.1 学习目标

  • 了解枚举的概念

  • 掌握枚举的格式

  • 掌握枚举的应用场景

  • 掌握枚举的使用

1.2 内容讲解

1.2.1 枚举的概述

枚举是 Java 中一种特殊的类,它可以定义固定数量的枚举实例,例如: 性别、交通信号灯、季节等等。

1.2.2 为什么要使用枚举

假设我们要定义一个人类,人类中包含姓名和性别。通常会将性别定义成字符串类型,效果如下:

public class Person {
    private String name;
    private String sex;

    public Person() {
    }

    public Person(String name, String sex) {
        this.name = name;
        this.sex = sex;
    }
	
    // 省略get/set/toString方法
}
public class Demo01 {
    public static void main(String[] args) {
        Person p1 = new Person("张三", "男");
        Person p2 = new Person("张三", "abc"); // 因为性别是字符串,所以我们可以传入任意字符串
    }
}

不使用枚举存在的问题:可以给性别传入任意的字符串,导致性别是非法的数据,不安全。

1.2.3 作用

一个方法接收的参数是固定范围之内的时候,那么即可使用枚举类型

1.2.4 格式

enum 枚举名 {
    第一行都是罗列枚举实例,这些枚举实例直接写大写名字即可。
}

入门案例:

枚举的本质是一个类,所以枚举中还可以有成员变量,成员方法等。

(1)定义枚举:MALE表示男,FEMALE表示女

public enum Gender {
    /**
     * 男
     */
    MALE("男"),
    /**
     * 女
     */
    FEMALE("女");
    private String tag;

    Gender(String tag) {
        this.tag = tag;
    }

    public String getTag() {
        return tag;
    }
}

(2)Perosn中的性别有String类型改为Sex枚举类型

public class Person {
    private String name;
    private Gender gender;

    public Person() {
    }

    public Person(String name, Gender gender) {
        this.name = name;
        this.gender = gender;
    }

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

    public String getName() {
        return name;
    }

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

    public Gender getGender() {
        return gender;
    }

    public void setGender(Gender gender) {
        this.gender = gender;
    }
}
public class TestEnum {
    public static void main(String[] args) {
        Person person = new Person("张三",Gender.MALE);

        System.out.println(person);

        //我们怎么才能获取到用户的性别的标记
        System.out.println(Gender.MALE.getTag());

    }
}

2. 反射

2.1 学习目标

  • 了解类的加载过程

  • 理解类初始化过程

  • 了解类加载器

  • 掌握获取Class对象的四种方式

  • 能够运用反射获取类型的详细信息

  • 能够运用反射动态创建对象

  • 能够运用反射动态获取成员变量并使用

  • 能够运用反射动态获取成员方法并使用

  • 能够运用反射获取泛型父类的类型参数

2.2 内容讲解

2.2.1 类加载(了解)

类在内存中的生命周期:加载-->使用-->卸载

2.2.2 类的加载过程

当程序主动使用某个类时,如果该类还未被加载到内存中,系统会通过加载、连接、初始化三个步骤来对该类进行初始化,如果没有意外,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载。

类的加载又分为三个阶段:

(1)加载:load

就是指将类型的class字节码数据读入内存

(2)连接:link

①验证:校验合法性等

②准备:准备对应的内存(方法区),创建Class对象,为类变量赋默认值,为静态常量赋初始值。

③解析:把字节码中的符号引用替换为对应的直接地址引用

(3)初始化:initialize(类初始化)即执行<clinit>类初始化方法,会给类的静态变量赋初始值

2.2.3 类的初始化

哪些操作会导致类的初始化?

(1)运行主方法所在的类,要先完成类初始化,再执行main方法

(2)第一次使用某个类型就是在new它的对象,此时这个类没有初始化的话,先完成类初始化再做实例初始化

(3)调用某个类的静态成员(类变量和类方法),此时这个类没有初始化的话,先完成类初始化

(4)子类初始化时,发现它的父类还没有初始化的话,那么先初始化父类

(5)通过反射操作某个类时,如果这个类没有初始化,也会导致该类先初始化

class Father{
	static{
		System.out.println("main方法所在的类的父类(1)");//初始化子类时,会初始化父类
	}
}

public class TestClinit1 extends Father{
	static{
		System.out.println("main方法所在的类(2)");//主方法所在的类会初始化
	}
	
	public static void main(String[] args) throws ClassNotFoundException {
		new A();//第一次使用A就是创建它的对象,会初始化A类
		
		B.test();//直接使用B类的静态成员会初始化B类
		
		Class clazz = Class.forName("com.atguigu.test02.C");//通过反射操作C类,会初始化C类
	}
}
class A{
	static{
		System.out.println("A类初始化");
	}
}
class B{
	static{
		System.out.println("B类初始化");
	}
	public static void test(){
		System.out.println("B类的静态方法");
	}
}
class C{
	static{
		System.out.println("C类初始化");
	}
}

哪些使用类的操作,但是不会导致类的初始化?

(1)使用某个类的静态的常量(static final)

(2)通过子类调用父类的静态变量,静态方法,只会导致父类初始化,不会导致子类初始化,即只有声明静态成员的类才会初始化

(3)用某个类型声明数组并创建数组对象时,不会导致这个类初始化

public class TestClinit2 {
	public static void main(String[] args) {
		System.out.println(D.NUM);
		
		System.out.println(F.num);
		F.test();
		
		G[] arr = new G[5];
	}
}
class D{
	public static final int NUM = 10;
	static{
		System.out.println("D类的初始化");
	}
}
class E{
	static int num = 10;
	static{
		System.out.println("E父类的初始化");
	}
	public static void test(){
		System.out.println("父类的静态方法");
	}
}
class F extends E{
	static{
		System.out.println("F子类的初始化");
	}
}

class G{
	static{
		System.out.println("G类的初始化");
	}
}

2.2.4 类的加载器

很多开发人员都遇到过java.lang.ClassNotFoundException或java.lang.NoClassDefError,想要更好的解决这类问题,或者在一些特殊的应用场景,比如需要支持类的动态加载或需要对编译后的字节码文件进行加密解密操作,那么需要你自定义类加载器,因此了解类加载器及其类加载机制也就成了每一个Java开发人员的必备技能之一。分为以下几种:

(1)引导类加载器(Bootstrap Classloader)又称为根类加载器

它负责加载jre/lib中的核心库
它本身不是Java代码实现的,也不是ClassLoader的子类,获取它的对象时往往返回null

(2)扩展类加载器(Extension ClassLoader)

它负责加载jre/lib/ext扩展库
它是ClassLoader的子类

(3)应用程序类加载器(Application Classloader)

它负责加载项目的classpath路径下的类

它是ClassLoader的子类

(4)自定义类加载器

当你的程序需要加载“特定”目录下的类,可以自定义类加载器;
当你的程序的字节码文件需要加密时,那么往往会提供一个自定义类加载器对其进行解码
后面会见到的自定义类加载器:tomcat中

2.2.5 Java系统类加载器的双亲委托模式

简单描述:

下一级的类加载器,如果接到任务时,会先搜索是否加载过,如果没有,会先把任务往上传,如果都没有加载过,一直到根加载器,如果根加载器在它负责的路径下没有找到,会往回传,如果一路回传到最后一级都没有找到,那么会报ClassNotFoundException或NoClassDefError,如果在某一级找到了,就直接返回Class对象。

应用程序类加载器 把扩展类加载器视为父加载器,

扩展类加载器 把 引导类加载器视为父加载器。

不是继承关系,是组合的方式实现的。

2.2.6 java.lang.Class类

Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法对于任意一个对象,都能够调用它的任意一个方法和属性这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。

要想解剖一个类,必须先要获取到该类的Class对象。而剖析一个类或用反射解决具体的问题就是使用相关API(1)java.lang.Class(2)java.lang.reflect.*。所以,Class对象是反射的根源。

哪些类型可以获取Class对象呢?

所有的Java类型,如:

//(1)基本数据类型和void
例如:int.class
	 void.class
//(2)类和接口
例如:String.class
	Comparable.class
//(3)枚举
例如:ElementType.class
//(4)注解
例如:Override.class
//(5)数组
例如:int[].class

2.2.7 获取Class对象的四种方式

(1)类型名.class

要求编译期间已知类型

(2)对象.getClass()

获取对象的运行时类型

(3)Class.forName(类型全名称),通常需要配置文件配置配合使用

可以获取编译期间未知的类型

(4)ClassLoader的类加载器对象.loadClass(类型全名称)

可以用系统类加载对象或自定义加载器对象加载指定路径下的类型

public class TestReflect {
    public static void main(String[] args) throws Exception{
        //第一种方式获取Class:类型名.class
        Class personClass = Person.class;
        /*System.out.println(personClass);*/
        //第二种方式获取Class:对象.getClass()
        Person person = new Person();
        Class<? extends Person> aClass = person.getClass();
        /*System.out.println(aClass);
        System.out.println(aClass==personClass);*/

        //第三种方式获取Class:Class.forName("类的全限定名")
        Class<?> aClass1 = Class.forName("com.lxt.item2.Person");
        //1.使用反射获取包名
        Package aPackage = personClass.getPackage();
        System.out.println(aPackage);
        String name = aPackage.getName();
        System.out.println(name);

        //2.获取修饰符
        int modifiers = personClass.getModifiers();
        //修饰符的描述是定义在jdk中的Modifier类中,使用常量定义的
        System.out.println(modifiers);

        //3.获取类名,getName是获取类的全限定名,getSimpleName是获取类的名字
        String name1 = personClass.getName();
        System.out.println(name1);
        System.out.println(personClass.getSimpleName());

        //4.获取父类的字节码对象
        Class superclass = personClass.getSuperclass();
        System.out.println(superclass.getSimpleName());

        //5.获取该类实现的所有接口的字节码对象
        Class[] interfaces = personClass.getInterfaces();
        for(Class i : interfaces){
            System.out.println(i);
        }

        //6.获取类的所有成员变量(非静态)
        Field[] declaredFields = personClass.getDeclaredFields();
        for(Field f : declaredFields){
            System.out.println(f.getName());
        }

        //7.获取类的所有构造函数
        Constructor[] declaredConstructors = personClass.getDeclaredConstructors();
        for(Constructor c:declaredConstructors){
            System.out.println(c);
        }

        //8.获取类的所有方法
        Method[] declaredMethods = personClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod.getName());
        }

    }
}

2.2.8 反射的概念

反射是一种机制/功能,利用该机制/功能可以在程序运行过程中对类进行解剖并操作类中的构造方法,成员方法,成员属性。

2.2.9 反射的应用场景

各种框架的设计(主要场景)

各大框架的内部实现也大量使用到了反射机制,所以要想学好这些框架,则必须要求了解反射机制。

Person类

public class Person implements Hello {
    private int age = 10;
    private String name = "张三";
    public String address = "深圳";

    public Person() {
        System.out.println("执行了无参构造");
    }

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

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

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

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

    public Integer getName(Integer n) {
        return n;
    }

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

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

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

    @Override
    public void sayHello() {
        System.out.println("hello world");
    }

    private void study(String course, int day) {
        System.out.println("努力学习:" + course + ",学习" + day + "天");
    }
}
public class TestCreate {
    public static void main(String[] args) throws Exception{
        //获取Person类的Class对象,也就是字节码文件的对象
        Class personClass = Person.class;
        //使用第一种方式创建Person类的对象
        //强转一定是建立在父子关系的前提下

        /*Person per1 = (Person)personClass.newInstance();
        System.out.println(o);*/

        //使用第二种方式创建Person类的对象
        //获取无参的构造函数
        /*Constructor declaredConstructor = personClass.getDeclaredConstructor();
        Person per2 = (Person)declaredConstructor.newInstance();
        System.out.println(per2);*/

        //获取有参的构造函数
        Constructor declaredConstructor = personClass.getDeclaredConstructor(int.class, String.class);
        Person per3 = (Person)declaredConstructor.newInstance(12, "李四");
        System.out.println(per3);

    }
}
public class TestMethod {
    public static void main(String[] args)throws Exception{
        Class personClass = Person.class;
        //1.获取某一个方法,例如:getName()
        //获取无参的getName方法
        Method getName = personClass.getDeclaredMethod("getName",Integer.class);
        Person o = (Person)personClass.newInstance();
        /*String str = (String)getName.invoke(o);
        System.out.println(str);*/
        Integer invoke = (Integer)getName.invoke(o,1);
        System.out.println(invoke);

        //暴力反射
        Method study = personClass.getDeclaredMethod("study",String.class,int.class);
        study.setAccessible(true);
        Object invoke1 = study.invoke(o,"语文",12);
    }
}
public class TestProperty {
    public static void main(String[] args) throws Exception{
        Class personClass = Person.class;
        Object o = personClass.newInstance();
        //2.1 获取Person的所有属性(只能获取自己的,包含公有的和私有的)
        Field[] declaredField = personClass.getDeclaredFields();
        for(Field d : declaredField){
            //获取每个属性的属性名和属性值
            //获取属性名
            String name = d.getName();
            //获取属性的类型
            Class<?> type = d.getType();
            //获取属性的修饰符
            int modifiers = d.getModifiers();
            //暴力反射:通过反射可以访问类的私有成员
            d.setAccessible(true);
            //获取属性的值
            Object value = d.get(o);//等于 对象.属性名
            System.out.println(name + "," + value + "," + type + "," + modifiers);
        }

        //2.2 单独获取某一个属性,比如获取name
        Field address = personClass.getDeclaredField("address");
        address.set(o,"北京");
        Object o1 = address.get(o);
        System.out.println(o1);
    }
}

2.3 Type接口的介绍(了解)

java.lang.reflect.Type接口及其相关接口用于描述java中用到的所有类型,是Java的反射中很重要的组成部分。Type 是 Java 编程语言中所有类型的公共高级接口。它们包括原始类型、参数化类型、数组类型、类型变量和基本类型。

有很多场景下我们可以获得Type,比如:

  1. 当我们拿到一个Class,用Class.getGenericInterfaces()方法得到Type[],也就是这个类实现接口的Type类型列表。

  2. 当我们拿到一个Class,用Class.getDeclaredFields()方法得到Field[],也就是类的属性列表,然后用Field. getGenericType()方法得到这个属性的Type类型。

  3. 当我们拿到一个Method,用Method.getGenericParameterTypes()方法获得Type[],也就是方法的参数类型列表。

  4. 当我们拿到一个Class,用clazz.getGenericSuperclass()这样就可以获取父类的泛型实参列表

2.3.1 Type的分类

Type接口包含了一个实现类(Class)和四个实现接口(TypeVariable, ParameterizedType, GenericArrayType, WildcardType),这四个接口都有自己的实现类,但这些实现类开发都不能直接使用,只能用接口。

  1. Class: 当需要描述的类型是普通Java类、数组、自定义类、 8种java基本类型 的时候, java会选择Class来作为这个Type的实现类,我们甚至可以直接把这个Type强行转换类型为Class。这些类基本都有一个特点:基本和泛型无关,其他4种Type的类型,基本都是泛型的各种形态。

  2. ParameterizedType: 当需要描述的类是泛型类时,比如List,Map等,不论代码里写没写具体的泛型,java会选择ParameterizedType接口做为Type的实现。ParameterizedType接口有getActualTypeArguments()方法,用于得到泛型的Type类型数组。

  3. GenericArrayType: 当需要描述的类型是泛型类的数组时,比如比如List[],Map[],type用GenericArrayType接口作为Type的实现。GenericArrayType接口有getGenericComponentType()方法,得到数组的组件类型的Type对象。

  4. WildcardType: 当需要描述的类型是泛型类,而且泛型类中的泛型被定义为(? extends xxx)或者(? super xxx)这种类型,比如List<? extends TestReflect>,这个类型首先将由ParameterizedType实现,当调用ParameterizedType的getActualTypeArguments()方法后得到的Type就由WildcardType实现。

示例代码获取泛型父类信息:

public class TestType {
    public static void main(String[] args) {
        //获取Son类的字节码对象
        Class clazz = Son.class;
        //通过子类的字节码对象获取父类的泛型
        Type type = clazz.getGenericSuperclass();

        //强转
        ParameterizedType parameterizedType = (ParameterizedType) type;

        //获取类型的泛型
        Type[] typeArguments = parameterizedType.getActualTypeArguments();
        for (Type typeArgument : typeArguments) {
            System.out.println(typeArgument);
        }

    }
    //泛型形参:<T,U>
    class Father<T,U>{

    }
    //泛型实参:<String,Integer>
    class Son extends Father <String,Integer>{

    }
}

2.4 动态创建和操作任何类型的数组

在java.lang.reflect包下还提供了一个Array类,Array对象可以代表所有的数组。程序可以通过使用Array类来动态的创建数组,操作数组元素等。

Array类提供了如下几个方法:

public static Object newInstance(Class<?> componentType, int... dimensions):创建一个具有指定的组件类型和维度的新数组。

public static void setXxx(Object array,int index,xxx value):将array数组中[index]元素的值修改为value。此处的Xxx对应8种基本数据类型,如果该属性的类型是引用数据类型,则直接使用set(Object array,int index, Object value)方法。

public static xxx getXxx(Object array,int index,xxx value):将array数组中[index]元素的值返回。此处的Xxx对应8种基本数据类型,如果该属性的类型是引用数据类型,则直接使用get(Object array,int index)方法。

public class TestArray {
    public static void main(String[] args) {
        //使用反射操作数组
        //1.使用反射创建一个String类型的数组,长度是5
        String[] o = (String[])Array.newInstance(String.class, 5);
        //2.往数组中存入数据
        for (int i = 0; i < 5; i++) {
            Array.set(o,i,"value"+i);
        }

        //使用Array获取数组中的元素
        for (int i = 0; i < 5; i++) {
            System.out.println(Array.get(o,i));
        }
    }
}

3. 注解

3.1 学习目标

  • 了解注解的概念

  • 了解JDK提供的三种基本注解

  • 掌握自定义注解

  • 掌握元注解

  • 掌握注解解析

3.2 内容讲解

3.2.1 什么是注解

注解英文是annotation,是一种代码级别的说明,和类 接口平级关系。相当于一种标记,在程序中加入注解就等于为程序打上某种标记,以后,javac编译器、开发工具和其他程序可以通过反射来了解你的类及各种元素上有无标记,看你的程序有什么标记,就去干相应的事,标记可以加在包、类,属性、方法,方法的参数以及局部变量上定义

3.2.2 注解的作用

(1)执行编译期的检查 例如:@Override

(2)分析代码(主要用途:替代配置文件); 用在框架里面, 注解开发

3.2.3 JDK提供的三个基本注解

  1. @Override:描述方法的重写.

  2. @SuppressWarnings:压制警告.

  3. @Deprecated:标记过时

3.2.4 自定义注解的语法

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation02 {
}
/**
 * 注解的作用:
 *  1. 作为标记
 *  2. 用于代替配置文件,存储一些配置信息
 *
 * 使用注解存储数据,那么就得给注解添加注解属性
 *  1. 语法: 属性类型 属性名();
 *  2. 属性类型都可以是哪些类型?
 *     2.1 String
 *     2.2 8种基本数据类型
 *     2.3 枚举类型
 *     2.4 Class类型
 *     2.5 注解类型
 *     2.6 上述类型的数组类型
 *
 *  3. 注解但凡定义了属性,那么我们在使用注解的时候,就要给注解的属性进行赋值
 *
 *  4. 注解属性可以使用default进行赋默认值,赋了默认值的属性,我们在使用该注解的时候可以不进行赋值
 *
 *  5. 如果注解属性是数组类型,但是赋值的时候该数组只有一个元素,那么可以省略数组的{}
 *
 *  6. 如果只有一个注解属性必须赋值,并且这个注解属性的属性名是value,那么赋值的时候可以省略 value=
 *
 * 元注解:
 *  1. Target 表示注解只能用在哪些位置
 *  2. Retention 表示该注解保留到哪个阶段
 */
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation01 {
    String str() default "奥巴马";
    int num() default 12;
    Color color() default Color.YELLOW;
    Class clazz() default UseAnnotation.class;
    MyAnnotation02 anno() default @MyAnnotation02;
    String[] value();
}

3.2.5 常用的元注解

(1)@Target:定义该注解作用在什么上面(位置),默认注解可以在任何位置. 值为:ElementType的枚举值

METHOD:方法

TYPE:类 接口

FIELD:字段

CONSTRUCTOR:构造方法声明

(2)@Retention:定义该注解保留到那个代码阶段, 值为:RetentionPolicy类型,默认只在源码阶段保留

SOURCE:只在源码上保留(默认)

CLASS:在源码和字节码上保留

RUNTIME:在所有的阶段都保留

java (源码阶段) ----编译---> .class(字节码阶段) ----加载内存--> 运行(RUNTIME)

public class UseAnnotation {

    private int age;

    @MyAnnotation02
    @MyAnnotation01(str = "hello", num = 1, color = Color.RED,
            clazz = UseAnnotation.class, anno = @MyAnnotation02, value = {"a","b"})
    public void say(String name){
        System.out.println(name);
    }

    public void study(){
        System.out.println("努力学习...");
    }
}
/**
 * 注解解析
 * 1. 目的:
 *       1.1 获取类、成员变量、成员方法、方法属性、构造函数等等上面的注解对象
 *       1.2 获取注解对象的属性
 *       1.3 判断某个类、成员变量、成员方法等等上面是否有某个注解
 * 2. 注解解析的API,解析注解是通过AnnotatedElement接口进行注解解析的
 *    2.1 T getAnnotation(Class<T>annotationType)  获取指定类型的注解
 *    2.2 boolean isAnnotationPresent(Class&lt;?extends Annotation&gt; annotationType) 判断是否存在指定类型的注解
 *
 * Method、Filed、Class、Constructor 都实现了AnnotatedElement接口
 *
 */
public class TestAnnotation {
    public static void main(String[] args) throws Exception{
        //注解解析:
        //目标1:判断UseAnnotation类的所有方法上,是否包含MyAnnotation02注解
        //1.1 使用反射获取该类的所有方法
        Class clazz = UseAnnotation.class;
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods){
            boolean annotationPresent = declaredMethod.isAnnotationPresent(MyAnnotation02.class);
            System.out.println(declaredMethod.getName()+","+annotationPresent);
        }

        //目标2:获取setAnnotation类的say方法上的MyAnnotation01注解,并且拿到该注解
        //2.1 使用反射获取say方法
        Method say = clazz.getDeclaredMethod("say", String.class);
        //2.2 获取到say方法上的MyAnnotation01注解
        MyAnnotation01 declaredAnnotation = say.getDeclaredAnnotation(MyAnnotation01.class);
        //注解的对象只能通过反射去获取,不能直接new
        System.out.println(declaredAnnotation.str());
        System.out.println(declaredAnnotation.num());
        System.out.println(declaredAnnotation.anno());
        System.out.println(declaredAnnotation.clazz());
        System.out.println(declaredAnnotation.color());
        System.out.println(declaredAnnotation.value());

    }

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/807726.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

7年经验之谈 —— 浅谈web性能测试

什么是性能测试&#xff1f; web性能应该注意些什么&#xff1f; 性能测试&#xff0c;简而言之就是模仿用户对一个系统进行大批量的操作&#xff0c;得出系统各项性能指标和性能瓶颈&#xff0c;并从中发现存在的问题&#xff0c;通过多方协助调优的过程。而web端的性能测试…

9.容器服务更新和发现

文章目录 容器服务更新和发现服务发现consul概念关键特性总结 部署consul架构初始化部署群集查看 部署registrator服务器consul-template 模板文件consul多节点 容器服务更新和发现 服务发现 什么是服务注册与发现 服务注册与发现是微服务架构中不可或缺的重要组件。起初服务…

吃透《西瓜书》第三章 线性模型:对数几率回归

&#x1f349; 吃瓜系列 教材&#xff1a;《机器学习》 周志华著 &#x1f552;时间&#xff1a;2023/7/26 目录 一、对数几率回归 1.1 定义和基本思想 1.2 对数记录回归建模 1.3 广义线性模型 1.3.1 指数族分布 1.3.2 广义线性模型的三条假设 1.4 对数几率回归的广义线…

百城巡展 | 人大金仓7月携手全国伙伴赋能“一带一路”沿线区域协同创新

盛夏7月&#xff0c;人大金仓“百城巡展”来到西宁、乌鲁木齐、银川、拉萨&#xff0c;携手核心伙伴、用户&#xff0c;打造科技创新合作平台&#xff0c;不断打磨数据库领域中国方案&#xff0c;赋能“一带一路”沿线区域协同创新&#xff0c;助力信创产业高质量发展&#xff…

【C++】Day7 标准莫板库

1. 实现vector相关函数 #include <iostream>using namespace std;template <typename T> class Myvector { private:T* first;T* last;T* end; public:Myvector(int size 10){ //构造函数this->first new T[size];this->last this->first;this-&g…

【计算机视觉中的 GAN 】 - 条件图像合成和 3D 对象生成(2)

一、说明 上文 【计算机视觉中的 GAN 】或多或少是GANs&#xff0c;生成学习和计算机视觉的介绍。我们达到了在 128x128 图像中生成可区分图像特征的程度。但是&#xff0c;如果你真的想了解GAN在计算机视觉方面的进展&#xff0c;你肯定必须深入研究图像到图像的翻译。…

【广州华锐互动】自来水厂净水流程3D模拟还原有哪些作用?

自来水厂净水流程3D模拟还原是一种新型的培训方式&#xff0c;它通过虚拟现实技术&#xff0c;将自来水工艺流程以三维立体的形式呈现出来&#xff0c;为学员提供直观、真实的学习体验。这种培训方式具有以下意义&#xff1a; 首先&#xff0c;自来水厂净水流程3D模拟还原可以…

SpringMvc+阿贾克斯

0目录 1.SpringMVC 加阿贾克斯 2.分页版 1.实战 创建数据库 创建工程和pom依赖 配置web.xml和applicationContext.xml 实体类 Mapper接口方法 Mapper.xml BookService BookSeriviceImpl 控制层 测试 加入findAll.html 测试 2.分页版 控制层 PostMan测…

六个步骤学会简单的数据清洗

在使用机器学习等各种模型来分析数据的时候&#xff0c;最重要的就是如何对原始数据进行清洗和加工&#xff0c;以下几个步骤实现最简单的数据清洗&#xff1b; 以下使用 kaggle 上的泰坦尼克号经典数据集为例子&#xff1b; daownload link&#xff1a;https://link.zhihu.c…

ModuleNotFoundError: No module named ‘xxx‘ 问题解决

ModuleNotFoundError: No module named ‘utils’ 这个问题发生的主要原因&#xff0c;我搜索网络&#xff0c;大概意思就是在py文件中使用了__main__函数&#xff0c;破坏了什么路径识别什么的&#xff0c; 遇到这个问题我发现我导入的utils这个pacakge是有的&#xff0c;那就…

【AutoGluon_02】更优精度与特征重要性

【AutoGluon_02】更优精度与特征重要性 1、优化改良版autogluon2、快速使用3、模型训练4、更高的输出精度5、模型评估&#xff08;1&#xff09;模型排行榜&#xff08;2&#xff09;输出各特征重要性&#xff08;3&#xff09;模型性能可视化 6、输出最终模型7、预测 除了auto…

Scala的trait和extend代码运用实战Demo例子

1、概述 在Scala中&#xff0c;trait是一种特殊的概念&#xff0c;它类似于Java中的接口。trait可以定义方法和字段&#xff0c;但是不能实例化。类可以扩展trait&#xff0c;从而获得trait中定义的方法和字段。 在Scala中&#xff0c;extend关键字用于扩展类或特质。当一个类…

2.playbook剧本

文章目录 playbook剧本创建剧本运行剧本定义和引用变量指定远程主机sudo切换用户when条件判断剧本格式迭代with_itemswith_listwith_flattenedwith_togetherwith_cartesianwith_nested Templates模块tags模块 playbook剧本 playbooks 本身由以下各部分组成 Tasks&#xff1a;任…

基于javeSprict的WebAPI详解

一、前言 作为后端开发&#xff0c;前端其实只需要了解一些就可以了&#xff0c;不需要了解多么深入。在前面我们已经学习了ECMAScript:的基础语法部分&#xff0c;还有DOM API和BOM API需要学习。 DOM API主要负责操作页面结构。 所有的WebAPI可以参考下面这个网址里面的信息…

高清视频制作GIF怎么操作?一个工具在线完成视频转GIF

一段视频为了方便传输分享想要做成GIF动画的时候要怎么操作呢&#xff1f;很简单&#xff0c;只需要一款专业的GIF在线制作工具-GIF中文网&#xff0c;使用视频转GIF&#xff08;https://www.gif.cn/&#xff09;功能&#xff0c;上新MP4格式视频&#xff0c;能够快速制作1分钟…

arm neon/fpu/mfloat

neon官网介绍: Arm Neon technology is an advanced Single Instruction Multiple Data (SIMD) architecture extension for the A-profile and R-profile processors. Neon technology is a packed SIMD architecture. Neon registers are considered as vectors of elements …

【达哥讲网络】第3集:数据交换的垫基石——二层交换原理

专业的网络工程师在进行网络设计时&#xff0c;会事先规划好不同业务数据的转发路径&#xff0c;一方面是为了满足用户应用需求&#xff0c;另一方面是为了提高数据转发效率、充分利用各设备/各链路的硬件或带宽资源。在进行网络故障排除时&#xff0c;理顺各路数据的转发路径也…

Android 开发代码规范

一. AndroidStudio开发工具规范 使用最新的稳定版本.统一文件的编码格式为utf-8. 清除每个类里面的无效的import导包.代码样式统一,比如&#xff0c;tab缩进4个空格&#xff0c;或者 tab size等如果没有特殊情况使用默认的配置即可。每行字数每行字符数不得超过 160 字符&…

C++笔记之++i和i++是原子操作吗?

C笔记之i和i是原子操作吗&#xff1f; code review! 文章目录 C笔记之i和i是原子操作吗&#xff1f;1.i是原子操作吗&#xff1f;2.i是原子操作吗&#xff1f;3.前置递增和后置递增 1.i是原子操作吗&#xff1f; 2.i是原子操作吗&#xff1f; 3.前置递增和后置递增

绝美!轮到AI写真爆火了!18种AI视频制作教程;Llama 2微调的极速指南;Nijijourney官方AI绘画课 | ShowMeAI日报

&#x1f440;日报&周刊合集 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; &#x1f916; 摄影写真面临AI技术洗牌&#xff0c;一键生成杂志大片的时代来了 软件&#xff1a;midjourney & Stable Diffusion 模型&#xff…