字符串为什么这么重要

写了多年java的开发应该对String不陌生,但是我却越发觉得它陌生。每学一门编程语言就会与字符串这个关键词打不少交道。看来它真的很重要。

字符串就是一系列的字符组合的串,如果写过C/C++的应该就了解,在字符串的操作上会有许多操作的函数与类,用于简化代码的开发。一方面是因为字符串在代码中会频繁用到,另一方面是因为字符串的操作非常麻烦。

最初我知道String的特殊待遇就是在delphi中,因为String在delphi里是一个关键字存在,与其他的基本类型是不一样的。那时就了解到了许多相关的知识。在java/.net也都对string做了专门的处理,可见重要性。

正因为字符串在程序中用的多,而且操作也多这就会带来内存占用与性能的问题,所需要特殊的关照一下,想象一下一个日志记录系统一天时间得用上多少字符串变量。

了解一下java中的String

java中提供了String类支持字符串的功能,毕竟字符串本质就是一堆字符的组合,那么就来看看它有什么特点吧。

  • String的特点

String把字符串还是存放在一个char数组中的,数据的操作围绕它展开,但有点特别的地方,代码如下

private final char value[];

可以发现这个char value[]是加了final的,也就是说一旦创建了值就不可变。这样就会导致每一次创建String只会有一个值,再对其进行字符串操作也必须生成新的值。java对这个处理使用了字符串常量池的概念。就是把字符串丢到一个池里,如果相同就用相同的。当然这也有个前提,就是要用下面的方式

String s = "abc";

这样做的时候jvm会在编译期就确定了,在运行时会先在常量池里查找是否有"abc",没有就添加并返回,有的话返回常量池的对象。这样做的好处是对于相同的字符串就不需要重复创建啦。 但是如果使用下面的代码

String s1 = new String("abc");

这个时候情景就变了,这里jvm会在堆栈里创建一个对象s1,只不过s1里的value也是指向"abc"的。后面在看字符串比较的时候会发现区别。

  • 字符串比较 看一段代码:
String s = "abc";
String s1 = "abc"; if (s == s1) {
System.out.println("s == s1");
}

问:这时s==s1吗?

答案是相等的,为什么呢?其实jvm会在s1创建时去常量区查找是否有相同值的字符串,如果有就返回给s1,这样s1就和s指向了同一个字符串,所以是相等的。

但是还有一种情况就不一样,

String s = "abc";
String s3 = new String("abc");
if (s == s3) {
System.out.println("s == s3");
}
else {
System.out.println("s != s3");
}

这个时候应该print出s != s3,这是因为new一个String对象后确实会创建一个新的变量。所以使用==比较的话自然就返回false了。

用到equals比较呢?

String s = "abc";
String s2 = new String("abc");
if (s.equals(s2)) {
System.out.println("s = s2");
}
else {
System.out.println("s != s2");
}

打印是s = s2,因为==是用于比较两个地址,而equals是用于比较两个变量的值。可以看一下equals的代码

public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}

在equals中,先是比较是否地址相同,如果不相同比较value,因为value都是"abc"自然就返回true。

  • intern方法

String里有一个intern方法,我们可以先试一下面的代码。

String s = "abc";
String s3 = new String("abc");
if (s.intern() == s3.intern()) {
System.out.println("s.intern = s3.intern");
}
else {
System.out.println("s.intern != s3.intern");
}

还是上面的s和s3,如果使用各自的intern方法返回的值比较则会输出s.intern = s3.intern。找了找资料结合注释了解到,这个intern方法其实是从字符串常量池里返回当前字符串,如果当前字符串已经存在了则返回当前字符串,如果当前字符串不存在,则将当前字符串放入常量池再返回。

有了这个解释就明白了,s和s3都通过intern返回的那么都是常量池里的"abc"咯,所以intern比较时是相等的。

认识一下StringBuffer和StringBuilder

  • StringBuffer和StringBuilder哪一个是线程安全的?

面试时遇到的这个问题,我突然有点懵,没太注意过这两个类,而且印象中java里只有一个StringBuffer呀?回来看了一下代码原来StringBuffer是线程安全的,也就是在字符串操作的方法上都有synchronized。

于是打开代码注释发现是Jdk1.5才开始有的StringBuilder,而且在后面版本加了个不加锁的类,看样子是解决非并发场景下的效率问题,不加锁对于操作大字符串还是有性能提升的。

嗯,不错,get了一个小知识。

出于好奇看了一下这两个类的代码,与String真有些类似,只不过这时的chat[] 已经是不带final的咯,这样就避免了String类操作时产生一堆字符串对象的问题。

char[] value;
  • StringBuffer和StringBuilder的作用

既然已经有了String,那这两个家伙有什么用呢?其实问题还是要和String的原理有关系。因为String是通过常量池管理的,这样解决的是相同字符串重复创建的问题,但大部分字符串都是不一样的,特别是在做字符串拼接操作时,如果用String的+进行拼接就会产生大量的字符串常量,非常的消耗性能与空间。

为解决这个问题就用到StringBuffer,本质上也就是通过一个可变的字符序列,在字符串操作时不需要生成新的对象,从而提升内存使用。

看看StringBuffer是怎么提升这个拼接性能的吧。 查看StringBuffer/StringBuilder的代码(JDK1.5+)发现它们都继承于AbstractStringBuilder。很多的代码其实都是在AbstractStringBuilder里完成的。因为这个问题由拼接引出的,在此我们就主要关注一下append方法吧。

public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);//确定容量
str.getChars(0, len, value, count);//取出str的字符放入到value数组中
count += len;//count累加
return this;
}

代码还是比较清楚的,整个过程最重要的就是使用String的getChars方法将str的值写入到当前对象的value中。而String的getChars方法如下:

public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
if (srcBegin < 0) {
throw new StringIndexOutOfBoundsException(srcBegin);
}
if (srcEnd > value.length) {
throw new StringIndexOutOfBoundsException(srcEnd);
}
if (srcBegin > srcEnd) {
throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
}
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}

可以看出最终是做了一个数组的复制,因为在AbstractStringBuilder中的value是个可变的char数组,这样的话对于字符串操作只需要在char数组上进行即可。不会像String那样生成新对象,所以说自然就变的高效了。

注:此文章为原创,欢迎转载,请在文章页面明显位置给出此文链接!
若您觉得这篇文章还不错请点击下右下角的推荐,非常感谢!
http://www.cnblogs.com/5207/

java中的字符串相关知识整理的更多相关文章

  1. [转]java中的字符串相关知识整理

    字符串为什么这么重要 写了多年java的开发应该对String不陌生,但是我却越发觉得它陌生.每学一门编程语言就会与字符串这个关键词打不少交道.看来它真的很重要. 字符串就是一系列的字符组合的串,如果 ...

  2. java中数组的相关知识

      1. 2.数组的命名方法 1)int[]ages=new int[5]; 2) int[]ages; ages=new int[5]; 3)int[]ags={1,2,3,4,5}; 4)int[ ...

  3. Redis相关知识整理

    Redis相关知识整理 1. Redis和MySQL的区别?a).mysql是关系型数据库,而redis是NOSQL,非关系型数据库.mysql将数据持久化到硬盘,读取数据慢,而redis数据先存储在 ...

  4. Java中Date各种相关用法

    Java中Date各种相关用法(一) 1.计算某一月份的最大天数 Java代码 Calendar time=Calendar.getInstance(); time.clear(); time.set ...

  5. 详解Java中的字符串

    字符串常量池详解 在深入学习字符串类之前, 我们先搞懂JVM是怎样处理新生字符串的. 当你知道字符串的初始化细节后, 再去写String s = "hello"或String s ...

  6. [原创]Java中的字符串比较,按照使用习惯进行比较

    java中的字符串比较一般可以采用compareTo函数,如果a.compareTo(b)返回的是小于0的数,那么说明a的unicode编码值小于b的unicode编码值. 但是很多情况下,我们开发一 ...

  7. 理解Java中的字符串类型

    1.Java内置对字符串的支持: 所谓的内置支持,即不用像C语言通过char指针实现字符串类型,并且Java的字符串编码是符合Unicode编码标准,这也意味着不用像C++那样通过使用string和w ...

  8. Java中的字符串驻留

    转自:http://www.cdtarena.com/javapx/201307/9088.html 最近在工作的时候,一句再正常不过的代码String a = “hello” + “world”;被 ...

  9. Java中的字符串拼接

    Java中的字符串拼接 1.设计源码 /** * @Title:IndexOf.java * @Package:com.you.freemarker.model * @Description: * @ ...

随机推荐

  1. 事务日志已满,原因为“ACTIVE_TRANSACTION”

    汇总篇:http://www.cnblogs.com/dunitian/p/4822808.html#tsql 异常处理汇总-数据库系列  http://www.cnblogs.com/dunitia ...

  2. runtime梳理。

    一.runtime简介 RunTime简称运行时.OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消息机制. 对于C语言,函数的调用在编译的时候会决定调用哪个函数. 对于OC的函数,属于 ...

  3. jQuery学习之路(8)- 表单验证插件-Validation

    ▓▓▓▓▓▓ 大致介绍 jQuery Validate 插件为表单提供了强大的验证功能,让客户端表单验证变得更简单,同时提供了大量的定制选项,满足应用程序各种需求.该插件捆绑了一套有用的验证方法,包括 ...

  4. 【Machine Learning】决策树案例:基于python的商品购买能力预测系统

    决策树在商品购买能力预测案例中的算法实现 作者:白宁超 2016年12月24日22:05:42 摘要:随着机器学习和深度学习的热潮,各种图书层出不穷.然而多数是基础理论知识介绍,缺乏实现的深入理解.本 ...

  5. 新手学习web遇到的一些乱码问题

    在新手学习web网站学习的时候经常会遇到?????这种乱码,对于刚起步的菜鸟来说真的很头痛,很容易打击继续学的信心当然了对于菜鸟的我最近也遇到过乱码问题,沉浸其中不能自拔,爱的深啊!!!!!我所遇到的 ...

  6. Android之网络数据存储

    一.网络保存数据介绍 可以使用网络来保存数据,在需要的时候从网络上获取数据,进而显示在App中. 用网络保存数据的方法有很多种,对于不同的网络数据采用不同的上传与获取方法. 本文利用LeanCloud ...

  7. can't run roscore 并且 sudo 指令返回 unable to resolve host

    I'm using ubuntu14 LTS. Problems: 1. When run roscore, got a mistake and an advice to ping the local ...

  8. 萌新笔记——linux下查看内存的使用情况

    windows上有各种软件可以进行"一键加速"之类的操作,释放掉一些内存(虽然我暂时不知道是怎么办到的,有待后续学习).而任务管理器也可以很方便地查看各进程使用的内存情况,如下图: ...

  9. Linux网络驱动--snull

    snull是<Linux Device Drivers>中的一个网络驱动的例子.这里引用这个例子学习Linux网络驱动. 因为snull的源码,网上已经更新到适合最新内核,而我自己用的还是 ...

  10. OpenGL ES: Array Texture初体验

    [TOC] Array Texture这个东西的意思是,一个纹理对象,可以存储不止一张图片信息,就是说是是一个数组,每个元素都是一张图片.这样免了频繁地去切换当前需要bind的纹理,而且可以节省系统资 ...