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

String的底层是一个final类型的字符串数组。

String的内存分析

实例化的方式

String str1 = "abc";
String str2 = new String("abc")//引用传递,str3直接指向str2的堆内存地址
String str3 = str2;
//=
//基本数据类型比较的是基本数据类型的值是否相同
//引用数据类型,比较的是引用数据类型地址值是否相同
//所以String类对象==比较的话,比较的是地址而不是值

String相关

字符串常量池在字符串中,如果采用直接赋值的方式(String str = “abc”)进行对象的实例化,则会将匿名对象"abc"放入对象池,每当下一次对不同的对象进行直接复制的时候回直接利用池中原有的匿名对象。

两种实例化方式的区别

  • 直接赋值(String stgr = “hello”):只开辟一块堆内存空间,并且会自动入池,不会产生垃圾
  • 构造方法(String str = new String(“abc”)):会开辟两块堆内存空间,其中一块堆内存会变成垃圾被系统回收,而且不能够自动入池,需要通过public String intern()方法进行手工入池,在开发过程中不会采用构造方法进行字符串的实例化

String类对象一旦声明则不可以改变,而改变的只是地址,原来的字符串还是存在的,并且产生垃圾

常用方法

int length()//获取字符串的长度
int indexIOf(int ch)//返回指定字符串在此字符串中第一次出现的位置
int lastIndex(int ch)//返回指定字符串在此字符串中的最后一次出现的索引
char charAt(int index)//返回字符串中index位置上的字符
char[] toCharrArry()//将此字符串转换为一个字符串数组
String valueOf(int i)//将int类型转换为fc
String toLowerCase()//将字符串中的所有字符都转换为小写
String toUpperCase()//将字符串中的所有字符都转换为大写
String replace(char oldStr,char newStr)//用newStr替换oldStr
String trim()//返回一个新字符串,去除原字符串首尾的空格
String[] split(String regex)//根据参数regex将原来的字符串分割为一个字符串数组
String substring(int beginIndex)//截取从索引beginIndex之后的所有的字符串
String substring(int beginIndex,int endIndex)//截取从beginIndex到endIndex索引之间的字符串
boolean equals(Object anObject)//与指定的字符串比较是否相等.
boolean startWith(String prefix)//判断此字符串是否以prefix开头
boolean endWith(String prefix)//判断此字符串是否以prefix结尾
boolean contains(char c)//判断否包含指定的字符
boolean isEmpty()//当且仅当字符串长度为0的时候返回true

为什么String要用final修饰呢?

因为String太过常用,Java类库的设计者在实现时做了一个小小的优化,即采用享元模式,没当生成一个新内容的字符串时,他们都会被添加到一个共享池中,当第二个再次生成相同的字符串的实例的时候,就共享此对象,而不是再创建一个新对象,但是这样的做法仅仅适合于通过"="符号进行的初始化。

在Object中,equals()是用来比较内存地址的,但是String重写了这个方法,用来比较内容,即使是不同的地址,只要内容一致,也会返回true。

String不可变的好处

可以实现多个变量引用堆内存中的同一个字符串实例,避免创建的开销。
当我们在传参的时候,使用不可变类不需要去考虑谁可能会修改其内部的值,如果使用可变类的话看,可能需要每次记得重新拷贝出里面的值,性能会有一定的损失。

字符串常量池

  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是设置了一个哈希表String Table来引用堆中的字符串实例,被引用就是被驻留.

享元模式

一个系统中如果有多处用到了相同的一个元素,那么我们应该只存储一份此元素,而让所有的地方都引用这一个元素。
Java中的String部分就是根据享元模式设计的,而那个存储元素的地方就叫做"字符串常量池-String pool"

相关文章: