枚举和泛型
一、枚举
1.定义
枚举 = 特殊的 final 类 + 单例 + 常量列表 + 可带行为,天然线程安全、可 switch、可策略。
知识点 | 说明 |
定义语法 | enum 名字 { 常量1, 常量2; } |
本质 | 编译后生成 final class Color extends java.lang.Enum<Color> 因此不能再继承其他类,但可以实现接口。 |
枚举常量 | 都是 public static final 的实例,自动调用私有构造方法完成初始化。 |
构造方法 | 只能 private,不能 public/protected;在类加载时由 JVM 调用一次。 |
常用自带方法 | values()、valueOf(String)、name()、ordinal() |
switch 支持 | 枚举天生可用于 switch,且编译器会检查完整性。 |
线程安全 | 枚举实例由 JVM 保证 懒加载 + 单例,天然线程安全,常用于单例模式。 |
2、易踩坑提示
1. 枚举构造器 不能 访问枚举的静态字段(初始化顺序原因)。
2. 不要在枚举构造函数里做耗时操作,会导致类加载卡死。
3. 使用 == 比较枚举即可,无需 equals ,因为枚举是单例。
3、示例
枚举类:Cat
1. 枚举天生就是 final 且单例的类,不能再被继承或 new。
2. 每一个枚举常量(A、B、C)都是该类的一个 public static final 实例。
3. 枚举可以像普通类一样拥有:属性、构造方法、普通方法、抽象方法等。
package com.qcby.枚举;public enum Cat {1. 定义枚举常量(实例)并立即调用构造方法完成初始化A("小黑", 2), 等价于 public static final Cat A = new Cat("小黑", 2);B("大橘", 3),C("小花", 4);2. 枚举成员变量:每个枚举实例(小黑/大橘/小花)都有 name 和 ageprivate String name;private int age;3. 枚举构造方法:必须是 **private**,不能是 public/protectedprivate Cat(String name, int age) {this.name = name;this.age = age;}4. 枚举普通方法:供外部或内部调用public void m1() {System.out.println("姓名:" + name + " 年龄:" + age);}5. 可写 main 方法快速测试public static void main(String[] args) {Cat.A.m1(); // 姓名:小黑 年龄:2Cat.B.m1(); // 姓名:大橘 年龄:3Cat.C.m1(); // 姓名:小花 年龄:4}
}
二、泛型
让类型参数化,默认都转成object。obj如果没赋值默认null。
把原来写死的类、接口或方法里的数据类型,变成一个可传入的“类型参数”,让同一份代码可以安全地处理不同种类的对象,而不用到处强制转换。
泛型定义:class 类名<泛型标识1, 泛型标识2, ...> 常用标识 T E K V
泛型使用:类名<数据类型> 对象名 = new 类名<数据类型>();
List<String> list = new ArrayList<>();
list.add("hello");
String s = list.get(0); // 无需强转,编译期检查
泛型只支持类类型(不能是基本类型 int、double…)基本类型不能转obj
在创建对象时,如果没有指定具体数据类型,则按 Object 处理
泛型类在逻辑上可看成多个不同类型,实际上仍是同一个类型(运行时都被擦除成原始类型)
子类也是泛型:子类和父类的泛型要保持一致;若父类未指明类型,则父类按 Object 处理
public class Child<T> extends Parent<T> {// 这里可以写子类特有代码父类的<T> 可以省
}
子类不是泛型:父类必须明确泛型的具体类型;若父类未指明,则按 Object 处理
public class Child extends Parent<T> {// 这里可以写子类特有代码
}
泛型接口:interface 接口名<泛型标识1, 泛型标识2, ...>
public class Cat implements Animal<String> {@Overridepublic String run() {// TODO Auto-generated method stubreturn null;}
}
实现类是泛型:实现类与接口的泛型要保持一致;若接口未指明类型,则接口按 Object 处理 实现类不是泛型:实现类必须为接口明确指明泛型类型;若接口未指明,则按 Object 处理
三、代理
代理对象在用户和目标对象之间插入额外服务(日志、事务、权限…),并且提供了静态代理与 动态代理两种实现路线。
1、代理模式总览
• 用户(Client):真正要调用功能的人/代码。
• 目标对象(RealSubject):真正干活的业务类。
• 代理对象(Proxy):持有目标对象引用,在调用前后加“额外服务”。
• 抽象接口/父类:保证代理与目标拥有相同的方法签名,用户无感知。
2. 分类
• 静态代理:代理类在 编译期 写好,一个代理只能服务一种接口/类。
静态代理有两种实现方式:
通过继承实现
通过组合实现
• 动态代理:代理类在 运行期 生成,一个代理处理器可服务多种目标。
JDK 动态代理:目标必须实现接口。
CGLIB 动态代理:目标可以没有接口,但必须有可继承的父类。
四、静态代理
抽象接口
public interface UserService {void save();
}
目标类
public class UserServiceImpl implements UserService {public void save() {System.out.println("保存用户");}
}
静态代理
public class UserServiceProxy implements UserService {private final UserService target;public UserServiceProxy(UserService target) {this.target = target;}public void save() {System.out.println("---开启事务---");target.save();System.out.println("---提交事务---");}
}
使用
UserService proxy = new UserServiceProxy(new UserServiceImpl());
proxy.save();