当前位置:网站首页>Shocked, 99.9% of the students didn't really understand the immutability of strings
Shocked, 99.9% of the students didn't really understand the immutability of strings
2022-08-04 04:45:00 【Mingming Ruyue Senior】
一、你以为的常识
1.1 不可变性的理解
A little bit of basic students know Java 中 String 字符串是“不可变”的,想要使用“可变字符串”可以使用 StringBuilder
和 StringBuffer
.
Most articles on string immutability are similar.
Immutable definition:
An immutable object is an object whose internal state remains constant after it has been entirely created. This means that once the object has been assigned to a variable, we can neither update the reference nor mutate the internal state by any means.
– 《Why String is Immutable in Java?》
The so-called immutable objects,That is, the internal state remains unchanged after the object is created.换句话说,Once the object is assigned to a variable,References will no longer be allowed to be altered in any way、修改内部状态.
1.2 Implementation of immutability
String “不可变性”的保障:
- (1) String 类被 final ,result in non-inheritance;
- (2) 存储 String 的字符的 char 数组为 final The reference cannot be changed.
- (3) All modification methods(如 concat)will return a new string object.
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
//省略其他
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
}
1.3 不可变性的好处
1.3.1 节省内存
由于字符串的不可变性,Different string variables can reference the same instance to save heap memory.
String s1 = "明明如月学长";
String s2 = "明明如月学长");
String s3 = new String("明明如月学长");
assertThat(s1 == s2).isTrue();
assertThat(s1 == s3).isFalse();
1.3.2 更安全
The immutability of strings guarantees security.
请看下面的示例代码,The validity check of the parameters is performed first,Then perform some minor thinks,Perform important tasks at the end:
void criticalMethod(String userName) {
//1 执行安全检查
if (!isAlphaNumeric(userName)) {
throw new SecurityException();
}
//2 Perform some minor tasks
initializeDatabase();
//3 重要的任务
connection.executeUpdate("UPDATE Customers SET Status = 'Active' " +
" WHERE UserName = '" + userName + "'");
}
If the string is mutable,The string is modified after the first security check is passed,The code may run with unexpected results,比如造成 SQL 注入等.
The immutability of strings also guarantees out-of-the-box security when accessed by multiple threads.
1.3.3 hashCode 缓存
大家可以看到 String 的 hashCode Calculations depend on the characters that make up the string,由于 String The immutability can be used hashCode 缓存起来.The source code can also be seen after the calculation,下次调用 hashCode 直接返回.
/** * Returns a hash code for this string. The hash code for a * {@code String} object is computed as * <blockquote><pre> * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1] * </pre></blockquote> * using {@code int} arithmetic, where {@code s[i]} is the * <i>i</i>th character of the string, {@code n} is the length of * the string, and {@code ^} indicates exponentiation. * (The hash value of the empty string is zero.) * * @return a hash code value for this object. */
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
二、怀疑人生
2.1 质疑
I don't know if you really thought about it,字符串真的不可变吗?
Even the string class is used final 修饰,String-valued character arrays are also used final 修饰,All modification methods return new string objects,Then the value must not be modified?
答案是否定的!!
We can use reflection to modify the value of a string object.
2.2 验证
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
String name ="明明如月学长 admin";
System.out.println("name 修改前:"+name+", hashCode:"+name.hashCode());
String newName = "明明如月学长";
System.out.println("newName:"+newName+", hashCode:"+newName.hashCode());
replace(name,newName);
System.out.println("name 修改后: "+name+", hashCode:"+name.hashCode());
}
private static void replace(String name,String newName) throws NoSuchFieldException, IllegalAccessException {
// remove private
Field value = String.class.getDeclaredField("value");
value.setAccessible(true);
// 去掉 final
Field mod = Field.class.getDeclaredField("modifiers");
mod.setAccessible(true);
mod.setInt(value, value.getModifiers() & ~Modifier.FINAL);
// 直接替换 value 字符数组
value.set(name, newName.toCharArray());
}
输出结果:
name 修改前:明明如月学长 admin, hashCode:557981902
newName:明明如月学长, hashCode:-292262689
name 修改后: 明明如月学长, hashCode:557981902
2.3 带来的问题
If the value of the string can be modified,The program may behave unexpectedly,
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.concurrent.TimeUnit;
public class StringDemo {
public static void main(String[] args) throws InterruptedException {
String name ="明明如月学长 admin";
new Thread(()->{
try {
mockExecute(name);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
TimeUnit.SECONDS.sleep(2);
new Thread(()->{
try {
mockInject(name);
} catch (Exception e) {
e.printStackTrace();
}
}).start();
TimeUnit.SECONDS.sleep(10);
}
private static synchronized void mockInject(String name) throws InterruptedException, NoSuchFieldException, IllegalAccessException {
// sleep 让 The child thread is executed wait
TimeUnit.SECONDS.sleep(1);
// Use a compliant name before checking
System.out.println("mockInject: 去掉 admin,绕过校验");
replace(name,"明明如月学长");
StringDemo.class.notifyAll();
System.out.println("mockInject: 恢复 admin");
// Change to non-compliant name after inspection
replace(name,"明明如月学长 admin");
System.out.println("mockInject: 恢复 admin 完毕");
StringDemo.class.notifyAll();
}
private static void replace(String name,String newName) throws NoSuchFieldException, IllegalAccessException {
Field value = String.class.getDeclaredField("value");
value.setAccessible(true);
Field mod = Field.class.getDeclaredField("modifiers");
mod.setAccessible(true);
mod.setInt(value, value.getModifiers() & ~Modifier.FINAL);
value.set(name, newName.toCharArray());
}
private static synchronized void mockExecute(String name) throws InterruptedException {
System.out.println("mockExecute: [1] name.contains(\"admin\"):"+name.contains("admin"));
StringDemo.class.wait();
//1 参数检查
if(name.contains("admin")){
throw new IllegalArgumentException("参数检查失败");
}
System.out.println("mockExecute: 不含 admin 关键字,Parameter check passed");
System.out.println("mockExecute: [2] name.contains(\"admin\"):"+name.contains("admin"));
//2 Perform secondary tasks
System.out.println("mockExecute: Perform secondary tasks");
//3 Execute important people
System.out.println("mockExecute: perform important tasks");
}
}
这里简单使用 wait/notify To simulate the problems caused by string modification in the case of multi-threading.
输出的结果:
mockExecute: [1] name.contains(“admin”):true
mockInject: 去掉 admin,绕过校验
mockInject: 恢复 admin
mockInject: 恢复 admin 完毕
mockExecute: 不含 admin 关键字,Parameter check passed
mockExecute: [2] name.contains(“admin”):false
mockExecute: Perform secondary tasks
mockExecute: perform important tasks
三、总结
The immutability of strings means passing String methods to modify a string will produce a new string formation.But it does not mean that the characters of the string must not be modified,We can do the same with strings through reflection“状态/值” 进行修改.
Normally no one would do that,Otherwise, there will be a lot of unexpected results BUG.
I would like to remind you through this article,尽信书不如无书,You have to think about what you see.
创作不易,如果本文对你有帮助,欢迎点赞、收藏加关注,你的支持和鼓励,是我创作的最大动力.
边栏推荐
- 【21天学习挑战赛】顺序查找
- 获取单选框选中内容
- 看DevExpress丰富图表样式,如何为基金公司业务创新赋能
- 【Ryerson情感说话/歌唱视听数据集(RAVDESS) 】
- 【技巧】借助Sentinel实现请求的优先处理
- Mobile payment online and offline payment scenarios
- Implementing a server-side message active push solution based on SSE
- There is an 8 hour difference between the docker installation of mysql and the host.
- 7-3 LVS+Keepalived Cluster Description and Deployment
- Deep learning -- CNN clothing image classification, for example, discussed how to evaluate neural network model
猜你喜欢
Postgresql source code (66) insert on conflict grammar introduction and kernel execution process analysis
2022年软件测试——精选金融银行面试真题
软件测试如何系统规划学习呢?
7-3 LVS+Keepalived Cluster Description and Deployment
if,case,for,while
信息学奥赛一本通 1312:【例3.4】昆虫繁殖
Shell 函数
10 Convolutional Neural Networks for Deep Learning 3
8.Haproxy 搭建Web集群
mysql index notes
随机推荐
C专家编程 第4章 令人震惊的事实:数组和指针并不相同 4.1 数组并非指针
7-2 LVS+DR概述与部署
How to systematically plan and learn software testing?
Embedded database development programming MySQL (full)
7.LVS负载均衡群集之原理叙述
[21 Days Learning Challenge] Image rotation problem (two-dimensional array)
How to automatically export or capture abnormal login ip and logs in elastic to the database?
2003. 每棵子树内缺失的最小基因值 DFS
Take care of JVM performance optimization (own note version)
Mini program + e-commerce, fun new retail
2.15 keil使用电脑端时间日期
【21天学习挑战赛】直接插入排序
el-Select 选择器 底部固定
C专家编程 第4章 令人震惊的事实:数组和指针并不相同 4.4 使声明与定义相匹配
【21天学习挑战赛】顺序查找
2023年PMP考试会用新版教材吗?回复来了!
share总结
How class only static allocation and dynamic allocation
This Thursday evening at 19:00, the fourth live broadcast of knowledge empowerment丨The realization of equipment control of OpenHarmony smart home project
数据治理平台项目总结和分析