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(比如链表反转)来验证一下你的理解?**
|
||||||
112
readme.md
Normal file
112
readme.md
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
这份笔记为你梳理了 Java 开发中最核心的语法和类型体系。你可以把它当作一张“速查表”,重点标注了和 C/C++ 的差异点。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Java 数据类型大地图
|
||||||
|
|
||||||
|
Java 的类型系统分为两大类:**基本类型**和**引用类型**。
|
||||||
|
|
||||||
|
### ① 基本类型 (Primitive Types)
|
||||||
|
|
||||||
|
Java 的基本类型长度是**固定**的,不随操作系统位数改变(这是跨平台的保证)。
|
||||||
|
|
||||||
|
| 类型 | 大小 | 默认值 | 包装类 (用于集合) | 备注 |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| **byte** | 1 byte | 0 | Byte | 很少用,多用于二进制流 |
|
||||||
|
| **short** | 2 bytes | 0 | Short | - |
|
||||||
|
| **int** | 4 bytes | 0 | **Integer** | 最常用 |
|
||||||
|
| **long** | 8 bytes | 0L | Long | 赋值需加 `L` 后缀 |
|
||||||
|
| **float** | 4 bytes | 0.0f | Float | 赋值需加 `f` 后缀 |
|
||||||
|
| **double** | 8 bytes | 0.0 | Double | 常用浮点数 |
|
||||||
|
| **char** | 2 bytes | '\u0000' | Character | **UTF-16 编码**,存一个汉字没问题 |
|
||||||
|
| **boolean** | 1 bit | false | Boolean | 只有 `true/false`,不能用 0/1 |
|
||||||
|
|
||||||
|
### ② 引用类型 (Reference Types)
|
||||||
|
|
||||||
|
除了上面 8 种,剩下的全是引用类型(类似于 C 的指针,但你不能直接操作内存地址)。
|
||||||
|
|
||||||
|
* **类 (Class)**:如 `String`, `User`
|
||||||
|
* **接口 (Interface)**:如 `List`
|
||||||
|
* **数组 (Array)**:如 `int[]`, `User[]`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 核心语法速记
|
||||||
|
|
||||||
|
### ① 变量与常量
|
||||||
|
|
||||||
|
```java
|
||||||
|
int age = 18; // 变量
|
||||||
|
final double PI = 3.1415; // 常量 (类似 C++ 的 const)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### ② 运算符 (与 C 几乎一致)
|
||||||
|
|
||||||
|
* 算术:`+`, `-`, `*`, `/`, `%`, `++`, `--`
|
||||||
|
* 关系:`==`, `!=`, `>`, `<`, `>=`, `<=`
|
||||||
|
* 逻辑:`&&` (短路与), `||` (短路或), `!`
|
||||||
|
* **特殊**:`+` 可以用于字符串拼接,如 `"Age: " + age`。
|
||||||
|
|
||||||
|
### ③ 条件与循环
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 条件
|
||||||
|
if (score > 60) { ... } else { ... }
|
||||||
|
|
||||||
|
// 循环 (和 C 没区别)
|
||||||
|
for (int i = 0; i < 10; i++) { ... }
|
||||||
|
while (condition) { ... }
|
||||||
|
|
||||||
|
// 增强型 for (专门遍历集合和数组)
|
||||||
|
for (String name : nameList) {
|
||||||
|
System.out.println(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 面向对象三要素 (封装、继承、多态)
|
||||||
|
|
||||||
|
* **封装**:使用 `private` 隐藏属性,通过 `public` 的 `getter/setter` 方法暴露。
|
||||||
|
* **继承**:使用 `extends` 关键字。Java 是**单继承**,一个儿子只能有一个亲爹。
|
||||||
|
* **多态**:父类引用指向子类对象 `List list = new ArrayList();`(这是后端开发的灵魂)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Java 集合框架 (STL 对应版)
|
||||||
|
|
||||||
|
在 `java.util` 包下,这是做服务器开发最常用的工具:
|
||||||
|
|
||||||
|
| 功能 | Java 接口 | 常用实现类 | C++ 对应 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| **动态数组** | `List` | `ArrayList` | `std::vector` |
|
||||||
|
| **链表** | `List` | `LinkedList` | `std::list` |
|
||||||
|
| **哈希表** | `Map` | **`HashMap`** | `std::unordered_map` |
|
||||||
|
| **排序映射** | `Map` | `TreeMap` | `std::map` |
|
||||||
|
| **唯一集合** | `Set` | `HashSet` | `std::unordered_set` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 两个“必须知道”的避坑指南
|
||||||
|
|
||||||
|
### 🔴 避坑 1:字符串比较
|
||||||
|
|
||||||
|
* **错误**:`if (str == "hello")` (这是在比较内存地址)
|
||||||
|
* **正确**:`if (str.equals("hello"))` (这是在比较内容)
|
||||||
|
|
||||||
|
### 🔴 避坑 2:自动装箱与拆箱
|
||||||
|
|
||||||
|
Java 集合不能直接存基本类型(如 `int`),必须存包装类(`Integer`)。
|
||||||
|
|
||||||
|
```java
|
||||||
|
ArrayList<Integer> list = new ArrayList<>();
|
||||||
|
list.add(10); // 自动装箱:把 int 变成 Integer 对象
|
||||||
|
int val = list.get(0); // 自动拆箱:把 Integer 变成 int
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**这份笔记你可以先存着。如果你想练习,我可以给你一个涉及到“继承和多态”的小案例,这对于理解 Spring 等后端框架非常有帮助。要试试吗?**
|
||||||
Reference in New Issue
Block a user