java基础解析系列(一)---String、StringBuffer、StringBuilder
java基础解析系列(一)---String、StringBuffer、StringBuilder
前言:本系列的主题是平时容易疏忽的知识点,只有基础扎实,在编码的时候才能更注重规范和性能,在出现bug的时候,才能处理更加从容。
我的博客目录
String
==问题
String s6=new String("jiajun");
String s1="jiajun";
String s2="jiajun";
System.out.println(s1==s2);//true
System.out.println(s1==s6);//false
- 看常量池中是否已有此字符串,如果有,将指针指向这个字符串
- 如果使用new来创建字符串对象,那么这个字符串是存放在堆中,无论堆中是否已有这个对象
String对象改变
public String substring(int beginIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
int subLen = value.length - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
- 从源码可以看出,任何操作都是创建一个新的对象,不影响原对象
StringBuffer和StringBuidler
初始容量
- StringBuilder和StringBuffer的构造参数来初始化容量
public StringBuilder() {
super(16);
}
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
- 默认情况下容量为16
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
public static char[] copyOf(char[] original, int newLength) {
char[] copy = new char[newLength];
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
- 从源码看出,在执行append方法的时候,会执行ensureCapacityInternal方法来保证容量,而如果超出容量的话,会重新创建一个char数组,并将旧的字符数组复制到新的字符数组
线程安全
public synchronized StringBuffer append(StringBuffer sb) {
toStringCache = null;
super.append(sb);
return this;
}
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
public StringBuilder append(StringBuffer sb) {
super.append(sb);
return this;
}
public StringBuilder append(String str) {
super.append(str);
return this;
}
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
- 可以看出,String的方法是加了synchronzied,也就加了锁,那么而在单线程的情况下或者不用考虑线程安全的情况下,那么StringBuilder的性能是更高的
toString方法
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
- 通过源码发现,toString方法会创建一个新的String对象
性能试验
间接相加和直接相加
public class d {
public static void main(String[] args) {
String s="I"+"love"+"jiajun";
String s1="I";
String s2="love";
String s3="jiajun";
String s4=s1+s2+s3;
}
}

- 通过反编译的结果可以看出,第一种方式字符串直接相加,在编译器就直接优化了”Ilovejiajun“
- 而第二种方式间接相加,从结果可以看出,是先创建一个StringBuilder,然后再apend,最后再toString方法,可以发现性能比第一种低
public class d {
public static void main(String[] args) {
String s="I"+"love"+"jiajun";
String s1="I";
String s2=s1+"lovejiajun";
System.out.println(s==s2);
}
}

- 同样从反编译的结果可以看出,第二种方式并没有被优化,也是通过StringBuilder来实现的,最后通过toString方法创建一个String对象,所以返回的false
- 但是当s1是用final修饰的却是不一样的,虚拟机会对其进行优化,所以不会像之前一样创建一个StringBuilder,最后在堆中产生一个对象
public class d {
public static void main(String[] args) {
final String s1="I";
String s2=s1+"lovejiajun";
String s3="Ilovejiajun";
//s1==s3
}
}

用+和用append
public class Demo3 {
public static void main(String[] args) {
run1();
run2();
}
public static void run1() {
long start = System.currentTimeMillis();
String result = "";
for (int i = 0; i < 10000; i++) {
result += i;
}
System.out.println(System.currentTimeMillis() - start);
}
public static void run2() {
long start = System.currentTimeMillis();
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 10000; i++) {
builder.append(i);
}
System.out.println(System.currentTimeMillis() - start);
}
//输出:223 1
- 从实验发现,用append效率更高,从实验一发现,当字符串相加的时候,实际上每次都会重新初始化StringBuilder然后执行相加,这样效率并不高
初始化容量
public class Demo3 {
public static void main(String[] args) {
test1();
test2();
}
public static void test1() {
StringBuilder sb = new StringBuilder(7000000);
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
sb.append("jiajun");
}
long end=System.currentTimeMillis()-start;
System.out.println(end);
}
public static void test2() {
StringBuilder sb = new StringBuilder();
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
sb.append("jiajun");
}
long end=System.currentTimeMillis()-start;
System.out.println(end);
}
//输出:18 26
}
- 通过实验可以看出,适当的初始化容量可以提高性能,因为当不初始化容量的时候,如果此时append超出容量,那么将会从新创建一个char数组,并且进行复制
总结
- 用new创建对象的时候,会在堆中创建对象,而如果是直接用引号形式的话,会先看常量池是否有此字符串,有的话指向常量池的字符串
- StringBuilder是非线程安全的,StringBuffer是线程安全的
- 使用StringBuilder和StringBuffer的时候最好初始化一个合适的容量,因为如果默认容量不够的话,会重新创建一个char数组,再进行复制
- 字符串相加的时候,直接相加的时候,编译器会进行优化,而如果是间接相加的时候,实际上会创建一个StringBuilder来进行append
我觉得分享是一种精神,分享是我的乐趣所在,不是说我觉得我讲得一定是对的,我讲得可能很多是不对的,但是我希望我讲的东西是我人生的体验和思考,是给很多人反思,也许给你一秒钟、半秒钟,哪怕说一句话有点道理,引发自己内心的感触,这就是我最大的价值。(这是我喜欢的一句话,也是我写博客的初衷)
作者:jiajun 出处: http://www.cnblogs.com/-new/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。如果觉得还有帮助的话,可以点一下右下角的【推荐】,希望能够持续的为大家带来好的技术文章!想跟我一起进步么?那就【关注】我吧。
java基础解析系列(一)---String、StringBuffer、StringBuilder的更多相关文章
- java基础解析系列(九)---String不可变性分析
java基础解析系列(九)---String不可变性分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)---In ...
- java基础解析系列(四)---LinkedHashMap的原理及LRU算法的实现
java基础解析系列(四)---LinkedHashMap的原理及LRU算法的实现 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析 ...
- java基础解析系列(五)---HashMap并发下的问题以及HashTable和CurrentHashMap的区别
java基础解析系列(五)---HashMap并发下的问题以及HashTable和CurrentHashMap的区别 目录 java基础解析系列(一)---String.StringBuffer.St ...
- java基础解析系列(六)---深入注解原理及使用
java基础解析系列(六)---注解原理及使用 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)---Integer ja ...
- java基础解析系列(七)---ThreadLocal原理分析
java基础解析系列(七)---ThreadLocal原理分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)-- ...
- java基础解析系列(八)---fail-fast机制及CopyOnWriteArrayList的原理
fail-fast机制及CopyOnWriteArrayList的原理 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列( ...
- java基础解析系列(十)---ArrayList和LinkedList源码及使用分析
java基础解析系列(十)---ArrayList和LinkedList源码及使用分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder jav ...
- java基础解析系列(十一)---equals、==和hashcode方法
java基础解析系列(十一)---equals.==和hashcode方法 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系 ...
- java基础解析系列(二)---Integer
java基础解析系列(二)---Integer 前言:本系列的主题是平时容易疏忽的知识点,只有基础扎实,在编码的时候才能更注重规范和性能,在出现bug的时候,才能处理更加从容. 目录 java基础解析 ...
随机推荐
- Android 程序打包及签名(转)
为什么要签名??? 开发Android的人这么多,完全有可能大家都把类名,包名起成了一个同样的名字,这时候如何区分?签名这时候就是起区分作用的. 由于开发商可能通过使用相同的Package Name来 ...
- Spring @Scheduled定时任务动态修改cron参数
在定时任务类上增加@EnableScheduling注解,并实现SchedulingConfigurer接口.(注意低版本无效) 设置一个静态变量cron,用于存放任务执行周期参数. 另辟一线程,用于 ...
- Jackson Gson Json.simple part 2
这篇blog介绍 Jackson 的特点和使用方法 Jackson支持三种使用方法 流API(streaming api Incremental parsing/generation) JsonPar ...
- 本地Chrome测试JS代码报错:XMLHttpRequest cannot load
这种file跨域问题在火狐下是不存在的 解决Chrome下file跨域问题: 在Chrome应用程序下,右键属性,目标处添加"--allow-file-access-from-files&q ...
- 简述项目中优化sql的方法,从哪些方面,sql语句性能如何分析?
查询速度慢的原因很多,常见如下几种 : .没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷) .I/O吞吐量小,形成了瓶颈效应. .没有创建计算列导致查询不优化. .内存不足 .网络 ...
- 《转载》WIN10 64位系统 32位Python2.7 PIL安装
http://blog.csdn.net/kanamisama0/article/details/53960281 首先安装这个真的出了好多问题,之前装过一次PIL也失败了,就一直没管,今天刚好找了机 ...
- 【EF框架】使用params参数传值防止SQL注入报错处理
通过SqlParameter传时间参数,代码如下: var param = new List<SqlParameter>(); param.Add(new SqlParameter(&qu ...
- 【cs229-Lecture5】生成学习算法:1)高斯判别分析(GDA);2)朴素贝叶斯(NB)
参考: cs229讲义 机器学习(一):生成学习算法Generative Learning algorithms:http://www.cnblogs.com/zjgtan/archive/2013/ ...
- springaop---->springaop的使用(一)
与大多数技术一样, AOP 已经形成了自己的术语.描述切面的常用术语有通知(advice).切点(pointcut)和连接点(join point).从今天开始,我们对spring的切面编程做一个总结 ...
- mac Intellij Idea Tmocat 启动报 Error running Tomcat: /conf/Catalina
原因:主要是tomcat下Catalina目录没有权限导致,将其设置读写权限即可 如果在刚刚启动tomcat时出现以下问题:Error running Tomcat 8.5.31: Error cop ...