优化JAVA和Script

This commit is contained in:
estom
2025-12-21 04:21:50 -05:00
parent b868a42c7f
commit 90ac7c0893
20 changed files with 1712 additions and 49 deletions

View File

@@ -154,7 +154,7 @@ Java既需要编译也需要解释执行。
``` java
/**
* Java Doc 中的祖师格式
* Java Doc 中的注释格式
* /
```
@@ -439,7 +439,7 @@ Java定义了位运算符应用于整数类型(int),长整型(long),短
| 操作符 | 描述 | 例子 |
|---------------------|--------------------------------------------|--------------------------------|
| | 如果相对应位都是1则结果为1否则为0 | AB得到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变成11变成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这时候就不会再判断第二个操作了。

View File

@@ -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如果两个数组包含相同数量的元素并且两个数组中的所有相应元素对都是相等的则认为这两个数组是相等的换句话说如果两个数组以相同顺序包含相同的元素则两个数组是相等的同样的方法适用于所有的其他基本数据类型ByteshortInt等
public static void fill(int[] a, int val)
将指定的 int 值分配给指定 int 型数组指定范围中的每个元素同样的方法适用于所有的其他基本数据类型ByteshortInt等
public static void sort(Object[] a)
对指定对象数组根据其元素的自然顺序进行升序排列同样的方法适用于所有的其他基本数据类型ByteshortInt等
public static void toString(array[])
依次打印元素
public static void stream()
转化成一个流
```

View File

@@ -216,7 +216,7 @@ static{
- 每次执行类,加载类的时候都会先执行静态代码块一次。
- 静态代码块是自动触发执行的,只要程序启动静态代码块就会先执行一次。
- 作用:在启动程序之前可以做资源的初始化,一般用于初始化静态资源。
- 作用:在启动程序之前可以做资源的初始化,一般用于初始化静态资源。注册驱动、加载 native 库、埋监控;
**案例演示**
@@ -242,7 +242,7 @@ public class DaimaKuaiDemo01 {
### 实例代码块
**实例代码块**
没有static修饰必须放在类下。与对象初始化一起加载。
没有static修饰必须放在类下。与对象初始化一起加载。且先于构造函数执行。
**格式**
@@ -256,8 +256,8 @@ public class DaimaKuaiDemo01 {
- 无static修饰。属于对象与对象的创建一起执行的。
- 每次调用构造器初始化对象,实例代码块都要自动触发执行一次。
- 实例代码块实际上是提取到每一个构造器中去执行的。
- 作用:实例代码块用于初始化对象的资源。
- 实例代码块实际上是提取到每一个构造器中去执行的。且先于构造函数执行。
- 作用:实例代码块用于初始化对象的资源。例如:设置复杂的初始值,
**案例演示**

View File

@@ -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);
}

View File

@@ -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)
- [mergeK/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`
#### mergeK/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) { ... }
```

View File

@@ -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如果两个数组包含相同数量的元素并且两个数组中的所有相应元素对都是相等的则认为这两个数组是相等的换句话说如果两个数组以相同顺序包含相同的元素则两个数组是相等的同样的方法适用于所有的其他基本数据类型ByteshortInt等
public static void fill(int[] a, int val)
将指定的 int 值分配给指定 int 型数组指定范围中的每个元素同样的方法适用于所有的其他基本数据类型ByteshortInt等
public static void sort(Object[] a)
对指定对象数组根据其元素的自然顺序进行升序排列同样的方法适用于所有的其他基本数据类型ByteshortInt等
public static void toString(array[])
依次打印元素
public static void stream()
转化成一个流
```

View 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); }
}
```

View File

@@ -0,0 +1,512 @@
# Rust标准库中的指针使用详解
## 📌 Rust指针概览
Rust中有两种主要指针类型
| 指针类型 | 符号 | 所有权 | 可变性 | 安全性 | 用途 |
|---------|------|--------|--------|--------|------|
| **引用** | `&T` / `&mut T` | 借用 | 受控 | 安全 | 日常使用 |
| **原始指针** | `*const T` / `*mut T` | 无 | 受控 | 不安全 | FFI、特殊场景 |
| **智能指针** | `Box`, `Rc`, `Arc` | 拥有 | 受控 | 安全 | 堆分配、共享所有权 |
---
## 1⃣ 不可变引用 &T
### 基础用法
```rust
// ✅ 创建不可变引用
fn main() {
let x = 5;
let r = &x; // r是指向x的不可变引用
println!("x: {}, r: {}", x, *r); // *r 解引用
// 输出x: 5, r: 5
}
```
### 多个不可变引用
```rust
fn main() {
let x = 5;
// ✅ 可以有多个不可变引用
let r1 = &x;
let r2 = &x;
let r3 = &x;
println!("r1: {}, r2: {}, r3: {}", r1, r2, r3);
// 都指向同一个x互不干扰
}
```
### 引用指针的特性
```rust
fn main() {
let x = vec![1, 2, 3];
let r = &x;
// ✅ 不可变引用可以调用只读方法
println!("len: {}", r.len());
println!("first: {:?}", r.first());
// ❌ 不能通过不可变引用修改
// r.push(4); // 编译错误
}
```
---
## 2⃣ 可变引用 &mut T
### 基础用法
```rust
// ✅ 创建可变引用
fn main() {
let mut x = 5;
let r = &mut x; // r是指向x的可变引用
*r += 1; // 通过可变引用修改值
println!("x: {}", x); // 输出x: 6
}
```
### 独占性规则
```rust
fn main() {
let mut x = 5;
let r1 = &mut x;
// ❌ 不能有第二个可变引用
// let r2 = &mut x; // 编译错误
*r1 += 1;
println!("r1: {}", r1);
// ✅ r1作用域结束后可以创建新的可变引用
let r2 = &mut x;
*r2 += 1;
println!("r2: {}", r2);
}
```
### 可变引用和不可变引用不能共存
```rust
fn main() {
let mut x = 5;
let r1 = &x; // ✅ 不可变引用
let r2 = &x; // ✅ 不可变引用
// ❌ 有了不可变引用后,不能创建可变引用
// let r3 = &mut x; // 编译错误
println!("r1: {}, r2: {}", r1, r2);
// ✅ r1, r2的作用域结束后可以创建可变引用
let r3 = &mut x;
*r3 += 1;
println!("r3: {}", r3);
}
```
### 修改容器内容
```rust
fn main() {
let mut vec = vec![1, 2, 3];
let r = &mut vec;
// ✅ 可以修改容器内容
r.push(4);
r[0] = 10;
println!("vec: {:?}", vec); // [10, 2, 3, 4]
}
```
---
## 3⃣ 原始指针 *const T 和 *mut T
### 创建原始指针
```rust
fn main() {
let x = 5;
// ✅ 创建不可变原始指针(安全)
let p1: *const i32 = &x as *const i32;
let mut y = 10;
// ✅ 创建可变原始指针(安全)
let p2: *mut i32 = &mut y as *mut i32;
// ❌ 解引用原始指针必须在unsafe块中
unsafe {
println!("*p1: {}", *p1); // 5
println!("*p2: {}", *p2); // 10
}
}
```
### 原始指针的特点
```rust
fn main() {
let x = 5;
let p1 = &x as *const i32;
// ✅ 原始指针可以被忽视借用规则
let p2 = &x as *const i32; // 可以创建多个
// ✅ 原始指针可以为null
let null_ptr: *const i32 = std::ptr::null();
unsafe {
if null_ptr.is_null() {
println!("指针为null");
}
}
}
```
### 原始指针的风险
```rust
// ❌ 危险用法:悬垂指针
fn dangle() -> *const i32 {
let x = 5;
&x as *const i32 // ❌ x在函数结束后被释放
}
fn main() {
let p = dangle();
unsafe {
// ❌ 访问已释放的内存(未定义行为)
// println!("{}", *p);
}
}
```
### 有效的原始指针用法
```rust
// ✅ 与FFI交互
extern "C" {
fn some_c_function(ptr: *const u8);
}
fn main() {
let x = vec![1, 2, 3];
unsafe {
// ✅ 传递给C函数
some_c_function(x.as_ptr());
}
}
// ✅ 手动内存管理
fn manual_allocation() {
unsafe {
let ptr = std::alloc::alloc(std::alloc::Layout::new::<i32>());
*(ptr as *mut i32) = 42;
println!("值: {}", *(ptr as *const i32));
std::alloc::dealloc(ptr, std::alloc::Layout::new::<i32>());
}
}
```
---
## 4⃣ Box<T>(堆分配)
### 基础用法
```rust
fn main() {
// ✅ Box分配在堆上
let b = Box::new(5);
println!("b: {}", b); // 5
// ✅ Box实现了Deref可以直接使用
let x = *b;
println!("x: {}", x); // 5
}
```
### 递归类型
```rust
// ❌ 普通递归类型无限大小
// enum List {
// Cons(i32, List), // 错误:大小无限
// Nil,
// }
// ✅ 使用Box使递归类型可行
#[derive(Debug)]
enum List {
Cons(i32, Box<List>),
Nil,
}
fn main() {
let list = List::Cons(1,
Box::new(List::Cons(2,
Box::new(List::Cons(3,
Box::new(List::Nil)
))
))
);
println!("{:?}", list);
}
```
### Box的转移所有权
```rust
fn main() {
let b = Box::new(5);
// ✅ Box实现了Move语义
let x = b; // 所有权转移
// ❌ b不再可用
// println!("{}", b); // 编译错误
println!("x: {}", x);
}
```
---
## 5⃣ Rc<T>(引用计数)
### 基础用法
```rust
use std::rc::Rc;
fn main() {
// ✅ 创建Rc
let rc1 = Rc::new(5);
let rc2 = rc1.clone(); // 克隆引用(不是数据)
let rc3 = rc1.clone();
println!("rc1: {}, rc2: {}, rc3: {}", rc1, rc2, rc3);
// ✅ 所有Rc共享同一个数据
println!("引用计数: {}", Rc::strong_count(&rc1)); // 3
}
```
### Rc的共享所有权
```rust
use std::rc::Rc;
#[derive(Debug)]
struct Node {
value: i32,
next: Option<Rc<Node>>,
}
fn main() {
// ✅ 多个所有者共享一个Node
let node = Rc::new(Node {
value: 1,
next: None,
});
let node2 = Rc::clone(&node);
let node3 = node.clone();
println!("引用计数: {}", Rc::strong_count(&node)); // 3
}
```
### Rc配合RefCell实现内部可变性
```rust
use std::rc::Rc;
use std::cell::RefCell;
fn main() {
// ✅ Rc<RefCell<T>>允许共享且可变的数据
let data = Rc::new(RefCell::new(vec![1, 2, 3]));
let data1 = data.clone();
let data2 = data.clone();
// ✅ 修改数据
data1.borrow_mut().push(4);
data2.borrow_mut().push(5);
println!("data: {:?}", data.borrow()); // [1, 2, 3, 4, 5]
}
```
### ❌ Rc不是线程安全的
```rust
use std::rc::Rc;
use std::thread;
fn main() {
let rc = Rc::new(5);
// ❌ Rc不实现Send和Sync
// let handle = thread::spawn(move || {
// println!("{}", rc); // 编译错误
// });
}
```
---
## 6⃣ Arc<T>(原子引用计数)
### 基础用法
```rust
use std::sync::Arc;
use std::thread;
fn main() {
// ✅ Arc是线程安全的
let arc = Arc::new(vec![1, 2, 3]);
let mut handles = vec![];
for i in 0..3 {
let arc_clone = arc.clone();
let handle = thread::spawn(move || {
println!("线程{}: {:?}", i, arc_clone);
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
}
```
### Arc的性能
```rust
use std::sync::Arc;
fn main() {
let arc = Arc::new(vec![1, 2, 3, 4, 5]);
// ✅ Arc使用原子操作更新计数线程安全但有开销
let clone1 = arc.clone(); // 原子操作增加计数
let clone2 = arc.clone(); // 原子操作增加计数
println!("强引用计数: {}", Arc::strong_count(&arc)); // 3
drop(clone1); // 原子操作减少计数
println!("强引用计数: {}", Arc::strong_count(&arc)); // 2
}
```
### Arc配合Mutex实现线程安全的共享可变数据
```rust
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
// ✅ Arc<Mutex<T>>是线程安全的共享可变数据
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("计数: {}", *counter.lock().unwrap()); // 10
}
```
---
## 7⃣ 内部可变性模式
### RefCell<T>
```rust
use std::cell::RefCell;
fn main() {
let x = RefCell::new(5);
// ✅ 通过不可变引用修改值
*x.borrow_mut() = 10;
println!("x: {}", x.borrow()); // 10
}
// ❌ 运行时借用检查失败
fn runtime_borrow_panic() {
let x = RefCell::new(5);
let r1 = x.borrow();
// ❌ 在不可变借用存在时进行可变借用
// let r2 = x.borrow_mut(); // panic!
}
```
### Cell<T>Copy类型
```rust
use std::cell::Cell;
fn main() {
let x = Cell::new(5);
// ✅ Cell<T>无运行时开销
x.set(10);
println!("x: {}", x.get()); // 10
}
```
### UnsafeCell<T>(基础构块)
```rust
use std::cell::UnsafeCell;
fn main() {
let x = UnsafeCell::new(5);
unsafe {
// ✅ UnsafeCell允许通过不可变引用修改
*x.get() = 10;
println!("x: {}", *x.get()); // 10
}
}
```
---
## 8⃣ Weak<T>(弱引用)

View File

@@ -0,0 +1,10 @@
sudo yum install tinyproxy -y # 或 dnf install tinyproxy -y
sudo apt update && sudo apt install tinyproxy -y
sudo vim /etc/tinyproxy/tinyproxy.conf
sudo systemctl restart tinyproxy
sudo systemctl enable tinyproxy
curl -x http://服务器IP:8888 http://httpbin.org/ip

View File

@@ -4,7 +4,7 @@ git push git@github.com:Estom/notes.git
# 统计代码的数量
find . -name "*.java" -exec wc -l {} \;#利用exec参数
find . -name "*.java" -exec wc -l {} \; # 利用exec参数
find . -name "*.java" |xargs wc -l # 利用xargs参数
find . -name "*.java" -exec wc -l {} \; | awk 'BEGIN{num=0} {num+=$1;print $1;}END{print num}'

13
test.sh Normal file
View File

@@ -0,0 +1,13 @@
#!/bin/sh
# 保存为 git-time.sh && chmod +x git-time.sh
echo "file,create_time,modify_time"
git ls-files | while read f; do
# 最早一次提交时间(近似创建)
c_time=$(git log --diff-filter=A --format=%ct -1 -- "$f" 2>/dev/null || echo '')
# 最近一次提交时间(变更)
m_time=$(git log -1 --format=%ct -- "$f")
# 转成可读格式(去掉下一行则保持 Unix 秒)
[ -n "$c_time" ] && c_time=$(date -d @$c_time '+%F %T') || c_time='MISSING'
m_time=$(date -d @$m_time '+%F %T')
echo "\"$f\",\"$c_time\",\"$m_time\""
done

View File

@@ -2,7 +2,7 @@
## 1 基础
### 位运算
### 位运算基础
符号|说明|特性
|----|----|---|
@@ -13,7 +13,14 @@
\>\> | 右移|
<< | 左移|
## 2 特殊性质
负数在计算机中使用补码表示,主要目标就是实现了最小值到最大值之间,位运算的连续性。
在字面数字运算中,-2 加1为-1-1 加1为00+1为1。是连续计算的。
在二级制补码计算中。也满足上述的计算规则。1111表示-1加一通过溢出一位变为0000成为0.
实际上-1是按位绝对值最大的数即1111。
### 特殊性质
操作 | 性质
|-----| -----|
n & (n - 1) | n中的最后一个1变成0。
@@ -24,7 +31,7 @@ n*2 | 等价于 左移一位 n << 1
n % 2 |等价于 判断二进制最右一位值 n \& 1
## 3 常见算法
## 2 常见算法
### 快速幂
* 使用二进制方法,将幂转换成二进制。二进制每个位的权重就是可以递推计算,与二分法效果相同。
@@ -90,7 +97,7 @@ int add(int a,int b){
}
```
## 4 位运算与编码
## 3 位运算与编码
* 编码有两种计算方式,一种是数值上计算负数的补码。另一种是按位运算计算负数的补码。前者是运算关系。后者是根据定义。
@@ -116,4 +123,428 @@ $$
X = (\sim |X|)+1
$$
* 为了满足数值计算,使得正数负数能够在同一的编码下进行加减计算。正数的机器数,原码,反码,补码,都是在固定字长下讨论的负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1。机器中只有加法而没有减法。
* 为了满足数值计算,使得正数负数能够在同一的编码下进行加减计算。正数的机器数,原码,反码,补码,都是在固定字长下讨论的负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1。机器中只有加法而没有减法。
## 4 位运算实现加减乘除
### 1⃣ 加法Addition
#### 基本思路
```
加法的本质:
1. XOR(^)用来计算不进位的加法
2. AND(&)用来计算进位
3. 左移(<<)用来进位
例如5 + 3 = 8
5 = 0101
3 = 0011
步骤1不进位加法 (5 ^ 3)
0101 ^ 0011 = 0110 = 6
步骤2进位 (5 & 3) << 1
(0101 & 0011) << 1 = 0001 << 1 = 0010 = 2
步骤3继续加法 6 + 2
重复上述步骤直到进位为0
```
#### 实现
```java
public class BitwiseOperations {
// ✅ 位运算实现加法
public static int add(int a, int b) {
while (b != 0) {
// 计算进位
int carry = (a & b) << 1;
// 计算不进位的和
a = a ^ b;
// 进位加到结果中
b = carry;
}
return a;
}
public static void main(String[] args) {
System.out.println("5 + 3 = " + add(5, 3)); // 8
System.out.println("10 + 20 = " + add(10, 20)); // 30
System.out.println("-5 + 3 = " + add(-5, 3)); // -2
}
}
/* 执行过程演示5 + 3
第一次循环:
a = 0101, b = 0011
carry = (0101 & 0011) << 1 = 0010 = 2
a = 0101 ^ 0011 = 0110 = 6
b = 2
第二次循环:
a = 0110, b = 0010
carry = (0110 & 0010) << 1 = 0100 = 4
a = 0110 ^ 0010 = 0100 = 4
b = 4
第三次循环:
a = 0100, b = 0100
carry = (0100 & 0100) << 1 = 1000 = 8
a = 0100 ^ 0100 = 0000 = 0
b = 8
第四次循环:
a = 0000, b = 1000
carry = (0000 & 1000) << 1 = 0
a = 0000 ^ 1000 = 1000 = 8
b = 0
b = 0退出循环
结果 a = 8
*/
```
#### 简洁版本
```java
public class BitwiseAdd {
public static int add(int a, int b) {
return b == 0 ? a : add(a ^ b, (a & b) << 1);
}
public static void main(String[] args) {
System.out.println(add(5, 3)); // 8
System.out.println(add(-1, 1)); // 0
}
}
```
---
### 2⃣ 减法Subtraction
#### 基本思路
```
减法的本质a - b = a + (-b)
-b的补码计算
-b = ~b + 1
例如5 - 3 = 5 + (-3) = 5 + (~3 + 1) = 5 + (-4) = 1
3 = 0011
~3 = 1100 = -4补码表示
-3 = ~3 + 1 = 1101 = -3
5 - 3 = 5 + (-3)(使用位运算加法)
```
#### 实现
```java
public class BitwiseSubtraction {
// ✅ 位运算实现加法
private static int add(int a, int b) {
while (b != 0) {
int carry = (a & b) << 1;
a = a ^ b;
b = carry;
}
return a;
}
// ✅ 位运算实现减法
public static int subtract(int a, int b) {
// a - b = a + (-b)
// -b = ~b + 1 = ~b + add(0, 1)
return add(a, add(~b, 1));
}
public static void main(String[] args) {
System.out.println("5 - 3 = " + subtract(5, 3)); // 2
System.out.println("10 - 20 = " + subtract(10, 20)); // -10
System.out.println("-5 - 3 = " + subtract(-5, 3)); // -8
}
}
/* 执行过程演示5 - 3
1. 计算 -3 = ~3 + 1
~3 = 1111...1100 (补码)
~3 + 1 = 1111...1101 = -3
2. 计算 5 + (-3) = 2
*/
```
#### 简洁版本
```java
public static int subtract(int a, int b) {
return add(a, add(~b, 1));
}
```
---
### 3⃣ 乘法Multiplication
#### 基本思路
```
乘法的本质a * b = a * (2^n + ... + 2^1 + 2^0)
例如5 * 3 = 5 * 2 + 5 * 1 = 10 + 5 = 15
3 的二进制0011
= 2^1 + 2^0
= 2 + 1
所以 5 * 3 = 5 * 2 + 5 * 1
= 5 << 1 + 5 << 0
= 10 + 5
= 15
算法:
1. 检查b的每一位
2. 如果该位是1就把a左移相应位数加到结果中
3. 重复直到b变为0
```
#### 实现
```java
public class BitwiseMultiplication {
private static int add(int a, int b) {
while (b != 0) {
int carry = (a & b) << 1;
a = a ^ b;
b = carry;
}
return a;
}
// ✅ 位运算实现乘法
public static int multiply(int a, int b) {
if (b == 0) return 0;
int result = 0;
int absA = a < 0 ? add(~a, 1) : a; // 获取绝对值
int absB = b < 0 ? add(~b, 1) : b;
// 遍历b的每一位
while (absB != 0) {
// 如果b的最低位是1
if ((absB & 1) == 1) {
result = add(result, absA);
}
// a左移b右移
absA = absA << 1;
absB = absB >> 1;
}
// 处理符号
if ((a < 0) ^ (b < 0)) { // 异号
result = add(~result, 1);
}
return result;
}
public static void main(String[] args) {
System.out.println("5 * 3 = " + multiply(5, 3)); // 15
System.out.println("5 * 0 = " + multiply(5, 0)); // 0
System.out.println("-5 * 3 = " + multiply(-5, 3)); // -15
System.out.println("5 * -3 = " + multiply(5, -3)); // -15
}
}
/* 执行过程演示5 * 3
5 = 0101
3 = 0011
第一次循环b最低位是1
result = 0 + 5 = 5
absA = 5 << 1 = 10
absB = 3 >> 1 = 1 (0001)
第二次循环b最低位是1
result = 5 + 10 = 15
absA = 10 << 1 = 20
absB = 1 >> 1 = 0
b = 0退出
结果 = 15
*/
```
#### 高效版本利用左移快速乘以2
```java
public static int multiply(int a, int b) {
if (b == 0) return 0;
int result = 0;
while (b != 0) {
// 如果b的最低位是1
if ((b & 1) == 1) {
result = add(result, a);
}
a = a << 1; // 相当于a * 2
b = b >> 1; // 相当于b / 2
}
return result;
}
```
---
### 4⃣ 除法Division
#### 基本思路
```
除法的本质a / b = 找出最大的q使得 b * q <= a
例如15 / 3 = ?
找最大的q使得 3 * q <= 15
3 * 5 = 15 <= 15 ✓
3 * 6 = 18 > 15 ✗
所以 15 / 3 = 5
使用二分查找或逐步减法:
15 - 3 = 12
12 - 3 = 9
9 - 3 = 6
6 - 3 = 3
3 - 3 = 0
共减了5次所以15 / 3 = 5
```
#### 实现版本1逐步减法效率低
```java
public class BitwiseDivision {
private static int add(int a, int b) {
while (b != 0) {
int carry = (a & b) << 1;
a = a ^ b;
b = carry;
}
return a;
}
private static int subtract(int a, int b) {
return add(a, add(~b, 1));
}
private static int multiply(int a, int b) {
if (b == 0) return 0;
int result = 0;
while (b != 0) {
if ((b & 1) == 1) {
result = add(result, a);
}
a = a << 1;
b = b >> 1;
}
return result;
}
// ✅ 位运算实现除法(逐步减法)
public static int divide(int a, int b) {
if (b == 0) throw new ArithmeticException("除数不能为0");
int absA = a < 0 ? add(~a, 1) : a;
int absB = b < 0 ? add(~b, 1) : b;
int result = 0;
// 逐步减法
while (absA >= absB) {
absA = subtract(absA, absB);
result = add(result, 1);
}
// 处理符号
if ((a < 0) ^ (b < 0)) {
result = add(~result, 1);
}
return result;
}
public static void main(String[] args) {
System.out.println("15 / 3 = " + divide(15, 3)); // 5
System.out.println("10 / 3 = " + divide(10, 3)); // 3
System.out.println("20 / 4 = " + divide(20, 4)); // 5
}
}
```
#### 实现版本2二分查找效率高⭐⭐⭐⭐⭐
```java
public class BitwiseDivisionBinary {
private static int add(int a, int b) {
while (b != 0) {
int carry = (a & b) << 1;
a = a ^ b;
b = carry;
}
return a;
}
private static int subtract(int a, int b) {
return add(a, add(~b, 1));
}
private static int multiply(int a, int b) {
if (b == 0) return 0;
int result = 0;
while (b != 0) {
if ((b & 1) == 1) {
result = add(result, a);
}
a = a << 1;
b = b >> 1;
}
return result;
}
// ✅ 位运算实现除法(二分查找,高效)
public static int divide(int a, int b) {
if (b == 0) throw new ArithmeticException("除数不能为0");
boolean negative = (a < 0) ^ (b < 0);
long absA = a < 0 ? -(long)a : a;
long absB = b < 0 ? -(long)b : b;
long result = 0;
// 从高位到低位进行二分
for (int i = 31; i >= 0; i--) {
// 如果 absB << i <= absA则第i位为1
if ((absB << i) <= absA) {
absA = subtract(absA, absB << i);
result = result | (1L << i); // 设置第i位为1
}
}
return (int)(negative
```