diff --git a/Java/01Java语言基础/01 Java基础.md b/Java/01Java语言基础/01 Java基础.md index a92e0691..807cbca3 100644 --- a/Java/01Java语言基础/01 Java基础.md +++ b/Java/01Java语言基础/01 Java基础.md @@ -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,这时候就不会再判断第二个操作了。 diff --git a/Java/01Java语言基础/04 Java数组.md b/Java/01Java语言基础/04 Java数组.md index 10936283..92bff75a 100644 --- a/Java/01Java语言基础/04 Java数组.md +++ b/Java/01Java语言基础/04 Java数组.md @@ -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() -转化成一个流 -``` \ No newline at end of file diff --git a/Java/01Java语言基础/05 面向对象的类与对象.md b/Java/01Java语言基础/05 面向对象的类与对象.md index 8c9c52e4..f03a8b05 100644 --- a/Java/01Java语言基础/05 面向对象的类与对象.md +++ b/Java/01Java语言基础/05 面向对象的类与对象.md @@ -216,7 +216,7 @@ static{ - 每次执行类,加载类的时候都会先执行静态代码块一次。 - 静态代码块是自动触发执行的,只要程序启动静态代码块就会先执行一次。 -- 作用:在启动程序之前可以做资源的初始化,一般用于初始化静态资源。 +- 作用:在启动程序之前可以做资源的初始化,一般用于初始化静态资源。注册驱动、加载 native 库、埋监控; **案例演示** @@ -242,7 +242,7 @@ public class DaimaKuaiDemo01 { ### 实例代码块 **实例代码块** -​ 没有static修饰,必须放在类下。与对象初始化一起加载。 +​ 没有static修饰,必须放在类下。与对象初始化一起加载。且先于构造函数执行。 **格式** @@ -256,8 +256,8 @@ public class DaimaKuaiDemo01 { - 无static修饰。属于对象,与对象的创建一起执行的。 - 每次调用构造器初始化对象,实例代码块都要自动触发执行一次。 -- 实例代码块实际上是提取到每一个构造器中去执行的。 -- 作用:实例代码块用于初始化对象的资源。 +- 实例代码块实际上是提取到每一个构造器中去执行的。且先于构造函数执行。 +- 作用:实例代码块用于初始化对象的资源。例如:设置复杂的初始值, **案例演示** diff --git a/Java/01Java语言基础/09 Java反射.md b/Java/01Java语言基础/09 Java反射.md index 1f5c98d9..7f7945e5 100644 --- a/Java/01Java语言基础/09 Java反射.md +++ b/Java/01Java语言基础/09 Java反射.md @@ -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); } diff --git a/Java/01Java语言基础/10 Java泛型.md b/Java/01Java语言基础/10 Java泛型.md index 2fd2167a..1413529a 100644 --- a/Java/01Java语言基础/10 Java泛型.md +++ b/Java/01Java语言基础/10 Java泛型.md @@ -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`(类型令牌)](#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` 通常没必要](#optional-extends-t-通常没必要) + - [`Comparable` 的正确写法](#comparable-的正确写法) # 泛型机制 @@ -349,6 +372,11 @@ public class GenericMethodDemo { Java 泛型的通配符是用于解决泛型之间引用传递问题的特殊语法。泛型与继承之间的关系 + +- `? extends T`:**上界**(协变,用于“读”) +- `? super T`:**下界**(逆变,用于“写”) + + 1. 无边界通配符 1. 无边界的通配符的主要作用就是让泛型能够接受未知类型的数据. 2. 固定上边界的通配符 @@ -371,6 +399,29 @@ public class Apple{} public class Apple{} ``` +口诀:**PECS** +**P**roducer-**E**xtends(生产者用 extends) +**C**onsumer-**S**uper(消费者用 super) + +例:读多写少 +```java +static double sum(List nums) { + double s = 0; + for (Number n : nums) s += n.doubleValue(); + return s; +} +``` +`List`、`List` 都能传进来。 + +例:写入 T +```java +static void addInts(List dst) { + dst.add(1); + dst.add(2); +} +``` +`List`、`List` 都能接收 Integer。 + ## 5 泛型中的 KTVE 泛型中的规范 @@ -391,7 +442,7 @@ public class Apple{} 实际上编译器不仅关注一个泛型方法的调用,它还会为某些返回值为限定的泛型类型的方法进行强制类型转换,由于类型擦除,返回值为泛型类型的方法都会擦除成 Object 类型,当这些方法被调用后,编译器会额外插入一行 checkcast 指令用于强制类型转换,这一个过程就叫做『泛型翻译』。 -## 7 泛型实例化 +## 7 泛型与反射 ### 反射 @@ -459,4 +510,460 @@ Class.forName(className).newInstance() } } -``` \ No newline at end of file +``` + +## 8 泛型最佳实践 + +继承体系里怎么用泛型:三种典型结构 + +### 8.1 父类泛型化(模板基类) +让子类指定具体类型: + +```java +abstract class Repository { + abstract E findById(ID id); + abstract void save(E e); +} + +class UserRepo extends Repository { + @Override User findById(Long id) { ... } + @Override void save(User u) { ... } +} +``` + +适合:DAO、Client、Handler 这类“同一个模式,不同实体类型”。 + +--- + +### 8.2 子类保留泛型(继续向下传递) +```java +class Box { T value; } + +class TimedBox extends Box { + long time; +} +``` + +适合:装饰/增强、公共能力复用(缓存、计时、审计)。 + +--- + +### 8.3 父类想“知道子类类型”:CRTP(自引用泛型) +用于**流式 API / Builder / DSL**,解决“父类方法返回子类类型”的问题: + +```java +abstract class BaseBuilder> { + protected abstract B self(); + public B withName(String name) { /*...*/ return self(); } +} + +class UserBuilder extends BaseBuilder { + @Override protected UserBuilder self() { return this; } +} +``` + +调用方能得到正确链式类型:`new UserBuilder().withName("a")...` + +--- + +## 9 复杂问题 + +### 复杂问题 1 类型安全地组合“处理器/策略/管道” +目标:一套管道把 `I -> O` 转换串起来,同时支持继承、多态组合。 + +#### 定义泛型接口 +```java +interface Handler { + O handle(I input); +} +``` + +#### 组合(关键:通配符) +组合函数:把 `Handler` 和 `Handler` 组合成 `Handler`。 + +```java +static Handler compose( + Handler h1, + Handler h2) { + return a -> h2.handle(h1.handle(a)); +} +``` + +为什么这么写? +- h1 能接收 A 的父类(`? super A`) +- h1 的输出是 B 的子类(`? extends B`) +- h2 同理 +这让组合对继承层次更“宽容”。 + +--- + +### 复杂问题 2:泛型 + 继承做“注册表/工厂/插件” +目标:根据类型拿到对应实现,例如 `Service` 按请求类型分发。 + +#### Key 用 `Class`(类型令牌) +```java +interface Processor { + void process(T input); +} + +class Registry { + private final Map, Processor> map = new HashMap<>(); + + public void register(Class type, Processor p) { + map.put(type, p); + } + + @SuppressWarnings("unchecked") + public Processor get(Class type) { + return (Processor) map.get(type); + } +} +``` + +要点: +- `register` 用 `Processor`:允许用更通用的处理器注册(比如处理 `Number` 的处理器也能处理 `Integer`)。 +- `get` 由于类型擦除,需要一次受控的强转;对外 API 仍然是类型安全的。 + +--- + +### 复杂问题 3:泛型集合 API 设计(复制、merge、填充) +#### 复制:经典 `copy` +```java +static void copy(List dst, List src) { + for (T t : src) dst.add(t); +} +``` +- src 生产 T:`extends` +- dst 消费 T:`super` + +#### merge:K/V 继承关系更复杂 +```java +static void putAll(Map dst, + Map 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 { + void save(T entity); + T findById(Long id); + List findAll(); + void update(T entity); + void delete(Long id); +} + +// 抽象实现 +public abstract class AbstractRepository + implements IRepository { + + protected Map 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 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 { + public List 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 { + void put(K key, V value); + V get(K key); + void remove(K key); + void clear(); +} + +// LRU缓存实现 +public class LRUCache implements CacheStrategy { + private final int capacity; + private final LinkedHashMap cache; + + public LRUCache(int capacity) { + this.capacity = capacity; + this.cache = new LinkedHashMap(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 { + private CacheStrategy strategy; + + public CacheManager(CacheStrategy 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 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 { + void onEvent(T event); +} + +// 事件发布器 +public class EventPublisher { + private Map, List>> listeners = + new HashMap<>(); + + @SuppressWarnings("unchecked") + public void subscribe(Class eventType, + EventListener listener) { + listeners.computeIfAbsent(eventType, k -> new ArrayList<>()) + .add(listener); + } + + @SuppressWarnings("unchecked") + public void publish(T event) { + List> eventListeners = + listeners.get(event.getClass()); + + if (eventListeners != null) { + for (EventListener listener : eventListeners) { + ((EventListener) 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 {} + +public interface Response {} + +// 2. 具体请求-响应对 +public class CreateUserReq implements Request { + private String name; + // getter/setter +} + +public class CreateUserRsp implements Response { + private Long id; +} + +// 3. 处理器接口 +public interface Handler, Rs extends Response> { + Rs handle(Rq request); +} + +// 4. 处理器实现 +public class CreateUserHandler implements Handler { + @Override + public CreateUserRsp handle(CreateUserReq req) { + return new CreateUserRsp(1L); + } +} + +// 5. 分发器(线程安全) +public class Dispatcher { + private final Map, Handler> map = new ConcurrentHashMap<>(); + + public , Rs extends Response> + void register(Class reqType, Handler handler) { + map.put(reqType, handler); + } + + @SuppressWarnings("unchecked") + public Rs execute(Request request) { + Handler h = map.get(request.getClass()); + if (h == null) throw new IllegalArgumentException("no handler"); + return ((Handler, 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` 或 `List` 更实用。 + +### 数组与泛型不兼容 +`new T[]` 不允许;用 `List` 或传 `IntFunction` / `Class` 创建。 + +### `Optional` 通常没必要 +多用 `Optional`;在参数处才常用通配符。 + +### `Comparable` 的正确写法 +```java +class A implements Comparable { ... } +``` +如果写工具方法: +```java +static > T max(List list) { ... } +``` diff --git a/Java/03Java标准集合类/11 Arrays和Collections.md b/Java/03Java标准集合类/11 Arrays和Collections.md index e69de29b..73c666bc 100644 --- a/Java/03Java标准集合类/11 Arrays和Collections.md +++ b/Java/03Java标准集合类/11 Arrays和Collections.md @@ -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() +转化成一个流 +``` \ No newline at end of file diff --git a/Java/09 Java面试总结/自引用泛型概述.md b/Java/09 Java面试总结/自引用泛型概述.md new file mode 100644 index 00000000..d7aebf91 --- /dev/null +++ b/Java/09 Java面试总结/自引用泛型概述.md @@ -0,0 +1,195 @@ +自引用泛型(Self-referential generics / F-bounded polymorphism,很多人也叫 CRTP 风格)指:**类型参数的上界本身又引用了这个类型参数**,典型形态是: + +```java +class Base> { ... } +``` + +含义:`T` 必须是“某个继承自 `Base` 的类型”,从而让 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> { + protected abstract B self(); + + public B withName(String n) { + // ... set field + return self(); + } +} + +class UserBuilder extends Base { + @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` 是 `Base` 的子类型 + +所以 `class UserBuilder extends Base` 合法; +但 `class X extends Base` 不合法(String 不是 Base)。 + +这种约束让 Base 能安全地把 `this` 视作 `B`(通过 `self()` 或强转),从而在 Base 里写出“返回子类”的 API。 + +--- + +## 3 为什么需要 `self()`?能不能直接 `(B) this`? +可以强转,但不推荐暴露在公共基类里。 + +### 方案 A:强转(常见但有风险) +```java +abstract class Base> { + @SuppressWarnings("unchecked") + protected B self() { return (B) this; } + + public B withName(String n) { return self(); } +} +``` + +风险在于:如果有人写了“破坏约束”的继承结构(通过原始类型 raw type 绕过),运行期可能 `ClassCastException`。 + +### 方案 B:子类实现 self(更稳) +```java +abstract class Base> { + 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 { ... } +``` + +工具方法里常见约束: + +```java +static > T max(List list) { ... } +``` + +这也是为了兼容继承关系(`? super T`)。 + +### 4.3 框架基类:返回子类、注册子类、DSL +例如: +- ORM/Query DSL:`Query> where(...)` +- 图结构/节点 API:`Node> addChild(N child)` + +--- + +## 5 多层继承时怎么写? +你可以把“自类型参数”一路传下去: + +```java +abstract class Base> { + protected abstract B self(); + public B common() { return self(); } +} + +abstract class Mid> extends Base { + public B mid() { return self(); } +} + +class Concrete extends Mid { + @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 不是 A +``` +自引用泛型的关键是:**参数必须是“自己”**(或至少保持一致的自类型),一般写成 `extends Base<当前类名>`。 + +### 6.3 组合(composition)有时比继承更简单 +如果只是想链式配置,有时用组合/返回 `this` 并配合接口也能做,别为了泛型而泛型。 + +--- + +## 7 一个更完整的“可复用 Builder 基类”例子 +```java +abstract class Builder> { + 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 { + 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); } +} +``` diff --git a/Rust/02 Rust内置工具库/01 指针工具.md b/Rust/02 Rust内置工具库/01 指针工具.md new file mode 100644 index 00000000..73d1e457 --- /dev/null +++ b/Rust/02 Rust内置工具库/01 指针工具.md @@ -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::()); + *(ptr as *mut i32) = 42; + println!("值: {}", *(ptr as *const i32)); + std::alloc::dealloc(ptr, std::alloc::Layout::new::()); + } +} +``` + +--- + +## 4️⃣ Box(堆分配) + +### 基础用法 + +```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), + 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(引用计数) + +### 基础用法 + +```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>, +} + +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>允许共享且可变的数据 + 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(原子引用计数) + +### 基础用法 + +```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>是线程安全的共享可变数据 + 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 + +```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(Copy类型) + +```rust +use std::cell::Cell; + +fn main() { + let x = Cell::new(5); + + // ✅ Cell无运行时开销 + x.set(10); + println!("x: {}", x.get()); // 10 +} +``` + +### UnsafeCell(基础构块) + +```rust +use std::cell::UnsafeCell; + +fn main() { + let x = UnsafeCell::new(5); + + unsafe { + // ✅ UnsafeCell允许通过不可变引用修改 + *x.get() = 10; + println!("x: {}", *x.get()); // 10 + } +} +``` + +--- + +## 8️⃣ Weak(弱引用) + diff --git a/MyScript/blog/run_blog.sh b/Script/blog/run_blog.sh similarity index 100% rename from MyScript/blog/run_blog.sh rename to Script/blog/run_blog.sh diff --git a/MyScript/multi_sync.bat b/Script/multi_sync.bat similarity index 100% rename from MyScript/multi_sync.bat rename to Script/multi_sync.bat diff --git a/MyScript/multi_sync.sh b/Script/multi_sync.sh similarity index 100% rename from MyScript/multi_sync.sh rename to Script/multi_sync.sh diff --git a/MyScript/network/autossh.service b/Script/network/autossh.service similarity index 100% rename from MyScript/network/autossh.service rename to Script/network/autossh.service diff --git a/MyScript/network/clash.sh b/Script/network/clash.sh similarity index 100% rename from MyScript/network/clash.sh rename to Script/network/clash.sh diff --git a/MyScript/network/frp-route.sh b/Script/network/frp-route.sh similarity index 100% rename from MyScript/network/frp-route.sh rename to Script/network/frp-route.sh diff --git a/MyScript/network/frpc.service b/Script/network/frpc.service similarity index 100% rename from MyScript/network/frpc.service rename to Script/network/frpc.service diff --git a/MyScript/network/ssh-route.sh b/Script/network/ssh-route.sh similarity index 100% rename from MyScript/network/ssh-route.sh rename to Script/network/ssh-route.sh diff --git a/Script/network/tinyproxy.sh b/Script/network/tinyproxy.sh new file mode 100644 index 00000000..6bf7f0ab --- /dev/null +++ b/Script/network/tinyproxy.sh @@ -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 \ No newline at end of file diff --git a/MyScript/start.sh b/Script/start.sh similarity index 89% rename from MyScript/start.sh rename to Script/start.sh index e0d821e9..aa78cd2b 100644 --- a/MyScript/start.sh +++ b/Script/start.sh @@ -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}' diff --git a/test.sh b/test.sh new file mode 100644 index 00000000..b6fe42da --- /dev/null +++ b/test.sh @@ -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 diff --git a/算法/A类:基本算法/3.9 位运算算法.md b/算法/A类:基本算法/3.9 位运算算法.md index 656495a4..41d580c3 100644 --- a/算法/A类:基本算法/3.9 位运算算法.md +++ b/算法/A类:基本算法/3.9 位运算算法.md @@ -2,7 +2,7 @@ ## 1 基础 -### 位运算 +### 位运算基础 符号|说明|特性 |----|----|---| @@ -13,7 +13,14 @@ \>\> | 右移| << | 左移| -## 2 特殊性质 +负数在计算机中使用补码表示,主要目标就是实现了最小值到最大值之间,位运算的连续性。 + +在字面数字运算中,-2 加1为-1,-1 加1为0,0+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。机器中只有加法而没有减法。 \ No newline at end of file +* 为了满足数值计算,使得正数负数能够在同一的编码下进行加减计算。正数的机器数,原码,反码,补码,都是在固定字长下讨论的负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+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 +``` \ No newline at end of file