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. 【C语言】编写函数,将一个数的指定位置置0或置1

    //编写函数,将一个数的指定位置置0或置1 #include <stdio.h> unsigned int set_bit(unsigned int num, int pos, int f ...

  2. 初识——Vim

    有些东西吧,总是碰见,低头不见抬头见,但又不知道是什么.用来干嘛的?总是搞的心里痒痒.所以一定要学习一下. 近期一段时间,总是碰到一个词儿:VIM,在这儿看到了,我不理他,隔一会儿丫的又跑我眼睛里,总 ...

  3. c24--预编译,宏定义(带参数,不带参数)

    // // main.c // 宏定义 #include <stdio.h> void test(); /* 预处理指令:在我们的文件翻译成0和1之前做的操作我们称之为预处理指令,一般以# ...

  4. CodeForces 131C C (组合)

    There are n boys and m girls attending a theatre club. To set a play "The Big Bang Theory" ...

  5. Python生成器实现杨辉三角打印

    def triangles(): c = [1] while 1: yield c a,b=[0]+c,c+[0] c=[a[i]+b[i] for i in range(len(a))] n = 0 ...

  6. 【MFC】模态、非模态对话框

    MFC 点击按钮,弹出另一个对话框 方法一:模态对话框 资源视图–Dialog–右键–添加资源–新建–对话框-,然后在已经生成的对话框中(解决资源视图中的dialog下的新生成的那个)右键–添加类.例 ...

  7. PythonOpenCV--Rtrees随机森林

    360确实很个性,哈哈,你个貔貅,只吃不吐! Rtrees介绍!参考链接:http://docs.opencv.org/modules/ml/doc/random_trees.html 原文链接:Py ...

  8. 创建一个dynamics CRM workflow (五) - Deploy Custom Workflows

    我们打开plugin registeration tool. 注册一个新的assembly. custom workflow 和 plugin注册的方法还有些不同. 这一步custom workflo ...

  9. 动态给某一个元素添加active

    <li class="one_data" data-id='+ navGroup.self_first_nav[i].id +'><a  href='+ navG ...

  10. IdentityServer4-HybridAndClientCredentials

    一.服务器 Client设置: new Client { ClientId = "mvc1", ClientName = "后台管理MVC客户端", Clien ...