`
shz2008bj
  • 浏览: 491566 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

对String对象的进一步分析

阅读更多
首先大家知道,String既可以作为一个对象来使用,又可以作为一个基本类型来使用。这里指的作为一个基本类型来使用只是指使用方法上的,比如 String s = "Hello",它的使用方法如同基本类型int一样,比如int i = 1;,而作为一个对象来使用,则是指通过new关键字来创建一个新对象,比如String s = new String("Hello")。但是它的内部动作其实还是创建了一个对象,这点稍后会说到。

其次,对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对象还有更深层次的应用。

这里我将分析一下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()方法,这样将对重复的消息进行共享机制,从而降低了内存消耗,提高了性能。

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

相关推荐

    Java String对象的经典问题

    ### Java String对象的经典问题 #### 一、String 类与对象机制概述 在Java中,`String`类是一个非常重要的类,它提供了丰富的功能用于处理文本数据。`String`类是不可变的(immutable),这意味着一旦一个`String`...

    创建string对象过程的内存分配:

    #### 四、进一步分析 ##### 字符串连接 使用字符串连接运算符 `+` 时,如 `String s = "abc" + "def";`,内存分配过程如下: - 在编译期间,`"abc" + "def"` 会被优化为 `"abcdef"`,因此最终代码相当于 `String s...

    string对象的操作pdf

    ### string对象的操作详解 #### 一、string 类型简介及基本操作 在现代 C++ 编程中,`std::string` 是一个极其重要的类,它提供了丰富的接口用于字符串的处理,大大简化了字符串操作的复杂性。下面将详细介绍 `std...

    StringBuffer和String_区别

    - **String**:在进行字符串拼接时会产生多个 String 对象,如果频繁进行这样的操作,将会消耗大量的内存资源。 #### 四、使用场景分析 1. **单线程环境**:在这种情况下,推荐使用 **StringBuilder**,因为它不...

    STL中的string介绍

    类似地,`tfstream`则是一个能够自动选择字符类型的流对象,这进一步增强了代码的通用性和可移植性。 #### 使用方法 使用`string`和`wstring`进行字符串操作非常直观。它们提供了丰富的成员函数,如`append()`、`...

    string中的常见问题

    根据提供的代码片段,我们可以进一步分析不同情况下的字符串比较结果: 1. **实例 1:** ```java String a = "ab"; String b = "a" + "b"; System.out.println(a == b); // 输出 true ``` 在这个例子中,`a` ...

    String与date的转换

    例如,在处理用户输入的数据、解析文本文件或与数据库交互时,通常需要将字符串转换为日期对象进行进一步的操作,如日期计算、排序等;相反地,也需要将日期对象格式化为特定格式的字符串以便于展示给用户或存储到...

    算法设计与分析基础 习题参考答案

    十进制转二进制是通过不断除以2并记录余数来实现的,而求最小差值的算法可以进一步优化以提高效率。 最后,习题1.4探讨了数组操作的问题,特别是如何在不依赖数组长度的情况下高效地删除元素。在有序数组中删除元素...

    String和StringBuffer 的区别 .txt

    // 这里创建了一个新的String对象,而不是修改原来的s1 System.out.println(s2); // 输出仍然是:STRING值不变-> StringBuffer b1 = new StringBuffer("StringBuffer值不变->"); StringBuffer b2 = b1; b2....

    前端开源库-astring

    此外,astring库生成的代码格式简洁,没有额外的缩进或空格,这在某些场景下是理想的,特别是当代码需要被其他工具进一步处理时。 在实际项目中,astring可以与其他前端工具链组件结合使用。比如,配合Babel进行...

    dotnet-FastString提供了两种使用字符串的方法可以比NET快得多

    在优化前,应先通过性能分析确定瓶颈,然后再决定是否采用`FastString`。同时,引入额外的库也可能增加项目的复杂性,因此权衡利弊是必要的。 在使用`FastString-master`这个压缩包时,首先解压后查看项目文档或源...

    通过HSDB来了解String值的真身在哪里1

    使用HSDB进行进一步分析,可以更深入地了解这些对象在内存中的分布。通过设置断点,观察堆栈帧,我们可以看到String对象在PermGen(永久代,用于存储类信息和静态变量)和NewGen(新生代,用于存放新创建的对象)中...

    C++下基于QT的String类自己实现

    通过阅读和分析这些文件,你可以深入理解自定义`String`类的具体实现细节,并可能从中学习到内存管理、面向对象编程以及C++语言特性的实际应用。如果有任何疑问,按照描述中的提示,你可以与作者进行探讨,进一步...

    java完美按格式化字符串String转sql.date

    通过这两个参数,我们可以将字符串解析成日期对象,然后进一步转换为`java.sql.Date`对象。 #### 三、代码实现 ```java import java.text.ParseException; import java.text.SimpleDateFormat; import java.util....

    Delphi字符串快速替换函数,速度超快 秒杀自带的StringReplace函数

    1. **使用原地替换**:如果可能,尝试在原始字符串上进行替换,而不是创建新的字符串对象,这可以减少内存分配和拷贝的开销。 2. **缓存查找模式**:对查找模式进行预处理,生成查找表,以便快速确定下一个可能的...

    通过设计、编制、调试一个典型的语法分析程序

    编译原理实验报告的主要目的是通过设计、编制、调试一个典型的语法分析程序,实现对词法分析程序所提供的单词序列进行语法检查和结构分析,进一步掌握常用的语法分析方法。 一、实验目的: * 通过设计、编制、调试...

    Struts2标签 s:if test 判断String类型的对象属性值和单字符是否相等的问题

    当涉及到对String类型的对象属性值与单个字符进行比较时,开发者可能会遇到一些微妙的问题。 #### 问题背景 在Struts2中,`<s:if>`标签用于条件渲染,即如果指定的测试表达式结果为真,则渲染该标签内的内容;反之...

    Java第4章 认识类和对象.含源代码

    通过提供的源代码,你可以进一步探索类和对象的实际应用,例如查看它们是如何相互协作完成特定任务的,或者分析类的设计是否遵循了良好的面向对象原则,如封装、继承和多态。同时,通过练习编写和运行这些代码,你将...

    Java面向对象基础练习题

    通过对上述练习题的分析,我们可以加深对Java面向对象编程中类和对象、实例变量、方法重载、构造方法、引用和this关键字的理解和应用。这些知识点对于日后进一步学习面向对象的高级特性,比如继承和多态,打下了坚实...

    Java的基础讲解和面向对象分析

    关于字符串的处理,Java提供了String类,它是不可变的对象,可以使用+操作符进行字符串拼接。此外,还可以使用StringBuilder或StringBuffer类进行更高效的动态字符串构建,特别是在循环中拼接字符串时。 在面向对象...

Global site tag (gtag.js) - Google Analytics