一、String缓冲池

首先我们要明确,String并不是基本数据类型,而是一个对象,并且是不可变的对象。查看源码就会发现String类为final型的(当然也不可被继承),而且通过查看JDK文档会发现几乎每一个修改String对象的操作,实际上都是创建了一个全新的String对象。

字符串为对象,那么在初始化之前,它的值为null,到这里就有必要提下””、null、new String()三者的区别。null 表示string还没有new ,也就是说对象的引用还没有创建,也没有分配内存空间给他,而””、new String()则说明了已经new了,只不过内部为空,但是它创建了对象的引用,是需要分配内存空间的。

java的虚拟机在内存中开辟出一块单独的区域,用来存储字符串对象,这块内存区域被称为字符串缓冲池。那个java的字符串缓冲池是如何工作的呢?

String a = "abc";
String b = "abc";
String c = new String("xyz");

例如上边的代码:

String a = "abc";

创建字符串的时候先查找字符串缓冲池中有没有相同的对象,如果有相同的对象就直接返回该对象的引用,如果没有相同的对象就在字符串缓冲池中创建该对象,然后将该对象的应用返回。对于这一步而言,缓冲池中没有abc这个字符串对象,所以首先创建一个字符串对象,然后将对象引用返回给a。

String b = "abc";

这一句也是想要创建一个对象引用变量b使其指向abc这一对象。这时,首先查找字符串缓冲池,发现abc这个对象已经有了,这是就直接将这个对象的引用返回给b,此时a和b就共用了一个对象abc,不过不用担心,a改变了字符串而影响了b,因为字符串都是常量,一旦创建就没办法修改了,除非创建一个新的对象。

String c = new String("xyz");

查找字符串缓冲池发现没有xyz这个字符串对象,于是就在字符串缓冲池中创建了一个xyz对象然后再将引用返回。

从上边的分析可以看出,当new一个字符串时并不一定是创建了一个新的对象,有可能是与别的引用变量共同使用了同一个对象。下面看几个常见的有关字符串缓冲池的问题。

二、创建了几个对象

String a = "abc";
String b = "abc";
String c = new String("xyz");
String d = new String("xyz");
String e = "ab" + "cd";

这个程序与上边的程序比较相似,我们分比来看一下:

1、String a = “abc”;这一句由于缓冲池中没有abc这个字符串对象,所以会创建一个对象;

2、String b = “abc”;由于缓冲池中已经有了abc这个对象,所以不会再创建新的对象;

3、String c = new String(“xyz”);由于没有xyz这个字符串对象,所以会首先创建一个xyz的对象,然后这个字符串对象由作为String的构造方法,在内存中(不是缓冲池中)又创建了一个新的字符串对象,所以一共创建了两个对象;

4、String d = new String(“xyz”);缓冲池中已有该字符串对象,则缓冲池中不再创建该对象,然后会在内存中创建一个新的字符串对象,所以只创建了一个对象;

5、String e = ”ab” + ”cd”;由于常量的值在编译的时候就被确定了。所以这一句等价于String e = ”abcd”;所以创建了一个对象;

所以创建的对象的个数分别是:1,0,2,1,1。

三、到底相等不相等

我们在学习java时就知道两个字符串对象相等的判断要用equal而不能使用==,但是学习了字符串缓冲池以后,应该知道为什么不能用==,什么情况下==和equal是等价的,首先,必须知道的是,equal比较的是两个字符串的值是否相等,而==比较的是两个对象的内存地址是否相等,下面我们就通过几个程序来看一下。

实例一:

public static void main(String[] args) { String s1 = "Monday"; String s2 = "Monday"; if (s1 == s2) System.out.println("s1 == s2"); else System.out.println("s1 != s2");
}

输出结果:

s1 == s2

分析:通过上边的介绍字符串缓冲池,我们知道s1和s2都是指向字符串缓冲池中的同一个对象,所以内存地址是一样的,所以用==可以判断两个字符串是否相等。

实例二:

public static void main(String[] args) { String s1 = "Monday"; String s2 = new String("Monday"); if (s1 == s2) System.out.println("s1 == s2"); else System.out.println("s1 != s2"); if (s1.equals(s2)) System.out.println("s1 equals s2"); else System.out.println("s1 not equals s2");
} 

输出结果:

s1 != s2
s1 equals s2 

分析:由上边的分析我们知道,String s2 = new String(“Monday”);这一句话没有在字符串缓冲池中创建新的对象,但是会在内存的其他位置创建一个新的对象,所以s1是指向字符串缓冲池的,s2是指向内存的其他位置,两者的内存地址不同的。

实例三:

public static void main(String[] args) { String s1 = "Monday"; String s2 = new String("Monday"); s2 = s2.intern(); if (s1 == s2) System.out.println("s1 == s2"); else System.out.println("s1 != s2"); if (s1.equals(s2)) System.out.println("s1 equals s2"); else System.out.println("s1 not equals s2");
}

输出结果:

s1 == s2
s1 equals s2 

分析:先来说说intern()这个方法的作用吧,这个方法的作用是返回在字符串缓冲池中的对象的引用,所以s2指向的也是字符串缓冲池中的地址,和s1是相等的。

实例四:

public static void main(String[] args) { String Monday = "Monday";  String Mon = "Mon";  String  day = "day";  System.out.println(Monday == "Mon" + "day");  System.out.println(Monday == "Mon" + day);  }

输出结果:

true
false

分析:第一个为什么等于true我们已经说过了,因为两者都是常量所以在编译阶段就已经能确定了,在第二个中,day是一个变量,所以不能提前确定他的值,所以两者不相等,从这个例子我们可以看出,只有+连接的两边都是字符串常量时,引用才会指向字符串缓冲池,都则都是指向内存中的其他地址。

实例五:

public static void main(String[] args) { String Monday = "Monday";  String Mon = "Mon";  final String  day = "day";  System.out.println(Monday == "Mon" + "day");  System.out.println(Monday == "Mon" + day);  }

输出结果:

true
true

分析:加上final后day也变成了常量,所以第二句的引用也是指向的字符串缓冲池。

Java提高篇 —— String缓冲池相关推荐

  1. Java提高篇——Java实现多重继承

    多重继承指的是一个类可以同时从多于一个的父类那里继承行为和特征,然而我们知道Java为了保证数据安全,它只允许单继承.有些时候我们会认为如果系统中需要使用多重继承往往都是糟糕的设计,这个时候我们往往需 ...

  2. 【转】java提高篇(十)-----详解匿名内部类

    原文网址:http://www.cnblogs.com/chenssy/p/3390871.html 在java提高篇-----详解内部类中对匿名内部类做了一个简单的介绍,但是内部类还存在很多其他细节 ...

  3. java提高篇(三十)-----Iterator

    本文转载地址:            http://blog.csdn.net/chenssy/article/details/37521461 迭代对于我们搞Java的来说绝对不陌生.我们常常使用J ...

  4. java提高篇(九)-----实现多重继承

    多重继承指的是一个类可以同时从多于一个的父类那里继承行为和特征,然而我们知道Java为了保证数据安全,它只允许单继承.有些时候我们会认为如果系统中需要使用多重继承往往都是糟糕的设计,这个时候我们往往需 ...

  5. java提高篇(八)----详解内部类

    可以将一个类的定义放在另一个类的定义内部,这就是内部类. 内部类是一个非常有用的特性但又比较难理解使用的特性(鄙人到现在都没有怎么使用过内部类,对内部类也只是略知一二). 第一次见面 内部类我们从外面 ...

  6. java提高篇之数组(2)

    前面一节主要介绍了数组的基本概念,对什么是数组稍微深入了一点点,在这篇博文中主要介绍数组的其他方面. 三.性能?请优先考虑数组 在java中有很多方式来存储一系列数据,而且在操作上面比数组方便的多?但 ...

  7. java提高篇之详解内部类

    转载自 java提高篇之详解内部类 内部类是一个非常有用的特性但又比较难理解使用的特性(鄙人到现在都没有怎么使用过内部类,对内部类也只是略知一二). 第一次见面 内部类我们从外面看是非常容易理解的,无 ...

  8. java提高篇之抽象类与接口

    转载自 java提高篇之抽象类与接口 接口和内部类为我们提供了一种将接口与实现分离的更加结构化的方法. 抽象类与接口是java语言中对抽象概念进行定义的两种机制,正是由于他们的存在才赋予java强大的 ...

  9. 【转】java提高篇(二)-----理解java的三大特性之继承

    [转]java提高篇(二)-----理解java的三大特性之继承 原文地址:http://www.cnblogs.com/chenssy/p/3354884.html 在<Think in ja ...

最新文章

  1. 70页论文,图灵奖得主Yoshua Bengio一作:“生成流网络”拓展深度学习领域
  2. 从贫困的“问题少年”到计算机博士,最后成为商界泰斗,“创业之神”吉姆•克拉克是如何走向封神之路的?...
  3. iOS学习笔记16-SQLite应用
  4. 埋点、数仓到中台:数据体系的从0到1
  5. 安卓之上传文件,即HTTP提交表单
  6. 多媒体技术创新与难点探索(内附讲师资料下载)
  7. 支付宝 统一支付 php,支付宝APP支付 统一下单 php服务端 tp5
  8. Linux进程O(1)调度算法,面试必考哦
  9. LAMP环境下配置虚拟主机和域名的跳转
  10. (23)VHDL实现乘法器
  11. 怎么自学python-结合学习经历,谈一谈如何学习Python
  12. VMware vSphere Hypervisor下载
  13. 简述AI技术的工程部署
  14. android模拟器模拟nfc功能吗,android – 开始使用NFC模拟器
  15. vue-cropper 自定义旋转任意角度
  16. 电路分析(电路原理)
  17. 小牛电动股权曝光:李彦持股4.4% 李一男持股降至28.1%
  18. Windows 10, version 21H2 (released Nov 2021) 简体中文版、英文版(企业版)下载
  19. DGP链游公会丨一文概览什么是GameFi链游
  20. 如何对待每逢佳节被逼婚

热门文章

  1. matlab的灰色关联,五种灰色关联度分析matlab代码
  2. 【转】DICOM简述!!!!
  3. C#多线程之旅(1)——介绍和基本概念
  4. java对象间的转型,详细讲述Java中的对象转型
  5. Python 数据分析三剑客之 Pandas(九):时间序列
  6. Python3 爬虫学习笔记 C06 【正则表达式】
  7. PWN-PRACTICE-BUUCTF-22
  8. CCIE-LAB-第八篇-SDWAN-Branch1_Branch2_Vmanage
  9. 【CodeForces - 599C 】Day at the Beach(思维)
  10. 【牛客 - 551D】CSL 的字符串(单调栈,思维)