JDK源码学习String篇中,有一处错误,String类用final【不能被改变的】修饰,而我却写成静态的,感谢CTO-淼淼的指正。
风一样的码农提出的String为何采用final的设计,阅读JDK源码的时候,有粗略的思考过,今天下班后又把《Thinking in Java》中关于final的内容重新看了一遍,对此写下一些关于自己的理解和想法。
String类中final关键字的使用
final关键字,用来描述一块数据不能被改变,两种可能理由:设计、效率
final使用的三种情况:数据、方法、类,final修饰类,类不能被继承,final修饰方法,方法不能被重载
final对于基本类型的修饰,使得数值不能被改变,但是用于对象引用,虽然能保证初始化指定一个对象后,就无法指向其他对象,但是对象本身确实可以修改的。
private final int[] a = {1, 2, 3}; a[0] = 2;
数组使用final修饰,引用地址不可改变,但是数组的数据却可以改变。
回过头来看String的源码设计中:
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[]; }
String本质上就是一个char数组,使用final修饰,虽然引用不可变,但是内容可变。
所以 final char value[];无法保证String的不变性。
这时候将 class String 使用final修饰,value[] 使用private授予私有访问权限。禁止String类被继承,防止被子类改写,从而保证String的不可改变,这是出于安全性的考虑。
JAVA中参数传递的是传引用,所以多个变量可能指向的是同一个String,如果其中一个变量改变String的内容,另一个变量取到的是改变后的内容,不符合设计的初衷。
public static String testStr(String s, String b) { s = b; b += "123"; return s; } public static void main(String[] args) { String s = new String("aaa"); String b = new String("bbb"); String ns = Test.testStr(s, b); System.out.println(s.toString()); }
关于效率的问题,涉及到JVM的处理机制,这一块不是很了解,以下内容节选自《Thinking in Java》
在Java的早期实现中,如果将一个方法声明为final,就是同意编译器将针对该方法的所有调用都转为内嵌调用。当编译器发现一个final方法调用命令时,它会根据自己的谨慎判断,跳过插入程序代码的这种正常方式而执行方法调用机制(将参数压入栈,跳至方法代码处并执行,然后跳回并清理栈中的参数,处理返回值),并且以方法体中的实际代码的副本来代替方法调用。这将消除方法调用的开销。
作者:江南久无雪
源码记:苦人所不苦,能人所不能,所谓成也
声明:原创博客请在转载时保留原文链接或者在文章开头加上本人博客地址,如发现错误,欢迎批评指正。