Files
java/Class/readme.md
2025-12-29 23:41:10 +08:00

263 lines
9.3 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

这两个问题问得非常精准,直接触及了 Java **工程化**的核心。
### 问题 1什么时候用 public、static什么时候单开文件
这是 Java 初学者最容易混淆的地方,我们可以这样理解:
#### **1. 什么时候用 `public`?(决定“谁能访问”)**
* **单开文件时**:如果你希望这个类能被整个项目中的其他类使用,就用 `public`
* **方法/属性**:如果你希望其他类能调用这个方法(比如 `getInfo()`),就用 `public`
* **如果不写**:默认是“包私有”,只有同一个文件夹下的类能访问。
#### **2. 什么时候用 `static`?(决定“是否属于类本身”)**
* **核心逻辑**`static` 意味着这个东西**不需要 `new` 就能用**。
* **工具类/方法**:比如 `Math.sqrt()`,你不需要 `new Math()` 就能算平方根,这就是静态的。
* **内部类**:如果你在一个类里面写另一个类,且不需要外部类的实例就能创建它,就用 `static`
* **注意**:在 `main` 方法里调用的本类方法,必须是 `static` 的。
#### **3. 什么时候单开一个文件?(决定“代码整洁度”)**
* **Java 惯例****绝大多数情况下,一个文件只写一个类**,且文件名就是类名。
* **你的 User 类**:虽然写在 `Main.java` 里能运行,但在做服务器开发时,`User` 是一个“模型Model应该单开一个 `User.java`
* **唯一例外**:如果一个类非常小,且只在某个类内部使用,可以写成“内部类”。
---
### 问题 2如何引入其他路径的文件
在 Java 中,我们不用“文件路径”来找代码,而是用 **`package`(包)**。这就像是用“行政区划”代替“坐标”。
#### **第一步:给文件定包名(文件开头)**
假设你有一个项目,结构如下:
```text
src/
├── com.myapp.model/
│ └── User.java
└── com.myapp.main/
└── Main.java
```
`User.java` 的第一行要写:
```java
package com.myapp.model;
public class User { ... }
```
#### **第二步引入Import**
`Main.java` 中,如果你想用那个 `User` 类,你需要在文件开头使用 `import` 关键字:
```java
package com.myapp.main;
// 引入具体路径下的类
import com.myapp.model.User;
public class Main {
public static void main(String[] args) {
User u = new User(...); // 现在可以用了!
}
}
```
#### **特殊的引入:`java.util.***`
你之前用的 `import java.util.Scanner;` 其实就是引入了 Java 官方提供的“工具包”里的 `Scanner` 类。
---
### 💡 总结一下:
1. **想给别人用**:加 `public`
2. **不想 `new` 就能用**:加 `static`
3. **正式写项目**:一个文件一个类,放在不同的 `package` 里。
**你现在可以尝试一下:新建两个文件 `User.java` 和 `Main.java`,把之前的代码拆开,看看能不能编译成功?这是迈向“真软件开发”的第一步!**
在 C++ 中我们叫 STLStandard Template Library在 Java 中,对应的这套东西叫做 **Java Collections Framework (Java 集合框架)**
它们全部位于 **`java.util`** 这个包下面。所以使用前,你得先 `import java.util.*;`
---
### 1. Java 集合框架大蓝图
Java 的集合主要分为两大派系:
* **Collection 接口**:存的是单个元素(类似 C++ 的 `vector`, `set`, `list`)。
* **Map 接口**存的是键值对Key-Value类似 C++ 的 `map`)。
---
### 2. 常用集合对比与用法
#### **A. 动态数组:`ArrayList**`
最常用的,对应 C++ 的 `std::vector`
* **特点**:查找快,增删慢(尾部除外)。
* **用法**
```java
ArrayList<String> list = new ArrayList<>();
list.add("Java");
list.get(0); // 访问
```
#### **B. 集合:`HashSet` 与 `TreeSet**`
对应 C++ 的 `std::unordered_set``std::set`
* **`HashSet`**:无序,速度极快(基于哈希表)。
* **`TreeSet`**:自动排序(基于红黑树)。
* **用法**
```java
Set<Integer> set = new HashSet<>();
set.add(1);
set.add(1); // 重复元素会被忽略
System.out.println(set.contains(1)); // true
```
#### **C. 映射:`HashMap` 与 `TreeMap**`
对应 C++ 的 `std::unordered_map``std::map`。这是**服务器开发**中最常用的数据结构,用于存储配置、用户信息等。
* **`HashMap`**键值对Key 不能重复。
* **用法**
```java
HashMap<String, Double> userAssets = new HashMap<>();
userAssets.put("Wan", -114514.0); // 存入
double asset = userAssets.get("Wan"); // 取出
```
---
### 3. 一个实战小例子
既然你之前写了 `User` 类,我们看看如何用 `Map` 把用户 ID 和对象关联起来,这样查询时就不需要循环遍历整个数组了:
```java
import java.util.HashMap;
import java.util.Map;
public class TestCollections {
public static void main(String[] args) {
// 创建一个 MapKey 是 ID (String)Value 是 User 对象
Map<String, User> userMap = new HashMap<>();
// 存入数据
userMap.put("0", new User("Wan", "0", 0, -114514));
userMap.put("1", new User("Zhang", "1", 1, 114514));
// 模拟服务器查询:通过 ID 直接秒杀查询,不需要 for 循环
String searchId = "0";
if (userMap.containsKey(searchId)) {
userMap.get(searchId).getInfo();
} else {
System.out.println("用户不存在");
}
}
}
```
---
### 4. 关键区别Java vs C++
* **泛型**Java 使用 `<Integer>`,注意必须是对象类型(包装类),不能直接用 `int`,得写 `Integer`;不能直接用 `double`,得写 `Double`
* **迭代器**:虽然 Java 也有 `Iterator`,但现在更流行用 `forEach` 或者是 `Stream API`
* **内存管理**:你不需要像 C++ 那样担心 `delete` 集合里的对象Java 的 GC垃圾回收会自动处理。
---
### 🛠️ 建议挑战
你可以尝试把之前代码里的 `User[] user = {...}` 换成 `ArrayList<User>`,然后试试看:
1.`list.add()` 添加几个用户。
2.`list.remove(0)` 删除第一个用户。
3. 看看 `list.size()` 的变化。
**你想深入了解一下为什么 Java 集合里不能存 `int` 这种基本类型(装箱/拆箱机制),还是直接动手试试 Map 的操作?**
``` cpp
class Solution {
    void flip(TreeNode* &left, TreeNode* &right){
        if(left == null && right == null) return;
        TreeNode tmp = left;
        left = right;
        right = tmp;
        flip(left->left, left->right);
        flip(right->left, right->right);
    }
    TreeNode* flipTree(TreeNode* root) {
        if(root == null) return root;
        flip(root->left, root->right);
        return root;
    }
}
```
完全正确!你这一句话就点到了 Java 内存管理的死穴。
作为一个 C++ 选手,你可以把 Java 的声明和初始化拆解为这两步:
1. **声明 `TreeNode node;**`:这相当于 C++ 里的 `TreeNode* node = nullptr;`。在栈上只给了你一个 8 字节64位系统的空间里面存的是全零啥也干不了一用就报 `NullPointerException`。
2. **执行 `new TreeNode()**`:这相当于 C++ 里的 `new` 关键字。JVM 跑到堆Heap里划拉出一块内存把对象的成员变量val, left, right填进去然后把这块地盘的**首地址**返回给栈上的 `node`。
---
### 💡 一个让你“豁然开朗”的对比
在 C++ 里,你有两种选法;而在 Java 里,你只有一种选法。
| 特性 | C++ (两种都有) | Java (只有一种) |
| --- | --- | --- |
| **栈对象** | `TreeNode node;` (直接分配内存) | **不存在** (只有基本类型能进栈) |
| **堆对象** | `TreeNode* node = new TreeNode();` | `TreeNode node = new TreeNode();` |
| **指针算术** | `node++` (可以) | **禁止** (安全第一) |
| **内存释放** | `delete node;` (手动) | **自动** (GC 负责) |
---
### 🚀 既然“指针本质”你已经通了,那我们聊聊“服务器”
在 Java 服务器开发中(比如使用 **Spring Boot** 框架这种“new 了才分配地址”的特性衍生出了一个极度重要的概念:**依赖注入 (Dependency Injection, DI)**。
**场景:** 你的 `LoginService` 需要一个 `DatabaseConnector`。
* 在 C++ 里,你可能要在构造函数里手写 `new`。
* 在 Java 里,你只需要声明 `private DatabaseConnector db;`,然后打个注解。
* **本质:** 框架在后台偷偷帮你执行了 `new`,并把那个**地址(引用)**塞给了你的变量。
这就是为什么 Java 适合做大型软件:它通过管理这些“指针”的指向,让你不需要关注对象是怎么创建的,只需要关注怎么用它。
---
### 🧠 最后一个关于指针的“脑筋急转弯”
既然你说类创建出来默认是指针(引用),那 **`String s1 = "Hello";`** 为什么不需要 `new`
这是 Java 为了性能搞的一个“特权”。如果你感兴趣,我们可以聊聊 **字符串常量池 (String Pool)** —— 这也是 Java 节省内存的一大杀手锏。
**或者,你现在想直接上手写个带“指针逻辑”的小 Demo比如链表反转来验证一下你的理解**