侧边栏壁纸
博主头像
cappuccino博主等级

路漫漫其修远兮,吾将上下而求索。

  • 累计撰写 38 篇文章
  • 累计创建 19 个标签
  • 累计收到 44 条评论

目 录CONTENT

文章目录

String类真的不可变的吗?“当然只是建议不可变!”

cappuccino
2021-04-29 / 0 评论 / 0 点赞 / 389 阅读 / 2,412 字

背景

对于String类的不可变(内容,大小)有着好奇心,于是想探个究竟再将其记录。

分析

Java开发众所周知String是不可变的,但是究竟哪里不可变呢,是内容不可变还是大小不可变,又是怎么个变法,对此真的头大!因此我苦思不得其解,终于通过实操以及源码让我找到梗了!

首先我看了String源码,String被final修饰,而String类内的方法也都是被final修饰的。众所周知String底层是char数组,这个数组继而也是被final修饰。
一旦被final修饰,其初始化后内容是不可变的。数组被final修饰,数组长度是不可变的。
StringApi.png


从上所述可得出: String 类的不可变真相,对于String类来说,不可变有两点含义:

1.字符串长度不可变,这是由于底层是char[]数组决定的。

2.字符串内容不可变,这是由于String类没有提供修改内容的方法导致的。

但是String又是可变的,怎么个变法呢?
下面我先抛出一个案例:

String str = "cappuccino";
str = "hello,cappuccino";
System.out.println(str);

1619694517_1_.jpg

由上面的案例可以看出str由“cappuccino”变成了“hello,cappuccino”,这个str变了呀,其实懂String的人都知道,在声明str字符串的时候其实只是在(JDK1.6、JDK1.8)方法区的字符串常量池中声明的一个地址而已,当你改变它的值的时候,不是重新给它赋值,而是重新申请了一个(JDK1.6、JDK1.8)方法区的字符串常量池地址,存储”hello,cappuccino”,srt引用指向存储”hello,cappuccino”的地址而已。

上面都讲的是String定义的字符串不可变,内容不可变,但是我就是想尝试一下让它变化。

  1. 我通过Java反射机制对String定义的字符串内容进行修改,静观其变。
String str = "卡布";
//str = "卡布奇诺";
//System.out.println(str);

System.out.println("str未被修改的值是:" + str);
//通过反射从方法区内存空间获取String类相关信息
Class clazz = String.class;
//获取String类里的value字段
Field f = clazz.getDeclaredField("value");
//绕过正常访问机制,增加访问权限,可以被修改
f.setAccessible(true);
//获取str属性值
char[] chars = (char[]) f.get(str);
//给str值重新赋值,改变的还是原来的char[]数组
chars[0] = '你';
chars[1] = '好';

System.out.println("str被修改后的值:" + str);

运行结果:
微信图片_20210429191128.png
str真的被修改过来了,这就是Java反射机制!

  1. 我再看看String定义的字符串长度可变不?

我在上面的代码基础上加上一行代码

chars[3] = '*';

微信图片_20210429191511.png
这样的对chars赋值是必然报错,原因是数组长度一旦确定,就不可修改。我就想能不能再通过反射机制来进行修改已经确定好了的数组长度呢?还真有。

  1. 以下就是使用反射机制进行数组长度的修改:
private static Object resizeArray(Object oldArray, int newSize) {
    int oldSize = java.lang.reflect.Array.getLength(oldArray);
    Class elementType = oldArray.getClass().getComponentType();
    Object newArray = java.lang.reflect.Array.newInstance(
            elementType,newSize);
    int preserveLength = Math.min(oldSize,newSize);
    if (preserveLength > 0)
        System.arraycopy (oldArray,0,newArray,0,preserveLength);
    return newArray;
}

以上的代码能实现数组大小的修改,其实只不过是将原有的数组copy到新定义了一个另外长度的数组上:System.arraycopy (oldArray,0,newArray,0,preserveLength);

希望各位通过本篇文章对String的不可变问题能有所收获~

0

评论区