Java的String的传递
写一篇记录前几天遇到的个小问题,这个问题说起来很尴尬,还挺基础的。公司的Java项目的代码都要严格按照checkStyle约定的规范编写,不符合规范就会打包报错。
所以遇到复杂的逻辑只能拆分成数个子函数进行处理,拆分成子函数就会有一些参数割裂开,比如主函数逻辑里的一些字符串了,Map了,List了诸如此类的子函数要想用必须用参数传递的方式传到子函数里。这里就要注意String字符串的传递。
先看一段代码,stackoverflow上说很多人会回答错程序的输出。
publicstaticvoidmain(String[] args) { Stringx=newString("ab"); change(x); System.out.println(x); } publicstaticvoidchange(Stringx) { x="cd"; }
如果你觉得是cd那这篇没白看,答案是ab。
那么这这段程序到底执行了什么,首先当"ab"创建后,Jvm在堆中分配对象所需内存空间。然后栈中的变量x指向了"ab"。
x保存了一个指向String对象的地址。x只是保存一个内存地址。在Java中只有值传递。当x通过参数传递给change()方法后,x被拷贝了一份,此时指向的内存地址还是跟主方法是一致的。接着change()创建了另一个对象"cd",并且将x指向了不同的地址空间。这时change()方法里的x就指向了"cd"。与主方法的对象就不是一个对象了。
上面的问题跟String的不可变没有关系。即使是StringBuilder,结果也一样。关键是变量存储的是引用,意思是在方法里改变了变量的引用地址,后续无论做什么改变都不会影响最初进来引用对象。
所以对于String字符串传递的情况只要保证进来的参数指向不发生变化即可,比如可以把change函数改成StringBuilder参数。
publicstaticvoidchange(StringBuilderx) { x.delete(0, 2).append("cd"); }
String原理
首先String本质上是一个字符串数组,用final修饰所以String有以下特性
String的特性
- 不变性:String 是只读字符串,是一个典型的 immutable 对象,对它进行任何操作,其实都是创建一个新的对象,再把引用指向该对象。不变模式的主要作用在于当一个对象需要被多线程共享并频繁访问时,保证字符串堆不可变,可以保证线程之间的数据一致性。
- 常量池优化:String 对象创建之后,会在字符串常量池中进行缓存,如果下次创建同样的对象时,会直接返回缓存的引用。
- final:使用 final关键字来定义 String 类,表示 String 类不能被继承,提高了系统的安全性
String为什么用final修饰
- 为了安全和性能
- 字符串共享是解决内存消耗以及庞大性能消耗的必然选择。共享就会出现线程安全性。为了保证线程安全性,让String不可修改
String与StringBuilder与StringBuffer
- String内部用final修饰,不可变
- StringBuilder效率较高与StringBuffer,毕竟线程不安全
- StringBuffer线程安全、内部用Synchrinzed修饰