SQL语句的拼接最好采用StringBuilder/StringBuffer而不是String吗?
因String是final的
主要是考虑拼接时的时间开销
String是否在某个版本拥有自动优化成StringBuilder/StringBuffer的底层实现?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
因String是final的
主要是考虑拼接时的时间开销
String是否在某个版本拥有自动优化成StringBuilder/StringBuffer的底层实现?
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
接受
或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
发布评论
评论(6)
1. 前言
最近看到几个有趣的关于Java核心类String的问题。
String类是如何实现其不可变的特性的,设计成不可变的好处在哪里。
为什么不推荐使用+号的方式去形成新的字符串,推荐使用StringBuilder或者StringBuffer呢。
翻阅了网上的一些博客和stackoverflow,结合自己的理解做一个汇总。
2. String类是如何实现不可变的
String类的一大特点,就是使用Final类修饰符。
Java SE 7 官方手册中的定义如上,如果你认为这个类已经定义完全并且不需要任何子类的话,可以将这个类声明为Final,Final类中的方法将永远不会被重写。
在Java中,String是被设计成一个不可变(immutable)类,一旦创建完后,字符串本身是无法通过正常手段被修改的。
选了substring方法来做一个代表,其他常见的涉及String操作的方法都是类似,如果你操作后的内容会和目前String中的内容不一致的话,那么都是重新创建一个新的String类返还,不会让你去修改内部的内容。
将String类设计成Final类,能够避免其方法被子类重写,从而破坏了它本身方法的实现,进而破坏了不可变的特性。
2.1 String类设计成不可变的好处
我们都不是Java语言的设计者,不知道其为何一定要设计成不可变,试着做一些猜想。
可以实现多个变量引用JVM内存中的同一个字符串实例。见后文String Pool的介绍。
安全性,String类的用途实在太广了,如果可以随意修改的,是不是很恐怖。
性能,String大量运用在哈希的处理中,由于String的不可变性,可以只计算一次哈希值,然后缓存在内部,后续直接取就好了。如果String类是可变的话,在进行哈希处理的时候,需要进行大量的哈希值的重新计算。
这是结合个人理解和stackoverflow上看的汇总,我们来看看Java语言的爸爸James Gosling是怎么说的。
这是James Gosling在2001年5月的一次访谈中,谈到了不可变类和String,大意就是 他会更倾向于使用不可变类,它能够缓存结果,当你在传参的时候,使用不可变类不需要去考虑谁可能会修改其内部的值,这个问题不存在的。如果使用可变类的话,可能需要每次记得重新拷贝出里面的值,性能会有一定的损失。
老爷子还说了,迫使String类设计成不可变的另一个原因是安全,当你在调用其他方法,比如调用一些系统级操作之前,可能会有一系列校验,如果是可变类的话,可能在你校验过后,其内部的值被改变了,可能引起严重的系统崩溃问题,这是迫使String类设计成不可变类的重要原因。
2.2 String Pool
上文说了,设计成不可变后,可以多个变量引用JVM上同一块地址,可以节省内存空间,相同的字符串不用重复占用Heap区域空间。
通常我们平时在使用字符串是,都是通过这种方式使用,那么JVM中的大致存储就是如下图所示。

两个变量同时引用了String Pool中的abc,如果String类是可变的话,也就不能存在String Pool这样的设计了。
在平时我们还会通过new关键字来生成String,那么新创建的String是否也会和上文中的示例一样共享同一个字符串地址呢。
答案是不会,使用new关键字会在堆区在创建出一个字符串,所以使用new来创建字符串还是很浪费内存的,内存结构如下图所示。

2.3 不推荐使用+来拼装字符串的原因。
首先我们来看这一段代码,应该是之前写代码比较常见的。
test3通过test1和test2拼接而成,我们看一下这个过程中的字节码。

从以上图我们可以看到,目前的JDK7的做法是,会通过新建StringBuilder的方式来完成这个+号的操作。这是目前的一个底层字节码的实现,那么是不是没有使用StringBuilder或者StringBuffer的必要了呢。还是有的,看下一个例子。
在上述代码中,我们还是使用+号进行拼接,但这次我们加了一个循环,看一下字节码有什么变化。

每次循环都会创建一个StringBuilder,在末尾再调用toString返还回去,效率很低。继续看下一个例子,我们直接使用StringBuilder,来做拼接。

每次循环体中只会调用之前创建的StringBuilder的append方法进行拼接,效率大大提高。
至于StringBuilder 的内部实现,诸位有兴趣可以自己再去看一下,本质上也是一个char数组上的操作,和StringBuffer的区别在于,StringBuffer是有做同步处理的,而StringBuilder没有。
3. 总结
本文主要探讨了String类设计为Final修饰和不可变类的原因,以及为何在日常工作中不推荐使用+号进行字符串拼接。
生产系统会拼接SQL?!!!个人demo项目也无所谓性能了
JDK1.5好像就优化为这样了
自行百度吧
问题一。sql语句靠字符串拼接??不怕sql注入的,尽管用。
问题二。是否有优化?。如果有大量字符串拼接,直接用StringBuilder,因为优化必定存在局限性。想知道原理?直接javap看字节码。
所有的字符串拼接都不应该用+吧?
不管怎么拼接你真的不怕SQL注入么?。。。