New Java
This commit is contained in:
263
Class/readme.md
Normal file
263
Class/readme.md
Normal file
@@ -0,0 +1,263 @@
|
||||
这两个问题问得非常精准,直接触及了 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++ 中我们叫 STL(Standard 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) {
|
||||
// 创建一个 Map,Key 是 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(比如链表反转)来验证一下你的理解?**
|
||||
Reference in New Issue
Block a user