文档章节

[探讨java深入的不变模式] java中String类的用法

Kerry_Han
 Kerry_Han
发布于 2013/08/30 14:02
字数 3074
阅读 49
收藏 0

不变模式(Immutable Pattern)顾名思义,它的状态在它的生命周期内是永恒的(晕,永恒的日月星晨,对象如人,
太渺小,谈不上永恒!
),不会改变的.对于其中的不变类(Immutable Class),它的实例可以在运行期间保持状态永远不会被
改变,所以不需要采取共享互斥机制来保护,如果运用得当可以节省大量的时间成本. 请注意上面这段话,不变模式其中的不变类,说明不变类只是不变模式中一个组成部分,不变类和与之相辅的可变
类,以及它们之间的关系才共同构成不变模式!所以在涉及不变模式的时候一定要研究一个类是不变的还是可变的(Mutable).



因为需要在这里把String类涉及的各个方面总结一下,另给出了一下SCJP题目。如有不足的地方,请在评论中指教,谢谢。

 

一、不变模式:

一个字符串对象创建后它的值不能改变。
String str1="hello";//创建一个对象hello,不会变;
System.out.println(str1);
str1+=" world!";//两个字符串对象粘粘,系统其实创建了一个新的对象,把Str1的指向改了,指向新的对象;hello就                     //变成了垃圾;
System.out.println(str1);
//如果一直这样创建会影响系统的效率;要频繁的改变字符串对象的值就用StringBuffer来描述;
StringBuffer sb=new StringBuffer("[");
sb.append("hehe");

sb.append("]");//append();不会制造垃圾,真正在改sb的值;
System.out.println(sb);

二、对象池

 

首先要明白,Object obj = new Object();

obj是对象的引用,它位于栈中, new Object() 才是对象,它位于堆中。

可以这样理解obj是拴在氢气球上的绳子,new Object() 是氢气球,我们通过对象的引用访问对象,就像我们那着绳子氢气球才不会跑掉。

 

 

创建一个Stirng对象,主要就有以下两种方式:

java 代码

  1. String str1 = new String("abc");    

  2. Stirng str2 = "abc";  

     虽然两个语句都是返回一个String对象的引用,但是jvm对两者的处理方式是不一样的。对于第一种,jvm会马上在heap中创建一个String对象,然后将该对象的引用返回给用户。对于第二种,jvm首先会在内部维护的strings pool中通过String的 equels 方法查找是对象池中是否存放有该String对象,如果有,则返回已有的String对象给用户,而不会在heap中重新创建一个新的String对象;如果对象池中没有该String对象,jvm则在heap中创建新的String对象,将其引用返回给用户,同时将该引用添加至strings pool中。注意:使用第一种方法创建对象时,jvm是不会主动把该对象放到strings pool里面的,除非程序调用 String的intern方法。看下面的例子:

java 代码

  1. String str1 = new String("abc"); //jvm 在堆上创建一个String对象   

  2.   

  3.  //jvm 在strings pool中找不到值为“abc”的字符串,因此   

  4.  //在堆上创建一个String对象,并将该对象的引用加入至strings pool中   

  5.  //此时堆上有两个String对象   

  6. Stirng str2 = "abc";   //并没有创建对象,因为对象池里已经有"abc" 了

  7.   

  8.  if(str1 == str2){   

  9.          System.out.println("str1 == str2");   

  10.  }else{   

  11.          System.out.println("str1 != str2");   

  12.  }   

  13.   //打印结果是 str1 != str2,因为它们是堆上两个不同的对象   

  14.   

  15.   String str3 = "abc";   

  16.  //此时,jvm发现strings pool中已有“abc”对象了,因为“abc”equels “abc”   

  17.  //因此直接返回str2指向的对象给str3,也就是说str2和str3是指向同一个对象的引用   

  18.   if(str2 == str3){   

  19.          System.out.println("str2 == str3");   

  20.   }else{   

  21.          System.out.println("str2 != str3");   

  22.   }   

  23.  //打印结果为 str2 == str3  

三、String / StringBuffer / StringBuilder 

 

    String类是字符串常量,是不可更改的常量。而StringBuffer是字符串变量,它的对象是可以扩充和修改的。

 

   StringBuffer sb=new StringBuffer("[");//创建StringBuffer对象
   sb.append("hehe");//把hehe加入字符串,变成 "[hehe"

   sb.append("]");//append();不会制造垃圾,真正在改sb的值;
   System.out.println(sb);

   String str = sb.toString();//把"[hehe]",赋值给一个字符串对象str

    StringBuilder,一个可变的字符序列。此类提供一个与 StringBuffer 兼容的 API,但不保证同步。该类被设计用作StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。

 

StringBuilder sb=new StringBuilder("[");//创建StringBuilder对象
sb.append("hehe");//把hehe加入字符串,变成 "[hehe"

sb.append("]");//append();不会制造垃圾,真正在改sb的值;
System.out.println(sb);

String str = sb.toString();//把"[hehe]",赋值给一个字符串对象str

 

 

四、String类中常用的方法

 

String 类包括的方法可用于检查序列的单个字符、比较字符串、搜索字符串、提取子字符串、创建字符串副本并将所有字符全部转换为大写或小写。不用去记,用到的时候查API,用的多了自然就熟悉了。

 

 

  java.lang.String

 char charAt (int index)

 返回index所指定的字符

 String concat(String str)

 将两字符串连接

 boolean endsWith(String str)

 测试字符串是否以str结尾

 boolean equals(Object obj)

 比较两对象

 char[] getBytes

 将字符串转换成字符数组返回

 char[] getBytes(String str)

 将指定的字符串转成制服数组返回

 boolean startsWith(String str)

 测试字符串是否以str开始

 int length()

 返回字符串的长度

 String replace(char old ,char new)

 将old用new替代

 char[] toCharArray

 将字符串转换成字符数组

 String toLowerCase()

 将字符串内的字符改写成小写

 String toUpperCase()

 将字符串内的字符改写成大写

 String valueOf(Boolean b)

 将布尔方法b的内容用字符串表示

 String valueOf(char ch)

 将字符ch的内容用字符串表示

 String valueOf(int index)

 将数字index的内容用字符串表示

 String valueOf(long l)

 将长整数字l的内容用字符串表示

 String substring(int1,int2)

 取出字符串内第int1位置到int2的字符串

 

view plaincopy to clipboardprint?

  1. import java.util.Collections;   

  2. import java.util.Comparator;   

  3. import java.util.LinkedList;   

  4. import java.util.List;   

  5. public class StringUsage {   

  6.  // 几个欧不太用的方法。   

  7.  public static void main(String[] args) {   

  8.   // 判断字符串首/尾。   

  9.   String contentS = "test String again. test ";   

  10.   System.out.println(contentS);   

  11.   System.out.println("contentS.startsWith(\"test\")\t"  

  12.     + contentS.startsWith("test"));   

  13.   System.out.println("contentS.endsWith(\"test\")\t"  

  14.     + contentS.endsWith("test"));   

  15.   // 忽略大小写的字符串比较方法。   

  16.   System.out   

  17.     .println("contentS.compareToIgnoreCase(contentS.toUpperCase()))\t"  

  18.       + contentS.compareToIgnoreCase(contentS.toUpperCase()));   

  19.   // 不区分大小写的字符串比较。   

  20.   Comparator<String> cmprStr = String.CASE_INSENSITIVE_ORDER;   

  21.   List<String> lstStr = new LinkedList<String>();   

  22.   // 准备数据。   

  23.   lstStr.add("test");   

  24.   lstStr.add("Test");   

  25.   lstStr.add("tEst");   

  26.   lstStr.add("rest");   

  27.   lstStr.add("sest");   

  28.      

  29.   // 不区分大小写的排序。   

  30.   Collections.sort(lstStr, cmprStr);   

  31.   System.out.println(lstStr);   

  32.   // 区分大小写的排序。   

  33.   Collections.sort(lstStr);   

  34.   System.out.println(lstStr);   

  35.   System.out.println("\ntest String.valueOf()");   

  36.      

  37.   // 用 valueOf 处理 null 对象的字符串转换。   

  38.   try {   

  39.    String ss = null;   

  40.    System.out.println(ss);   

  41.    System.out.println(String.valueOf(null)); // 比较奇怪的地方   

  42.   } catch (RuntimeException e1) {   

  43.       e1.printStackTrace();   

  44.   }   

  45.      

  46.   BigDecimal db = null;   

  47.   try {   

  48.    System.out.println(((BigDecimal)db).toString());   

  49.   } catch (Exception e) {   

  50.    e.printStackTrace();   

  51.   }   

  52.   try {   

  53.    System.out.println(String.valueOf(db));   

  54.   } catch (Exception e) {   

  55.    e.printStackTrace();   

  56.   }   

  57.   db = new BigDecimal("100.00");   

  58.   try {   

  59.    System.out.println(((BigDecimal)db).toString());   

  60.   } catch (Exception e) {   

  61.    e.printStackTrace();   

  62.   }   

  63.  }   

  64. }  

 

五、SCJP题目

 

String a="Hello World!";
String b="Hello World!";
a=b?  a和b是否相等? 为什么?

String a=new String("Hello World!");
String b="Hello World!";
a=b?  a和b是否相等 ? 为什么?

 

解释:

1. 首先String不属于8种基本数据类型,String是一个对象。
因为对象的默认值是null,所以String的默认值也是null;但它又是一种特殊的对象,有其它对象没有的一些特性。  

2. new String()和new String(“”)都是申明一个新的空字符串,是空串不是null;

3. String str="kvill";

    String str=new String (“kvill”);的区别:  
在这里,我们不谈堆,也不谈栈,只先简单引入常量池这个简单的概念。  
常量池(constant pool)指的是在编译期被确定,并被保存在已编译的.class文件中的
一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量。  
看例1:  
String s0="kvill";  
String s1="kvill";  
String s2="kv" + "ill";  
System.out.println( s0==s1 );  
System.out.println( s0==s2 );  
结果为:  
true  
true  
首先,我们要知道Java会确保一个字符串常量只有一个拷贝。  
因为例子中的s0和s1中的”kvill”都是字符串常量,它们在编译期就被确定了,所以
s0==s1为true;而”kv”和”ill”也都是字符串常量,当一个字符串由多个字符串常量连
接而成时,它自己肯定也是字符串常量,所以s2也同样在编译期就被解析为一个字符串常量,所以s2也是常量池中”kvill”的一个引用。所以我们得出s0==s1==s2;  

用new String() 创建的字符串不是常量,不能在编译期就确定,所以new String() 
创建的字符串不放入常量池中,它们有自己的地址空间。  

看例2:  
String s0="kvill";  
String s1=new String("kvill");  
String s2="kv"+ new String("ill");  
System.out.println( s0==s1 );  
System.out.println( s0==s2 );  
System.out.println( s1==s2 );  
结果为:  
false  
false  
false  
例2中s0还是常量池中”kvill”的应用,s1因为无法在编译期确定,所以是运行时创
建的新对象”kvill”的引用,s2因为有后半部分new String(“ill”)所以也无法在编译
期确定,所以也是一个新创建对象”kvill”的应用;明白了这些也就知道为何得出此结果了。  

4. String.intern():  
再补充介绍一点:存在于.class文件中的常量池,在运行期被JVM装载,并且可以扩充。String的intern()方法就是扩充常量池的一个方法;当一个String实例str调用intern()方法时,Java查找常量池中是否有相同Unicode的字符串常量,如果有,则返回其的引用,如果没有,则在常量池中增加一个Unicode等于str的字符串并返回它的引用;看例3就清楚了。  

例3:  
String s0= "kvill";  
String s1=new String("kvill");  
String s2=new String("kvill");  
System.out.println( s0==s1 );  
System.out.println( “**********” );  
s1.intern(); 
s2=s2.intern(); //把常量池中"kvill"的引用赋给s2  
System.out.println( s0==s1);  
System.out.println( s0==s1.intern() );  
System.out.println( s0==s2 );  
结果为:  
false  
**********  
false //虽然执行了s1.intern(),但它的返回值没有赋给s1  
true //说明s1.intern()返回的是常量池中”kvill”的引用  
true 

 
最后我再破除一个错误的理解:  
有人说,“使用String.intern()方法则可以将一个String类的保存到一个全局Strin
g表中,如果具有相同值的Unicode字符串已经在这个表中,那么该方法返回表中已有字符串的地址,如果在表中没有相同值的字符串,则将自己的地址注册到表中“如果我把他说的这个全局的String表理解为常量池的话,他的最后一句话,“如果在表中没有相同值的字符串,则将自己的地址注册到表中”是错的:  

看例4:  
String s1=new String("kvill");  
String s2=s1.intern();  
System.out.println( s1==s1.intern() );  
System.out.println( s1+" "+s2 );  
System.out.println( s2==s1.intern() );  
结果:  
false  
kvill kvill  
true  
在这个类中我们没有声名一个"kvill"常量,所以s1.intern()同new String("kvill")是不同的,当我们调用s1.intern()后就在常量池中新添加了一个"kvill"常量,原来的不在常量池中的"kvill"仍然存在,也就不是“将自己的地址注册到常量池中”了。  
s1==s1.intern()为false说明原来的“kvill”仍然存在;  
s2现在为常量池中“kvill”的地址,所以有s2==s1.intern()为true。  

5. 关于equals()和==:  

这个对于String简单来说就是比较两字符串的Unicode序列是否相当,如果相等返回true;而==是比较两字符串的地址是否相同,也就是是否是同一个字符串的引用。  

6. 关于String是不可变的  

这一说又要说很多,大家只要知道String的实例一旦生成就不会再改变了,比如说:
String str=”kv”+”ill”+” “+”ans”;  
就是有4个字符串常量,首先”kv”和”ill”生成了”kvill”存在内存中,然后”kvill”又和” “ 生成 ”kvill “存在内存中,最后又和生成了”kvill ans”;并把这个字符串的地址赋给了str,就是因为String的“不可变”产生了很多临时变量,这也就是为什么建议用StringBuffer的原因了,因为StringBuffer是可改变的

本文转载自:

共有 人打赏支持
Kerry_Han
粉丝 13
博文 174
码字总数 54257
作品 0
海淀
程序员
一个java初学者的心得体验!

学习了一学期的Java课程,觉得是该总结自己的心得体会了。开始学习任何一门课(包括java),兴趣最重要。 还记得我编写的第一个经典程序“Hello Word”。从那时起我已开始走进Java的世界。 当时...

嘿你好夏天
01/12
0
0
JVM 虚拟机(对象创建,类加载器,执行引擎等),

1.揭开 Java 对象创建的奥秘? 2.class 文件结构详解? 3.详解 Java 类的加载过程? > Java 对象创建,class 文件结构 Java对象模型 。Java对象保存在堆内存中。在内存中,一个Java对象包含三...

desaco
08/29
0
0
Java面试中,遇到这类面试题最吃亏!

从你接触 Java开发到现在,你对 Java最直观的印象是什么呢?是它宣传的 “Compile once, run anywhere”,还是目前看已经有些过于形式主义的语法呢?你对于 Java平台到底了解到什么程度?请你...

Java大蜗牛
07/20
0
0
2018年Java编程学习面试最全知识点总结

Java是一种可以撰写跨平台应用软件的面向对象的程序设计语言。Java 技术具有卓越的通用性、高效性、平台移植性和安全性,广泛应用于PC、数据中心、游戏控制台、科学超级计算机、移动电话和互...

Java小辰
05/14
0
0
从 java bean 的内省到 dbutils 的应用

java bean 内省的基础 java bean 的内省,其实可以算是反射的一种基础应用,关于 java 的反射,无非就是获得对应的类、属性、方法、修饰符等的应用,对于 java 的反射探讨,可以点击参考 ja...

peiquan
07/04
0
0

没有更多内容

加载失败,请刷新页面

加载更多

什么是集群、分布式、集中式、伪分布式

1. 集中式 将项目等部署到同一台机器上,对机器性能要求比较高,一般会用多台机器备份,否则,如果机器出现死机等状况,整个项目将不能运行。 eg:就好比你要盖一座房子,你房子就给一个人盖...

吴伟祥
52分钟前
1
0
微服务写的最全的一篇文章

今年有人提出了2018年微服务将疯狂至死,可见微服务的争论从未停止过。在这我将自己对微服务的理解整理了一下,希望对大家有所帮助。 1.什么是微服务 1)一组小的服务(大小没有特别的标准,...

Java烂猪皮
53分钟前
1
0
Java 并发编程原理

为什么要使用多线程? 通过多线程提高吞吐量 伸缩性比较好,可以增加 CPU 核心来提高程序性能 什么场景下使用多线程? 如:tomcat BIO Java 如何实现多线程? Thread、Runnable、ExecutorSer...

aelchao
今天
2
0
谨慎的覆盖clone方法

说在前面 有些专家级程序员干脆从来不去覆盖clone方法,也从来不去调用它,除非拷贝数组。 其他方式 可以提供一个构造函数或者工厂去实现clone功能。 相比于clone,它们有如下优势: 不依赖于...

XuePeng77
今天
1
0
什么是最适合云数据库的架构设计?

分布式数据库技术发展多年,但是在应用、业务的驱动下,分布式数据库的架构一直在不断发展和演进。 开源金融级分布式数据库SequoiaDB,经过6年的研发,坚持从零开始打造数据库核心引擎。在技...

巨杉数据库
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部