「java面试_01」String类为什么是final

createh52周前 (12-14)技术教程19

1. 什么是不可变?

String不可变很简单,如下图,给一个已有字符串"abcd"第二次赋值成"abcedl",不是在原内存地址上修改数据,而是重新指向一个新对象,新地址。

2. String为什么不可变?

翻开JDK源码,java.lang.String类起手前三行,是这样写的:

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
  /** String本质是个char数组. 而且用final关键字修饰.*/    
   private final char value[];
   private int hash;
    ...}

首先String类是用final关键字修饰,这说明String不可继承。再看下面,String类的主力成员字段value是个char[ ]数组,而且是用final修饰的。final修饰的字段创建以后就不可改变。
有的人以为故事就这样完了,其实没有。因为虽然value是不可变,也只是value这个引用地址不可变。挡不住Array数组是可变的事实。Array的数据结构看下图

也就是说Array变量只是stack上的一个引用,数组的本体结构在heap堆。String类里的value用final修饰,只是说stack里的这个叫value的引用地址不可变。没有说堆里array本身数据不可变。看下面这个例子,

final int[] value={1,2,3}int[] another={4,5,6};value=another;    //编译器报错,final不可变

value用final修饰,编译器不允许我把value指向堆区另一个地址。但如果我直接对数组元素动手,分分钟搞定。

final int[] value={1,2,3};value[2]=100;  //这时候数组里已经是{1,2,100}

或者更粗暴的反射直接改,也是可以的。

final int[] array={1,2,3};Array.set(array,2,100); //数组也被改成{1,2,100}

所以String是不可变,关键是因为SUN公司的工程师,在后面所有String的方法里很小心的没有去动Array里的元素,没有暴露内部成员字段。

private final char value[]这一句里,private的私有访问权限的作用都比final大。而且设计师还很小心地把整个String设成final禁止继承,避免被其他人继承后破坏。所以String是不可变的关键都在底层的实现,而不是一个final。考验的是工程师构造数据类型,封装数据的功力。

3.不可变有什么好处?

为了安全。

class Test{

    public static String appendStr(String s){
        s +="bbb";
        return s;
    }

    public static StringBuilder appendStr(StringBuilder sb){
        return sb.append("bbb");
    }

    public static void main(String[] args) {
        String s = new String("aaa");
        String ns = Test.appendStr(s);
        System.out.println("string aaa>>> " + s.toString());

        StringBuilder sb = new StringBuilder("aaa");
        StringBuilder nsb = Test.appendStr(sb);
        System.out.println("stringBuilder aaa >>>" +sb.toString());
    }
}
//输出如下
//string aaa>>> aaa
// stringbuilder aaa >>> aaabbb

如果程序员不小心像上面例子里,直接在传进来的参数上加"bbb",因为Java对象参数传的是引用,所以可变的的StringBuilder参 数就被改变了。可以看到变量sb在Test.appendSb(sb)操作之后,就变成了"aaabb"。有的时候这可能不是程序员的本意。所以String不可变的安全性就体现在这里。


还有一个大家都知道,就是在并发场景下,多个线程同时读一个资源, 不会引发竟态条件的。只有对资源做写操作才有危险。可对象不能被写,所以线程安全。后别忘了String另外一个字符串常量池的属性。像下面这样字符串one和two都用字面量"something"赋值。它们其实都指向同一个内存地址。

String one = "someString";String two = "someString";



这样在大量使用字符串的情况下,可以节省内存空间,提高效率。但之所以能实现这个特性,String的不可变性是最基本的一个必要条件。要是内存里字符串内容能改来改去,这么做就完全没有意义了。

说明

(1):“在Java里面参数传递都是按值传递”这句话的意思是:按值传递是传递的值的拷贝,按引用传递其实传递的是引用的地址值,所以统称按值传递。

(2):在Java里面只有基本类型和按照下面这种定义方式的String是按值传递,其它的都是按引用传递。就是直接使用双引号定义字符串方式:String str = “Java”;

相关文章

JSON 字符串是如何被解析的?JsonParser了解一下

版本约定Jackson 版本:2.11.0Spring Framework 版本:5.2.6.RELEASESpring Boot 版本:2.3.0.RELEASE什么叫读 JSON?就是把一个 JS...

Java字符串拼接技术演进及阿里巴巴的贡献

阿里妹导读本文主要讲述了Java字符串拼接技术的演进历程,以及阿里巴巴贡献的最新实现 PR 20273。0. 写在前面的省流版下图是Java字符串拼接实现的技术演进路线,最新的实现 PR 20273是...

每天一点Java知识(java的面经和答案)

Java面试是技术面试中常见的一部分,涉及到Java编程语言的核心概念、数据结构、算法、以及一些系统设计等方面。1.Java的基本数据类型有哪些?回答:Java有8种基本数据类型:byte: 1字节,...

2022最全java面试题及答案(208道)你能坚持到哪一道呢?

本文分为十九个模块,分别是:「Java 基础、容器、多线程、反射、对象拷贝、Java Web 、异常、网络、设计模式、Spring/Spring MVC、Spring Boot/Spring Clou...

我把Java基础编程及思维导图整理的超级详细,小白都能看懂

Java基础编程及其思维导图目录:Java学习导图一、Java基本语法1.关键字与标识符 2.变量分类 3.运算符 4.流程控制二、数组1.数组概述 2.一维数组 3.二维数组 4.数组常见算法 5....