JavaSE语法(String类)

String类

String类是Java中重要的类。

常用方法

字符串的构造

字符串有三种常用的构造方式:
字符串的构造方式

Java的官方文档:https://docs.oracle.com/javase/8/docs/api/index.html

现在我们看一下,String是如何存储字符串的:
String存储字符串情况
通过打断点,我们可以看到,每一个String类都有两个成员:一个是value,另一个是hash。(注意,可以看到字符串在存储的时候结尾是没有/0)

观察String类的源码,我们可以看到:String类是一个不可被继承的类,value是一个数组类型。
String类的源码,没有截全
也就是说,String类中并没有存储字符串本身。

String类的存储方式

现在我定义了三个String类:str1, str2, str3. 让我们看一下在虚拟机内的存储情况。
定义了三个String类
String类的存储情况
可以看到,String类就是和类的存储情况一样。实际上,String类就是一种类,当然就和类的存储方式一样。但是此时涉及“常量池”的问题,后面再说。

对象名.length() 可以获取字符串的长度
也可以直接 字符串.length() 获取字符串长度
对象名.isEmpty() 可以查询字符串是否为空
String类里的方法

字符串的比较

比较字符串是否相等

equals()
在字符串比较时,不能直接使用 == 判别。因为此时编译器比较的是两个字符串的引用数值,并不是真正比较字符串的内容。使用equals()来比较字符串里的内容。
字符串比较

equalsIgnoreCase()
忽略字符串大小写进行判等
使用equalsIgnoreCase()

String类里重写了equals方法String重写了equals

比较字符串的大小

compareTo()
按照每一个字符判断字符串的大小,如果都一样则判断字符串的长度,返回一个int整数。
compareTo()
compareToIgnoreCase()
忽略字符大小写比较
compareToIgnoreCase()

字符串查找相关方法

charAt() : 查找指定下标的元素
charAt()
indexOf(int ch): 找到指定元素第一次出现的位置,没有返回-1
indexOf(int ch)
indexOf((int ch, int fromIndex): 从指定位置找元素第一次出现的位置,没有返回-1
indexOf((int ch, int fromIndex)
int indexOf(String str):找到指定字符串出现的位置,返回字符串中第一个字符的下标
int indexOf(String str)
int indexOf(String str, int fromIndex): 从指定位置开始找指定字符串出现的位置,返回字符串中第一个字符的下标
int indexOf(String str, int fromIndex)
int lastIndexOf(int ch):从后往前找指定字符出现的位置,没有返回-1
int lastIndexOf(int ch)
int lastIndexOf(int ch, int fromIndex):从指定位置从后往前找指定字符,没有返回-1
int lastIndexOf(int ch, int fromIndex)
int lastIndexOf(String str): 从后往前找指定字符串的位置
int lastIndexOf(String str)
int lastIndexOf(String str, int fromIndex): 从指定位置从后往前找指定字符串的位置
int lastIndexOf(String str, int fromIndex)

字符串转换
数值和字符串转换

valueOf():String有多种valueOf()方法使用
valueOf()方法
valueOf()
parse_xx():不同的包装类就有不同的parse*()方法来将字符串转成对应类型的数字。例如Integer里就有parseInt()方法将字符串转换成整型数字。
在这里插入图片描述

字符大小写转换

toUpperCase()toLowerCase():将所给的字符串的字符转换成大/小写。假如该字符已经是大/小写,或者该字符没有大小写的说法,则不变。
大写和小写转换

字符串中的"///223232"什么都没有变

字符串转数组

toCharArray():将字符串转为数组
toCharArray()

字符串格式化

format():将字符串格式化
format()

字符串替换

replace():将字符串中的元素替换成新的元素
重载的replace()

由上图可以看到,replace有两个方法,一个要传入的数据类型是"char",另一个是"CharSequence"。在下面的图中展示列String类继承的实现的接口,其中有CharSequence。
Srtring继承CharSequence

replace()
replace()将新元素替换了老元素。

注意:replace在替换的时候,并不是直接在原字符串里修改,而是生成一个新的字符串。

replaceAll()replaceFirst():将所有(第一个)的字符串替换。
在这里插入图片描述

字符串拆分

split(String regex)split(String regex, int limit):将字符串按照指定的元素进行切分。
split()

注意:split()方法返回的对象类型为String[]

split()方法后面还可以填入想要拆分的个数。编译器会从所指定的元素开始拆分,直到数量满足要求就停下。
含有limit

  1. 有些特殊字符在做拆分符号的时候需要加上转义字符 \\,例如 . | + *
    特殊字符处理
    2.当 \ 是分割符号时,需要写成 \\\\(字符串里也不允许只有一个\出现) (真难写,以后估计也不用)
    \作为分割字符
    3.如果有多个分割字符,用|进行表示
    多个分割字符
字符串截取

substring():指定截取范围。可以只指定起始位置,也可以起始位置和结束位置都指定(左左闭右开区间)。
substring()

其他操作

trim():去除掉字符串左右两边的空格,字符串中间的空格不管。
trim()

字符串常量池

在Java程序中,为了使得代码的运行效率更快,更节省内存,给8中基本类型和String类设置了常量池。

为了节省存储空间以及程序的运行效率,Java中引入了(简单了解即可):

  1. Class文件常量池:每个.Java源文件编译后生成.Class文件中会保存当前类中的字面常量以及符号信息
  2. 运行时常量池:在.Class文件被加载时,.Class文件中的常量池被加载到内存中称为运行时常量池,运行时常量池每个类都有一份
  3. 字符串常量池

字符串常量池底层是一个StringTable类实现的一个固定大小的HashTable。我们现在看如下代码:
字符串常量池
出现这个结果的原因,是str1和str2均存在了字符串常量池中,而str3和str4则是新创建了对象。在JVM中的存储情况,如图所示:
字符串存储情况
首先,堆里创建了一个char[]类型的字符组“hello”,然后创建了一个String类,将字符组的地址0x10赋值给String类的value。然后,该String类的地址0x99分别赋值到str1和自负床常量池中(这里的操作并不是简单的地址赋值,而是放到了哈希表中,再将哈希值放到常量池中,但是这里不细说)。接下来,str2在创建的时候,在常量池中寻找有没有String类的value值是“hello”,发现存在,则直接将该String类的地址拿过来,因此,str1和str2里存的地址相同。
接下来,str3和str4的创建语句因为都有“new String”,因此它们俩都先在堆上创建了一个新的String类对象,然后将地址传到栈上。接下来,因为堆上已经有需要的字符组,因此,直接把该字符组的地址引用过来。因此,str3和str4里的值不相同,也不和str1、str2相同。

intern方法
该方法是将手动创建的String对象添加到常量池当中。
intern方法
从上图中可以看到,使用intern方法之后,代码结果从false变成了true。此时,在第一个代码中,编译器的内部存储是这样的:
1

首先,在堆上创建了char[]类型的字符组"a b c",将地址传给栈上的ch。然后,创建String类的s1对象后,将堆上的“a b c”复制一份新的出来,并将新的字符组的地址赋给s1里的value,再将String
对象地址赋给栈上的s1。现在,"a b c"只是放在了对上,常量池里并没有。因此,在代码 String s2 = “abc”; 执行后,程序发现常量池里并没有“abc”,因此又在堆上生成了新的“abc”。所以,第一个图里的代码结果为false。

2
第二段代码是这样的,前两行代码不变,第三行代码 s1.intern(); 将0x20的字符组放到了常量池中。因此,在后面s2在生成String时,在常量池中发现了相同的内容,因此直接将地址0x99直接拿过来引用。因此,第二段代码的结果为true。

注意:当常量池中已经存在要放入的字符串时,字符串便不会再放入。如下图所示:
注意事项
此时“abc”已经存在,intern操作便不再执行。

字符串的不可变性

String类型是不可变的,字符串中的内容是不可修改的。原因是String类里的value方法是被private和final修饰,而类里又没有提供get或者set方法。前面提到的对字符串的操作均是生成了一个新的字符串。
字符串不可变的原因

为什么 String 要设计成不可变的?(不可变对象的好处是什么?) (先简单看看)

  1. 方便实现字符串对象池. 如果 String 可变, 那么对象池就需要考虑写时拷贝的问题了.
  2. 不可变对象是线程安全的.
  3. 不可变对象更方便缓存 hash code, 作为 key 时可以更高效的保存到 HashMap 中.
StringBuilder和StringBuffer

由于String的不可修改性,Java提供了StringBuilder和StringBuffer这两个类来对字符串进行修改,也就是说,使用这两个类里的方法对字符串进行修改的时候,不会创建新的对象。

类里的方法

StringBuilder和StringBuffer这两个类里的方法大同小异,唯一的区别在于StringBuffer里的方法都被synchronized修饰。synchronized意为同步的,是多线程里“锁”的概念。也就是说,StringBuffer是线程安全的,而StringBuilder是线程不安全的。因次,StringBuffer多用于多线程,而StringBuilder多用于单线程。
二者区别

并不是说StringBuffer是线程安全的,就比StringBuilder更好。频繁的开锁和上锁也要消耗大量的系统资源,因此,二者都有不同的业务场景。

  1. String、StringBuffer、StringBuilder的区别
    String的内容不可修改,StringBuffer与StringBuilder的内容可以修改.
    StringBuffer与StringBuilder大部分功能是相似的
    StringBuffer采用同步处理,属于线程安全操作;而StringBuilder未采用同步处理,属于线程不安全操作

END

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值