您的位置:首页 > 编程语言 > Java开发

Java常用类(二)String类详解

2017-10-23 14:52 351 查看
前言

  在我们开发中经常会用到很多的常用的工具类,这里做一个总结。他们有很多的方法都是我们经常要用到的。所以我们一定要把它好好的掌握起来!

一、String简介

1.1、String(字符串常量)概述

  在API中是这样描述:

    String 类代表字符串。Java 程序中的所有字符串字面值(如 "abc" )都作为此类的实例实现。
    字符串是常量;它们的值在创建之后不能更改。字符串缓冲区支持可变的字符串。因为 String 对象是不可变的,所以可以共享。

  java.lang.String:

    

public class TestString {
public static void main(String[] args) {
// 创建字符串对象
String s1 = "helloworld";
String s2 = " helloworld ";
String s3 = " hello world ";
System.out.println("---" + s1 + "---");
System.out.println("---" + s1.trim() + "---");
System.out.println("---" + s2 + "---");
System.out.println("---" + s2.trim() + "---");
System.out.println("---" + s3 + "---");
System.out.println("---" + s3.trim() + "---");
System.out.println("-------------------");

// String[] split(String str)
// 创建字符串对象
String s4 = "aa,bb,cc";
String[] strArray = s4.split(",");
for (int x = 0; x < strArray.length; x++) {
System.out.println(strArray[x]);
}
}

}


测试

  结果:

      


四、String的不可变性

当我们去阅读源代码的时候,会发现有这样的一句话:

  


意思就是说:String是个常量,从一出生就注定不可变。

我想大家应该就知道为什么String不可变了,String类被final修饰,官方注释说明创建后不能被改变,但是为什么String要使用final修饰呢? 

4.1、前言

  了解一个经典的面试题:

public class Apple {
public static void main(String[] args) {
String a = "abc";
String b = "abc";
String c = new String("abc");
System.out.println(a==b);  //true
System.out.println(a.equals(b));  //true
System.out.println(a==c);  //false
System.out.println(a.equals(c));  //true
}
}


  内存图:

    


4.2、分析

  因为String太过常用,JAVA类库的设计者在实现时做了个小小的变化,即采用了享元模式,每当生成一个新内容的字符串时,他们都被添加到一个共享池中,当第二次再次生成同样内容的字符串实例时,

  就共享此对象,而不是创建一个新对象,但是这样的做法仅仅适合于通过=符号进行的初始化。  

  需要说明一点的是,在object中,equals()是用来比较内存地址的,但是String重写了equals()方法,用来比较内容的,即使是不同地址,只要内容一致,也会返回true,这也就是为什么a.equals(c)返回true的原因了。

4.3、String不可变的好处

  可以实现多个变量引用堆内存中的同一个字符串实例,避免创建的开销。

  我们的程序中大量使用了String字符串,有可能是出于安全性考虑。

  大家都知道HashMap中key为String类型,如果可变将变的多么可怕。

  当我们在传参的时候,使用不可变类不需要去考虑谁可能会修改其内部的值,如果使用可变类的话,可能需要每次记得重新拷贝出里面的值,性能会有一定的损失。

五、字符串常量池

5.1、字符串常量池概述

  1)常量池表(Constant_Pool table)

    Class文件中存储所有常量(包括字符串)的table。
    这是Class文件中的内容,还不是运行时的内容,不要理解它是个池子,其实就是Class文件中的字节码指令。

  2)运行时常量池(Runtime Constant Pool) 

    JVM内存中方法区的一部分,这是运行时的内容
    这部分内容(绝大部分)是随着JVM运行时候,从常量池转化而来,每个Class对应一个运行时常量池
    上一句中说绝大部分是因为:除了 Class中常量池内容,还可能包括动态生成并加入这里的内容

  3)字符串常量池(String Pool)

    这部分也在方法区中,但与Runtime Constant Pool不是一个概念,String Pool是JVM实例全局共享的,全局只有一个
    JVM规范要求进入这里的String实例叫“被驻留的interned string”,各个JVM可以有不同的实现,HotSpot是设置了一个哈希表StringTable来引用堆中的字符串实例,被引用就是被驻留。

5.2、亨元模式

  其实字符串常量池这个问题涉及到一个设计模式,叫“享元模式”,顾名思义 - - - > 共享元素模式
  也就是说:一个系统中如果有多处用到了相同的一个元素,那么我们应该只存储一份此元素,而让所有地方都引用这一个元素
  Java中String部分就是根据享元模式设计的,而那个存储元素的地方就叫做“字符串常量池 - String Pool”

5.3、详细分析

  举例:

int x  = 10;
String y = "hello";


  1)首先,
10
"hello"
会在经过javac(或者其他编译器)编译过后变为Class文件中
constant_pool table
的内容

  2)当我们的程序运行时,也就是说JVM运行时,每个Class
constant_pool table
中的内容会被加载到JVM内存中的方法区中各自Class的
Runtime Constant Pool。


  3)一个没有被String Pool包含的Runtime Constant Pool中的字符串(这里是"hello")会被加入到String Pool中(HosSpot使用hashtable引用方式),步骤如下:   

    一是:在Java Heap中根据"hello"字面量create一个字符串对象
    二是:将字面量"hello"与字符串对象的引用在hashtable中关联起来,键 - 值 形式是:"hello" = 对象的引用地址。

   另外来说,当一个新的字符串出现在Runtime Constant Pool中时怎么判断需不需要在Java Heap中创建新对象呢?

  策略是这样:会先去根据equals来比较Runtime Constant Pool中的这个字符串是否和String Pool中某一个是相等的(也就是找是否已经存在),如果有那么就不创建,直接使用其引用;反之,如上3

  如此,就实现了享元模式,提高的内存利用效率。

  举例:

      使用String s = new String("hello");会创建几个对象

      会创建2个对象

      首先,出现了字面量"hello",那么去String Pool中查找是否有相同字符串存在,因为程序就这一行代码所以肯定没有,那么就在Java Heap中用字面量"hello"首先创建1个String对象。

      接着,new String("hello"),关键字new又在Java Heap中创建了1个对象,然后调用接收String参数的构造器进行了初始化。最终s的引用是这个String对象.

      

喜欢就点个“推荐”!         
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: