`
macheng365
  • 浏览: 28024 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

String深入讲解

阅读更多
1.对String对象的比较方法需要了解。
Java里对象之间的比较有两种概念,这里拿String对象来说:一种是用"=="来比较,这种比较是针对两个String类型的变量的引用,也就是说如果两个String类型的变量,它们所引用同一个String对象(即指向同一块内存堆),则"=="比较的结果是true。另一种是用Object对象的equals()方法来比较,String对象继承自Object,并且对equals()方法进行了重写。两个String对象通过equals()方法来进行比较时,其实就是对String对象所封装的字符串内容进行比较,也就是说如果两个String对象所封装的字符串内容相同(包括大小写相同),则equals()方法将返回true。

现在开始将对String对象的创建做具体的分析。

首先看以下代码段:

String s1 = new String("Hello");鲲鹏网
String s2 = new String("Hello");

System.out.println(s1 == s2);
System.out.println(s1.equals(s2));

以上代码段的打印结果是:

false
true

这个结果相信大家很好理解,两个String类型的变量s1和s2都通过new关键字分别创建了一个新的String对象,这个new关键字为创建的每个对象分配一块新的、独立的内存堆。因此当通过"=="来比较它们所引用的是否是同一个对象时,将返回false。而通过equals()方法来比较时,则返回true,因为这两个对象所封装的字符串内容是完全相同的。

好,现在把上面的代码段修改如下:

String s1 = new String("Hello");
String s2 = s1;

System.out.println(s1 == s2);
System.out.println(s1.equals(s2));

以上代码段的打印结果是:

true
true

这个结果应该更好理解,变量s1还是通过new关键字来创建了一个新的String对象,但这里s2并没有通过new关键字来创建一个新的String对象,而是直接把s1赋值给了s2,即把s1的引用赋值给了s2,所以s2所引用的对象其实就是s1所引用的对象。所以通过"=="来比较时,返回true。既然它们引用的都是同一个对象,那么通过equals()方法来比较时,肯定也返回true,这里equals()方法其实在对同一个对象进行比较,自己肯定等于自己咯。

说到此,其实应该没什么大问题,因为这些都是标准的创建对象的动作,但是String对象还有另一种使用方法,也就是开头所提到的可以作为一个基本类型来使用,请看以下代码段:

String s = "Hello";

看到这里,相信一些初学的朋友或者对String对象还没搞清楚的朋友开始疑惑了,你肯定会问,这个使用方法在其内部到底发生了什么?其实这就是String对象容易混淆的关键!

其实在启动程序时,虚拟机会创建一块String对象的String缓冲池。当String对象作为一个基本类型来使用时,比如:String s = "Hello";,虚拟机会先在这个String缓冲池内寻找是否有相同值的String对象存在,如果存在,则把这个String对象的引用赋值给s。如果不存在,虚拟机会先在这个String缓冲池内创建此String对象,然后把引用赋值给s。

说到这里,相信大家已经开始明白了。那么请看下面的代码段:

String s1 = "Hello";
String s2 = "Hello";

System.out.println(s1 == s2);
System.out.println(s1.equals(s2));

以上代码段的打印结果是:

true
true

为什么这个结果?那么来分析一下。首先这两个String对象都是作为一个基本类型来使用的,而不是通过new关键字来创建的,因此虚拟机不会为这两个String对象分配新的内存堆,而是到String缓冲池中来寻找。

首先为s1寻找String缓冲池内是否有与"Hello"相同值的String对象存在,此时String缓冲池内是空的,没有相同值的String对象存在,所以虚拟机会在String缓冲池内创建此String对象,其动作就是new String("Hello");。然后把此String对象的引用赋值给s1。

接着为s2寻找String缓冲池内是否有与"Hello"相同值的String对象存在,此时虚拟机找到了一个与其相同值的String对象,这个String对象其实就是为s1所创建的String对象。既然找到了一个相同值的对象,那么虚拟机就不在为此创建一个新的String对象,而是直接把存在的String对象的引用赋值给s2。

这里既然s1和s2所引用的是同一个String对象,即自己等于自己,所以以上两种比较方法都返回ture。

到这里,对String对象的基本概念应该都已经理解了。
 
现在我来小结一下:
针对String作为一个基本类型来使用:

1。如果String作为一个基本类型来使用,那么我们视此String对象是String缓冲池所拥有的。
2。如果String作为一个基本类型来使用,并且此时String缓冲池内不存在与其指定值相同的String对象,那么此时虚拟机将为此创建新的String对象,并存放在String缓冲池内。
3。如果String作为一个基本类型来使用,并且此时String缓冲池内存在与其指定值相同的String对象,那么此时虚拟机将不为此创建新的String对象,而直接返回已存在的String对象的引用。

针对String作为一个对象来使用:

1。如果String作为一个对象来使用,那么虚拟机将为此创建一个新的String对象,即为此对象分配一块新的内存堆,并且它并不是String缓冲池所拥有的,即它是独立的。

理解了以上内容后,请看以下代码段:

String s1 = "Hello";
String s2 =  new String("Hello");



System.out.println(s1 == s2);
System.out.println(s1.equals(s2));

以上代码段的打印结果是:

false
true

根据上面的小结来进行分析。第一行是把String作为一个基本类型来使用的,因此s1所引用的对象是属于String缓冲池内的。并且此时String缓冲池内并没有与其值相同的String对象存在,因此虚拟机会为此创建一个新的String对象,即new String("Hello");。第二行是把String作为一个对象来使用的,因此s2所引用的对象不属于String缓冲池内的,即它是独立的。通过new关键字,虚拟机会为此创建一个新的String对象,即为它分配了一块新的内存堆。因此"=="比较后的结果是false,因为s1和s2所引用的并不是同一个对象,它们是独立存在的。而equals()方法所返回的是true,因为这两个对象所封装的字符串内容是完全相同的。

现在,相信大家已经完全搞清楚String对象是怎么一回事了:)但是到此并没有结束,因为String对象还有更深层次的应用。

 
 
 
 
2.对String的更深的理解
这里我将分析一下String对象的intern()方法的应用。
intern()方法将返回一个字符串对象的规范表示法,即一个同该字符串内容相同的字符串,但是来自于唯一字符串的String缓冲池。这听起来有点拗口,其实它的机制有如以下代码段:

String s = new String("Hello");
s = s.intern();

以上代码段的功能实现可以简单的看成如下代码段:

String s = "Hello";

你一定又开始疑惑了?那么你可以先看第二个代码段。第二个代码段的意思就是从String缓冲池内取出一个与其值相同的String对象的引用赋值给s。如果String缓冲池内没有与其相同值的String对象存在,则在其内为此创建一个新的String对象。那么第一段代码的意思又是什么呢?我们知道通过new关键字所创建出的对象,虚拟机会为它分配一块新的内存堆。如果平凡地创建相同内容的对象,虚拟机同样会为此分配许多新的内存堆,虽然它们的内容是完全相同的。拿String对象来说,如果连续创建10个相同内容的String对象(new String("Hello")),那么虚拟机将为此分配10块独立的内存堆。假设所创建的String对象的字符串内容十分大,假设一个Stirng对象封装了1M大小的字符串内容,那么如果我们创建10个此相同String对象的话,我们将会毫无意义的浪费9M的内存空间。我们知道String是final类,它所封装的是字符串常量,因此String对象在创建后其内部(字符串)值不能改变,也因此String对象可以被共享。所以对于刚才提到的假设,我们所创建的10个相同内容的String对象,其实我们只需为此创建一个String对象,然后被其它String变量所共享。要实现这种机制,唯一的、简单的方法就是使用String缓冲池,因为String缓冲池内不会存在相同内容的String对象。而intern()方法就是使用这种机制的途径。在一个已实例化的String对象上调用intern()方法后,虚拟机会在String缓冲池内寻找与此Stirng对象所封装的字符串内容相同值的String对象,然后把引用赋值给引用原来的那个String对象的String类型变量。如果String缓冲池内没有与此String对象所封装的字符串内容相同值的String对象存在,那么虚拟机会为此创建一个新的String对象,并把其引用赋值给引用原来的那个String对象的String类型变量。这样就达到了共享同一个String对象的目的,而原先那个通过new关键字所创建出的String对象将被抛弃并被垃圾回收器回收掉。这样不但降低了内存的使用消耗,提高了性能,而且在String对象的比较上也同样更方便了,因为相同的String对象将被共享,所以要判断两个String对象是否相同,则只需要使用"=="来比较,而无需再使用equals()方法来比较,这样不但使用起来更方便,而且也提高了性能,因为String对象的equals()方法将会对字符串内容拆解,然后逐个进行比较,如果字符串内容十分大的话,那么这个比较动作则大大降低了性能。

说到此,大家可能对具体应用还有点模糊,那么我来举个简单的示例,以便阐述以上概念:

假设有一个类,它有一个接收消息的方法,这个方法记录用户传来的消息(假设消息内容可能较大,并且重复率较高),并且把消息按接收顺序记录在一个列表中。我想有些朋友会这样设计:

import java.util.*;

public class Messages {

ArrayList messages = new ArrayList();

public void record(String msg) {
messages.add(msg);
}

public List getMessages() {
return messages;
}
}

这种设计方案好吗?假设我们重复的发送给record()方法同一个消息(消息来自不同的用户,所以可以视每个消息为一个new String("...")),并且消息内容较大,那么这个设计将会大大浪费内存空间,因为消息列表中记录的都是新创建的、独立的String对象,虽然它们的内容都相同。那么怎么样可以对其进行优化呢,其实很简单,请看如下优化后的示例:

import java.util.*;

public class Messages {

ArrayList messages = new ArrayList();

public void record(String msg) {
messages.add(msg.intern());
}

public List getMessages() {
return messages;
}
}

正如你所看到的,原先record()方法中的messages.add(msg);代码段变成了messages.add(msg.intern());,仅仅对msg参数调用了intern()方法,这样将对重复的消息进行共享机制,从而降低了内存消耗,提高了性能。

这个例子的确有点牵强,但是这里只是为了阐述以上概念!

至此,String对象的迷雾都被消除了,大家只要牢记这些概念,以后再复杂的String应用都可以建立在此基础上来进行分析。

分享到:
评论

相关推荐

    C++ string深入详解(最新版)

    C++ string深入详解(最新版)。。。。。。。。。。。。。

    C string深入详解2.0版_C++_string_

    学习string类相关的操作是学习c++语言很重要的一个内容,c++的string类详细讲解

    关于java String中intern的深入讲解

    主要给大家介绍了关于java String中intern的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用java具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧

    C#沉淀之委托的深入讲解

    什么是委托 要传递方法,就必须把方法的细节封装在一钟新类型的对象中,即委托。委托是一种特殊类型的对象,其特殊之处在于,我们以前定义的所有对象都包含数据,而委托只包含一个或多个方法的地址。...

    深入讲解Python中的迭代器和生成器

    在Python中,很多对象都是可以通过for语句来直接遍历的,例如list、string、dict等等,这些对象都可以被称为可迭代对象。至于说哪些对象是可以被迭代访问的,就要了解一下迭代器相关的知识了。 迭代器 迭代器对象...

    尚硅谷_宋红康_第9章_Java常用类.pdf

    3.技术讲解更深入、更全面: ·课程共30天,715个知识视频小节,涉及主流Java使用的方方面面,全而不冗余 ·全程内容涵盖数据结构、设计模式、JVM内存结构等深度技术 ·企业级笔试面试题目深入源码级讲解,拒绝死记...

    Android自定义属性深入理解

    仿照系统TextView的自定义属性,以及讲解obtainStyledAttributes各个参数的用处,详情请见博客https://blog.csdn.net/qq_29951983/article/details/80219746

    StringBuilder为什么线程不安全深入讲解

    主要给大家介绍了关于StringBuilder为什么线程不安全的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用StringBuilder线程具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧

    python中的反斜杠问题深入讲解

    前言 python本身使用 \ 来转义一些特殊字符,比如在字符串中加入引号的时候 s = 'i\'m superman' print(s) # i'm superman 为了防止和字符串本身的引号冲突,使用 \ 来转义,一般情况下这个也不会引起什么问题,...

    iOS常见的几个修饰词深入讲解

    前言: 最近公司在扩招,做为公司仅有的唯一一个首席iOS开发工程师(手动滑稽),我不得不硬着头皮上阵。 然后却发现很多人的水平和年限严重不符,公司招的人都是3年+以上经验的人,然而这些人中有一半连修饰词的作用...

    深入讲解Java编程中类的生命周期

    引言  最近有位细心的朋友在阅读笔者的文章时,对java类的生命周期问题有一些疑惑,笔者打开百度搜了一下相关的问题,看到网上的资料很少有把这个问题讲明白的,主要是因为目前国内java方面的教材大多只是告诉你...

    kotlin gson反序列化默认值失效深入讲解

    Gson反序列化原理 原理简述 gson反序列化主要分为两个过程: 根据TypeToken创建出对象 根据json字符串解析数据,对对象属性赋值 对象的创建 ConstructorConstructor.get 先尝试获取无参构造函数 ...

    Go语言中TCP/IP网络编程的深入讲解

    前言 大家可能乍一看,通过TCP/IP层连接两个进程会感觉可怕, 但是在Go语言中可能比你想象的要简单的多。下面话不多说了,来一起看看详细的介绍吧。 TCP/IP层发送数据的应用场景 当然很多情况下,不是大多数情况下...

    C++ 语法详解

    C++语法详解》对C++的语法进行了全面介绍和深入讲解,内容包括:C++整型、字符型、浮点型、声明、定义、typedef、运算符、表达式、左值、选择语句、循环语句、指针、数组、函数和标识符的作用域、类基础、类作用域及...

    Java工程师必学系列课程之5--《字符串及正则表达式》视频课程

    本课程专门讲解字符串相关的知识,将从字符串的存储方式、底层的运行方式等各方面深入讲解其中的原理和技巧。此外,对字符串进行更高级的处理,又要用到正则表达式的相关知识。正则表达式广泛应用于各种与字符串处理...

    深入解析Go语言的io.ioutil标准库使用

    今天我们讲解的是golang标准库里边的io/ioutil包–也就是package io/ioutil 1.ioutil.ReadDir(dirname string)这个函数的原型是这样的 func ReadDir(dirname string) ([]os.FileInfo, error) 不难看出输入的是...

    深入c#绘制验证码的详解

    2.首先定义CheckCode()方法,以生成4为英文及数字组成的字符串序列: 代码如下:private string CheckCode() { int number; char code; string checkCode = String.Empty; Random random = new Random(); for ...

    编程课件+Java系列课程+PPT课件+Java教学

    - 字符串与集合:深入String类和集合框架,如ArrayList、HashMap。 - 异常处理:介绍try-catch和自定义异常。 五、I/O与文件操作 - I/O流:讲解输入输出流的基本概念。 - 文件操作:指导文件的读写和操作。 六、...

    Android VideoView类实例讲解

    本节使用系统的示例类VideoView继续SurfaceView类相关内容的讲解,以让大家能更深入理解Android系统中图形绘制基础类的实现原理。也许你会发现无法改变VideoView类的控制方面,我们可以通过重构VideoView类来实现...

    Java超神之路.rar

    1.CoreJava,就是java基础、JDK的类库,很多童鞋都会说,JDK我懂,但是懂还不足够,知其然还要知其所以然,JDK的源代码写的非常好,要经常查看,对使用频繁的类,比如String,集合类(List,Map,Set)等数据结构要...

Global site tag (gtag.js) - Google Analytics