晚上好,愿这深深的夜色给你带来安宁,让温馨的夜晚抚平你一天的疲惫,美好的梦想在这个寂静的夜晚悄悄成长。
文章目录
目录
前言
一、枚举
1.1 枚举的概念
编辑
1.2 枚举的特点
1.3 枚举的实际运用
1. 状态机,描述属性的多种状态
2. 策略模式,让客户端动态切换不同的策略
3. 数据字典,定义一组错误代码展示对应的错误消息
4. 迭代器模式,枚举类型的循环使用
二、注解
2.1 注解的概念
2.注解的特点
2.1 格式:
2.2 注解的分类
1. 内置注解:
2. 自定义注解:
2.3 用途
2.4 使用反射获得是否被指定注解修饰
1. 判断类是否被注解修饰
2. 判断方法是否被注解修饰
2.5 模拟tomcat输入对应的路径执行对应的方法
1. 自定义@Controller注解
2. 自定义@RequestMapping注解
3. 自定两个控制器
4. 模拟tomcat
总结
前言
枚举和注解都是 Java 中重要的语言特性,枚举用于定义常量集合和类型安全,而注解是元数据的一种形式,可以在代码中添加元数据信息,用来提供给编译器和其他工具进行特定的处理。通过合理使用枚举和注解,可以提高代码的可读性、灵活性和功能性。注解就一句核心:注解是给程序看的注释,注解离开了反射什么也不是。
一、枚举
枚举是一种特殊的数据类型,用于定义一组命名的常量集合。
1.1 枚举的概念
1. 类被public final修饰,默认继承Enum类,因此不能被继承也不可继承。
2. 成员变量默认被public static final enum修饰,并且多个成员之间用逗号隔开,会按照声明顺序按照0开始分配序号。name值为变量名。CompareTo默认根据序号比较。
3. 默认:直接打印返回的是变量名。因为底层toString()方法默认返回name
1.2 枚举的特点
-
声明常量集合:枚举允许开发者定义一组具名的常量,这些常量在整个程序中可以被安全地引用。
-
类型安全:枚举提供了类型安全性,编译器会检查枚举常量的使用,避免使用错误的常量值。
-
增强代码可读性:通过枚举,可以使用具有语义的名称来表示常量,提高代码的可读性和可维护性。
-
支持方法和属性:枚举不仅可以包含常量,还可以定义方法和属性,使得枚举可以拥有更复杂的行为。
-
switch 语句的支持:Java 的枚举可以很方便地与 switch 语句结合使用,提高代码的简洁性和可读性。
1.3 枚举的实际运用
1. 状态机,描述属性的多种状态
标记一个商品表中的订单属性的状态,其中含有枚举常量,新订单、处理中、完成、取消
package enum_demo1;/*** @author windStop* @version 1.0* @description 枚举作用一:状态机* @date 2024年07月26日16:05:43*/
//枚举订单状态
enum OrderStatus {NEW, // 新订单PROCESSING, // 处理中COMPLETED, // 完成CANCELED; // 取消
}
//订单JavaBean
class Order {private OrderStatus status;public OrderStatus getStatus() {return status;}public void setStatus(OrderStatus status) {this.status = status;}
}
public class FiniteStateMachine {public static void main(String[] args) {Order order = new Order();//1. 获取订单状态OrderStatus currentStatus = order.getStatus();//2. 设置订单状态order.setStatus(OrderStatus.NEW); // 设置为新订单状态order.setStatus(OrderStatus.PROCESSING); // 设置为处理中状态order.setStatus(OrderStatus.COMPLETED); // 设置为完成状态order.setStatus(OrderStatus.CANCELED); // 设置为取消状态//3. 使用订单状态if (order.getStatus() == OrderStatus.NEW) {// 处理新订单的逻辑} else if (order.getStatus() == OrderStatus.PROCESSING) {// 处理处理中的订单逻辑} else if (order.getStatus() == OrderStatus.COMPLETED) {// 处理完成的订单逻辑} else if (order.getStatus() == OrderStatus.CANCELED) {// 处理取消的订单逻辑}}
}
2. 策略模式,让客户端动态切换不同的策略
策略模式(Strategy Pattern)是一种行为设计模式,它允许定义一系列算法,并将每种算法封装到具有共同接口的独立类中,使它们可以互相替换。策略模式使得算法可以独立于客户端而变化,从而使得客户端可以根据需要在运行时选择算法。
模拟用户选择的按钮选择对应的支付方式。
package enum_demo2;
/*** @author windStop* @version 1.0* @description 枚举作用二:策略模式* @date 2024年07月26日16:06:26*/
public enum PaymentMethod {CREDIT_CARD {@Overridepublic void pay(double amount) {// 实现信用卡支付逻辑}},PAYPAL {@Overridepublic void pay(double amount) {// 实现PayPal支付逻辑}},BANK_TRANSFER {@Overridepublic void pay(double amount) {// 实现银行转账支付逻辑}};public abstract void pay(double amount);
}
3. 数据字典,定义一组错误代码展示对应的错误消息
通过枚举类型 ErrorCode
来定义一组固定的错误代码和对应的错误消息,以提高代码的可读性和维护性,并用于统一管理常量值或数据字典。
package enum_demo3;/*** @author windStop* @version 1.0* @description 枚举作用三:用枚举类来表示数据字典或常量值* @date 2024年07月26日16:11:07*/
public enum ErrorCode {INVALID_INPUT(1001, "Invalid input"),NETWORK_ERROR(2001, "Network error"),DATABASE_ERROR(3001, "Database error");private final int code;private final String message;ErrorCode(int code, String message) {this.code = code;this.message = message;}public int getCode() {return code;}public String getMessage() {return message;}
}
4. 迭代器模式,枚举类型的循环使用
虽然这里称为迭代器模式,但更确切地说,它展示了枚举类型的循环使用。通过 getNextDay
方法,可以方便地获取当前一周中某一天的下一天,使得处理周期性任务或日期操作更加便捷和可读。
package enum_demo4;
/*** @author windStop* @version 1.0* @description 枚举功能四:迭代器模式* @date 2024年07月26日16:14:24*/
public enum Weekday {MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;/*** getNextDay 是一个静态方法,用于获取当前给定 currentDay 的下一天是什么。* @param currentDay* @return* currentDay.ordinal() 返回 currentDay 枚举常量的序号(从 0 开始)。* values() 方法返回 Weekday 枚举类型的所有枚举常量的数组。* (currentDay.ordinal() + 1) % values().length 计算出当前 currentDay 的下一个枚举常量的序号。由于枚举是循环的,所以用取模运算确保循环。* values()[...] 使用计算得到的序号获取下一个枚举常量。*/public static Weekday getNextDay(Weekday currentDay) {return values()[(currentDay.ordinal() + 1) % values().length];}
}
二、注解
2.1 注解的概念
Java 注解(Annotation)是 Java 语言提供的一种元数据(metadata)机制,用于在源代码中添加关于程序元素的额外信息,这些信息可以被编译器、工具和运行时框架使用。注解以
@
符号开头,放置在类、方法、变量等元素的定义前面。给程序的注释。注解本身是没有任何作用的,注解只是用来标记,而这个注解真正的功能都是由框架通过反射来实现的。离开了反射什么也不是。
2.注解的特点
注解只有成员变量,没有方法。注解的属性在注解的定义中以“无形参的抽象方法”形式来声明,其方法名定义了该属性的名字,其返回值定义了该属性的类型。
默认会继承java.lang.annotation.Annotation 接口。
2.1 格式:
public @interface MyAnno {String value();String name() default "匿名";int age() default 21;
}
- 看似属性很像一个 抽象类,可千万不要理解成抽象类。
- 这些成员变量用于定义注解的元素。编译器会在底层自动生成相关的接口和方法,以便在运行时获取注解的元数据。
- 注解的属性在注解的定义中以“无形参的抽象方法”形式来声明,其方法名定义了该属性的名字,其返回值定义了该属性的类型。
- default 声明默认值。
2.2 注解的分类
1. 内置注解:
- 元注解: 用于注解其他注解的注解,如
@Retention
、@Target
、@Documented
等。
- 预定义注解: Java提供了一些内置的注解,如
@Override
、@Deprecated
、@SuppressWarnings
等,用于标记方法的重写、标记过时的方法或类、抑制编译器警告等。
-
@Override:用于标识一个方法覆盖了父类中的方法。
-
@Deprecated:用于标记已过时的方法、类或字段,表示不推荐使用,通常是因为有更好的替代方法。
-
@SuppressWarnings:抑制编译器警告,可以用于方法、类、字段等,告诉编译器忽略特定的警告信息。
-
@SafeVarargs:用于抑制 Java 编译器关于使用可变参数方法时的警告信息。
-
@FunctionalInterface:用于指示接口类型是一个函数式接口,即只包含一个抽象方法的接口,可以用作 Lambda 表达式的类型。
-
@Native:用于标记一个方法是本地方法,即通过 JNI(Java Native Interface)调用的方法。
-
@Target:用于指定注解可以应用的地方,例如方法、类、字段等。
-
@Retention:用于指定注解的保留策略,即注解在什么级别可用,如源码(SOURCE)、类文件(CLASS)、运行时(RUNTIME)。
-
@Documented:用于指定注解是否包含在 JavaDoc 中。
2. 自定义注解:
- 可以定义自己的注解,通过元注解指定注解的使用方式、目标和保留策略,定义注解的成员变量来传递注解的参数信息,并通过反射进行获取。
1.定义自定注解
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnno {String value();String name() default "匿名";int age() default 21;
}
2. 定义javabean
public class Student {@MyAnno("小明")private String name;private Integer age;
}
3. 通过反射使用注解,并将注解的值赋值给javabean,从而实现spring中的@Value
package anno_demo1;import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*** @author windStop* @version 1.0* @description 测试注解* @date 2024年07月27日15:00:47*/
public class Test{public static void main(String[] args) throws Exception{Class<Student> studentClass = Student.class;Student student = studentClass.getDeclaredConstructor().newInstance();Method[] declaredMethods = studentClass.getDeclaredMethods();for (Method method : declaredMethods) {method.setAccessible(true);// 判断方法上是否存在Init注解,存在就返回true,否则返回falseif (method.isAnnotationPresent(MyAnno.class)) {// 返回该方法的注解MyAnno init = method.getAnnotation(MyAnno.class);// 执行这个方法并且将注解的值给其赋值method.invoke(student, init.value());}}Field name = studentClass.getDeclaredField("name");name.setAccessible(true);MyAnno annotation = name.getAnnotation(MyAnno.class);name.set(student,annotation.value());System.out.println(student);}
}
2.3 用途
-
作用:
- 元数据标记:注解提供了一种在代码中添加元数据(metadata)的方式,用于描述类、方法、字段等程序元素的信息。
- 编译时处理:可以在编译时和运行时通过反射机制获取注解信息,从而影响程序的行为。
- 代码分析工具:许多开发工具和框架利用注解来配置和扩展应用,例如 Spring 框架中的
@Autowired
、@RequestMapping
等。
-
在项目中的应用:
- 配置管理:用于配置和管理各种应用程序的行为和属性,如数据源配置、事务管理等。
- 代码分析和验证:通过自定义注解来实现代码风格检查、安全检查等。
- 框架集成:许多流行的框架和库都使用注解来简化配置和扩展,如 Spring、JUnit 等。
- 文档生成:通过注解生成文档,如使用 Swagger 的
@Api
和@ApiOperation
注解。
2.4 使用反射获得是否被指定注解修饰
1. 判断类是否被注解修饰
// 首先判断Test类上是否使用了TestAnnotation注解
boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
2. 判断方法是否被注解修饰
method.isAnnotationPresent(MyAnno.class)
下述是一个代码找出所有被@MyAnno修饰的方法 ,并将value赋值给形参
public static void main(String[] args) throws Exception{Class<Student> studentClass = Student.class;Student student = studentClass.getDeclaredConstructor().newInstance();Method[] declaredMethods = studentClass.getDeclaredMethods();for (Method method : declaredMethods) {method.setAccessible(true);// 判断方法上是否存在Init注解,存在就返回true,否则返回falseif (method.isAnnotationPresent(MyAnno.class)) {// 返回该方法的注解MyAnno init = method.getAnnotation(MyAnno.class);// 执行这个方法并且将注解的值给其赋值method.invoke(student, init.value());}}
}
3. 判断属性是否被注解修饰
//判断属性有没有被注解修饰boolean annotationPresent = name.isAnnotationPresent(MyAnno.class);
取出name属性并将其上面的MyAnno注解的值赋值给其成员变量
Student student = studentClass.getDeclaredConstructor().newInstance(); Field name = studentClass.getDeclaredField("name");name.setAccessible(true);MyAnno annotation = name.getAnnotation(MyAnno.class);name.set(student,annotation.value());System.out.println(student);
2.5 模拟tomcat输入对应的路径执行对应的方法
1. 自定义@Controller注解
介绍使用到的元注解
@Target(ElementType.TYPE):该注解用于类上
@Documented:会生成到javadoc中
@Retention(RetentionPolicy.RUNTIME):该注解会被保留到运行时期
package anno_tomcat.tomcat_method;import java.lang.annotation.*;/*** @author windStop* @version 1.0* @description 标记注解:标记是一个请求控制器* @date 2024年07月27日20:04:38*/
@Target(ElementType.TYPE)
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {String[] value() default "";
}
2. 自定义@RequestMapping注解
自定在方法上。生效到运行时期
package anno_tomcat.tomcat_method;import java.lang.annotation.*;/*** @author windStop* @version 1.0* @description 映射路径注解* @date 2024年07月27日20:11:44*/
@Target(ElementType.METHOD)
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {String value();
}
3. 自定两个控制器
package anno_tomcat.tomcat_method;/*** @author windStop* @version 1.0* @description 测试:给方法声明注解的作用,获取URL和请求路径* @date 2024年07月27日20:03:51*/
@SuppressWarnings("all")
@Controller
public class StudentController {@RequestMapping("/addStudent")public boolean addStudent(int id){System.out.println("学生添加成功");return true;}@RequestMapping("/updateStudent")public boolean updateStudent(int id){System.out.println("学生修改成功");return true;}@RequestMapping("/deleteStudent")public boolean deleteStudent(int id){System.out.println("学生删除成功");return true;}@RequestMapping("/getStudent")public Student getStudent(String name){System.out.println("学生查询成功");return new Student(name,18);}
}
package anno_tomcat.tomcat_method;/*** @author windStop* @version 1.0* @description 测试book* @date 2024年07月27日21:21:38*/
@Controller
public class BookController {@RequestMapping("/showBook")public void show(int a){System.out.println("show方法");}@RequestMapping("/addBook")public void addBook(int a) {System.out.println("正在添加图书");}
}
4. 模拟tomcat
我现在是tomcat开发程序员,我现在要使用未来某个时刻的二逼程序员写的代码。
思路分析:
- 首先,我不知道他是什么时候写的,并且类名叫什么,
- 但我可以给他提供一个规范,就是实现@Controller注解,并且项目在当前包中,路径映射写在@RequestMapping中。
- 最后,他只要符合我的要求,我就可以使用他的代码。
代码分析:
- 不知道程序员什么时候写,那就需要使用反射的Class.forName(className),来在运行时动态加载类的机制,适合需要根据运行时条件加载不同类的场景。
- 扫描含有@Controller注解的全类名,进行获取反射类,然后获取出所有方法,在扫描出含有@RequestMapping,注解并且提取其属性和你输入属性对比,相同调用其注解标识的方法。
package anno_tomcat.tomcat_method;import java.io.File;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;/*** @author windStop* @version 1.0* @description 模拟tomcat,使用用户的代码* @date 2024年07月27日20:14:06*/
public class TomcatDemo {public static void main(String[] args) throws Exception {Scanner sc = new Scanner(System.in);//1. 获取全类名List<String> listClass = findClass("anno_tomcat.tomcat_method");String url = sc.next();//用户输入的urlfor (String className : listClass) {Class<?> aClass = Class.forName(className);//2. 判断是否包含Controller注解if (aClass.isAnnotationPresent(Controller.class)) {//创建javabean对象Object o = aClass.getDeclaredConstructor().newInstance();//3. 根据输入的全类名执行对应的方法//忽略 用户输入的 /
// url = url.replace("/", "");Method[] methods = aClass.getDeclaredMethods();for (Method method : methods) {//4. 只查询被@RequestMapping修饰的方法if (method.isAnnotationPresent(RequestMapping.class)){// 获取注解,这个相当于是真正的拿到注解了,只有获取到这个才能获取到注解当中设置的值RequestMapping annotation = method.getAnnotation(RequestMapping.class);if (url.equals(annotation.value())){method.invoke(o,0);}}}}}}/*** 查找指定路径的全类名* @return*/public static List<String> findClass(String packageName){//指定要查看的包名String packagePath = packageName.replace('.', '\\'); // 将包名转换为路径格式List<String> result = new ArrayList<>();// 获取当前工作目录下指定包的路径String currentDirectory = System.getProperty("user.dir");String packageDirectoryPath = currentDirectory + "\\src\\" + packagePath;File packageDirectory = new File(packageDirectoryPath);// 检查该路径是否存在且为目录if (packageDirectory.exists() && packageDirectory.isDirectory()) {// 获取目录下的文件列表File[] files = packageDirectory.listFiles();//获得所有子文件if (files != null) {for (File file : files) {String str = packageName;//包名str = str + "." + file.getName();//全类名//删除.java后缀//找出最后一个点的位置int lastIndex = str.lastIndexOf('.');str = str.substring(0,lastIndex);result.add(str);}} else {System.out.println("在包中找不到文件 " + packageName);}} else {System.out.println("包 " + packageName + " 没有找到");}return result;}
}
总结
这二者在以后Spring框架中是十分重要的,在Spring框架中,枚举(Enums)和注解(Annotations)各自发挥着重要的作用。枚举通常用于定义常量集合和限制参数取值范围,增强代码的可读性和安全性。而注解则用于声明依赖关系、配置项、AOP切面、事务管理以及RESTful API映射等方面,提供了声明式和便捷的编程方式。枚举和注解的结合使得Spring应用更加灵活、简洁和易于维护,是现代Java开发中不可或缺的核心特性。因此我觉得他们是非常值得我们学习的知识。