mirror of
https://github.com/Estom/notes.git
synced 2026-02-11 06:15:45 +08:00
优化JAVA和Script
This commit is contained in:
@@ -154,7 +154,7 @@ Java既需要编译也需要解释执行。
|
||||
|
||||
``` java
|
||||
/**
|
||||
* Java Doc 中的祖师格式
|
||||
* Java Doc 中的注释格式
|
||||
* /
|
||||
```
|
||||
|
||||
@@ -439,7 +439,7 @@ Java定义了位运算符,应用于整数类型(int),长整型(long),短
|
||||
| 操作符 | 描述 | 例子 |
|
||||
|---------------------|--------------------------------------------|--------------------------------|
|
||||
| & | 如果相对应位都是1,则结果为1,否则为0 | (A&B),得到12,即0000 1100 |
|
||||
| | | 如果相对应位都是 0,则结果为 0,否则为 1 | (A | B)得到61,即 0011 1101 |
|
||||
| \| | 如果相对应位都是 0,则结果为 0,否则为 1 | (A \| B)得到61,即 0011 1101 |
|
||||
| ^ | 如果相对应位值相同,则结果为0,否则为1 | (A ^ B)得到49,即 0011 0001 |
|
||||
| 〜 | 按位取反运算符翻转操作数的每一位,即0变成1,1变成0。 | (〜A)得到-61,即1100 0011 |
|
||||
| << | 按位左移运算符。左操作数按位左移右操作数指定的位数。 | A << 2得到240,即 1111 0000 |
|
||||
@@ -453,8 +453,7 @@ Java定义了位运算符,应用于整数类型(int),长整型(long),短
|
||||
| 操作符 | 描述 | 例子 |
|
||||
|------------|--------------------------------------------------|--------------------|
|
||||
| && | 称为逻辑与运算符。当且仅当两个操作数都为真,条件才为真。 | (A&&B)为假。 |
|
||||
|
|
||||
`||` | 称为逻辑或操作符。如果任何两个操作数任何一个为真,条件为真。 | (A`||`B)为真。 |
|
||||
| `\|\|` | 称为逻辑或操作符。如果任何两个操作数任何一个为真,条件为真。 | (A`\|\|`B)为真。 |
|
||||
| ! | 称为逻辑非运算符。用来反转操作数的逻辑状态。如果条件为true,则逻辑非运算符将得到false。 | !(A&&B)为真。 |
|
||||
|
||||
* 短路运算。当使用与逻辑运算符时,在两个操作数都为true时,结果才为true,但是当得到第一个操作为false时,其结果就必定是false,这时候就不会再判断第二个操作了。
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
- [数组参数](#数组参数)
|
||||
- [数组返回值](#数组返回值)
|
||||
- [多维数组](#多维数组)
|
||||
- [2 Arrays类](#2-arrays类)
|
||||
- [方法概述](#方法概述)
|
||||
- [具体方法](#具体方法)
|
||||
# Java 数组
|
||||
## 1 概述
|
||||
|
||||
@@ -120,30 +117,3 @@ s[1][0] = new String("to");
|
||||
s[1][1] = new String("you");
|
||||
s[1][2] = new String("!");
|
||||
```
|
||||
|
||||
|
||||
## 2 Arrays类
|
||||
|
||||
### 方法概述
|
||||
|
||||
* 给数组赋值:通过 fill 方法。
|
||||
* 对数组排序:通过 sort 方法,按升序。
|
||||
* 比较数组:通过 equals 方法比较数组中元素值是否相等。
|
||||
* 查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找法操作。
|
||||
|
||||
|
||||
### 具体方法
|
||||
```java
|
||||
public static int binarySearch(Object[] a, Object key)
|
||||
用二分查找算法在给定数组中搜索给定值的对象(Byte,Int,double等)。数组在调用前必须排序好的。如果查找值包含在数组中,则返回搜索键的索引;否则返回 (-(插入点) - 1)。
|
||||
public static boolean equals(long[] a, long[] a2)
|
||||
如果两个指定的 long 型数组彼此相等,则返回 true。如果两个数组包含相同数量的元素,并且两个数组中的所有相应元素对都是相等的,则认为这两个数组是相等的。换句话说,如果两个数组以相同顺序包含相同的元素,则两个数组是相等的。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。
|
||||
public static void fill(int[] a, int val)
|
||||
将指定的 int 值分配给指定 int 型数组指定范围中的每个元素。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。
|
||||
public static void sort(Object[] a)
|
||||
对指定对象数组根据其元素的自然顺序进行升序排列。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。
|
||||
public static void toString(array[])
|
||||
依次打印元素
|
||||
public static void stream()
|
||||
转化成一个流
|
||||
```
|
||||
@@ -216,7 +216,7 @@ static{
|
||||
|
||||
- 每次执行类,加载类的时候都会先执行静态代码块一次。
|
||||
- 静态代码块是自动触发执行的,只要程序启动静态代码块就会先执行一次。
|
||||
- 作用:在启动程序之前可以做资源的初始化,一般用于初始化静态资源。
|
||||
- 作用:在启动程序之前可以做资源的初始化,一般用于初始化静态资源。注册驱动、加载 native 库、埋监控;
|
||||
|
||||
**案例演示**
|
||||
|
||||
@@ -242,7 +242,7 @@ public class DaimaKuaiDemo01 {
|
||||
### 实例代码块
|
||||
|
||||
**实例代码块**
|
||||
没有static修饰,必须放在类下。与对象初始化一起加载。
|
||||
没有static修饰,必须放在类下。与对象初始化一起加载。且先于构造函数执行。
|
||||
|
||||
**格式**
|
||||
|
||||
@@ -256,8 +256,8 @@ public class DaimaKuaiDemo01 {
|
||||
|
||||
- 无static修饰。属于对象,与对象的创建一起执行的。
|
||||
- 每次调用构造器初始化对象,实例代码块都要自动触发执行一次。
|
||||
- 实例代码块实际上是提取到每一个构造器中去执行的。
|
||||
- 作用:实例代码块用于初始化对象的资源。
|
||||
- 实例代码块实际上是提取到每一个构造器中去执行的。且先于构造函数执行。
|
||||
- 作用:实例代码块用于初始化对象的资源。例如:设置复杂的初始值,
|
||||
|
||||
**案例演示**
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
### Class对象
|
||||
|
||||
Class对象的作用
|
||||
* 每个类都有一个**Class**对象,包含了与类有关的信息,代表整个字节码。代表一个类型,代表整个类。。当编译一个新类时,会产生一个同名的 .class 文件,该文件内容保存着 Class 对象。
|
||||
* 每个类都有一个**Class**对象,包含了与类有关的信息,代表整个字节码。代表一个类型,代表整个类。当编译一个新类时,会产生一个同名的 .class 文件,该文件内容保存着 Class 对象。
|
||||
|
||||
* 类加载相当于 Class 对象的加载,类在第一次使用时才动态加载到 JVM 中。也可以使用 `Class.forName("com.mysql.jdbc.Driver")` 这种方式来控制类的加载,该方法会返回一个 Class 对象。
|
||||
|
||||
@@ -113,7 +113,6 @@ class ReflectTest02{
|
||||
// 通过反射机制,获取Class,通过Class来实例化对象
|
||||
Class c = Class.forName("javase.reflectBean.User");
|
||||
// newInstance() 这个方法会调用User这个类的无参数构造方法,完成对象的创建。
|
||||
// 重点是:newInstance()调用的是无参构造,必须保证无参构造是存在的!
|
||||
Object obj = c.newInstance();
|
||||
System.out.println(obj);
|
||||
}
|
||||
|
||||
@@ -14,8 +14,31 @@
|
||||
- [4 泛型通配符](#4-泛型通配符)
|
||||
- [5 泛型中的 KTVE](#5-泛型中的-ktve)
|
||||
- [6 泛型的实现原理](#6-泛型的实现原理)
|
||||
- [7 泛型实例化](#7-泛型实例化)
|
||||
- [7 泛型与反射](#7-泛型与反射)
|
||||
- [反射](#反射)
|
||||
- [8 泛型最佳实践](#8-泛型最佳实践)
|
||||
- [8.1 父类泛型化(模板基类)](#81-父类泛型化模板基类)
|
||||
- [8.2 子类保留泛型(继续向下传递)](#82-子类保留泛型继续向下传递)
|
||||
- [8.3 父类想“知道子类类型”:CRTP(自引用泛型)](#83-父类想知道子类类型crtp自引用泛型)
|
||||
- [9 复杂问题](#9-复杂问题)
|
||||
- [复杂问题 1 类型安全地组合“处理器/策略/管道”](#复杂问题-1-类型安全地组合处理器策略管道)
|
||||
- [定义泛型接口](#定义泛型接口)
|
||||
- [组合(关键:通配符)](#组合关键通配符)
|
||||
- [复杂问题 2:泛型 + 继承做“注册表/工厂/插件”](#复杂问题-2泛型--继承做注册表工厂插件)
|
||||
- [Key 用 `Class<T>`(类型令牌)](#key-用-classt类型令牌)
|
||||
- [复杂问题 3:泛型集合 API 设计(复制、merge、填充)](#复杂问题-3泛型集合-api-设计复制merge填充)
|
||||
- [复制:经典 `copy`](#复制经典-copy)
|
||||
- [merge:K/V 继承关系更复杂](#mergekv-继承关系更复杂)
|
||||
- [复杂问题 4:构建泛型数据库框架](#复杂问题-4构建泛型数据库框架)
|
||||
- [复杂问题 5:构建泛型缓存框架](#复杂问题-5构建泛型缓存框架)
|
||||
- [复杂问题 6:构建泛型事件系统](#复杂问题-6构建泛型事件系统)
|
||||
- [复杂问题 7:根据请求值类型定位返回值类型](#复杂问题-7根据请求值类型定位返回值类型)
|
||||
- [9 常见坑与建议](#9-常见坑与建议)
|
||||
- [不要在外部 API 滥用 `T extends Object`](#不要在外部-api-滥用-t-extends-object)
|
||||
- [避免返回 `List<?>` 这种“无法用”的类型](#避免返回-list-这种无法用的类型)
|
||||
- [数组与泛型不兼容](#数组与泛型不兼容)
|
||||
- [`Optional<? extends T>` 通常没必要](#optional-extends-t-通常没必要)
|
||||
- [`Comparable` 的正确写法](#comparable-的正确写法)
|
||||
|
||||
# 泛型机制
|
||||
|
||||
@@ -349,6 +372,11 @@ public class GenericMethodDemo {
|
||||
|
||||
Java 泛型的通配符是用于解决泛型之间引用传递问题的特殊语法。泛型与继承之间的关系
|
||||
|
||||
|
||||
- `? extends T`:**上界**(协变,用于“读”)
|
||||
- `? super T`:**下界**(逆变,用于“写”)
|
||||
|
||||
|
||||
1. 无边界通配符<?>
|
||||
1. 无边界的通配符的主要作用就是让泛型能够接受未知类型的数据.
|
||||
2. 固定上边界的通配符<? extends E>
|
||||
@@ -371,6 +399,29 @@ public class Apple<T extends A>{}
|
||||
public class Apple<T supers A>{}
|
||||
```
|
||||
|
||||
口诀:**PECS**
|
||||
**P**roducer-**E**xtends(生产者用 extends)
|
||||
**C**onsumer-**S**uper(消费者用 super)
|
||||
|
||||
例:读多写少
|
||||
```java
|
||||
static double sum(List<? extends Number> nums) {
|
||||
double s = 0;
|
||||
for (Number n : nums) s += n.doubleValue();
|
||||
return s;
|
||||
}
|
||||
```
|
||||
`List<Integer>`、`List<Double>` 都能传进来。
|
||||
|
||||
例:写入 T
|
||||
```java
|
||||
static void addInts(List<? super Integer> dst) {
|
||||
dst.add(1);
|
||||
dst.add(2);
|
||||
}
|
||||
```
|
||||
`List<Number>`、`List<Object>` 都能接收 Integer。
|
||||
|
||||
## 5 泛型中的 KTVE
|
||||
|
||||
泛型中的规范
|
||||
@@ -391,7 +442,7 @@ public class Apple<T supers A>{}
|
||||
实际上编译器不仅关注一个泛型方法的调用,它还会为某些返回值为限定的泛型类型的方法进行强制类型转换,由于类型擦除,返回值为泛型类型的方法都会擦除成 Object 类型,当这些方法被调用后,编译器会额外插入一行 checkcast 指令用于强制类型转换,这一个过程就叫做『泛型翻译』。
|
||||
|
||||
|
||||
## 7 泛型实例化
|
||||
## 7 泛型与反射
|
||||
|
||||
|
||||
### 反射
|
||||
@@ -459,4 +510,460 @@ Class.forName(className).newInstance()
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
```
|
||||
|
||||
## 8 泛型最佳实践
|
||||
|
||||
继承体系里怎么用泛型:三种典型结构
|
||||
|
||||
### 8.1 父类泛型化(模板基类)
|
||||
让子类指定具体类型:
|
||||
|
||||
```java
|
||||
abstract class Repository<ID, E> {
|
||||
abstract E findById(ID id);
|
||||
abstract void save(E e);
|
||||
}
|
||||
|
||||
class UserRepo extends Repository<Long, User> {
|
||||
@Override User findById(Long id) { ... }
|
||||
@Override void save(User u) { ... }
|
||||
}
|
||||
```
|
||||
|
||||
适合:DAO、Client、Handler 这类“同一个模式,不同实体类型”。
|
||||
|
||||
---
|
||||
|
||||
### 8.2 子类保留泛型(继续向下传递)
|
||||
```java
|
||||
class Box<T> { T value; }
|
||||
|
||||
class TimedBox<T> extends Box<T> {
|
||||
long time;
|
||||
}
|
||||
```
|
||||
|
||||
适合:装饰/增强、公共能力复用(缓存、计时、审计)。
|
||||
|
||||
---
|
||||
|
||||
### 8.3 父类想“知道子类类型”:CRTP(自引用泛型)
|
||||
用于**流式 API / Builder / DSL**,解决“父类方法返回子类类型”的问题:
|
||||
|
||||
```java
|
||||
abstract class BaseBuilder<B extends BaseBuilder<B>> {
|
||||
protected abstract B self();
|
||||
public B withName(String name) { /*...*/ return self(); }
|
||||
}
|
||||
|
||||
class UserBuilder extends BaseBuilder<UserBuilder> {
|
||||
@Override protected UserBuilder self() { return this; }
|
||||
}
|
||||
```
|
||||
|
||||
调用方能得到正确链式类型:`new UserBuilder().withName("a")...`
|
||||
|
||||
---
|
||||
|
||||
## 9 复杂问题
|
||||
|
||||
### 复杂问题 1 类型安全地组合“处理器/策略/管道”
|
||||
目标:一套管道把 `I -> O` 转换串起来,同时支持继承、多态组合。
|
||||
|
||||
#### 定义泛型接口
|
||||
```java
|
||||
interface Handler<I, O> {
|
||||
O handle(I input);
|
||||
}
|
||||
```
|
||||
|
||||
#### 组合(关键:通配符)
|
||||
组合函数:把 `Handler<A,B>` 和 `Handler<B,C>` 组合成 `Handler<A,C>`。
|
||||
|
||||
```java
|
||||
static <A, B, C> Handler<A, C> compose(
|
||||
Handler<? super A, ? extends B> h1,
|
||||
Handler<? super B, ? extends C> h2) {
|
||||
return a -> h2.handle(h1.handle(a));
|
||||
}
|
||||
```
|
||||
|
||||
为什么这么写?
|
||||
- h1 能接收 A 的父类(`? super A`)
|
||||
- h1 的输出是 B 的子类(`? extends B`)
|
||||
- h2 同理
|
||||
这让组合对继承层次更“宽容”。
|
||||
|
||||
---
|
||||
|
||||
### 复杂问题 2:泛型 + 继承做“注册表/工厂/插件”
|
||||
目标:根据类型拿到对应实现,例如 `Service` 按请求类型分发。
|
||||
|
||||
#### Key 用 `Class<T>`(类型令牌)
|
||||
```java
|
||||
interface Processor<T> {
|
||||
void process(T input);
|
||||
}
|
||||
|
||||
class Registry {
|
||||
private final Map<Class<?>, Processor<?>> map = new HashMap<>();
|
||||
|
||||
public <T> void register(Class<T> type, Processor<? super T> p) {
|
||||
map.put(type, p);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Processor<T> get(Class<T> type) {
|
||||
return (Processor<T>) map.get(type);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
要点:
|
||||
- `register` 用 `Processor<? super T>`:允许用更通用的处理器注册(比如处理 `Number` 的处理器也能处理 `Integer`)。
|
||||
- `get` 由于类型擦除,需要一次受控的强转;对外 API 仍然是类型安全的。
|
||||
|
||||
---
|
||||
|
||||
### 复杂问题 3:泛型集合 API 设计(复制、merge、填充)
|
||||
#### 复制:经典 `copy`
|
||||
```java
|
||||
static <T> void copy(List<? super T> dst, List<? extends T> src) {
|
||||
for (T t : src) dst.add(t);
|
||||
}
|
||||
```
|
||||
- src 生产 T:`extends`
|
||||
- dst 消费 T:`super`
|
||||
|
||||
#### merge:K/V 继承关系更复杂
|
||||
```java
|
||||
static <K, V> void putAll(Map<? super K, ? super V> dst,
|
||||
Map<? extends K, ? extends V> src) {
|
||||
for (var e : src.entrySet()) {
|
||||
dst.put(e.getKey(), e.getValue());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 复杂问题 4:构建泛型数据库框架
|
||||
|
||||
```java
|
||||
// 基础实体类
|
||||
public abstract class BaseEntity {
|
||||
private Long id;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
// 通用Repository接口
|
||||
public interface IRepository<T extends BaseEntity> {
|
||||
void save(T entity);
|
||||
T findById(Long id);
|
||||
List<T> findAll();
|
||||
void update(T entity);
|
||||
void delete(Long id);
|
||||
}
|
||||
|
||||
// 抽象实现
|
||||
public abstract class AbstractRepository<T extends BaseEntity>
|
||||
implements IRepository<T> {
|
||||
|
||||
protected Map<Long, T> database = new HashMap<>();
|
||||
protected Long idCounter = 0L;
|
||||
|
||||
@Override
|
||||
public void save(T entity) {
|
||||
entity.setId(++idCounter);
|
||||
database.put(entity.getId(), entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T findById(Long id) {
|
||||
return database.get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> findAll() {
|
||||
return new ArrayList<>(database.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(T entity) {
|
||||
database.put(entity.getId(), entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Long id) {
|
||||
database.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
// 具体实现
|
||||
public class UserRepository extends AbstractRepository<User> {
|
||||
public List<User> findByName(String name) {
|
||||
return findAll().stream()
|
||||
.filter(user -> user.getName().equals(name))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
public class User extends BaseEntity {
|
||||
private String name;
|
||||
private String email;
|
||||
|
||||
public User(String name, String email) {
|
||||
this.name = name;
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
// getters and setters
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
public String getEmail() { return email; }
|
||||
public void setEmail(String email) { this.email = email; }
|
||||
}
|
||||
|
||||
// 使用
|
||||
UserRepository userRepo = new UserRepository();
|
||||
User user = new User("张三", "zhangsan@example.com");
|
||||
userRepo.save(user);
|
||||
System.out.println(userRepo.findById(1L).getName());
|
||||
```
|
||||
|
||||
### 复杂问题 5:构建泛型缓存框架
|
||||
|
||||
```java
|
||||
// 缓存策略接口
|
||||
public interface CacheStrategy<K, V> {
|
||||
void put(K key, V value);
|
||||
V get(K key);
|
||||
void remove(K key);
|
||||
void clear();
|
||||
}
|
||||
|
||||
// LRU缓存实现
|
||||
public class LRUCache<K, V> implements CacheStrategy<K, V> {
|
||||
private final int capacity;
|
||||
private final LinkedHashMap<K, V> cache;
|
||||
|
||||
public LRUCache(int capacity) {
|
||||
this.capacity = capacity;
|
||||
this.cache = new LinkedHashMap<K, V>(capacity, 0.75f, true) {
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry eldest) {
|
||||
return size() > capacity;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(K key, V value) {
|
||||
cache.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(K key) {
|
||||
return cache.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(K key) {
|
||||
cache.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
cache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// 缓存管理器
|
||||
public class CacheManager<K, V> {
|
||||
private CacheStrategy<K, V> strategy;
|
||||
|
||||
public CacheManager(CacheStrategy<K, V> strategy) {
|
||||
this.strategy = strategy;
|
||||
}
|
||||
|
||||
public void set(K key, V value) {
|
||||
strategy.put(key, value);
|
||||
}
|
||||
|
||||
public V get(K key) {
|
||||
V value = strategy.get(key);
|
||||
if (value == null) {
|
||||
// 可以从数据库加载
|
||||
System.out.println("从数据库加载: " + key);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
// 使用
|
||||
CacheManager<String, User> userCache =
|
||||
new CacheManager<>(new LRUCache<>(100));
|
||||
|
||||
User user = new User("张三", "zhangsan@example.com");
|
||||
userCache.set("user:1", user);
|
||||
User cached = userCache.get("user:1");
|
||||
```
|
||||
|
||||
### 复杂问题 6:构建泛型事件系统
|
||||
|
||||
```java
|
||||
// 事件基类
|
||||
public abstract class Event {
|
||||
private long timestamp;
|
||||
|
||||
public Event() {
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
// 具体事件
|
||||
public class UserCreatedEvent extends Event {
|
||||
private User user;
|
||||
|
||||
public UserCreatedEvent(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
||||
// 事件监听器接口
|
||||
public interface EventListener<T extends Event> {
|
||||
void onEvent(T event);
|
||||
}
|
||||
|
||||
// 事件发布器
|
||||
public class EventPublisher {
|
||||
private Map<Class<?>, List<EventListener<?>>> listeners =
|
||||
new HashMap<>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Event> void subscribe(Class<T> eventType,
|
||||
EventListener<T> listener) {
|
||||
listeners.computeIfAbsent(eventType, k -> new ArrayList<>())
|
||||
.add(listener);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Event> void publish(T event) {
|
||||
List<EventListener<?>> eventListeners =
|
||||
listeners.get(event.getClass());
|
||||
|
||||
if (eventListeners != null) {
|
||||
for (EventListener<?> listener : eventListeners) {
|
||||
((EventListener<T>) listener).onEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 使用
|
||||
EventPublisher publisher = new EventPublisher();
|
||||
|
||||
publisher.subscribe(UserCreatedEvent.class, event -> {
|
||||
System.out.println("用户创建事件: " + event.getUser().getName());
|
||||
});
|
||||
|
||||
User user = new User("李四", "lisi@example.com");
|
||||
publisher.publish(new UserCreatedEvent(user));
|
||||
```
|
||||
|
||||
### 复杂问题 7:根据请求值类型定位返回值类型
|
||||
|
||||
“请求-响应对”+“策略分发”
|
||||
|
||||
```java
|
||||
// 1. 标记接口:Request<对应的 Response>
|
||||
public interface Request<R extends Response> {}
|
||||
|
||||
public interface Response {}
|
||||
|
||||
// 2. 具体请求-响应对
|
||||
public class CreateUserReq implements Request<CreateUserRsp> {
|
||||
private String name;
|
||||
// getter/setter
|
||||
}
|
||||
|
||||
public class CreateUserRsp implements Response {
|
||||
private Long id;
|
||||
}
|
||||
|
||||
// 3. 处理器接口
|
||||
public interface Handler<Rq extends Request<Rs>, Rs extends Response> {
|
||||
Rs handle(Rq request);
|
||||
}
|
||||
|
||||
// 4. 处理器实现
|
||||
public class CreateUserHandler implements Handler<CreateUserReq, CreateUserRsp> {
|
||||
@Override
|
||||
public CreateUserRsp handle(CreateUserReq req) {
|
||||
return new CreateUserRsp(1L);
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 分发器(线程安全)
|
||||
public class Dispatcher {
|
||||
private final Map<Class<?>, Handler<?, ?>> map = new ConcurrentHashMap<>();
|
||||
|
||||
public <Rq extends Request<Rs>, Rs extends Response>
|
||||
void register(Class<Rq> reqType, Handler<Rq, Rs> handler) {
|
||||
map.put(reqType, handler);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <Rs extends Response> Rs execute(Request<Rs> request) {
|
||||
Handler<?, ?> h = map.get(request.getClass());
|
||||
if (h == null) throw new IllegalArgumentException("no handler");
|
||||
return ((Handler<Request<Rs>, Rs>) h).handle(request);
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 使用
|
||||
Dispatcher dispatcher = new Dispatcher();
|
||||
dispatcher.register(CreateUserReq.class, new CreateUserHandler());
|
||||
|
||||
CreateUserRsp rsp = dispatcher.execute(new CreateUserReq("tom"));
|
||||
System.out.println(rsp.getId());
|
||||
```
|
||||
|
||||
|
||||
## 9 常见坑与建议
|
||||
|
||||
### 不要在外部 API 滥用 `T extends Object`
|
||||
没意义,默认就是 Object 上界。
|
||||
|
||||
### 避免返回 `List<?>` 这种“无法用”的类型
|
||||
返回 `List<T>` 或 `List<? extends Base>` 更实用。
|
||||
|
||||
### 数组与泛型不兼容
|
||||
`new T[]` 不允许;用 `List<T>` 或传 `IntFunction<T[]>` / `Class<T>` 创建。
|
||||
|
||||
### `Optional<? extends T>` 通常没必要
|
||||
多用 `Optional<T>`;在参数处才常用通配符。
|
||||
|
||||
### `Comparable` 的正确写法
|
||||
```java
|
||||
class A implements Comparable<A> { ... }
|
||||
```
|
||||
如果写工具方法:
|
||||
```java
|
||||
static <T extends Comparable<? super T>> T max(List<? extends T> list) { ... }
|
||||
```
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
|
||||
|
||||
## 1 Arrays类
|
||||
|
||||
### 方法概述
|
||||
|
||||
* 给数组赋值:通过 fill 方法。
|
||||
* 对数组排序:通过 sort 方法,按升序。
|
||||
* 比较数组:通过 equals 方法比较数组中元素值是否相等。
|
||||
* 查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找法操作。
|
||||
|
||||
|
||||
### 具体方法
|
||||
```java
|
||||
public static int binarySearch(Object[] a, Object key)
|
||||
用二分查找算法在给定数组中搜索给定值的对象(Byte,Int,double等)。数组在调用前必须排序好的。如果查找值包含在数组中,则返回搜索键的索引;否则返回 (-(插入点) - 1)。
|
||||
public static boolean equals(long[] a, long[] a2)
|
||||
如果两个指定的 long 型数组彼此相等,则返回 true。如果两个数组包含相同数量的元素,并且两个数组中的所有相应元素对都是相等的,则认为这两个数组是相等的。换句话说,如果两个数组以相同顺序包含相同的元素,则两个数组是相等的。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。
|
||||
public static void fill(int[] a, int val)
|
||||
将指定的 int 值分配给指定 int 型数组指定范围中的每个元素。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。
|
||||
public static void sort(Object[] a)
|
||||
对指定对象数组根据其元素的自然顺序进行升序排列。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。
|
||||
public static void toString(array[])
|
||||
依次打印元素
|
||||
public static void stream()
|
||||
转化成一个流
|
||||
```
|
||||
195
Java/09 Java面试总结/自引用泛型概述.md
Normal file
195
Java/09 Java面试总结/自引用泛型概述.md
Normal file
@@ -0,0 +1,195 @@
|
||||
自引用泛型(Self-referential generics / F-bounded polymorphism,很多人也叫 CRTP 风格)指:**类型参数的上界本身又引用了这个类型参数**,典型形态是:
|
||||
|
||||
```java
|
||||
class Base<T extends Base<T>> { ... }
|
||||
```
|
||||
|
||||
含义:`T` 必须是“某个继承自 `Base<T>` 的类型”,从而让 Base 在编译期“知道”子类的精确类型。
|
||||
|
||||
---
|
||||
|
||||
## 1 要解决什么问题:父类方法想返回“子类类型”
|
||||
### 没用自引用泛型时的问题
|
||||
```java
|
||||
class Base {
|
||||
Base withName(String n) { return this; }
|
||||
}
|
||||
class UserBuilder extends Base { }
|
||||
|
||||
UserBuilder b = new UserBuilder();
|
||||
b.withName("a") // 返回 Base
|
||||
.withName("b"); // 链式调用类型会退化成 Base
|
||||
```
|
||||
|
||||
你会失去子类特有方法的链式调用:
|
||||
|
||||
```java
|
||||
b.withName("a").onlyInUser(); // 编译不过,因为 withName 返回 Base
|
||||
```
|
||||
|
||||
### 用自引用泛型(让返回类型保持为子类)
|
||||
```java
|
||||
abstract class Base<B extends Base<B>> {
|
||||
protected abstract B self();
|
||||
|
||||
public B withName(String n) {
|
||||
// ... set field
|
||||
return self();
|
||||
}
|
||||
}
|
||||
|
||||
class UserBuilder extends Base<UserBuilder> {
|
||||
@Override protected UserBuilder self() { return this; }
|
||||
|
||||
public UserBuilder onlyInUser() { return this; }
|
||||
}
|
||||
```
|
||||
|
||||
现在:
|
||||
|
||||
```java
|
||||
new UserBuilder()
|
||||
.withName("a")
|
||||
.onlyInUser()
|
||||
.withName("b");
|
||||
```
|
||||
|
||||
链式调用始终保持 `UserBuilder` 类型。
|
||||
|
||||
---
|
||||
|
||||
## 2 它到底“类型上”在约束什么?
|
||||
这句:
|
||||
|
||||
```java
|
||||
B extends Base<B>
|
||||
```
|
||||
|
||||
表示:`B` 不是随便的类型,它必须满足:
|
||||
|
||||
- `B` 是 `Base<B>` 的子类型
|
||||
|
||||
所以 `class UserBuilder extends Base<UserBuilder>` 合法;
|
||||
但 `class X extends Base<String>` 不合法(String 不是 Base<String>)。
|
||||
|
||||
这种约束让 Base 能安全地把 `this` 视作 `B`(通过 `self()` 或强转),从而在 Base 里写出“返回子类”的 API。
|
||||
|
||||
---
|
||||
|
||||
## 3 为什么需要 `self()`?能不能直接 `(B) this`?
|
||||
可以强转,但不推荐暴露在公共基类里。
|
||||
|
||||
### 方案 A:强转(常见但有风险)
|
||||
```java
|
||||
abstract class Base<B extends Base<B>> {
|
||||
@SuppressWarnings("unchecked")
|
||||
protected B self() { return (B) this; }
|
||||
|
||||
public B withName(String n) { return self(); }
|
||||
}
|
||||
```
|
||||
|
||||
风险在于:如果有人写了“破坏约束”的继承结构(通过原始类型 raw type 绕过),运行期可能 `ClassCastException`。
|
||||
|
||||
### 方案 B:子类实现 self(更稳)
|
||||
```java
|
||||
abstract class Base<B extends Base<B>> {
|
||||
protected abstract B self();
|
||||
}
|
||||
```
|
||||
|
||||
子类返回 `this`,不需要 unchecked cast,更清晰。
|
||||
|
||||
---
|
||||
|
||||
## 4 典型使用场景
|
||||
### 4.1 Builder / Fluent API(最常见)
|
||||
让基类提供通用链式方法(name、id、tags…),子类还能追加自身方法且不中断链式。
|
||||
|
||||
### 4.2 “可比较/可排序”这类自类型约束(JDK 经典)
|
||||
`Comparable` 是个“弱化版”案例:
|
||||
|
||||
```java
|
||||
class MyType implements Comparable<MyType> { ... }
|
||||
```
|
||||
|
||||
工具方法里常见约束:
|
||||
|
||||
```java
|
||||
static <T extends Comparable<? super T>> T max(List<? extends T> list) { ... }
|
||||
```
|
||||
|
||||
这也是为了兼容继承关系(`? super T`)。
|
||||
|
||||
### 4.3 框架基类:返回子类、注册子类、DSL
|
||||
例如:
|
||||
- ORM/Query DSL:`Query<T extends Query<T>> where(...)`
|
||||
- 图结构/节点 API:`Node<N extends Node<N>> addChild(N child)`
|
||||
|
||||
---
|
||||
|
||||
## 5 多层继承时怎么写?
|
||||
你可以把“自类型参数”一路传下去:
|
||||
|
||||
```java
|
||||
abstract class Base<B extends Base<B>> {
|
||||
protected abstract B self();
|
||||
public B common() { return self(); }
|
||||
}
|
||||
|
||||
abstract class Mid<B extends Mid<B>> extends Base<B> {
|
||||
public B mid() { return self(); }
|
||||
}
|
||||
|
||||
class Concrete extends Mid<Concrete> {
|
||||
@Override protected Concrete self() { return this; }
|
||||
public Concrete only() { return this; }
|
||||
}
|
||||
```
|
||||
|
||||
调用:`new Concrete().common().mid().only();` 都保持 `Concrete`。
|
||||
|
||||
---
|
||||
|
||||
## 6 常见坑
|
||||
### 6.1 原始类型(raw type)会破坏类型安全
|
||||
```java
|
||||
class Bad extends Base { // 使用 raw type
|
||||
@Override protected Base self() { return this; }
|
||||
}
|
||||
```
|
||||
这会导致泛型约束失效,可能埋下运行期强转问题。尽量禁止 raw type。
|
||||
|
||||
### 6.2 “写错自类型”导致诡异编译错误
|
||||
```java
|
||||
class A extends Base<B> { ... } // B 不是 A
|
||||
```
|
||||
自引用泛型的关键是:**参数必须是“自己”**(或至少保持一致的自类型),一般写成 `extends Base<当前类名>`。
|
||||
|
||||
### 6.3 组合(composition)有时比继承更简单
|
||||
如果只是想链式配置,有时用组合/返回 `this` 并配合接口也能做,别为了泛型而泛型。
|
||||
|
||||
---
|
||||
|
||||
## 7 一个更完整的“可复用 Builder 基类”例子
|
||||
```java
|
||||
abstract class Builder<B extends Builder<B>> {
|
||||
protected String name;
|
||||
protected int age;
|
||||
|
||||
protected abstract B self();
|
||||
|
||||
public B name(String n) { this.name = n; return self(); }
|
||||
public B age(int a) { this.age = a; return self(); }
|
||||
}
|
||||
|
||||
class UserBuilder extends Builder<UserBuilder> {
|
||||
private String email;
|
||||
|
||||
@Override protected UserBuilder self() { return this; }
|
||||
|
||||
public UserBuilder email(String e) { this.email = e; return this; }
|
||||
|
||||
public User build() { return new User(name, age, email); }
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user