Skip to content

Commit

Permalink
🚀 add: 光猫&路由器拨号 局域网共享 路由&桥接 R2S安装OpenWrt
Browse files Browse the repository at this point in the history
  • Loading branch information
tim-qtp committed Jan 20, 2025
1 parent d121908 commit 3e277a6
Show file tree
Hide file tree
Showing 44 changed files with 4,990 additions and 86 deletions.
6 changes: 5 additions & 1 deletion src/.vuepress/sidebar/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,11 @@ export const zhSidebar = sidebar({
prefix: "clash",
collapsible: true,
children: [
"Openclash"
"1.Openclash",
"2.IPLeak",
"3.LAN sharing",
"4.Clash Verge Rev",
"5.R2S install openwrt system"
],
},
"1.VPN",
Expand Down
1 change: 0 additions & 1 deletion src/.vuepress/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ export default hopeTheme({

plugins: {
comment: {
// @ts-expect-error: You should generate and use your own comment service
provider: 'Waline',
},

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
---
order: 1
author:
title: "序列化和反序列化"
category:
- Java基础
- 序列化
- 反序列化

---

主要是解决网络通信中对象传输的问题,网络传输的数据必须是二进制的,但是在java中都是对象,是没办法传输对象的!

**序列化**:将Java对象转化成可传输的字节序列格式(字节流、JSON、xml),以便于传输和存储

**反序列化**:将字节序列化数据,里面的描述信息和状态,转化成Java对象的过程



### serialVersionUlD又有什么用?

`private static final long serialVersionUID 1L;`

经常会看到这样的代码,这个ID其实就是用来验证序列化的对象和反序列化对应的对象的心是否是一致的。

所以这个ID的数字其实不重要,无论是1L还是idea自动生成的,只要序列化的时候对象的serialVersionUID和反序列化的时候对象的serialVersionUlD一致的话就行。

如果没有显式指定serialVersionUlD,则编译器会根据类的相关信息自动生成一个。

所以如果你没有定义一个serialVersionUlD然后序列化一个对象之后,在反序列化,之前把对象的类的结构改了,比如增加了一个成员变量,则此时的反序列化会失败。

因为类的结构变了,所以serialVersionUlD就不一致了。所以serialVersionUlD就是起验证作用。



### Java序列化不包含静态变量

简单地说就是序列化之后存储的内容不包含静态变量的值,看一下下面的代码就很清晰了。

```java
public class Test implements Serializable {
private static final long serialVersionUID = 1L; // 序列化版本号
public static int yes = 1; // 静态变量

public static void main(String[] args) {
try {
// 序列化
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("tim的無級攻略"));
out.writeObject(new Test()); // 将 Test 对象写入文件
out.close();

// 修改静态变量的值
Test.yes = 2;

// 反序列化
ObjectInputStream oin = new ObjectInputStream(new FileInputStream("tim的無級攻略"));
Test t = (Test) oin.readObject(); // 从文件中读取对象
oin.close();

// 输出静态变量的值
System.out.println(t.yes); // 输出 2,而不是 1
} catch (Exception e) {
e.printStackTrace();
}
}
}
```



### 解释一下序列化的过程和作用?

第一步,实现 Serializable 接口。

```java
public class Person implements Serializable {
private String name;
private int age;

// 省略构造方法、getters和setters
}
```

第二步,使用 ObjectOutputStream 来将对象写入到输出流中。

```java
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("person.ser"));
```

第三步,调用 ObjectOutputStream 的 writeObject 方法,将对象序列化并写入到输出流中。

```java
Person person = new Person("沉默王二", 18);
out.writeObject(person);
```

10 changes: 10 additions & 0 deletions src/Java/basic/10.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
order: 2
author:
title: "Java"
category:
- Java基础
---



10 changes: 10 additions & 0 deletions src/Java/basic/11.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
order: 2
author:
title: "Java"
category:
- Java基础
---



10 changes: 10 additions & 0 deletions src/Java/basic/12.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
order: 2
author:
title: "Java"
category:
- Java基础
---



10 changes: 10 additions & 0 deletions src/Java/basic/13.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
order: 2
author:
title: "Java"
category:
- Java基础
---



10 changes: 10 additions & 0 deletions src/Java/basic/14.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
order: 2
author:
title: "Java"
category:
- Java基础
---



10 changes: 10 additions & 0 deletions src/Java/basic/15.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
order: 2
author:
title: "Java"
category:
- Java基础
---



10 changes: 10 additions & 0 deletions src/Java/basic/16.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
order: 2
author:
title: "Java"
category:
- Java基础
---



10 changes: 10 additions & 0 deletions src/Java/basic/17.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
order: 2
author:
title: "Java"
category:
- Java基础
---



10 changes: 10 additions & 0 deletions src/Java/basic/18.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
order: 2
author:
title: "Java"
category:
- Java基础
---



170 changes: 170 additions & 0 deletions src/Java/basic/2.Immutable classes in Java.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
---
order: 2
author:
title: "Java中的不可变类"
category:
- Java基础
- String
---

### 不可变类就是一个对象创建后字段属性无法修改的类

关键特征:

1. 声明类为 final,防止子类继承。

2. 类的所有字段都是 private 和 final,确保它们在初始化后不能被更改。

3. 通过构造函数初始化所有字段。

4. 不提供任何修改对象状态的方法(如 setter 方法)。


Java 中的经典不可变类有:String、Integer、BigDecimal、LocalDate 等。

String怎么就不可变了,我声明一个String,然后重新赋值,不就可以改变了吗

```java
String a = "过年爽死了";
a = "找工作你就不爽了!"
System.out.println(a)
```

将变量重新赋值,那只是创建了一个新对象,然后将新对象的引用,赋值给了变量,之前的对象是没有受到影响的。

### 为什么String是不可变的呢?

- String 类被 [final 关键字](https://javabetter.cn/oo/final.html)修饰,所以它不会有子类,这就意味着没有子类可以[重写](https://javabetter.cn/basic-extra-meal/override-overload.html)它的方法,改变它的行为。
- String 类的数据存储在 `char[]` 数组中,而这个数组也被 final 关键字修饰了,这就表示 String 对象是没法被修改的,只要初始化一次,值就确定了。

```java
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
}
```

第一,可以保证 String 对象的安全性,避免被篡改,毕竟像密码这种隐私信息一般就是用字符串存储的。

```java
public class SecurityExample {
public static void main(String[] args) {
String password = "mySecretPassword";

authenticateUser(password);

// 假如 String 是可变的,可能会发生以下情况
System.out.println("Original password: " + password); // 仍然应该是 "mySecretPassword"
}

public static void authenticateUser(String password) {
// 进行验证逻辑(这里只是举例)
if ("mySecretPassword".equals(password)) {
System.out.println("Authentication successful");
}

// 假如 String 是可变的,恶意代码可能会篡改字符串内容
password.replace("mySecretPassword", "hackedPassword");
}
}
```

第二,保证哈希值不会频繁变更。毕竟要经常作为[哈希表](https://javabetter.cn/collection/hashmap.html)的键值,经常变更的话,哈希表的性能就会很差劲。

在 String 类中,哈希值是在第一次计算时缓存的,后续对该哈希值的请求将直接使用缓存值。这有助于提高哈希表等数据结构的性能。以下是一个简单的示例,演示了字符串的哈希值缓存机制:

```java
String text1 = "沉默王二";
String text2 = "沉默王二";

// 计算字符串 text1 的哈希值,此时会进行计算并缓存哈希值
int hashCode1 = text1.hashCode();
System.out.println("第一次计算 text1 的哈希值: " + hashCode1);

// 再次计算字符串 text1 的哈希值,此时直接返回缓存的哈希值
int hashCode1Cached = text1.hashCode();
System.out.println("第二次计算: " + hashCode1Cached);

// 计算字符串 text2 的哈希值,由于字符串常量池的存在,实际上 text1 和 text2 指向同一个字符串对象
// 所以这里直接返回缓存的哈希值
int hashCode2 = text2.hashCode();
System.out.println("text2 直接使用缓存: " + hashCode2);
```

在这个示例中,创建了两个具有相同内容的字符串 text1 和 text2。首次计算 text1 的哈希值时,会进行实际计算并缓存该值。当再次计算 text1 的哈希值或计算具有相同内容的 text2 的哈希值时,将直接返回缓存的哈希值,而不进行重新计算。

由于 String 对象是不可变的,其哈希值在创建后不会发生变化。这使得 String 类可以缓存哈希值,提高哈希表等数据结构的性能。如果 String 是可变的,那么在每次修改时都需要重新计算哈希值,这会降低性能。

第三,可以实现[字符串常量池](https://javabetter.cn/string/constant-pool.html),Java 会将相同内容的字符串存储在字符串常量池中。这样,具有相同内容的字符串变量可以指向同一个 String 对象,节省内存空间。

“由于字符串的不可变性,String 类的一些方法实现最终都返回了新的字符串对象。”

“就拿 `substring()` 方法来说。”

```java
public String substring(int beginIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
int subLen = value.length - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}
```

`substring()` 方法用于截取字符串,最终返回的都是 new 出来的新字符串对象。

“还有 `concat()` 方法。”

```java
public String concat(String str) {
int olen = str.length();
if (olen == 0) {
return this;
}
if (coder() == str.coder()) {
byte[] val = this.value;
byte[] oval = str.value;
int len = val.length + oval.length;
byte[] buf = Arrays.copyOf(val, len);
System.arraycopy(oval, 0, buf, val.length, oval.length);
return new String(buf, coder);
}
int len = length();
byte[] buf = StringUTF16.newBytesFor(len + olen);
getBytes(buf, 0, UTF16);
str.getBytes(buf, len, UTF16);
return new String(buf, UTF16);
}
```

`concat()` 方法用于拼接字符串,不管编码是否一致,最终也返回的是新的字符串对象。

`replace()` 替换方法其实也一样。

这就意味着,不管是截取、拼接,还是替换,都不是在原有的字符串上进行的,而是重新生成了新的字符串对象。也就是说,这些操作执行过后,**原来的字符串对象并没有发生改变**



### 如何实现一个不可变类?

看String就知道了

```java
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
}
```

String类用final修饰,表示无法被继承;

String本身是一个char数组,然后用final修饰,不过final只能限制引用不可变,限制不了数组内部的数据,所以还不够;

所以 `value` 是用 `private` 修饰的,并且没有暴露出 `set` 方法,这样外部其实就接触不到 `value`,所以无法修改。

当然还是有修改的需求,比如 `replace` 方法,所以这时候就需要返回一个新对象来作为结果。
Loading

0 comments on commit 3e277a6

Please sign in to comment.