String可以说是Java中使用最多最频繁、最特殊的类,因为同时也是字面常量,而字面常量包括基本类型、String类型、空类型。

一. String的使用

1. String的不可变性

/**
* The {@code String} class represents character strings. All
* string literals in Java programs, such as {@code "abc"}, are
* implemented as instances of this class.
* <p>
* Strings are constant; their values cannot be changed after they
* are created. String buffers support mutable strings.
* Because String objects are immutable they can be shared. For example:
* ...
*/ public final class String {
private final char value[];
}

String对象一旦在堆中创建出来,就无法再修改。因为String对象放在char数组中,该数组由final关键字修饰,不可变。

2. 定义一个字符串

/**
* 定义一个字符串
*/
String str1 = "helloworld";
String str2 = "helloworld";
//也可以,但基本不这样写
String str3 = new String("helloworld");
System.out.println(str1 == str2);
System.out.println(str1 == str3); //运行结果 true, false

上面三句代码怎么理解呢?这里需要先引入一个概念,字符串常量池

字符串常量池是一块特殊的独立内存空间,放在Java Heap中 { 在Jdk7.0之前字符串常量池存放在PermGen中,Jdk7.0的时候移动到了Java Heap(在堆中仍然是独立的),Jdk8.0的时候去掉了PermGen,用Metaspace进行取代 } ,Java内存模型不是本章讨论的重点。

str1和str2引用的字符串字面量就在字符串常量池中,而str3引用的对象在Java Heap中。

怎么,还不太好理解?举个例子

工作一天,到下班时间了,准备看会儿金瓶.,算了,《三国演义》,打开小说网站,在线阅读;过了半个小时,女票回家了,看《三国演义》也是她想做的事儿,我看网址发给她,好,她也开始看了,再过半个小时,我爸回来了,他也是三国迷,但是他不喜欢在线看,因此在书店买了一本看。

上面提到的小说网站就是一个字符串常量池,包含了很多字符串字面量,如《三国演义》、《西游记》、《红楼梦》等,每个字符串字面量在常量池中保持独一份,无论谁进网站看《三国演义》都是同样的网址和同样的内容。

我和女票就是str1和str2,我们看的都是同一个网站的《三国演义》,不仅内容一样,引用的地址也一样(字符串常量池中保留了唯一的“helloworld”),因此str1 == str2 运行结果为true

而我爸就是str3,与我和女票都不一样,虽然看的内容也是《三国演义》,但是通过实体书籍来看,引用地址不一样,同时一本书籍不支持多个人同时看(字符串对象在java heap中,且每次new都会新建一个对象),因此str1 == str3 运行结果为false。

一个字符串字面量总是引用String类的同一个实例,因为被String.intern()方法限定了,同样我们可以调用该方法将堆中的String对象放到字符串常量池中,这样做可以提升内存使用效率,同时可以让所用使用者共享唯一的实例。

System.out.println(str1 == str3.intern());
//运行结果为true

那么该方法的实现逻辑是怎么样的呢,我们看一下源码

/**
* Returns a canonical representation for the string object.
* <p>
* A pool of strings, initially empty, is maintained privately by the
* class {@code String}.
* <p>
* When the intern method is invoked, if the pool already contains a
* string equal to this {@code String} object as determined by
* the {@link #equals(Object)} method, then the string from the pool is
* returned. Otherwise, this {@code String} object is added to the
* pool and a reference to this {@code String} object is returned.
* <p>
* It follows that for any two strings {@code s} and {@code t},
* {@code s.intern() == t.intern()} is {@code true}
* if and only if {@code s.equals(t)} is {@code true}.
* <p>
* All literal strings and string-valued constant expressions are
* interned. String literals are defined in section 3.10.5 of the
* <cite>The Java&trade; Language Specification</cite>.
*
* @return a string that has the same contents as this string, but is
* guaranteed to be from a pool of unique strings.
*/
public native String intern();

我们发现这是一个native方法,看一下注释,发现str3.intern()方法大致流程是:

当执行intern()时,会先判断字符串常量池中是否含有相同(通过equals方法)的字符串字面量,如果有直接返回字符串字面量;如果不含,则将该字符串对象添加到字符串常量池中,同时返回该对象在字符串常量池的引用。

返回的引用需要赋值才可,否则还是会指向堆中的地址,即:

String str4 = new String("helloChina");
System.out.println(str4.intern() == str4);//false
str4 = str4.intern();
String str5 = "helloChina";
String str6 = "helloZhonghua"
System.out.println(str4 == str5);//true

下面我们看一下内存结构

3. 再次赋值给已定义的字符串

str6 = "helloHuaxia";

我们开始已经说了String是由final关键字修饰,不可变,那么此时在内存中如何体现呢?

4. String 对 “+” 的处理

String str7 = "good good" + " study";
String str8 = "good good study";
system.out.println(str7 == str8);

通过编译工具后得到

String str7 = "good good study";
String str8 = "good good study";

因此我们可以发现编译器在编译期间就是进行变量合并,而不会在常量池中创建三个对象 “good good”,“ study”,"good good study"。str7 == str8 运行结果 true。

但如果这样

String str9 = "good good ";
String str10 = str9 + "study";
system.out.println(str8 == str10);//false

这时运行结果为false,通过String变量 + 字符常量方式得到的结果会在堆中,不在常量池中,当然可以通过intern()方法放进常量池中,同时不仅“+”如此,调用substring(),toUpperCase(),trim()等返回的都是String在堆中的地址。

5. String常用的方法

//str1 == "hello,world ";

//获取长度
str1.length()//12; //截取位置2到5之间的字符串(包括位置2,不包括位置5,从0开始)
str1.substring(2,5);//"llo" //判断是否含有字符串“ello”
str1.contains("ello");//true,通过indexOf实现 //获取ello在str1中的开始位置
str1.indexOf("ello");//1 //将字符串转化为字符串数据
str1.split(",");//["hello","world"] //去掉字符串两侧空格
str1.trim();//"hello,world"

二. 总结

本文从String的不可变性,String创建时字面量和String对象的不同,字符串字面量常量池,字符串的内存结构,常用的String相关方法的描述,若有不对之处,请批评指正,望共同进步,谢谢!

Java基础(三) String深度解析的更多相关文章

  1. Java基础常用类深度解析(包含常见排序算法)

    目录 一.工具类 1.1.工具类的设计 1.1.1.公共静态方法 1.2.单例模式 二.包装类 2.1.基本类型的包装类 2.1.1.Integer 2.1.1.1.Integer >> ...

  2. Java基础-处理json字符串解析案例

    Java基础-处理json字符串解析案例 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 作为一名开发人员,想必大家或多或少都有接触到XML文件,XML全称为“extensible ...

  3. Java 8 Optional 类深度解析

    Java 8 Optional 类深度解析 身为一名Java程序员,大家可能都有这样的经历:调用一个方法得到了返回值却不能直接将返回值作为参数去调用别的方法.我们首先要判断这个返回值是否为null,只 ...

  4. Java基础之String、StringBuffer、StringBuilder浅析

    Java基础之String.StringBuffer.StringBuilder浅析 一.前言: 位于java.lang包下的String.StringBuilder.StringBuffer一般都是 ...

  5. String深度解析

    文章出处:http://my.oschina.net/xiaohui249/blog/170013 一.引题 String类型是比较特殊的一种类型,同时也是面试经常被问到的一个知识点,本文结合java ...

  6. 3.Java基础:String对象的创建和使用

    一.常用的创建方式 String s1=”abc“: String s2=”abc“: s1==s2    ==> true 解析:s1和s2指向的是同一个字符串池地址 二.不常用的创建方式 S ...

  7. 【Java基础】String 相关知识点总结

    String 相关知识点总结 字符串的不可变性 概述 String 被声明为 final,因此它不可继承 在 Java8 中,String 内部使用 char 数组存储数据 public final ...

  8. Java基础之String中equals,声明方式,等大总结

    无论你是一个编程新手还是老手,提到String你肯定感觉特别熟悉,因为String类我们在学习java基础的时候就已经学过,但是String类型有我们想象的那么简单吗?其实不然,String类型的知识 ...

  9. Java 基础三、接口与内部类

    1.   在Java程序语言中,接口是对类的一种描述.例如Arrays类中sort方法声明可以对对象进行排序,但前提是对象所属的类必须实现Comparable接口. public interface ...

随机推荐

  1. Linux虚拟文件系统(VFS)学习

    虚拟文件系统(Virtual Filesystem)也可称之为虚拟文件系统转换(Virtual Filesystem Switch),是一个内核软件层,用来处理与Unix标准文件系统相关的全部系统调用 ...

  2. 如何将字段中带逗号的SQLite数据库数据导入到MySQL

    以前在数据库导入中没有遇到过什么问题,如下这样导入 load data local infile 'D:\data.csv' into table table1 fields terminated b ...

  3. eCognition学习记录

    作者:朱金灿 来源:http://blog.csdn.net/clever101 昨天公司从外面请了人讲解eCognition的最新进展及项目二次开发应用情况.我做了大致下面记录: 1.  eCogn ...

  4. 弄App Store提示和技巧推荐

    众所周知上苹果的主页推荐是对产品最佳(高曝光率+零广告费)推广,然而苹果却对选择的方式和规则讳莫如深. 下面是搜集的一些获得推荐的开发人员的经验. 1. 产品要新颖.且质量上乘.这个质量包括非常多细节 ...

  5. IT企业级应?开发模式演化

    原文:IT企业级应?开发模式演化 前端研发流程 传统To B类系统的研发模式 探索 & 思考设计模式库(DPL)设计语⾔设计语⾔详解基于MVVM模式的Web框架 & UI库优化后的开发 ...

  6. mod_timer函数及其他定时器函数

    当一个定时器已经被插入到内核动态定时器链表中后,我们还能够改动该定时器的expires值.函数mod_timer()实现这一点 改动注冊入计时器列表的handler的起动时间 int mod_time ...

  7. eclipse中JUnit工具的使用

  8. 如若已在管理后台更新域名配置,请刷新项目配置后重新编译项目,操作路径:“项目-域名信息” http://www.mysite.com 不在以下 request 合法域名列表中

    报错如图 报错文字如下: 如若已在管理后台更新域名配置,请刷新项目配置后重新编译项目,操作路径:“项目-域名信息” http://www.mysite.net 不在以下 request 合法域名列表中 ...

  9. linux nano 命令

    linux nano一linux像pico文本编辑软件,功能少.但是,基本能满足要求

  10. 【Java】Java中的数据类型

    Java 语言是一种强类型语言.通俗点说就是,在 Java 中存储的数据都是有类型的,而且必须在编译时就确定其类型.Java 数据类型分为两类,如图: 在 Java 的领域里,基本数据类型变量存的是数 ...