《编程导论(Java)·7.4.4 String对象问题》
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对象问题》的更多相关文章
- 简单物联网:外网访问内网路由器下树莓派Flask服务器
最近做一个小东西,大概过程就是想在教室,宿舍控制实验室的一些设备. 已经在树莓上搭了一个轻量的flask服务器,在实验室的路由器下,任何设备都是可以访问的:但是有一些限制条件,比如我想在宿舍控制我种花 ...
- 利用ssh反向代理以及autossh实现从外网连接内网服务器
前言 最近遇到这样一个问题,我在实验室架设了一台服务器,给师弟或者小伙伴练习Linux用,然后平时在实验室这边直接连接是没有问题的,都是内网嘛.但是回到宿舍问题出来了,使用校园网的童鞋还是能连接上,使 ...
- 外网访问内网Docker容器
外网访问内网Docker容器 本地安装了Docker容器,只能在局域网内访问,怎样从外网也能访问本地Docker容器? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Docker容器 ...
- 外网访问内网SpringBoot
外网访问内网SpringBoot 本地安装了SpringBoot,只能在局域网内访问,怎样从外网也能访问本地SpringBoot? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装Java 1 ...
- 外网访问内网Elasticsearch WEB
外网访问内网Elasticsearch WEB 本地安装了Elasticsearch,只能在局域网内访问其WEB,怎样从外网也能访问本地Elasticsearch? 本文将介绍具体的实现步骤. 1. ...
- 怎样从外网访问内网Rails
外网访问内网Rails 本地安装了Rails,只能在局域网内访问,怎样从外网也能访问本地Rails? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Rails 默认安装的Rails端口 ...
- 怎样从外网访问内网Memcached数据库
外网访问内网Memcached数据库 本地安装了Memcached数据库,只能在局域网内访问,怎样从外网也能访问本地Memcached数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装 ...
- 怎样从外网访问内网CouchDB数据库
外网访问内网CouchDB数据库 本地安装了CouchDB数据库,只能在局域网内访问,怎样从外网也能访问本地CouchDB数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Cou ...
- 怎样从外网访问内网DB2数据库
外网访问内网DB2数据库 本地安装了DB2数据库,只能在局域网内访问,怎样从外网也能访问本地DB2数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动DB2数据库 默认安装的DB2 ...
- 怎样从外网访问内网OpenLDAP数据库
外网访问内网OpenLDAP数据库 本地安装了OpenLDAP数据库,只能在局域网内访问,怎样从外网也能访问本地OpenLDAP数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动 ...
随机推荐
- 【C语言】编写函数,将一个数的指定位置置0或置1
//编写函数,将一个数的指定位置置0或置1 #include <stdio.h> unsigned int set_bit(unsigned int num, int pos, int f ...
- 初识——Vim
有些东西吧,总是碰见,低头不见抬头见,但又不知道是什么.用来干嘛的?总是搞的心里痒痒.所以一定要学习一下. 近期一段时间,总是碰到一个词儿:VIM,在这儿看到了,我不理他,隔一会儿丫的又跑我眼睛里,总 ...
- c24--预编译,宏定义(带参数,不带参数)
// // main.c // 宏定义 #include <stdio.h> void test(); /* 预处理指令:在我们的文件翻译成0和1之前做的操作我们称之为预处理指令,一般以# ...
- CodeForces 131C C (组合)
There are n boys and m girls attending a theatre club. To set a play "The Big Bang Theory" ...
- 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 ...
- 【MFC】模态、非模态对话框
MFC 点击按钮,弹出另一个对话框 方法一:模态对话框 资源视图–Dialog–右键–添加资源–新建–对话框-,然后在已经生成的对话框中(解决资源视图中的dialog下的新生成的那个)右键–添加类.例 ...
- PythonOpenCV--Rtrees随机森林
360确实很个性,哈哈,你个貔貅,只吃不吐! Rtrees介绍!参考链接:http://docs.opencv.org/modules/ml/doc/random_trees.html 原文链接:Py ...
- 创建一个dynamics CRM workflow (五) - Deploy Custom Workflows
我们打开plugin registeration tool. 注册一个新的assembly. custom workflow 和 plugin注册的方法还有些不同. 这一步custom workflo ...
- 动态给某一个元素添加active
<li class="one_data" data-id='+ navGroup.self_first_nav[i].id +'><a href='+ navG ...
- IdentityServer4-HybridAndClientCredentials
一.服务器 Client设置: new Client { ClientId = "mvc1", ClientName = "后台管理MVC客户端", Clien ...