String是Java API中使用频率第一的类,曾经在CSDN论坛上。至少每一个月都有相关的贴子,讨论==和equals()。

本节介绍这一部分的内容,也有一个重要更正


String文字在程序中被大量地使用。String文字作为引用,指向一个String对象。比如"baby"指向的一个String对象,该对象保存的数据主要有一个char[]引用和3个int值

字符串拘留

为了有效地利用堆(享元模式),加快字符串处理效率(以==比較替代equals(Object)比較),多种语言如Java、C#和Python等,都支持字符串拘留/集中营(string interning)技术,即对每个不同的字符串值仅保存一个拷贝(前提是它必须是不变对象)。

Java中两种拘留方式:

编译器将一个类中全部的String文字和常量表达式(如"ba"+"by"、"ba"+3+2等)加以分析。求出常量表达式的结果——String文字。然后只将不同的String文字表示为class文件的各个CONSTANT_String_info项(同样的这时就统一了)。在类加载时。依照它的符号引用CONSTANT_Utf8_info,提取二进制表示的各字符并在“堆”中创建String对象,并将该对象的引用在一个HashMap中注冊。

在该HashMap中注冊过的全部String对象的集合,有时候称为字符串池(string interning pool)。该HashMap驻留在方法区。而字符串池在堆中(注意。如同Java不在栈中分配对象空间一样,只具有逻辑上的含义)。

package jvm.internedStrings;
public class OnlyOneCopy{
static String str1 = "abc";
String str2 = "a"+"bc";
public void foo(){
String str3 = "a"+"b"+'c'; // 'c'不是"c"
System.out.println(str1==str3);
}
}

加载OnlyOneCopy 时,JVM依照class文件的常量池中CONSTANT_String_info项创建一个String对象。由于编译器自己主动支持字符串拘留技术,因而将刚才创建的String对象的引用"abc"在HashMap中注冊并交给不知名变量(假定为#2)保存。

类的初始化阶段。静态变量str1被初始化,即取出#2的引用"abc"赋值给str1;当在某处创建OnlyOneCopy对象时,将初始化事实上例域str2。即将#2的值赋值给str2;当某个程序调用方法foo()时。将#2的值赋值给str3。

注意,3个引用变量所处的各种位置

最后的结果是:通过==能够推断str1、str2和str3三者指向同一个对象

但要注意,假设字符串的连接操作符中包括变量,则编译器无法足够聪明地确定该表达式的值。比如String str1 = "abc";String
str2 =str1+ "";

str1和 str2 指向不同的对象。

假设要降低多个字符串拷贝,有两个手段达到该目的:以final修饰str1;使用String的intern()方法,如str2= (str1+""). intern()。

xxx.intern()意味着将xxx的内容通过equals(Object)方法,推断HashMap中是否存在一个 相等对象的引用。

假设存在则将该引用作为xxx自己的引用;假设不存在,则将xxx的值在HashMap中注冊,本String对象入池。

字符串池中的String对象是否能被垃圾回收?在现代的JVM实现中,一个被拘String对象,假设它不是编译时常量并且它不再被引用。则能够被垃圾回收。

字符子串



String 类的substring(int begin,intend)返回消息接受对象的范围为[begin.. end)的子字符串对象。并且该String对象是没有被拘留的

我们知道,String对象保存的数据主要有一个char[]引用和几个int值。JVM创建子字符串对象本身的代价极小。代价主要在char[]变量v指向的char[]。

String str =  "abc……十万个为什么".substring(0,1);

Java 7u6之前。创建的子字符串对象时。并不须要复制不论什么字符,子字符串和原字符串对象共享一个底层char[]对象,子字符串只是对原String对象的几个int成员变量(偏移量、长度等)加以更改。

可是。假设底层char[]对象在后面并不须要。则str本来仅仅须要一个字符。可是其char[]变量v指向的整个char[]。比方说10W长度的空间得不到释放。因而Java 7中。将底层char[]对象截取所需部分并复制。

參考JDK源码例如以下:

//JDK 6
String(int offset, int count, char value[]) {
this.value = value;
this.offset = offset;
this.count = count;
} public String substring(int beginIndex, int endIndex) {
//check boundary
return new String(offset + beginIndex, endIndex - beginIndex, value);
}

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveXFqMjA2NQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

//JDK 7
public String(char value[], int offset, int count) {
//check boundary
this.value = Arrays.copyOfRange(value, offset, offset + count);
} public String substring(int beginIndex, int endIndex) {
//check boundary
int subLen = endIndex - beginIndex;
return new String(value, beginIndex, subLen);
}

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveXFqMjA2NQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

【图 7-10 子字符串对象】从上图更正为下图。


练习7-1.:没有其它"abc"干扰时,String str = new String("abc") 会在堆中创建几个String对象

练习7-2.:介绍字符串拘留 (string interning)技术的意义。提示:空间和时间

练习7-3.:String文字能够被垃圾回收吗?

练习7-4.:编程:大量拘留String。观察在字符串较小如"1"、"2"和较大如"interninterninterninternintern1"时PermGen space可以拘留String的个数。

练习7-x1.:

String str1 = "abc"; 

String str2 = str1.substring(0,1); // "a" 

System.out.println("a"==str2);

输出:_____ ;原因_____________。

练习7-x2.:为什么在Java7u6之后,程序猿不须要写这种代码:

String str =  "abc……十万个为什么";

str =  str.substring(0,1)+"";

《编程导论(Java)·7.4.4 String对象问题》的更多相关文章

  1. 简单物联网:外网访问内网路由器下树莓派Flask服务器

    最近做一个小东西,大概过程就是想在教室,宿舍控制实验室的一些设备. 已经在树莓上搭了一个轻量的flask服务器,在实验室的路由器下,任何设备都是可以访问的:但是有一些限制条件,比如我想在宿舍控制我种花 ...

  2. 利用ssh反向代理以及autossh实现从外网连接内网服务器

    前言 最近遇到这样一个问题,我在实验室架设了一台服务器,给师弟或者小伙伴练习Linux用,然后平时在实验室这边直接连接是没有问题的,都是内网嘛.但是回到宿舍问题出来了,使用校园网的童鞋还是能连接上,使 ...

  3. 外网访问内网Docker容器

    外网访问内网Docker容器 本地安装了Docker容器,只能在局域网内访问,怎样从外网也能访问本地Docker容器? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Docker容器 ...

  4. 外网访问内网SpringBoot

    外网访问内网SpringBoot 本地安装了SpringBoot,只能在局域网内访问,怎样从外网也能访问本地SpringBoot? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装Java 1 ...

  5. 外网访问内网Elasticsearch WEB

    外网访问内网Elasticsearch WEB 本地安装了Elasticsearch,只能在局域网内访问其WEB,怎样从外网也能访问本地Elasticsearch? 本文将介绍具体的实现步骤. 1. ...

  6. 怎样从外网访问内网Rails

    外网访问内网Rails 本地安装了Rails,只能在局域网内访问,怎样从外网也能访问本地Rails? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Rails 默认安装的Rails端口 ...

  7. 怎样从外网访问内网Memcached数据库

    外网访问内网Memcached数据库 本地安装了Memcached数据库,只能在局域网内访问,怎样从外网也能访问本地Memcached数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装 ...

  8. 怎样从外网访问内网CouchDB数据库

    外网访问内网CouchDB数据库 本地安装了CouchDB数据库,只能在局域网内访问,怎样从外网也能访问本地CouchDB数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Cou ...

  9. 怎样从外网访问内网DB2数据库

    外网访问内网DB2数据库 本地安装了DB2数据库,只能在局域网内访问,怎样从外网也能访问本地DB2数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动DB2数据库 默认安装的DB2 ...

  10. 怎样从外网访问内网OpenLDAP数据库

    外网访问内网OpenLDAP数据库 本地安装了OpenLDAP数据库,只能在局域网内访问,怎样从外网也能访问本地OpenLDAP数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动 ...

随机推荐

  1. SQL SERVER读书笔记:nolock

    让查询语句不去申请共享锁,从而消除死锁,效果立竿见影. 缺点: 1.脏读 2.只能解决共享锁(S锁)参与的问题 3.需要修改语句本身才能使用

  2. luogu1771 方程的解

    题目大意 对于不定方程a1+a2+…+ak-1+ak=g(x),其中k≥2且k∈N,x是正整数,g(x)=x^x mod 1000(即x^x除以1000的余数),x,k是给定的数.我们要求的是这个不定 ...

  3. 现代英特尔® 架构上的 TensorFlow* 优化——正如去年参加Intel AI会议一样,Intel自己提供了对接自己AI CPU优化版本的Tensorflow,下载链接见后,同时可以基于谷歌官方的tf版本直接编译生成安装包

    现代英特尔® 架构上的 TensorFlow* 优化 转自:https://software.intel.com/zh-cn/articles/tensorflow-optimizations-on- ...

  4. sublime中BracketHighlighter 插件使用 (转)

    sublime中BracketHighlighter 插件使用 1.打开package Control,选择install Package 2.输入BracketHighlighter,回车 3.这样 ...

  5. [.Net] C# Excel操作类 ExcelHelper

    实现C#与Excel文件的交互操作,实现以下功能: 1.DataTable 导出到 Excel文件 2.Model数据实体导出到 Excel文件[List<Model>] 3.导出数据到模 ...

  6. HDU1412 {A} + {B}

    2019-05-17 10:15:01 每个元素之间有一个空格隔开. 每行最后一的元素后面没有空格,区别于HDU人见人爱A - B 注意使用STL的时候要清空 .  a.clear(); #inclu ...

  7. MFC:“Debug Assertion Failed!” ——自动生成的单文档程序项目编译运行就有错误

    今天照着孙鑫老师的VC++教程学习文件的操作,VS2010,单文档应用程序,项目文件命名为File,也就有了自动生成的CFileDoc.CFileView等类,一进去就编译运行(就是最初自动生成的项目 ...

  8. Android网络编程随想录(四)

    前面三篇文章从最基础的TCP,HTTP协议理论开始,然后介绍了在Android的开发中所使用的HttpClient和HttpUrlConnection这两种Http客户端.在本文中,我们一起来学习一下 ...

  9. BZOJ2134: 单选错位(期望乱搞)

    Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 1101  Solved: 851[Submit][Status][Discuss] Descripti ...

  10. ContentProvider 的使用

    1.简单示例:通过ContentProvider暴露数据库,然后读取数据. 2.先加上一个工具类,用来使用copy assets下面的db文件代码如下: public class MyDBOpenHe ...