Java中的Junit、类加载时机与机制、反射、注解及枚举

目录

Java中的Junit、类加载时机与机制、反射、注解及枚举

Junit

Junit介绍与使用

Junit注意事项

Junit其他注解

类加载时机与机制

类加载时机

类加载器介绍

获取类加载器对象

双亲委派机制和缓存机制

反射

获取类对象

获取类对象的构造方法

使用反射获取的构造方法创建对象

获取类对象的成员方法

使用反射获取的成员方法

获取类对象的成员属性

使用反射获取的成员属性

反射实用案例

注解

介绍

定义注解和属性

注解的使用

解析注解

元注解

枚举

基本使用

枚举中的常用方法


Java中的Junit、类加载时机与机制、反射、注解及枚举

Junit

Junit介绍与使用

在Java中,Junit是一个单元测试框架,可以代替main方法去执行其他的方法,其可以单独执行一个方法,测试该方法是否能跑通

因为Junit是一个第三方工具包,与Lombok一样需要先导入对应的jar包并进行解压,不同的是,Junit有两个包,并且相互依赖,所以都需要进行解压。这里可以使用一个文件夹名为lib,将两个解压包放在lib文件下,再对lib文件夹进行整体Add as Library

使用时,在需要进行测试的方法上方写上@Test注解,此时就会在对应方法所在行左侧显示运行按钮,点击即可运行对应的方法而不会执行其他方法

例如下面的代码:

public class Test01 {@Testpublic void method() {System.out.println("测试Junit");}
}

一个类中可以有多个有@Test方法:

public class Test01 {@Testpublic void method() {System.out.println("测试Junit");}@Testpublic void method01() {System.out.println("测试Junit-2");}
}

如果有多个@Test方法,想要运行所有的@Test方法,就可以点击类名所在行的运行按钮

Junit注意事项

  1. @Test不能修饰静态方法
  2. @Test不能修饰带参数的方法
  3. @Test不能修饰带返回值的方法

例如:

public class Test01 {@Testpublic static void method02() {System.out.println("测试Junit-3");}
}// 报错:Method 'method02' annotated with '@Test' should be non-static 

Junit其他注解

  1. @Before:被@Before修饰的方法会在每一个@Test方法执行前执行,但是本身不可以单独执行。特点:有多个@Test修饰的方法时会执行多次
  2. @After:被@Before修饰的方法会在每一个@Test方法执行后执行,但是本身不可以单独执行。特点:有多个@Test修饰的方法时会执行多次
  3. @BeforeClass:被@BeforeClass修饰的方法会在所有@Test方法执行前执行,但是本身不可以单独执行。特点:不论是否有多个@Test修饰的方法,该方法始终只会执行一次,并且被修饰的方法必须为静态方法
  4. @AfterClass:被@AfterClass修饰的方法会在所有@Test方法执行后执行,但是本身不可以单独执行。特点:不论是否有多个@Test修饰的方法,该方法始终只会执行一次,并且被修饰的方法必须为静态方法

例如:

public class Test01 {@Testpublic void method() {System.out.println("测试Junit");}@Testpublic void method01() {System.out.println("测试Junit-2");}@Beforepublic void before() {System.out.println("before");}@Afterpublic void after() {System.out.println("after");}@BeforeClasspublic static void beforeClass() {System.out.println("beforeClass");}@AfterClasspublic static void afterClass() {System.out.println("afterClass");}
}输出结果:
beforeClass
before
测试Junit
after
before
测试Junit-2
after
afterClass

类加载时机与机制

类加载时机

在Java中,以下五种情况中的一种情况出现,JVM就会将class文件加载进入内存:

  1. 实例化对象
  2. 实例化子类对象或者接口实现类对象(此时创建子类对象会初始化父类)
  3. 执行main方法
  4. 直接使用类名调用静态成员
  5. 使用反射创建class对象

因为class也属于文件,所以JVM也需要使用IO流中的操作将class文件读取到内存,但并不是JVM直接加载,而是使用一个名为ClassLoader的类加载器进行加载

类加载器介绍

下面的类加载器将基于JDK8分析

JVM会使用ClassLoader的类加载器加载类,在Java中,类加载器有以下三种:

  1. BootStrapClassLoader:根类加载器,也称为引导类加载器,主要加载Java中的核心类,例如System类、String类等(在路径jre/lib/rt.jar下)
  2. ExtClassLoader:扩展类加载器,负责jre的扩展目录中的jar包的加载(在路径jdk\jre\lib\ext下)
  3. AppClassLoader:系统类加载器,负责在JVM启动时加载来自Java命令的class文件(可以理解为自定义类),以及classPath环境变量所指定的jar包(可以理解为第三方jar包)

在Java中,三种不同的类加载器会加载不同的类,而其三者的关系「从类加载机制层面」如下:

AppClassLoader的父类加载器是ExtClassLoader

ExtClassLoader的父类加载器是BootStrapClassLoader

需要注意,他们从代码级别上来看,没有子父类继承关系,但是他们都有一个共同的父类,即 ClassLoader
并且 BootStrapClassLoader是无法直接获取到的,因为其底层是C语言编写的
// AppClassLoader类声明
public class AppletClassLoader extends URLClassLoader
// ExtClassLoader类声明
static class ExtClassLoader extends URLClassLoader// 拥有共同的父类
public class URLClassLoader extends SecureClassLoader implements Closeable
public class SecureClassLoader extends ClassLoader

获取类加载器对象

在Java代码中,可以通过下面的两个方法获取类加载器对象:

  1. Class对象中的方法:getClassLoader(),使用时:类名.class.getClassLoader(),作用是获取某一个使用的类加载器
  2. ClassLoader类中的方法:getParent(),使用时:ClassLoader对象.getParent(),作用是获取指定类加载器的父类加载器
public class Test {public static void main(String[] args) {// 自定义类ClassLoader classLoader = Test.class.getClassLoader();System.out.println(classLoader);// 第三方类ClassLoader classLoader1 = IOUtil.class.getClassLoader();System.out.println(classLoader1);// 获取二者的父类System.out.println(classLoader.getParent());if (classLoader1 != null) {System.out.println(classLoader1.getParent());} else {System.out.println("父类加载器无法获取");}// 系统类ClassLoader classLoader2 = Scanner.class.getClassLoader();System.out.println(classLoader2);}
}输出结果:
sun.misc.Launcher$AppClassLoader@18b4aac2
null
sun.misc.Launcher$ExtClassLoader@4554617c
父类加载器无法获取
null

双亲委派机制和缓存机制

双亲委派机制,也称为全盘负责委托机制,表示类加载器在加载类时,首先不会自己去尝试加载这个类,而是把请求交给父类加载器来完成,每一层的类加载器都会遵循这样的规则。

类加载器的缓存机制:一个类加载到内存之后,缓存中也会保存一份儿,后面如果再使用此类,如果缓存中保存了这个类,就直接返回他;如果没有才加载这个类,下一次如果有其他类在使用的时候就不会重新加载了,直接去缓存中拿

类加载器加载类的过程图如下:

图解析:

一个类准备加载进内存时,首先遇到的类加载器就是AppClassLoader,如果AppClassLoader中的缓存已经存在该类,则直接返回该类,否则向上找类加载器ExtClassLoader,同样,如果ExtClassLoader的缓存已经存在该类,则直接返回,否则向上找类加载器BootStrapClassLoader,如果BootStrapClassLoader缓存依旧没有该类,则判断类是否是该类加载需要加载的,不是继续向下直到遇到属于加载该类的类加载器

所以,类加载器的双亲委派和缓存机制共同造就了加载类的特点:保证了类在内存中的唯一性

反射

在Java中,反射的作用是获取类对象,通过这个类对象获取对应类中的成员属性(重新赋值)、成员方法(调用方法)和构造方法(实例化对象)

类对象:在Java中,一切皆是对象,而class文件加载进内存就会生成对应的对象,该对象就被称为class对象

Class类:描述Class对象就是Class

同样,对于成员变量、成员方法和构造方法来说也有对应的对象和类:

  1. 成员变量:对应的成员变量对象为Field对象,描述Field对象的类称为Field
  2. 成员方法:对应的成员方法对象为Method对象,描述Method对象的类称为Method
  3. 构造方法:对应的构造方法对象为Constructor对象,描述Constructor对象的类称为Constructor

获取类对象

获取类对象是反射成立的第一步,而一共有三种方式获取类对象:

  1. 调用Object中的方法:Class <?> getClass(),该方法返回一个类对象
  2. 通过class成员获取类对象:基本数据类型/引用数据类型.class
  3. Class类中的静态方法:static Class<?> forName(String className),该方法的参数为类的全限定名,该方法返回一个类对象
类的全限定名即为类所在的包名,即 package后面的内容+指定类名

例如,下面的代码:

public class Test {public static void main(String[] args) throws Exception{// 1. 调用Object中的方法:Class <?> getClass(),该方法返回一个类对象Scanner scanner = new Scanner(System.in);Class<? extends Scanner> aClass = scanner.getClass();System.out.println(aClass);// 2. 通过class成员获取类对象:基本数据类型/引用数据类型.classClass<Scanner> scannerClass = Scanner.class;System.out.println(scannerClass);// 3. Class类中的静态方法:static Class<?> forName(String className),该方法的参数为类的全限定名,该方法返回一个类对象Class<?> aClass1 = Class.forName("com.epsda.advanced.test_reflect.Test");System.out.println(aClass1);}
}输出结果:
class java.util.Scanner
class java.util.Scanner
class com.epsda.advanced.test_reflect.Test

在IDEA中获取类的全限定名:

  1. 右键需要全限定名的类->选择Copy Path/Reference...->选择Copy Reference
  2. forName方法中输入需要全限定名的类名,按下Tab或者Enter
如果按住Ctrl+鼠标左键点击类的全限定名可以跳转到指定类时,说明全限定名正确

在实际开发中最常用的获取类对象的方式是第二种,但是最通用的方式是第三种,因为第三种的参数是String类型,后面可以结合配置文件xxx.propertiesProperties集合中的load方法加载类的全限定名

获取类对象的构造方法

获取类对象的构造方法一共有四种方法:

  1. 获取类对象中所有public构造方法:使用Class类中的方法:Constructor<?>[] getConstructors(),该方法返回一个构造方法类的对象
  2. 获取类对象中指定的public构造方法:Class类中的方法:Constructor<T> getConstructor (Class<?>... parameterTypes),该方法的参数为指定的public构造方法参数类型对应的类对象,返回一个构造方法类的对象。如果指定的public构造方法没有参数,则可以不传递任何内容
  3. 获取类对象中所有构造方法(包括publicprivate):Constructor<?>[] getDeclaredConstructors(),该方法返回一个构造方法类的对象
  4. 获取类对象中指定的构造方法(包括publicprivate):Constructor<T> getDeclaredConstructor (Class<?>... parameterTypes),该方法的参数为指定的构造方法的参数,返回一个构造方法类的对象。如果指定的构造方法没有参数,则可以不传递任何内容

下面示例使用的类:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {private String name;public Integer age;private Person(String name) {this.name = name;}
}

使用如下:

public class Test01 {public static void main(String[] args) throws Exception{Class<Person> personClass = Person.class;// 获取所有public构造方法Constructor<?>[] constructors = personClass.getConstructors();for (Constructor<?> constructor : constructors) {System.out.println(constructor);}// 获取指定public构造方法Constructor<Person> constructor = personClass.getConstructor(String.class, Integer.class);System.out.println(constructor);// 获取所有构造方法Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors();for (Constructor<?> declaredConstructor : declaredConstructors) {System.out.println(declaredConstructor);}// 获取指定的构造方法Constructor<Person> declaredConstructor = personClass.getDeclaredConstructor(String.class);System.out.println(declaredConstructor);}
}

使用反射获取的构造方法创建对象

使用Constructor类中的方法: T newInstance(Object...initargs),参数传递对应对象初始值,如果获取到的是无参构造,则参数不传递,否则传递对应的值,该方法返回一个对应类的对象

public class Test02 {public static void main(String[] args) throws Exception{Class<Person> personClass = Person.class;Constructor<Person> declaredConstructor = personClass.getDeclaredConstructor(String.class, Integer.class);Person person = declaredConstructor.newInstance("张三", 16);System.out.println(person);}
}

如果获取到的是无参构造,则可以直接使用类对象.newInstance(),简写为如下的代码:

public class Test03 {public static void main(String[] args) throws Exception{// 无参构造默认反射方式Class<Person> personClass = Person.class;Constructor<Person> constructor = personClass.getConstructor();Person person = constructor.newInstance();System.out.println(person);// 简写为Person person1 = personClass.newInstance();System.out.println(person);}
}
但是,上面的简写形式已经被弃用(修饰为 @Deprecated),不过依旧可以使用

如果获取到的是私有构造方法,则需要使用Constructor的父类AccessibleObject中的方法void setAccessible(boolean flag)将私有构造方法的权限修改为public,参数有两个值:true代表修改,false表示不修改

public class Test04 {public static void main(String[] args) throws Exception{Class<Person> personClass = Person.class;Constructor<Person> personConstructor = personClass.getDeclaredConstructor(String.class);// 修改权限personConstructor.setAccessible(true);Person person = personConstructor.newInstance("张三");System.out.println(person);}
}

上面获取私有构造方法并创建对象也被称为暴力反射

获取类对象的成员方法

获取类对象的成员方法一共有四种方法:

  1. 获取类对象中所有public成员方法:使用Class类中的方法:Method[] getMethods(),该方法返回一个成员方法类的对象
  2. 获取类对象中指定的public成员方法:Class类中的方法:Method getMethod (String name, Class<?>... parameterTypes),该方法的第一个参数为指定的public成员方法名,第二个参数为指定的public成员方法参数类型对应的类对象,返回一个成员方法类的对象。如果指定的public成员方法没有参数,则第二个参数可以不传递任何内容
  3. 获取类对象中所有成员方法(包括publicprivateprotected):Method[] getDeclaredMethods(),该方法返回一个成员方法类的对象
  4. 获取类对象中指定的成员方法(包括publicprivate):Method getDeclaredMethod (String name, Class<?>... parameterTypes),该方法的参数为指定的成员方法的参数,返回一个成员方法类的对象。如果指定的public成员方法没有参数,则第二个参数可以不传递任何内容
需要注意, getMethods方法会获取到本类和其父类的所有 public方法,但是 getDeclaredMethods方法只会获取到本类中的 privatepublicprotected方法

例如下面的代码:

public class Test05 {public static void main(String[] args) throws Exception{Class<Person> personClass = Person.class;// 获取所有public方法Method[] methods = personClass.getMethods();for (Method method : methods) {System.out.println(method);}// 获取指定的public方法Method method = personClass.getMethod("setName", String.class);System.out.println(method);// 获取所有的方法Method[] declaredMethods = personClass.getDeclaredMethods();for (Method declaredMethod : declaredMethods) {System.out.println(declaredMethod);}// 获取指定的方法Method walk = personClass.getDeclaredMethod("walk");System.out.println(walk);}
}

使用反射获取的成员方法

使用成员方法类对象调用Object类中的方法Object invoke(Object obj, Object... args)可以使用获取到的成员方法,第一个参数传递成员方法所在类的对象,第二个参数传递获取到的成员方法参数对应的值,该方法返回一个Object对象。该方法的返回值根据调用对象对应的方法是否有返回值决定,如果调用对象对应的方法有返回值,则与调用对象对应的方法的值相同,否则为null

public class Test06 {public static void main(String[] args) throws Exception{Class<Person> personClass = Person.class;Person person = personClass.newInstance();Method setName = personClass.getDeclaredMethod("setName", String.class);Method getName = personClass.getDeclaredMethod("getName");Object set = setName.invoke(person, "张三");Object get = getName.invoke(person);System.out.println(get);}
}

如果需要操作类对象中的私有方法,与私有构造方法一样,需要使用AccessibleObject中的方法void setAccessible(boolean flag)将私有构造方法的权限修改为public

public class Test06 {public static void main(String[] args) throws Exception{Class<Person> personClass = Person.class;Person person = personClass.newInstance();Method declaredMethod = personClass.getDeclaredMethod("walk");declaredMethod.setAccessible(true);declaredMethod.invoke(person);}
}

获取类对象的成员属性

获取类对象的构造方法一共有四种方法:

  1. 获取类对象中所有public成员属性:使用Class类中的方法:Field[] getFields(),该方法返回一个成员属性类的对象
  2. 获取类对象中指定的public成员属性:Class类中的方法:Field getField(String name),该方法的参数为指定的public成员属性名,返回一个成员属性类的对象
  3. 获取类对象中所有成员属性(包括publicprivate):Field[] getDeclaredFields(),该方法返回一个成员属性类的对象
  4. 获取类对象中指定的成员属性(包括publicprivateprotected):Field getDeclaredField(String name),该方法的参数为指定的public成员属性名,返回一个成员属性类的对象

例如:

public class Test07 {public static void main(String[] args) throws Exception{Class<Person> personClass = Person.class;// 获取所有public成员属性Field[] fields = personClass.getFields();for (Field field : fields) {System.out.println(field);}// 获取指定的public成员属性Field age = personClass.getField("age");System.out.println(age);// 获取所有成员属性Field[] declaredFields = personClass.getDeclaredFields();for (Field declaredField : declaredFields) {System.out.println(declaredField);}// 获取指定成员属性Field declaredField = personClass.getDeclaredField("name");System.out.println(declaredField);}
}

使用反射获取的成员属性

使用成员属性类对象调用Object中的方法:void set(Object obj, Object value)为获取到的成员属性赋值,第一个参数传递成员属性所在类的对象,第二个参数传递属性值

使用成员属性类对象调用Object中的方法:Object get(Object obj)获取指定成员属性的值,参数传递成员属性所在类的对象,该方法返回一个Object对象,该对象中的值即为成员属性的值

public class Test08 {public static void main(String[] args) throws Exception{Class<Person> personClass = Person.class;Person person = personClass.newInstance();Field age = personClass.getField("age");age.set(person, 18);Object o = age.get(person);System.out.println(o);}
}

同样,如果是私有成员,则需要使用AccessibleObject中的方法void setAccessible(boolean flag)将私有构造方法的权限修改为public

public class Test08 {public static void main(String[] args) throws Exception{Class<Person> personClass = Person.class;Person person = personClass.newInstance();Field declaredField = personClass.getDeclaredField("name");declaredField.setAccessible(true);declaredField.set(person, "张三");Object o1 = declaredField.get(person);System.out.println(o1);}
}

反射实用案例

在配置文件中,配置类的全限定名,以及配置一个方法名,通过解析配置文件,让配置好的方法执行起来,配置文件的内容如下:

className=包名.Person
methodName=walk

步骤:

  1. 创建properties配置文件,配置信息,需要注意,这个配置文件不能直接放到模块或者项目下,否则在out文件夹中不存在该文件,最常见的做法是在指定目录下创建一个名为resources文件夹,并将这个文件夹标记为Resources Root,然后将配置文件放在这个文件夹内部
  2. 读取配置文件,解析配置文件。读取配置文件可以使用properties集合中的load方法,解析配置文件时不建议直接在创建IO流对象时传递properties文件的地址,这样导致该地址为死地址,而且out文件夹下不会存在resources文件夹。推荐方法:因为配置文件也属于文件,在Java中加载该文件时会产生对应的对象,使用ClassLoader获取当前类的类加载器对象,再使用该对象调用getResourceAsStream ("配置文件名")方法获取InputStream对象。这种方式会自动扫描resources下的文件,可以简单理解为扫描out路径下的配置文件
  3. 根据解析出来的className,创建Class对象
  4. 根据解析出来的methodName,获取对应的方法
  5. 执行方法

IDEA中「将这个文件夹标记为Resources Root」的步骤图:

如果上面的方式中没有显示Resources Root,则可以考虑下面的步骤:

参考代码如下:

// 配置文件
className=com.epsda.advanced.test_reflect_exercise.Person
methodName=walk// 自定义类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {private String name;private Integer age;public void walk() {System.out.println("人在行走");}
}// 测试
package com.epsda.advanced.test_reflect_exercise;import org.junit.Test;import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;/*** ClassName: Test09* Description: 测试** @author 憨八嘎* @version 1.0*/
public class Test09 {@Testpublic void method () throws Exception{ClassLoader classLoader = Test09.class.getClassLoader();InputStream resourceAsStream = classLoader.getResourceAsStream("test.properties");// 读取配置文件Properties properties = new Properties();properties.load(resourceAsStream);// 根据key取出其中的值String methodName = properties.getProperty("methodName");String className = properties.getProperty("className");// 创建Class对象Class<?> aClass = Class.forName(className);Object o = aClass.newInstance();Method declaredMethod = aClass.getDeclaredMethod(methodName);declaredMethod.invoke((Person)o);}
}输出结果:
人在行走

目录结构:

注解

介绍

在Java中,注解也是一种引用数据类型,与类、接口、枚举和Record类同层次

注解常见的作用如下:

  1. 说明:对代码进行说明,生成doc文档(API文档)
  2. 检查:检查代码是否符合条件,例如:@Override@FunctionalInterface
  3. 分析:对代码进行分析,起到了代替配置文件的作用

JDK中常见的注解:

  1. @Override:检测此方法是否为重写方法
    • JDK5版本,支持父类的方法重写
    • JDK6版本,支持接口的方法重写
  2. @Deprecated:表示方法已经过时,不推荐使用,但是依旧可以使用
  3. @SuppressWarnings:消除警告,例如消除所有警告:@SuppressWarnings("all")

在IDEA中,一般被警告的方法默认会有黄色底色,例如下图:

定义注解和属性

在Java中,可以使用下面的格式定义注解:

public @Interface 注解名 {// 属性
}

在注解体内的属性,本质是抽象方法,但是在使用时,与成员属性相同,使用成员属性名=值的方式

属性的定义有两种方式:

  1. 数据类型 属性名():定义一个没有默认值的属性,使用注解时就必须赋值
  2. 数据类型 属性名() default 值:定义一个有默认值的属性,使用注解时可以不需要赋值

可以作为属性的类型:

  1. 所有基本数据类型
  2. String类型
  3. 枚举类型
  4. 注解类型
  5. Class类型
  6. 上面所有类型的一维数组(不可以是二维数组)

例如:

public @interface Book {//书名String bookName();//作者String[] author();//价格int price();//数量int count() default 10;
}

注解的使用

使用注解本质就是为每一个属性(抽象方法)赋值,一般使用位置有下面几种:

  1. 类名上
  2. 方法上
  3. 成员变量上
  4. 局部变量上
  5. 参数位置

使用格式如下:

  1. 普通属性:@注解名(属性名 = 值, 属性名 = 值...)
  2. 属性中有数组:@注解名(属性名 = {元素1,元素2...})

注解使用时需要注意:

  1. 空注解(注解中没有任何的属性)可以直接使用
  2. 不同的位置可以使用一样的注解,但是同样的位置不能使用一样的注解
  3. 使用注解时,如果此注解中有属性没有默认值,则注解中对应的属性一定要赋值。如果有多个属性,用,隔开;如果注解中的属性值有默认值,那么不用显示写,也不用重新赋值
  4. 如果注解中的属性有数组,使用{}
  5. 如果注解中只有一个属性,并且属性名叫value,那么使用注解的时候,属性名不用写,直接写值(包括普通类型和数组)

例如:

// 自定义注解
public @interface Book {//书名String bookName();//作者String[] author();//价格int price();//数量int count() default 10;
}// 测试
@Book(bookName = "寓言故事",author = {"张三","李四"},price = 10,count = 20)
public class BookShelf {
}

解析注解

解析注解即为取出注解对应属性的值,注解涉及的接口是:AnnotatedElement接口,实现类有: AccessibleObjectClassConstructorExecutableFieldMethodPackageParameter

解析思路如下:

  1. 判断指定位置上有没有使用指定的注解:使用方法:boolean isAnnotationPresent(Class<? extends Annotation> annotationClass),如果存在注解则返回为true,否则返回false
  2. 如果有,则获取指定的注解:使用方法:getAnnotation(Class<T> annotationClass)
  3. 通过注解获取指定的值

例如:

// 自定义注解
public @interface Book {//书名String bookName();//作者String[] author();//价格int price();//数量int count() default 10;
}// 自定义类
@Book(bookName = "寓言故事",author = {"张三","李四"},price = 10,count = 20)
public class BookShelf {
}// 测试
public class Test01 {public static void main(String[] args) {//1.获取BookShelf的class对象Class<BookShelf> bookShelfClass = BookShelf.class;//2.判断bookShelf上有没有Book注解boolean b = bookShelfClass.isAnnotationPresent(Book.class);//3.判断,如果b为true就获取if (b) {Book book = bookShelfClass.getAnnotation(Book.class);System.out.println(book.bookName());System.out.println(Arrays.toString(book.author()));System.out.println(book.price());System.out.println(book.count());}}
}

上面的代码没有在控制台中打印运行结果,原因是注解并没有在内存中出现,而class文件在内存中运行,所以导致方法无法获取到对应的注解

元注解

元注解也是注解,这个注解用来管理其他注解,一般管理下面的方面:

  1. 控制注解的使用位置
    1. 控制注解是否能在类上使用
    2. 控制注解是否能在方法上使用
    3. 控制注解是否能在构造上使用等
  2. 控制注解的生命周期(加载位置)
    • 控制注解是否能在源码中出现
    • 控制注解是否能在class文件中出现
    • 控制注解是否能在内存中出现

使用元注解:

  1. 注解@Target:控制注解的使用位置,其属性是个枚举数组ElementType[] value();,枚举的成员可以类名直接调用。常见的成员有:
    • TYPE:控制注解能使用在类上
    • FIELD:控制注解能使用在属性上
    • METHOD:控制注解能使用在方法上
    • PARAMETER:控制注解能使用在参数上
    • CONSTRUCTOR:控制注解能使用在构造上
    • LOCAL_VARIABLE:控制注解能使用在局部变量上
  2. 注解@Retention:控制注解的生命周期(加载位置),其属性是一个枚举对象RetentionPolicy,常见的成员有:
    • SOURCE:控制注解能在源码中出现(默认)
    • CLASS:控制注解能在class文件中出现
    • RUNTIME:控制注解能在内存中出现
需要注意, @Target如果不指定属性,默认是全部可用

使用元注解就可以解决前面解析注解部分无法读取到注解的问题:

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Book {//书名String bookName();//作者String[] author();//价格int price();//数量int count() default 10;
}// 测试
public class Test01 {public static void main(String[] args) {//1.获取BookShelf的class对象Class<BookShelf> bookShelfClass = BookShelf.class;//2.判断bookShelf上有没有Book注解boolean b = bookShelfClass.isAnnotationPresent(Book.class);//3.判断,如果b为true,就获取if (b){Book book = bookShelfClass.getAnnotation(Book.class);System.out.println(book.bookName());System.out.println(Arrays.toString(book.author()));System.out.println(book.price());System.out.println(book.count());}}
}输出结果:
寓言故事
[张三, 李四]
10
20

枚举

基本使用

枚举属于五大引用数据类型中的一种:类、数组、接口、注解、枚举

定义枚举格式如下:

public enum 枚举类名{}

在Java中,所有枚举的父类都是Enum

枚举的特点如下:

  1. 每一个枚举都是static final,但是定义枚举是不能显示写出
  2. 每一个枚举由逗号分隔
  3. 写完所有的枚举值之后,最后一个枚举后方需要加;
  4. 枚举值名字最好大写
  5. 使用时使用枚举类名直接调用枚举成员
  6. 枚举中的成员都是当前枚举类类型的对象
  7. 枚举类中的构造方法都是private修饰

基本使用如下:

public enum Status {RUNNING,WAITING,STOPPED;
}

如果想为每一个枚举赋值,可以在枚举类中定义成员和构造方法,并对外提供获取方法就可以获取到每一个枚举值对应的值

public enum Status {RUNNING("运行"),WAITING("等待"),STOPPED("暂停");private String name;private Status(String name) {this.name = name;}
}

测试如下:

public class Test {public static void main(String[] args) {System.out.println(Status.RUNNING);System.out.println(Status.RUNNING.getName());}
}输出结果:
RUNNING
运行

枚举中的常用方法

方法名

说明

String toString()

返回枚举值的名字

values()

返回所有与的枚举值

valueOf(String str)

将一个字符串转成枚举类型

例如下面的代码:

// 枚举
public enum Status {RUNNING("运行"),WAITING("等待"),STOPPED("暂停");private String name;private Status(String name) {this.name = name;}public String getName() {return name;}
}// 测试
public class Test01 {public static void main(String[] args) {Status status = Status.RUNNING;System.out.println(status.toString());System.out.println(status);Status[] values = Status.values();for (Status value : values) {System.out.println(value);}Status status1 = Status.valueOf("RUNNING");System.out.println(status1);}
}

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

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

相关文章

从0-1搭建海外社媒矩阵,详细方案深度拆解

做买卖&#xff0c;好的产品质量固然是关键&#xff0c;但如何让更多的消费者知道&#xff1f;营销推广是必不可少的。在互联网时代&#xff0c;通过社交媒体推广已经成为跨境电商卖家常用的广告手段。 如何通过海外社交媒体矩阵扩大品牌影响力&#xff0c;实现营销目标&#…

【开源项目】数字孪生智慧停车场——开源工程及源码

飞渡科技数字孪生停车场管理平台&#xff0c;基于国产数字孪生3D渲染引擎&#xff0c;结合数字孪生、物联网IOT&#xff0c;以及车牌自动识别、视频停车诱导等技术&#xff0c;实现停车场的自动化、可视化和无人化值守管理。 以3D可视化技术为基础&#xff0c;通过三维场景完整…

240927-各种卷积最清晰易懂blender动画展示

240927-一些常用卷积清晰易懂的blender动画展示&#xff08;Conv、GConv、DWConv、1*1Conv、Shuffle&#xff09; 在几个月前&#xff0c;写过一篇关于卷积过程中输入图像维度变化的博客240627_关于CNN中图像维度变化问题_图像的尺寸为什么又四个维度-CSDN博客&#xff0c;但是…

猫鱼分干(模拟---拆分步骤)

算法分析&#xff1a; 注意&#xff1a;总是更新遍历方向上的元素&#xff08;eg. 左 i-1 和 i &#xff1a;更新i&#xff09;区分水平和分配量从左向右&#xff1a;只要右侧水平大于左侧&#xff0c;即右侧等于左侧值加一从右向左&#xff1a;若左侧水平大于右侧&#xf…

一次实践:给自己的手机摄像头进行相机标定

文章目录 1. 问题引入2. 准备工作2.1 标定场2.2 相机拍摄 3. 基本原理3.1 成像原理3.2 畸变校正 4. 标定解算4.1 代码实现4.2 详细解析4.2.1 解算实现4.2.2 提取点位 4.3 解算结果 5. 问题补充 1. 问题引入 不得不说&#xff0c;现在的计算机视觉技术已经发展到足够成熟的阶段…

c++day08

思维导图 栈 #include <iostream>using namespace std;template <typename T> class Stack { private:static const size_t MAX 100; // 定义固定容量T data[MAX]; // 存储栈元素的数组size_t len; // 当前栈的大小public:…

浅谈电气火灾监控系统在变电所的应用

摘要&#xff1a;阐述电气火灾监控系统在变电所的应用&#xff0c;电气火灾监控系统的管理措施&#xff0c;包括运行标准、运行模式、运行原则、警报阈值、监控显示。安科瑞叶西平1870*6160015 关键词:监控系统&#xff1b;警报阀值&#xff1b;运行模式&#xff1b;医院&…

高效免费!PDF秒变Word,在线免费转换工具推荐!!!

#创作灵感# 工作中&#xff0c;总是需要将pdf文件转换成word文件&#xff0c;便于后期编辑、处理、使用&#xff0c;但是又没有wps会员&#xff0c;虽然去淘宝买&#xff0c;一天也就8毛钱左右&#xff0c;但是转换文件的工作几乎每天都需要做&#xff0c;长此以往&#xff0c;…

7.字符串 Strings

作业系统链接 字符串文字可以使用单引号、双引号或三引号来定义&#xff0c;其中三引号特别适用于多行字符串。转义序列如\n&#xff08;换行&#xff09;和\t&#xff08;制表符&#xff09;在字符串中起到特殊作用。字符串方法如replace()、strip()、lower()和upper()提供了丰…

外国名人面孔识别系统源码分享

外国名人面孔识别检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer…

艺术作品风格识别系统源码分享

艺术作品风格识别检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer…

Java数据结构--List介绍

前言&#xff1a; 数据结构可以说是一门思想&#xff0c;当我们在对数据处理、储存的时候需要用到。 前面我用C语言写过数据结构的相关内容&#xff0c;在Java阶段的数据结构思想是一样的&#xff0c;就是有些地方实现的方式是有区别的。 因此在Java阶段前期的数据结构&#xf…

Python的包管理工具pip安装

Python的包管理工具pip安装 一、安装步骤1.检查 pip是否已安装2.安装 pip方法一&#xff1a;通过 ​ensurepip​ 模块安装(推荐)方法二&#xff1a;通过 ​get-pip.py​ 脚本安装&#xff08;经常应为网络域名问题连接不上&#xff09; 3.验证pip安装4.创建别名5.更新pip 二、常…

找不到msvcr100.dll怎么解决?总结6个有效的解决方法

在使用计算机的过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“msvcr100.dll丢失”。这个问题可能会让我们感到困惑和无助&#xff0c;但是不用担心&#xff0c;本文将为大家介绍六种实用的解决方法&#xff0c;帮助你轻松解决这个问题。 一&#xff…

Lenovo SR850服务器亮黄灯维修和升级CPU扩展模块

佛山市三水区某高校1台Lenovo Thinksystem SR850服务器黄灯故障到现场检修 和 升级3号和4号CPU。加强服务器的计算性能&#xff1b; 故障情况是该学校it管理员这一天看到这台SR850服务器前面板亮了一个黄灯&#xff0c;但是目前系统运行正常&#xff0c;出于安全考虑&#xff0…

【如何学习操作系统】——学会学习的艺术

&#x1f41f;作者简介&#xff1a;一名大三在校生&#xff0c;喜欢编程&#x1fab4; &#x1f421;&#x1f419;个人主页&#x1f947;&#xff1a;Aic山鱼 &#x1f420;WeChat&#xff1a;z7010cyy &#x1f988;系列专栏&#xff1a;&#x1f3de;️ 前端-JS基础专栏✨前…

[数据结构] 二叉树题目(一)

目录 一.翻转二叉树 1.1 题目 1.2 示例 1.3 分析 1.4 解决 ​编辑 二. 相同的树 2.1 题目 2.2 示例 2.3 分析 2.4 解决 三. 对称二叉树 3.1 题目 3.2 示例 3.3 分析 3.4 解决 一.翻转二叉树. - 力扣&#xff08;LeetCode&#xff09; 1.1 题目 1.2 示例 1.3 分…

python的 __name__和__doc__属性

__name__属性 __name__属性 用于判断当前模块是不是程序入口&#xff0c;如果当前程序正在使用&#xff0c;__name__的值为__main__。 在编写程序时&#xff0c;通常需要给每个模块添加条件语句&#xff0c;用于单独测试该模块的功能。 每个模块都有一个名称&#xff0c;当一…

大屏走马灯与echarts图表柱状图饼图开发小结

一、使用ant-design-vue的走马灯(a-carousel)注意事项 <!-- 左边的轮播图片 --><a-carousel :after-change"handleCarouselChange" autoplay class"carousel" :transition"transitionName"><div v-for"(item, index) in it…

每日论文4——09TCAS1分数锁相环的动态电流匹配电荷泵和门偏置线性化技术

《Dynamic Current-Matching Charge Pump and Gated-Offset Linearization Technique for Delta-Sigma Fractional- PLLs》09TCAS1 本文CP的结构比较简洁&#xff0c;没有使用OPA&#xff0c;所以相比起来减小了功耗和面积&#xff0c;但是目测起来匹配效果是不如用OPA的CP的。…