第一节 String类型的方法参数

运行下面这段代码,其结果是什么?

package com.test;

public class Example {

    String str = new String("good");
char[] ch = { 'a', 'b', 'c' }; public static void main(String[] args) {
Example ex = new Example();
ex.change(ex.str, ex.ch);
System.out.println(ex.str);
System.out.println(ex.ch);
} public void change(String str, char ch[]) {
str = "test ok";
ch[0] = 'g';
} }

结果如下:

good
gbc

解说:java 中String是 immutable的,也就是不可变,一旦初始化,引用指向的内容是不可变的(注意:是内容不可变)。

  也就是说,假设代码中有String str = “aa”;str=“bb”;,则第二条语句不是改变“aa”原来所在存储地址中的内容,而是另外开辟了一个空间用来存储“bb”;同时由于str原来指向的“aa”现在已经不可达,jvm会通过GC自动回收。
 
  在方法调用时,String类型和数组属于引用传递,在上述代码中,str作为参数传进change(String str, char ch[]) 方法,方法参数str指向了类中str指向的字符串,但str= "test ok"; 语句使得方法参数str指向了新分配的地址,该地址存储“test ok”,而原来的str仍然指向“good”。对于数组而言,在change方法中,方法参数ch指向了类中ch指向的数组,ch[0] = 'g';语句改变了类中ch指向的数组的内容

我们再来看下面这段代码,它的运行结果是什么?

package com.test;

public class Example {

    String str = new String("good");
char[] ch = { 'a', 'b', 'c' }; public static void main(String[] args) {
Example ex = new Example();
ex.change(ex.str, ex.ch);
System.out.println(ex.str);
System.out.println(ex.ch);
} public void change(String str, char ch[]) {
str = str.toUpperCase();
ch = new char[]{ 'm', 'n' };
} }

结果如下:

good
abc

结合前面的解释进行理解,这个结果是不是在意料之中?!

根据JDK中java.lang.String的源码进行分析,从中可以得出String类型的对象不可变的原因,大致上有如下两个:

  1、java.lang.String类型在实现时,其内部成员变量全部使用final来修饰,保证成员变量的引用值只能通过构造函数来修改;

  2、java.lang.String类型在实现时,在外部可能修改其内部存储值的函数实现中,返回时一律构造新的String对象或者新的byte数组或者char数组;

仅凭第1点还不能保证其不可变特性:假如通过String类型的toCharArray方法可以直接访问String类型内部定义的char数组,那么即便String类型内部的char数组使用了final来修饰,也仅仅保证这个成员变量的引用不可变,而无法保证引用指向的内存区域不可变。

第2点保证了外部不可能修改java.lang.String类型对象的内部属性,从而保证String对象是不可变的。


第二节 String类型变量的赋值

2.1 String变量赋值方式:s2=new String(s1)

下面这段代码的运行结果是什么

package com.soft;

public class ExecutorsDemo {

    public static void main(String[] args) {
String s1="abc"+"def";
String s2=new String(s1);
if(s1.equals(s2))
System.out.println("equals succeeded");
if(s1==s2)
System.out.println("==succeeded");
}
}

结果:

equals succeeded

解说:上述代码中,s1与s2指向不同的对象,但是两个对象的内容却是一样的,故“s1==s2”为假,s1.equals(s2)为真。

此处我们来细说一下"=="与equals的作用:

  (1)"=="操作符的作用

    A、用于基本数据类型的比较

    B、判断引用是否指向堆内存的同一块地址

  (2)equals的作用

    用于判断两个变量是否是对同一个对象的引用,即堆中的内容是否相同,返回值为布尔类型

2.2 String变量赋值方式:s2 = s1

package com.soft;

public class ExecutorsDemo {

    public static void main(String[] args) {
String s1 = new String("java");
String s2 = s1; System.out.println(s1==s2);
System.out.println(s1.equals(s2));
}
}

结果:

true
true

解说:如果理解了前面那个例子的运行情况,那么这个就是一目了然的事情,此处s1与s2指向同一个对象,"=="操作符的作用之一就是判断引用是否指向堆内存的同一块地址,equals的作用是判断两个变量是否是对同一个对象的引用(即堆中的内容是否相同),故此处均输出“true”


第三节 将字符数组或字符串数组转换为字符串

此处再补充两个应用场景

一、将字符数组转换为字符串

下面代码中的两种方式均可直接将字符数组转换为字符串,不需要遍历拼接

package com.test;

public class Main {

    public Main() {
} public static void main(String[] args) {
char[] data = {'a', 'b', 'c'};
// String str = new String(data);
String str = String.valueOf(data);
System.out.println(str);
} }

此处可以看一下其他作者的文章以深入理解:【Java】数组不能通过toString方法转为字符串  http://www.cnblogs.com/ningvsban/p/3955483.html

二、将字符串数组转换为字符串

下面的代码是我们常用的方式,循环拼接

package com.test;

public class Main {

    public Main() {
} public static void main(String[] args) {
String[] ary = {"abc", "123", "45"};
String s = "";
for(String temp : ary) {
s=s.concat(temp);//和下面的一行二选一即可
// s += temp;
}
System.out.println(s);
} }

上述代码段不需要过多解释了


第四节 StringBuffer和StringBuilder

提到String,就不得不提一下JDK中另外两个常用来表示字符串的类,StringBuffer和StringBuilder。在编写java代码的过程中有时要频繁地对字符串进行拼接,如果直接用“+”拼接的话会建立很多的String型对象,严重的话会对服务器资源和性能造成不小的影响;而使用StringBuilder和StringBuffer能解决以上问题。根据注释,StringBuffer可谓老资格了,从JDK1.0时即伴随Java征战世界,而StringBuilder直到JDK1.5时才出现。面试时,StringBuffer和StringBuilder的区别也是常问的话题,StringBuffer是线程安全的,而StringBuilder不是线程安全的。

一、StringBuffer和StringBuilder的共同点:

1、用来完成字符串拼接操作;

2、都是可变对象,对象内的字符缓存会随着拼接操作而动态扩展;

3、构造时传入内部缓存大小时,可以降低缓存扩展的次数,明显提升字符串拼接操作的效率;

二、StringBuffer和StringBuilder的区别:

1、StringBuilder的方法都是线程不安全的,从另外一个角度讲,StringBuilder类型的对象在做字符串拼接操作时,由于少了线程同步的操作,执行效率上有很大提升;

2、StringBuffer的方法都加上了synchronized关键字,因而在一定的场景下,StringBuffer类型的对象都是线程安全的,但在执行效率上,由于多了线程同步的操作,因而会有少许的损失;

在大多数场景下,字符串拼接操作都是不需要考虑多线程环境下对结果的影响的,因而使用StringBuilder类型可以提升代码的执行效率。

在多个线程的代码中共享同一个StringBuffer类型的对象时,需要关注synchronized关键字对最终结果的影响。由于StringBuffer类的实现中,仅仅对每个方法使用了synchronized修饰,这只能保证在多线程场景下,访问StringBuffer对象的同一个方法时可以保证最终结果的一致性,假如一个线程访问A方法,另外一个线程方法B方法,则由于加锁对象的不同,可能会出现不一致的现象,这是需要程序员特别要注意的地方。类似的,可以参考Vector的实现和应用场景。

针对上面的将字符串数组转换为字符串,可以借助上面提到的StringBuilder(当然StringBuffer也可以),代码如下:

package com.test;

public class Main {

    public Main() {
} public static void main(String[] args) {
String[] ary = {"abc", "123", "45"};
StringBuilder sb = new StringBuilder();
for(int i = 0; i < ary.length; i++){
sb. append(ary[i]);
}
String newStr = sb.toString();
System.out.println(newStr);
} }

参考资料

这里有两篇文章,值得一读:

(1)三分钟理解Java中字符串(String)的存储和赋值原理 http://blog.csdn.net/zhuiwenwen/article/details/12351565

(2)Java之内存分析和String对象 http://www.cnblogs.com/devinzhang/archive/2012/01/25/2329463.html

java中String类型变量的赋值问题的更多相关文章

  1. 关于JAVA中string直接初始化赋值和new的区别,是否可以联系到int[]的情况

    String str1 = "ABC"; String str2 = new String("ABC"); String str1 = “ABC”;可能创建一个 ...

  2. java中String new和直接赋值的区别

        Java中String new和直接赋值的区别     对于字符串:其对象的引用都是存储在栈中的,如果是编译期已经创建好(直接用双引号定义的)的就存储在常量池中,如果是运行期(new出来的)才 ...

  3. Java中String对象创建机制详解()

    一String 使用 private final char value来实现字符串存储 二Java中String的创建方法四种 三在深入了解String创建机制之前要先了解一个重要概念常量池Const ...

  4. 探秘Java中String、StringBuilder以及StringBuffer

    探秘Java中String.StringBuilder以及StringBuffer 相信String这个类是Java中使用得最频繁的类之一,并且又是各大公司面试喜欢问 到的地方,今天就来和大家一起学习 ...

  5. java中String类学习

    java中String类的相关操作如下: (1)初始化:例如,String s = “abc”; (2)length:返回字符串的长度. (3)charAT:字符操作,按照索引值获得字符串中的指定字符 ...

  6. Java中String对象的不可变性

    首先看一个程序 package reverse; public class Reverse { public static void main(String[] args) { String c1=n ...

  7. java中String类、StringBuilder类和StringBuffer类详解

    本位转载自http://www.cnblogs.com/dolphin0520/p/3778589.html  版权声明如下: 作者:海子 出处:http://www.cnblogs.com/dolp ...

  8. Java中String类型细节

    Java中String类型细节 一 . String两种初始化方式 1 . String str1= “abc”;//String类特有的创建字符对象的方式,更高效 在字符串缓冲区中检测”abc”是否 ...

  9. 在Java中String类为什么要设计成final?String真的不可变吗?其他基本类型的包装类也是不可变的吗?

    最近突然被问到String为什么被设计为不可变,当时有点懵,这个问题一直像bug一样存在,竟然没有发现,没有思考到,在此总结一下. 1.String的不可变String类被final修饰,是不可继承和 ...

随机推荐

  1. SpringMVC 返回 html 视图页面,SpringMVC与Servlet,Servlet重定向与转发

    1. SpringMVC与Servlet的关系 SpringMVC框架是建立在Servlet之上的,提供各种功能,各种封装,各种方便的同时,它一点儿也没有限制Servlet,我们完全可以在Spring ...

  2. 快速解决mysql Lost connection to MySQL server at 'reading initial communication packet及can't connect to mysql server on 'localhost'

    今天在使用Navicat连一个远程mysql时,总是提示连接不成功,提示Lost connection to MySQL server at 'reading initial communicatio ...

  3. sudo简单命令语法及配置

    参考:http://yangrong.blog.51cto.com/6945369/1289452, https://wiki.archlinux.org/index.php/Sudo_(%E7%AE ...

  4. 解决在iOS8环境下,当用户关闭定位服务总开关时,无法将APP定位子选项加入定位权限列表的问题

    关键点:- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizati ...

  5. C++ 数组长度 以及 数组名作为参数传递给函数 以及 为什么不在子函数中求数组长度

    在看排序,首先是插入排序,思路理清后想用代码实现,然后问题来了: 如何求数组长度? 如果没记错,在Java中应该是有直接可用的方法的, Python中(序列)也有.len,在C/C++中,字符串倒是有 ...

  6. Cassandra 分布式集群

    1 实施Cassandra集群,并验证集群功能正常,抓图实验过程 2 为什么说对于布隆过滤器有"确定某个元素是否在某个集合中的代价和总的元素数目无关"?误判率和元素数目有关吗?为什 ...

  7. python之2048

    #-*- coding:utf-8 -*- import curses from random import randrange, choice # generate and place new ti ...

  8. TestNG之执行测试类方式

    TestNG提供了很多执行方式,下面做简单介绍. 1.XML指明测试类,按照类名执行,其中可以指定包名,也可指定无包名: 带包名,运行ParameterSample类和ParameterTest类 & ...

  9. 【Ext.Net学习笔记】05:Ext.Net GridPanel的用法(包含Filter、Sorter、Grouping、汇总(Summary)的用法)

    GridPanel是用来显示数据的表格,与ASP.NET中的GridView类似. GridPanel用法 直接看代码: <ext:GridPanel runat="server&qu ...

  10. 我的opencv之旅:ios人脸识别

    学习opencv有一年多了,这本来是我的毕业设计的一部分,但是因为不能突出专业重点,所以换了个课题. opencv在vc.android.ios下都能用,其中vc和android下的教程和主题贴最多, ...