写点什么

Java 避坑指南:Java 中 java.lang.String 你真的以为是不可变的吗?java11 和 java17 是相同的结果吗?

作者:崔认知
  • 2022 年 8 月 13 日
    北京
  • 本文字数:1393 字

    阅读完需:约 5 分钟

Java避坑指南:Java中 java.lang.String你真的以为是不可变的吗?java11和java17是相同的结果吗?

来源:https://mp.weixin.qq.com/s?__biz=Mzg4MzcwMTk0Mw==&mid=2247484155&idx=1&sn=3d8c4ca2bb5176eafddee665f6bc619f&chksm=cf422323f835aa352e4d495582f66fb12cf39c2d588eba51bd12899e624ebb5cf67c0a80d3d4&token=213707339&lang=zh_CN#rd


java.lang.String 真是不可变的吗?在 java11 中,反射能修改值


反射修改 string,导致 string 内容改变。


示例在 java11 版本下测试:


package com.example.demo;
import java.lang.reflect.Field;import java.util.Arrays;
/**
@author 认知科技技术团队
微信公众号:认知科技技术团队*/public class Demo {public static void main(String[] args) throws ReflectiveOperationException {String a = "崔认知";String b = "崔认知";System.out.println("反射更改String前");System.out.println(a);System.out.println(b);System.out.println(a.hashCode());System.out.println(b.hashCode());
}}

复制代码


反射修改的关键是:获取内部的

private final byte[] value;byte
复制代码

数组,对此数组中值修改。


Field value = String.class.getDeclaredField("value");value.setAccessible(true);byte[] byteValue = (byte[]) value.get(a);
Arrays.fill(byteValue, (byte)0);
复制代码

结果:



字符串内容被修改,修改 a,但是 b 也被修改了,这和 jvm 中的 String Pool 有关系,可以参考 Caching the String literals and reusing them saves a lot of heap space because different String variables refer to the same object in the String pool. String intern pool serves exactly this purpose.https://www.baeldung.com/java-string-immutable#1-introduce-tostring-pool


但是 hashCode 值没变。hashCode 值没变,是因为字符串内部对此值做了缓存:

/** Cache the hash code for the string */private int hash; // Default to 0public int hashCode() {int h = hash;if (h == 0 && value.length > 0) {hash = h = isLatin1() ? StringLatin1.hashCode(value): StringUTF16.hashCode(value);}return h;}
复制代码


虽然我们修改成功了,但是 java 也给我们打印了警告⚠️日志,WARNING: An illegal reflective access operation has occurredWARNING: Illegal reflective access by com.example.demo.Demo (file:/Users/cuirenzhi/gitlab/demo1/target/classes/) to field java.lang.String.valueWARNING: Please consider reporting this to the maintainers of com.example.demo.DemoWARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operationsWARNING: All illegal access operations will be denied in a future release


我们这种反射修改字符串内部的数据是不合法的,反射底层会有校验。



java.lang.String 真是不可变的吗?在 java17 中,反射就不能修改值


java17 下运行的结果:



⚠️java17 中反射就不能修改值,java17 中,不再已警告日志输出,而是直接异常输出控制台,再次抛出异常,我们的代码不能运行了。


图片


图片


反射修改值做了很多限制:Module 及 Module 导出权限、修改的值的权限(PUBLIC、PRIVATE 等权限)做了很多校验。


小结


java.lang.String,反射修改内部的 private final byte[] value 值,在 java11 和 java17 版本中有不同的行为结果:


java11 中可以被修改,而且可能还会影响相同内容的其他字符串;


java17 中不可以被修改,此时反射校验逻辑不通过,会抛出异常;

发布于: 刚刚阅读数: 4
用户头像

崔认知

关注

认知科技技术团队-微信公众号 2021.07.30 加入

认知科技技术团队

评论

发布
暂无评论
Java避坑指南:Java中 java.lang.String你真的以为是不可变的吗?java11和java17是相同的结果吗?_崔认知_InfoQ写作社区