之前有很长时间没进行更新了,现在开始会重新进行java基础的学习,所以会开始进行java基础方面的更新,今天会进行string字符串的学习。
String在底层被final(声明的变量或者对象不可以扩展/改变)修饰,故其不可变。其底层采用的是字符(JDK8)或字节数组(JDK11)实现。string类位于java.lang包中,具有丰富的方法(计算字符串的长度、比较字符串、连接字符串等)
1.String字符串的声明方式
String声明有三种不同的形式:
String str = "hello" ;
String str1 = new String() ;
String str2 = new String("hello") ;
其中,第一种直接将值复制给字符串,后面两种是基于创建一个String对象去创建字符串
其本质区别大概率还是在于是否指向同一块地址空间
2.字符串length()方法
string类里面封装了length()方法,用于确定字符串的长度
String password = "123456";
System.out.println(password.length());
注意:数组中的length是一个属性,而字符串中的length()是一个方法
在jdk8之前,字符串底层使用的是字符数组,他的好处在于不用考虑存储不了的情况,对于中文来书,一个字符可以存储一个中文汉字,但是对于字母来说,一个字符也只能存储一个字母,但是这样一来,就会造成空间的一个浪费。每次存储一个字母,都会浪费对应的一个字节,长此以来,就会造成存储空间的大量浪费,降低其性能。
但是对jdk11以后的版本,length()方法用的则为字节数组,这种情况下,一个字节就只能存储一个字母,两个字节就可以存储一个汉字,不会造成字节空间的浪费,此时我们只需要进行右位移,去获得完整的字符内容。
jdk8 length()方法 -利用的value是char类型
public int length() {return value.length;
}
jdk11以后 length()方法 -利用的value是byte类型
这里延伸介绍一下String的一些特性
其中这里的value就是用字节存储的字符串数组
coder其实就是代表当前的字符编码形式 0代表Latin编码,1代表utf16
public int length() {//根据对应的字符编码进行向右位移,获取完整的字符内容return value.length >> coder();
}
然后我们可以发现,jdk11之后的length会变成字节型数组,这也就需要我们去进行右移来判断到底是一个中文汉字还是一个字母,从而获得完整的字符内容。运用字节型数组,可以很大程度的节省存储空间,提供整体的算法性能
如果是一个中文,则coder()返回为1,右移一位;如果是一个英文,则coder()返回为0,数组保持不变,因为该数组为一个字节数组。
3.字符串中的==和equals之间的区别
3.1 equals方法
euqals方法主要是用来判断存储在两个字符型对象的内容是否一致。同样的,在jdk8之前会通过字符进行比较,在jdk11之后就通过字节进行比较。
public boolean equals(Object anObject) {if (this == anObject) {return true;}if (anObject instanceof String) {String anotherString = (String)anObject;int n = value.length;if (n == anotherString.value.length) {char v1[] = value;char v2[] = anotherString.value;int i = 0;while (n-- != 0) {if (v1[i] != v2[i])return false;i++;}return true;}}return false;}
在jdk8中,首先,会利用==判断是否为同一对象,如果是的话,就直接返回true ,没有继续判断的意义了,其次,先判断是否为String类型,判断之后,此时anObject只有可能是string或者是比string高级的父类类型Object,然后我们将其强制转换为string类型。接着判断二者长度是否一致,如果一致则继续判断,否则返回false。接着,将这两个字符型数组从后向前遍历进行一一对比,只要存在不一致,即可返回false,否则,则返回true。
以上大概就是jdk8中equals方法的底层代码设计,接下来我们看下jdk11的底层代码设计
public boolean equals(Object anObject) {if (this == anObject) {return true;}return (anObject instanceof String aString)&& (!COMPACT_STRINGS || this.coder == aString.coder)&& StringLatin1.equals(value, aString.value);
}public static boolean equals(byte[] value, byte[] other) {if (value.length == other.length) {for (int i = 0; i < value.length; i++) {if (value[i] != other[i]) {return false;}}return true;}return false;
}
首先,同样的,先进行==判断,如果两个存储对象地址一致,则直接返回true,否则进行三个与的判断,第一个同样是在判断参数对象是否为string类型,如果是则将其直接赋值给aString,同时进行下一步的判断,判断其是否为同一编码格式,如果不是,则直接返回false。如果是进行,下一步判断,此时是整个算法的精华所在,因为jdk11利用的是字节型数组,所以即使是中文进行了拆分,对其比较也不会造成影响,因为索引并不会改变,然后设计的equals方法就和jdk8一样,对每一个字节内容进行等值判断。
3.2==比较
第一种情况,对于基本数据类型,==是对其对应的值判断是否相等,一致则返回true,否则返回false。对于两个对象,会比较两个对象中的堆的内存地址是否相同,相同返回true,不同返回false。
4.字符串比较
对于登录时不考虑大小写这种情况,string为我们提供了一种方法
equalsIgnoreCase()方法 忽略大小写比较是否相同,通常在验证码中使用
toLowerCase()方法 当前字符为纯英文,将其转换为小写
toUpperCase()方法 当前字符为纯英文,将其转换为大写
其实忽略大小写的方法底层就是用的转换小写、大写的方法,将其转换成大写和小写进行等值的判断,从而满足忽略大小写的情况这样。
5.字符串连接
一般情况下,字符串连接只有两种,一种是使用“+”,一种是使用String类的concat()方法
至于为什么要用concat?一方面,concat相对于“+”号,能提高代码的可读性,方便进行维护,否则多个连接最后就是一大堆“+”号,其次,concat可以进行"链式编程",所以就可以进行多个字符串连接这样
6.常用提取方法
字符串存在一些常用的提取方法,这与JavaScript中的语法几乎一致
第一个,返回字符对应字符串的位置
第二个,返回第一次出现value的位置
第三个和第四个都是从后向前查找,返回对应的索引
public int indexOf(String str,int fromIndex)
从某个索引位置开始查找,返回找到的对应索引位置,找不到就返回-1
第一个,提取从位置索引开始的字符串部分
第二个·,从起始索引,到结束索引,截取中间的字符串,这里要注意是左闭右开的,也就是其实索引可以取到,但是结束索引取不到,只截取到其前面一个索引
第三个,trim()方法,去除字符串中的空白格
7.字符串拆分
split()方法,将一个字符串分割为多个子字符串,结果作为字符串数组返回
不过需要注意,该字符串必须存在可以分割的相同分割元素,不然无法进行分割
对于其底部设计,其原理还是利用substring进行判断这样,大家感兴趣可以了解一下
8.StringBuffer和StringBuilder
StringBuffer即为String增强版
其对字符串进行频繁修改(字符串连接时),使用StringBuffer类大大提高程序运行效率(对比String) 但是由于其内部方法都加了同步锁,解决对应的多线程安全问题,所以会造成性能的下降,因此,sun公司又引入了StringBuilder这个类,该类主要就是取消了同步锁,增强了性能,但是只能在保证线程安全的情况下使用
创建对象
StringBuffer sb = new StringBuffer();
StringBuffer sb = new StringBuffer("aaa");StringBuilder sb = new StringBuilder();
StringBuilder sb = new StringBuilder("aaa");
作用
//StringBuffer 与StringBuilder 用法基本相似
sb.tosTring(); //转化为String类型
sb.append("**");//追加字符串
sb.insert(1,"**");//在对应位置插入字符串
-
StringBuffer
-
线程安全
-
性能较差
-
-
StringBuilder
-
性能较高
-
安全性较差
-
9.其他方法
对于String来说,还有一些其他方法
replace() 替代字符串:用字符串去替代 满足条件的所有可替代内容
repalceAll()替代字符串:用正则表达式去替代 满足正则表达式的所有内容
replace(String target, String replacement)//regex:正则表达式
replaceAll(String regex, String replacement)String str = "今天天气真特么晴朗,我的心情真特么的爽!" ;
System.out.println(str.replaceAll("真特么", "***"));
matches(String regex):告知该字符串是否与给定的正则表达式匹配
String regex = "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$" ;String telephone = "zhangsan@163.com" ;boolean flag = telephone.matches(regex) ;System.out.println(flag);
isEmpty() :判断字符串是否为空(关注字符串长度)
isBlank():判断字符串是否为空(关注字符串本身内容)
String str = " " ;//str.isEmpty():判断数组中的长度是否为零,如果包含了空格,并不会认为是空的。
//str.isBlank():判断非空白的字符串内容,如果包含了空格,还会认为是空的。
System.out.println(str.isEmpty()); // false
System.out.println(str.isBlank()); // true
以上就是字符串的所有内容了,如有疑问还请各位评论区多多提出!