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

java 之 String的那些事儿

    博客分类:
  • java
阅读更多
equals方法和==的区别
  首先大家知道,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对象的创建做具体的分析:

1、/////////////////////////////////////////////////////////////////////////
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,因为这两个对象所封装的字符串内容是完全相同的。

2、//////////////////////////////////////////////////////////////////////////////////
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()方法其实在对同一个对象进行比较,自己肯定等于自己咯。

3、//////////////////////////////////////////////////////////////////////////////
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缓冲池所拥有的,即它是独立的。

理解了以上内容后,请看以下代码段:
4、/////////////////////////////////////////////////////////////////////////////
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()方法,这样将对重复的消息进行共享机制,从而降低了内存消耗,提高了性能。

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

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

相关推荐

    java编程那些事儿

    ### Java编程那些事儿 #### 程序设计概述与核心要素 **程序设计**,作为一项专业技能,涉及将现实世界的问题转化为计算机可执行的指令序列。它不仅仅是一门技术,更是一种思维方式的体现。本章节将从三个方面展开...

    java编程那些事儿-陈跃峰71-102

    《Java编程那些事儿》是陈跃峰先生的一本关于Java编程技术的著作,该书深入浅出地介绍了Java编程中的重要概念和实践技巧。这里我们主要聚焦于压缩包中的部分内容,涉及Java集合框架、时间日期处理、文件操作、多线程...

    Java那些事儿之(一)读取property配置文件

    在Java编程中,管理应用程序的配置参数是一项至关重要的任务。`properties`文件是Java中用于存储配置信息的标准格式,它以键值对的形式保存数据,易于读写且跨平台兼容。本文将深入探讨如何在Java中读取和操作`...

    Java编程那些事儿55—方法重载和参数传递

    ### Java编程那些事儿55—方法重载和参数传递 #### 方法重载(Overloading) 在Java编程语言中,**方法重载**(Overloading)是一个非常重要的概念,它允许我们定义多个具有相同名称但参数列表不同的方法。这种方法...

    Java那些必须知道的事儿_CodingPark

    Java是世界上最流行的编程语言之一,尤其在企业级应用开发中占据主导地位。了解其核心概念对于成为一名合格的Java开发者至关重要。以下是一些Java开发者必须掌握的关键知识点: 1. **Java对象实例化**: - **new ...

    Android中JNI编程的那些事儿.docx

    - 首先,在Java类中声明`native`方法,例如`public static native String sayHello(String name);` - 使用`javah`命令(或者在Android Studio中通过Gradle构建过程)生成对应的头文件(如`com_okwap_testjni.h`)...

    JAVA看不了string源码-java-skills::hot_springs:在此存储库中,您可以获得许多Java技能

    中那些值得我们了解的事儿,我在这里,你在哪里? 索引 朝花夕拾 从 JDK 9 开始,超级根类Object中的finalize()方法被标记为@Deprecated; 同样是从 JDK 9 开始,String类由char[]数组实现修改为由 byte[]数组实现。...

    开源! 用于生成 .java 源文件的 Java API

    Java诗人 JavaPoet是一个用于生成源文件的Java API .java。 源文件生成在执行注释处理或与元数据文件(例如数据库模式、... public static void main(String[] args) { System.out.println("Hello, JavaPoet!"); } }

    数据库连接池的实现java

    String sql = "SELECT * FROM mytable"; PreparedStatement pstmt = conn.prepareStatement(sql); ResultSet rs = pstmt.executeQuery(); // 处理结果集... } catch (Exception e) { e.printStackTrace(); } ...

    聊聊 Netty 那些事儿之 Reactor 在 Netty 中的实现(创建篇).doc

    public static void main(String[] args) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(); // 主 Reactor Group EventLoopGroup workerGroup = new NioEventLoopGroup(); // 从 Reactor...

    Web Service 那点事儿(2)—— 使用 CXF 开发 SOAP 服务 - ImportNew1

    【Web Service 那点事儿(2)—— 使用 CXF 开发 SOAP 服务】 Web Service是一种基于标准协议的,用于不同系统间交换数据的技术。SOAP(Simple Object Access Protocol)是Web Service常用的一种通信协议,它定义了...

    Freemarker那点事儿1

    特别是对于Map和List对象的处理,Freemarker要求value必须为String类型,这在实际开发中可能会带来不便。 在内容中,我们看到作者正在处理一个“分销系统”的通用组件,发现用户信息和菜单信息是由一个名为`_const_...

    java request.getParameter中文乱码解决方法

    在 Java 开发中,如果框架搭建的不完善或者初学者在学习过程中,出现中文乱码是经常的事儿。那么,为什么会出现中文乱码呢?原因是 Http 请求传输时将 URL 以 ISO-8859-1 编码,服务器收到字节流后默认会以 ISO-8859...

    阿里巴巴 Java 编码指南 pmd 实现和 IDE 插件

    注意:例如,应使用decode(String source, Stringencode)而不是已弃用的方法decode(StringencodeStr)。一旦接口被弃用,接口提供者就有义务提供新的接口。同时,客户端程序员有义务检查它的新实现是什么。 ...

    手把手教你直播解决方案.docx

    老罗的入场是真正的将直播带货这个事儿给完全的行业化了。本文将介绍如何基于服务来实现一套直播的解决方案。 什么是推流、拉流、直播和点播? 在开始之前,让我们先来了解一些基本概念。 * 推流:主播将本地视频...

    XStream完整jar

    XStream是一个开源的Java库,它能够将Java对象序列化为XML,并且可以从XML反序列化回Java对象。这个库非常适用于需要在应用程序之间交换数据或者持久化对象到存储介质如文件或数据库的情况。XStream的强项在于其简洁...

    安卓开发-屏保程序

    String action = intent.getAction(); if(action.equals(Intent.ACTION_SCREEN_OFF)){ // 捕捉到了黑屏事件,启动播放屏保的 Activity Intent screenSaverIntent = new Intent(); screenSaverIntent.setFlags...

Global site tag (gtag.js) - Google Analytics