Java常用类
编辑时间:2021/03/17
读完本节:大概花费30分钟,共3898词
文章目录
1.关于锁的操作续上篇
- 会导致锁释放的操作:
- 当前线程的同步方法、同步代码块的执行结束
- 当前线程在同步代码块、同步方法中遇到break、return终止了该代码块、该方法的执行
- 当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束
- 当前线程在同步代码块、同步方法中执行了线程对象 的wait()方法,当前线程暂停,并释放锁
- 不会导致锁释放的操作
- 线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行
- 线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放同步监视器。当然在实际使用中,不应该使用已经过时的suspend()和resume()方法来控制线程
2.String与基本数据类型的转换
- 字符串→基本数据类型、包装类
- Integer包装类的public static int parseInt(Stirng s):可以将由”数字“字符组成的字符串转换为整型
- 类似的,使用java.lang包中的Byte、Short、Long、Float、Double类调相应的类方法可以将由”数子“字符注册的字符串,转为相应的基本数据类型
- 基本数据类型、包装类→字符串
- 调用String类的public String valueOf(int n)可将int型转换位字符串
- 相应的调用valueOf(byte b)、valueOf(float f)、valueOf(double d)、valueOf(boolean b)可由参数的相应类型转换到字符串
3.String与字符数组的转换
- 字符数组→字符串
- String类的构造器:String(char[])和String(char[], int offset ,int length)分别用字符数组中的全部祖父和部分字符创建字符串对象
- 字符串→字符数组
- public char[] toCharArray():将字符串中的全部字符存放在一个字符数组中的方法
- public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin):提供了将指定索引范围内的字符串存放到数组中的方法
4.String与字节数组转换
- 字节数组→字符串
- String(byte[]):通过使用平台默认字符集解码指定的byte数组,构造一个新的String
- String(byte[], int offset, int length):用指定的字节数组的一部分,即从数组起始位置offset开始取length个字节构造一个字符串对象
- 字符串→字节数组
- public byte[] getBytes():使用平台默认的字符集将此String编码为byte序列,并将结果存储到一个新的byte数组中
- public byte[] getBytes(String charsetName):使用指定大的字符集将此String编码到byte序列,并将结果存储到新的byte数组
5.字符串相关的类
-
String的特性:
-
String类:代表字符串。Java程序中所有字符串的字面值(如“abc”)都作为此类的实例实现
-
String是一个final类,代表不可变的字符序列,不可被继承
-
String实现了Serializable接口,表示字符串是支持序列化的;String实现了Comparable接口,表示String可以比较大小
-
String对象的字符内容是存储在一个字符数组value[]中的。value的定义:public final char value[]
-
字符串是常量,用双引号“”引起表示。他们的值在创建之后不能更改,这一特性称为“不可变性”;不可变特性表现如下:
- 当对字符串重新赋值时,需要重写指定的内存区域赋值,不能使用原有的value进行赋值
- 当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值
- 当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值
import org.junit.Test; /** * @Author: xuehai.XUE * @MailBox: xuehai.xue@qq.com * @Date: 2021/3/17 13:36 * @Description: */ public class StringTest { @Test public void Test1(){ //String字面量的定义方式 String s1 = "abc"; String s2 = "abc"; s1 = "hello"; //比较s1和s2的地址值 System.out.println(s1 == s2); System.out.println(s1);//hello System.out.println(s2);//abc String s3 = "abc"; s3 += "def"; //s3对不可变的value这个char型数组进行了扩容 System.out.println(s3);//abcdef String s4 = "nba"; String s5 = s4.replace('n', 'c'); System.out.println(s4);//nba System.out.println(s5);//cba } }
通过字面量的方式(不同于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中,字符串常量池不会存贮相同内容的字符串。
上述代码内存简析:
-
String实例化的方式:
-
方式1:通过字面量定义的方式
-
方式2:通过new + 构造器的方式
public void Test2(){ //通过字面量定义的方式:此时s1和s2的数据“javaEE”声明在方法区的字符串常量池中 String s1 = "javaEE"; String s2 = "javaEE"; //通过new + 构造器的方式:此时的s3和s4保存的地址值,是数据在堆空间中开辟空间以后对应的地址值 String s3 = new String("javaEE"); String s4 = new String("javaEE"); System.out.println(s1 == s2);//ture System.out.println(s1 == s3);//false System.out.println(s1 == s4);//false System.out.println(s3 == s4);//false }
-
面试题:String s = new Srting(“abc”);方式创建对象,在内存中创建了几个对象?
两个,一个是堆空间中new的结构,另一个是char[]对应常量池中的数据“abc”
-
String str1 = “abc”;与String str2 = new String(“abc”);的区别
- 字符串常量存储在字符串常量池,目的是共享
- 字符串非常量对象储存在堆中
-
-
字符串不同拼接操作的对比
结论:
- 常量与常量的拼接结果在常量池。且常量池中不会出现相同内容的常量
- 只要其中有一个是变量,结果就在堆中
- 如果拼接的结果调用intern()方法,返回值就在常量池中
@Test public void Test3(){ String s1 = "javaEE"; String s2 = "Anaconda"; String s3 = "javaEEAnaconda"; String s4 = "javaEE" + "Anaconda"; String s5 = s1 + "Anaconda"; String s6 = "javaEE" + s2; String s7 = s1 + s2; String s8 = (s1 + s2).intern(); System.out.println(s3 == s4);//true System.out.println(s3 == s5);//false System.out.println(s3 == s6);//false System.out.println(s5 == s6);//false System.out.println(s5 == s7);//false System.out.println(s6 == s7);//false System.out.println(s4 == s8);//true }
-
字符串相关的类:String常用方法:
- int length():返回字符串的长度:return value.length
- char charAt():返回某索引处的字符return value[index]
- boolean isEmpty():判断是否是空字符串:return value.length == 0
- String toLowerCase():使用默认语言环境,将String中所有字符转换为小写并返回
- String toUpperCase():使用默认语言环境,将String中所有字符转换为大写并返回
- String trim():返回字符串的副本,忽略签到空白和尾部空白
- boolean equals(Object obj):重写了Object中的方法,String中重写的equals用于比较字符串的内容是否相同
- boolean equalsIgnoreCase(String anotherString):与equals方法类似,但是忽略串中字符的大小写
- String concat(String str):将指定字符串连接到此字符串的结尾。等价于用“+”
- int compareTo(String anotherString):比较两个字符串的大小,比如“a”和“c”,“a”.compareTo(“c”)返回-2
- String substring(int beginIndex):返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后一个字符的子字符串
- String substring(int beginIndex, endIndex):返回以一个新的字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串
- boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束
- boolean startsWith(String perfix):测试此字符串是否以指定的前缀开始
- boolean startsWith(String perfix, int toffset):测试此字符串从指定索引开始的子字符串是否以指定的前缀开始
- boolean contains(CharSequence s):当且仅当此字符串包含指定的char值序列时,返回ture
- int indexOf(Stirng str):返回指定子字符串在此字符串中第一次出现的索引,如果没找到返回-1
- int indexOf(String str, int fromIndex):返回指定子字符串在次字符串中第一次出现的索引,从指定的fromIndex开始,如果没找到返回-1
- int lastIndexOf(String str):返回指定子字符串在此字符串中最右边出现处的索引,如果没找到返回-1。如"javaEEAnaconda".lastIndexOf(“a”) = 13
- int lastIndexOf(Stirng str, int formIndex):返回指定子字符串在此字符串中最右边出现处的索引,从指定索引开始反向搜索,如果没找到返回-1
- String replace(char oldChar, char newChar):返回一个新的字符串,它是通过用newChar替换此字符串中出现所有oldChar得到的
- String replace(CharSequence target, CharSequence replacement):使用指定的字面值替换序列替换此字符转所有匹配字面值目标序列的子字符串
- String replaceAll(String regex, String replacement):使用给定的replacement替换此字符串所有匹配给定的正则表达式的子字符串
- String replaceFirst(String regex, String repalcement):使用给定的replacement替换此字符串匹配给定的正则表达式的第一个子字符串
- boolean matches(String regex):告知此字符串是否匹配给定的正则表达式
- String[] split(Stirng regex):根据给定正则表达式的匹配拆分此字符串
- String[] split(String regex, int limit):根据匹配给定的正则表达式来拆分此字符串,最多不超过limit个,如果超过了,剩下的全部都放到最后一个元素中
-
-
StringBuffer类
-
java.lang.StringBuffer代表可变的字符序列,JDK1.0中声明,可以对字符串内容进行增删,此时不会产生新的对象
-
很多方法和String相同
-
作为参数传递时,方法内部可以改变值
-
StringBuffer中定义的value的char型数组并没有声明final,value可以不断扩容,并且使用count记录当前value数组中有效字符的个数
-
StringBuffer类不同于String,其对象必须使用构造器生成。StringBuffer有三个构造器
- StringBuffer():初始容量为16的字符串缓冲区
- StringBuffer(int size):构造指定容量的字符串缓冲区
- StringBuffer(String str):将内容初始化为指定字符串内容
-
StringBuffer类常用的方法
-
StringBuffer append(xxx):提供了很多append方法,用于进行字符串拼接
-
StringBuffer delete(int start, int end):删除指定位置的内容
-
StringBuffer replace(int start, int end, String str):把[start,end)位置替换为str
-
StringBuffer insert(int offset, xxx):在指定位置插入xxx
-
StringBuffer reverse():把当前字符序列逆转
补充:
- 当append和insert时,如果原来的value数组长度不够,可以扩容
- 如上方法可以支持方法链的操作
-
public int indexOf(String str):返回指定子字符串在此字符串中第一次出现的索引,如果没找到返回-1
-
public String substring(int start, int end):返回以一个新的字符串,它是此字符串从start开始截取到end(不包含)的一个子字符串
-
public int length():获取字符串的长度
-
public charAt(int n):获取字符串中指定索引位置的字符
-
public setCharAt(int n, char ch):将指定索引位置的字符更改为指定的字符
-
-
-
StringBuilder类
- StringBuilder和StringBuffer非常类似,均代表可变的字符序列,而且提供相关功能的方法也一样
-
StringBuffer和StringBuilder方法小总结:
增:append(xxx)
删:delete(int start, int end)
改:setCharAt(int n, char ch) / replace(int start, int end, String str)
查:charAt(int n)
插:insert(int offset, xxx)
长度:length();
遍历:for() + charAt()
6.对比String、StringBuffer、StringBuilder
-
String和StringBuffer从JDK1.0就已经提供了,而StringBuilder从JDK5.0后才提供
-
String是不可变字符序列、StringBuffer、StringBuilder是可变的字符序列,String在底层使用的是一个final char[]存储的,而StringBuffer和StringBuilder就直接使用char[]存储
-
三者的效率不同:Stirng效率最低;StringBuffer效率次之;StringBuilder效率最高。原因是每次String添加字符或者改变整个字符串都需要重新新建一个名为value的char型数组,而对于StringBuffer和StringBulider的增删改则不需要新建value数组,除非数组满了,对于新增和插入需要扩容。而造成StringBuffer和StringBuilder效率差异是因为StringBuffer是线程安全的。
/** * @Author: xuehai.XUE * @MailBox: xuehai.xue@qq.com * @Date: 2021/3/17 18:07 * @Description: */ public class StringBufferBuilderTest { public static void main(String[] args) { long startTime = 0L; long endTime = 0L; String text = ""; StringBuffer buffer = new StringBuffer(""); StringBuilder builder = new StringBuilder(""); //对比 startTime = System.currentTimeMillis(); for (int i = 0; i <20000; i++) { buffer.append(String.valueOf(i)); } endTime = System.currentTimeMillis(); System.out.println("StringBuffer的执行时间:" + (endTime - startTime) + "ms"); startTime = System.currentTimeMillis(); for (int i = 0; i < 20000; i++) { builder.append(String.valueOf(i)); } endTime = System.currentTimeMillis(); System.out.println("StringBuilder的执行时间:" + (endTime - startTime) + "ms"); startTime = System.currentTimeMillis(); for (int i = 0; i < 20000; i++) { text = text + i; } endTime = System.currentTimeMillis(); System.out.println("String的执行时间:" + (endTime - startTime) + "ms"); } }
-
三者的线程安全问题:相比于StringBuffer,虽然StringBuilder效率更高,但是它是以牺牲线程安全为代价实现的,若是在多线程操作共享数据的过程中,StringBuilder可能会导致线程不安全的问题出现。
-
三者的参数传递问题:作为参数传递的话,方法内部的String不会改变其值,StringBuffer和StringBuilder会改变其值
-
源码分析:
String str = new String();//char[] value = new char[0]; String str1 = new String("abc");//char value = new char[]{'a','b','c'}; StringBuffer sb1 = new StringBuffer();//char[] value = new char[16];底层创建了一个长度是16的数组 sb1.append('a');//value[0] = 'a'; sb1.append('b');//value[1] = 'b'; StringBuffer sb2 = new StringBuffer("abc");//char[] value = new char["abc".length() + 16]; System.out.println(sb2.length());//3,实际上获取的是有内容的长度,即count的长度
7.StringBuffer和StringBuilder的扩容问题
-
StringBuffer和StringBuilder都继承于AbstractStringBuilder.
-
AbstractStringBuilder中有一个方法叫:newCapacity,用于扩容value数组的长度
/** * Returns a capacity at least as large as the given minimum capacity. * Returns the current capacity increased by the same amount + 2 if * that suffices. * Will not return a capacity greater than {@code MAX_ARRAY_SIZE} * unless the given minimum capacity is greater than that. * * @param minCapacity the desired minimum capacity * @throws OutOfMemoryError if minCapacity is less than zero or * greater than Integer.MAX_VALUE */ private int newCapacity(int minCapacity) { // overflow-conscious code //令value的长度左移相当于长度乘2,然后加2作为新的数组容量 int newCapacity = (value.length << 1) + 2; if (newCapacity - minCapacity < 0) { //如果当前传入的newCapacity减去minCapacity,小于0,说明长度足够了,这个时候令newCapacity等于minCapacity newCapacity = minCapacity; } //已知左移会产生0,因此当左移小于等于0时,或者当前容量最大值减去newCapacity小于零为真返回hugeCapacity(minCapacicy),否则直接返回newCapacity return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0) ? hugeCapacity(minCapacity) : newCapacity; } /** 用于处理左移出现小于0或等于0的问题 */ private int hugeCapacity(int minCapacity) { if (Integer.MAX_VALUE - minCapacity < 0) { // overflow throw new OutOfMemoryError(); } return (minCapacity > MAX_ARRAY_SIZE) ? minCapacity : MAX_ARRAY_SIZE; }
-
因此默认情况下,如果要添加的底层数组存不下了,那就需要扩容底层的数组。默认<<1 + 2,同时将原有数组中的元素复制到新的数组中,建议在开发中使用StringBuffer(int capacity)或StringBuilder(int capacity)