前言:在我们学习String这个专题之前,让我们回忆一下,在我们之前学习的C语言中有没有String类型,答案是没有的,在C++和java中都引进了字符串类型,这样让我们在日常处理字符串时,增添了诸多便利。 前期文章: [java篇]包,继承,组合 [java篇]多态,抽象类,接口 [java篇]图书管理系统,是你的期末大作业吗?
废话不多说,开干!!!
1.认识String类
让我们先看看String类到底是个啥? 在我们学习java语法的时候,我们不可能把所有的java语法都记得非常清楚,所以我们就提供了一个专门查阅有关java学习的API文档。
1.String类型,在java.lang包下,在我们使用String类型的时候,不用手动导包,系统会自行导包。 2.String类型被final修饰,表示这个类型不可更改 ,这个类不能被继承,String类属于一个密封类。 3.String类型继承了Object类型 4.String类型实现了Comparator接口(在前期文章介绍过),CharSxequence接口,Serializable接口。
当我们要学习一个类型的时候,首先要了解它的构造方法,这样我们在之后的学习中才会如鱼得水。 我了个去,这这这,怎么这么多呢?这要学到什么时候。 其实我们常用的一共就是用两种构造方法,上面的构造方法我们知道即可。
1.String str = "hello world"; 2.String str1 = new String("hello world");
创建字符串
我们在创建字符串的时候一般有两种方法:
直接赋值,即String str = “hello world”; 另一种是利用构造方法:String str = new String(“abc”);在括号里面的字符串,被存储到new出的对象里面的value数组 。
字符串常量池
字符串常量池是利用哈希表实现的,所以在常量池中不允许出现相同的字符串,String类型,采用共享模式,如果两个变量所指向的字符串相等,那么在字符串常量池中只会保留一份字符串,让两个变量同时指向这个字符串。 其实在jvm中没有划分这个区域的。 来分析一下代码:
public static void main(String[] args) {
String str = "123";
String str1 = "123";
System.out.println(str == str1);
}
这个应该很好判断吧,但是你是不是按这个思路想的呢? 请看内存图: 他们比较的是两个引用是否相等 ,str和str1的字符串内容都是hello 所以只能在字符串常量池中存储一份"hello",而String类型又具有共享模式,所以两个引用同时都指向一块内存空间。
构造方法下字符串在堆中的存储
String str = new String(abc); str作为一个引用,它引用了一个对象,这个对象里面有一个value数组,这个数组指向的就是添加进来的字符串。 利用构造方法赋值的缺点: 在堆中必须要开辟两块内存空间,并且在有一份会成为垃圾空间。 同一个字符串可能被存放多次,浪费空间。
请简述String类型两种赋值的区别: 第一种:直接赋值:在堆中开辟了一块内存空间,并且传入的字符串会自动的保存在字符串常量池中。 第二种:利用构造方法进行赋值,在堆中会开辟两块内存空间,并且传入的字符串不会入池,必须通过intern()方法,手动入池。
案例分析:
请大家阅读以下代码,并回答true or false
案例一:
public static void main1(String[] args) {
String str1 = "hello world";
String str2 = new String("hello world");
System.out.println(str1 == str2);
}
大家先根据题目做出解答哦,以下会具体分析
单眼一看,这有什么难得,不就是比较吧,这不两个字符串相等,不就是true嘛。 哈哈哈,上当了吧,答案是false,其实它们比较的是是两个字符串的引用,这个引用就相当于与我们在C语言阶段学习的指针一样,当时java里面没有指针哦,这个引用也没有C/C++里面的指针那么的灵活。 str1作为一个引用,在栈帧里面开辟空间 ,而赋予str的值 ,是在堆上存放 的 str2作为一个引用,它引用了一个String类型的对象,而这个引用在栈上开辟空间,对象被存放在堆上。
看图说话: 分析:
- str1直接赋值,把"hello world"这个字符串传给了str1,在堆中,凡是被双引号引起来的常量,都会存放在字符串常量池中。
- str2是一个引用,他指向了一个对象,在对象里边存在一个value数组,这个数组又引用了对象里边存放的值,即对象里面的value数组引用了"hello world"这个字符串,又因为这个字符串要存放到字符串常量池中去,而在此之前池中已经有了"hello world"这个字符串并且在前文中说道字符串常量池是由哈希表构成的,所以这个对象就指向字符串常量池中已有的字符串常量。
案例二:
请看下一段代码:
public static void main(String[] args) {
String str1 = "hello world";
String str2 = "hello " + "world";
System.out.println(str1 == str2);
}
答案是 true 为什么呢? 因为字符串常量在编译阶段就已经实现了连接,即"hello " + “world” 变成了"hello world",又因为在执行str2这段代码时,在堆中的字符常量池中已经有了"hello world"这个字符串,当str2指向的这个已经连接好的字符串将要存放到池中的时候,先要检查池中是否已经有了将要存放的字符串,如果有的话,str2就指向这个已有的字符串。 不信的话,那么我们就反编译一下,看是否在编译的时候已经实现了拼接。 看图说话: 常量在编译的过程中,就已经被运算了。
案例三:
请阅读一下代码:
public static void main(String[] args) {
String str1 = "hello world";
String str2 = "hello " + new String("world");
System.out.println(str1 == str2);
}
答案是false 为什么呢?
因为当str2这段代码执行的过程中引用了一个String对象,这个对象指向存放在字符串常量池中放入字符串"world",而这个被String对象所引用的字符串"world"要和在池中的"hello"实现拼接,形成了一个新的对象,至于拼接的过程这就牵连到了StringBuffer/StringBuilder知识点 ,我们在文章后半段介绍,而这个str3指向了这个新的对象,自然和str1所指向的"hello world"所在的地址不相同。
看图说话:
案例四:
请阅读以下代码:
public static void main(String[] args) {
String str1 = "hello world";
String str2 = "hello ";
String str3 = str2 + "world";
System.out.println(str1 == str3);
}
答案是:false 为什么呢?
因为当我们执行到了str3这句代码时,str2变成了变量,那么让我们回忆一下,变量的概念是什么,所谓变量就是在编译的时候不知道变量里边是什么值,只有在运行的时候才知道里面是什么值。在字符串拼接的过程中,会形成一个新的对象,str3就指向这个对象,str1指向的是一个字符串,显然他们指向的不是同一块地址,所以是false.
看图说话:
案例五:
请阅读一下代码:
public static void main(String[] args) {
String str1 = "hello world";
String str2 = new String("hello ") + new String("world");
System.out.println(str1 == str2);
}
当大家做到这个题的时候,就很容以判断出,答案肯定是false,因为str1和str2所指向的不是同一块地址。
看图说话:
案例六:
请阅读一下代码:
public static void main(String[] args) {
String str1 = "hello world";
String str2 = new String("hello world");
str2.intern();
System.out.println(str1 == str2);
}
答案是:false
在这里就不得不介绍 intern()方法 了,这个方法的作用是把一个字符串手动填入字符串常量池。 我们已知str2作为一个引用,它引用了一个String对象,在这个对象里面有一个value数组,这个value数组又引用了一个字符串,这个字符串"hello world"已经在字符串常量池中已经有了,即使手动的填入这个已有的字符串也是不行的,就是也为字符串常量池是由哈希表实现的,在哈希表中,不允许有重复的值出现。所以str2.intern(),这段代码,存不存在代码的结果都是一样的,等于它啥也没干。
看图说话: 它的内存图和案例一一样,在这里就不过多赘述。
案例七:
请阅读一下代码:
public static void main(String[] args) {
String str1 = new String("1") + new String("1");
str1.intern();
String str2 = "11";
System.out.println(str1 == str2);
}
答案是:true 为什么呢?
分析: 两个对象所指向的字符串都是"1",当两个字符串拼接之后形成了新的字符串"11",然后手动的添加到字符串常量池中,当代码执行到str2这句代码时,它所指向的字符串"11",在池中已经有了,所以无需添加,这时候str2指向被拼接好的字符串"11",假设这时候字符串"11"的地址为0x123,那么指向它的对象这时候的地址也是0x123,所以str1和 str2的的地址相同
看图说话:
案例八:
请阅读一下代码:
public static void main(String[] args) {
String str1 = new String("1") + new String("1");
String str2 = "11";
str1.intern();
System.out.println(str1 == str2);
}
答案是:false 为什么呢?
因为两个对象拼接好的字符串"11",然后str2指向字符串"11",而现在的字符串常量池中里的字符串"11"是str2这个引用指向的 ,现在手动的把拼接好的字符串手动添加到字符串常量池中,但是已经有了这个字符,所以手动添加不了 str1指向的是一个字符串,str2指向了一个被引用的对象,他们的引用不相同,所以是false.
案例九:
请看一下代码:
public static void func(String str) {
str = "bit";
}
public static void main(String[] args) {
String str = "gaobo";
func(str);
System.out.println(str);
}
答案是: gaobo 为什么呢?
因为我们这里的main方法中的str指向的是一个被存放到堆区中的字符串常量池中的字符串,而我们向func方法中传去的是一个str引用,起初func方法中的str引用和main方法中的str引用同时指向同一块地址,但是在func方法中str这个引用指向了一个新的地址 (即在堆区的字符串常量池中的"bit"的地址),所以两个不是指向的是一块地址,所以他们不相同。依据上述的代码我们可以知道最后打印的结果为"gaobo".
看图说话:
案例十:
请看一下代码:
public static void main(String[] args) {
String str1 = "Hello";
String str2 = str1;
str1 = "world";
System.out.println(str1 == str2);
}
答案是:false 为什么呢?
这个思路和上一个题的思路基本一致,str1是一个引用,这个引用指向堆区中的字符串常量池中的"Hello",str2引用了str1所引用的对象(即字符串"Hello")所以起初两个引用指向同一块地址,但是str1这个引用又指向了一块新的地址(即"world"所在的地址空间),所以在比较两个引用的时候肯定不相等。
看图说话:
正确理解==和equal的区别
- 双等于比较的是两个字符串引用是否相等,即他们所指向的地址是否相等
- equal是比较两个字符串中的内容是否相同。
equal方法的使用:
public static void main(String[] args) {
String str1 = "hello world";
String str2 = "hello world";
System.out.println(str1.equals(str2));
}
两个字符串内容相同,返回true,否则返回false.但是如果有一个字符串为空,那也只能这样写了。
public static void main(String[] args) {
String str1 = null;
String str2 = "hello world";
System.out.println(str2.equals(str1));
}
因为如果把str1放到了equal()方法之前比较,就会发生空指针异常
理解字符串不可变:
我们在文章前面也看到可String.java的源码,知道了String这个类(被final修饰)是一个密封类,不能被继承,同时String所引用的对象也不能被修改。 字符串是一种不可变的对象,它的内容不可修改 让我们看一下下面的代码:
String str = "hello" ;
str = str + " world" ;
str += "!!!" ;
System.out.println(str);
hello world!!!
表面上看去,就直接在把要加的字符串添加到原来的字符串后面就好了,但是不是那样的。
请详细看他的内存图: 在字符串常量池中的字符串,在拼接时,每拼接一次就会产生一个新的对象,当代码执行到str = str + "world"时,str相当于一个变量,变量和字符串拼接需要在堆中重新开辟一块新的内存空间,而这个内存空间中的value数组就会指向拼接好的字符串"hello world",在拼接"!!!“的时候也是一样的,还需要开辟一个新的内存空间,在新的内存空间中的value数组就会指向拼接好的"hello world!!!”,在这是str这个引用指向拼接好的字符串。 大家可以想一想,如果不断地这样两个是不是就会造成空间浪费。只为得到最后一个拼接好的字符串,那么为了拼接好的这个字符串,那么在这个拼接好的字符串后面是不是有着许许多多的字符串为了拼接所开辟的内空间,使堆区的闲余空间减少,当我们介绍到了StringBuffer/StringBuilder的时候就不用这样拼接一次开辟一次空间了,就直接在原字符串的末尾拼接。 这样也体现出了字符串的不可变,不能在同一个字符串上修改。
那么如果我们实在要对字符串进行修改呢? 详细请看一下代码:
String str = "Hello";
str = "h" + str.substring(1);
System.out.println(str);
hello
在这里就先介绍一下substring()方法,它的作用是对字符串进行截取,根据下标,截取这个下标,这个下标之后所对应字符串中的所用元素 。 在上述代码中就是截取了"hello",1下标之后的所有字符串(“ello”),然后与字符串"h"进行拼接,但是这样也不是在字符串"h"之后直接拼接的,还是需要在堆中重新开辟一块空间,这个空间中的value数组又指向了拼接好的"hello"字符串。
那么我们真的想在原字符串中,做出修改呢,那就必须简单的介绍一下反射了(这里是简单介绍)
反射是面向对象编程的一种重要特性,在有些语言中被称为"自省" 反射的特性:反射是在程序运行的过程中,获取修改某个对象的具体信息(成员属性,成员方法),相当于让对象更好的认识自己。
那我们现在就利用反射的特性对字符串做出修改。 我们可以从String类的源码中看到String所引用的字段,是不可被修改的,并且这个字段在类中是私有属性。
String str = "hello world";
Class c = String.class;
Field field = c.getDeclaredField("value");
field.setAccessible(true);
char[] vals = (char[]) field.get(str);
vals[0] = 'H';
System.out.println(vals);
运行结果:Hello world 并且是在原有的字符串上进行修改,没有重新开辟新的空间。
2.字符,字节,字符串
字符和字符串
一.字符与字符串 字符串内部包含一个和字符串一样的char[]数组,字符串可以和字符进行相互转化。
No | 方法名称 | 类型 | 描述 |
---|
1 | public String(char[]chars) | 构造方法 | 将字符转换成为字符串 | 2 | public String(char[] chars,int offest,int count) | 构造方法 | offest表示偏移位置,count表示偏移量,把字符数组中从offest之后偏移count个字符转变为字符串 | 3 | public charAt(int index) | 普通方法 | index表示下标,在字符串中找到对应下标的字符 | 4 | public toCharArray(String str) | 普通方法 | 将字符串转变为字符数组 |
方法一: 将字符转换成为字符串
public static void main(String[] args) {
char[] array = {'a', 'b', 'c', 'd'};
String str = new String(array);
System.out.println(str);
}
方法二: 从偏移点出发,把偏移量个字符转变成为字符串
public static void main(String[] args) {
char[] chars = {'a','b','c','d','e','f'};
String str = new String(chars,2,3);
System.out.println(str);
}
方法三: 在字符串中找到下标为i的字符
public static void main(String[] args) {
String str = "abcdefg";
char ch = str.charAt(5);
System.out.println(ch);
}
方法四: 将字符串转换成为字符数组
public static void main(String[] args) {
String str = "abcdef";
char []chars = str.toCharArray();
System.out.println(Arrays.toString(chars));
}
方法小练: 判断一个字符串中是不是全是数字? 已知字符串为"158946a9";
public static boolean isNUmber(String str){
if(str.length() == 0){
return false;
}
if(str == null){
return false;
}
for(int i = 0;i<str.length();i++){
if(str.charAt(i)>'9' || str.charAt(i) < '0'){
return false;
}
}
return true;
}
public static void main(String[] args) {
String str = "158946a9";
System.out.println(isNUmber(str));
}
字节与字符串:
字节常用于数据传输和编码转化,String也可以和字节之间进行相互转化
NO | 方法名称 | 类型 | 描述 |
---|
1 | public String(Byte[] byte) | 构造方法 | 将字节数组转换成为字符串 | 2 | pubic String(Byte[] byte,int offest,int count) | 构造方法 | 从字节数组的offest的偏移点到count个字节元素转换成为字符串 | 3 | public byte[] getBytes(String str) | 普通方法 | 将字符串中的所有字符转换成为字节数组 | 4 | public byte[] getBytes(String CharsetName)trows unspportedEncodingException | 普通方法 | 编码处理 |
方法一: 将字节数组转换成为字符串
public static void main(String[] args) {
byte []bytes = {97,98,99,100};
String str = new String(bytes);
System.out.println(str);
}
方法二: 从字节数组的offest的偏移点到count个字节元素转换成为字符串
public static void main(String[] args) {
byte[]bytes = {97,98,99,100,101,102};
String str = new String(bytes,1,3);
System.out.println(str);
}
方法三: 将字符串中的所有字符转换成为字节数组
public static void main(String[] args) {
String str = "abcdef";
byte[]bytes = str.getBytes();
System.out.println(Arrays.toString(bytes));
}
方法四: 编码处理 1.UTF-8编码:
public static void main(String[] args) java.io.UnsupportedEncodingException{
String str = "abcde高";
byte[]bytes = str.getBytes("UTF-8");
System.out.println(Arrays.toString(bytes));
}
2.GBK编码:
public static void main(String[] args)throws java.io.UnsupportedEncodingException {
String str = "abcde高";
byte[]bytes = str.getBytes("GBK");
System.out.println(Arrays.toString(bytes));
}
所以在UTF-8编码和GBK编码之后字符数组的结果不同。
分析在什么的情况下使用字符数组,在那种场合使用字节数组
- byte[] 是把 String 按照一个字节一个字节的方式处理, 这种适合在网络输, 数据存储这样的场景下使用. 更适合针对二进制数据来操作.
- char[] 是吧 String 按照一个字符一个字符的方式处理, 更适合针对文本数据来操作, 尤其是包含中文的时候
3.字符串常见操作:
字符串比较:
我们在之前介绍的是字符串比较函数equal(),它区分字符的大小写,在这里我们将具体的介绍有关字符串比较的方法
NO | 方法介绍 | 类型 | 作用 |
---|
1 | public boolean equal(String str) | 普通方法 | 比较字符串(区分大小写) | 2 | public boolean equalIgnoreCase(String str) | 普通方法 | 比较字符串是否相等(不区分大小写) | 3 | public int compareTo(String str) | 普通方法 | 比较字符串大小如果一个字符串比另一个字符串大就返回正数,否则返回负数,相等的时候返回0 |
方法一: 比较字符串(区分大小写)
public static void main(String[] args) {
String str = "hello";
String str1 = "Hello";
System.out.println(str.equals(str1));
}
方法二: 比较字符串是否相等(不区分大小写)
public static void main(String[] args) {
String str = "hello";
String str1 = "Hello";
System.out.println(str.equalsIgnoreCase(str1));
}
方法三: 比较字符串大小如果一个字符串比另一个字符串大就返回正数,否则返回负数,相等的时候返回0
public static void main12(String[] args) {
String str = "hello";
String str1 = "Hello";
System.out.println(str.compareTo(str1));
}
public static void main(String[] args) {
String str = "abcdef";
String str1 = "abc";
System.out.println(str.compareTo(str1));
}
不知道大家发现了没有,如果字符串的长度相等,compareTo()方法返回的是字符的ASII码的差值,当字符串长度不相等时它返回的是他们的长度之差。 那么让我们看一下compareTo()方法的源码
字符串拆分:
可以根据一个字符串,以特定的字符进行分割。成为若干份子字符串。
No | 方法名称 | 类型 | 作用 |
---|
1 | public String split(String regex) | 普通 | 分割字符串 | 2 | public String split(String regex,int limit) | 普通 | 分割字符串,limit表示分割组数的极限值 |
方法一: 分割字符串
public static void main(String[] args) {
String str = "a b c d e f";
String str1 = " ";
String[]strings = str.split(str1);
for (String s:strings) {
System.out.println(s);
}
}
方法二: 分割字符串,limit表示分割组数的极限值
public static void main(String[] args) {
String str = "a b c d e f";
String str1 = " ";
String[]strings = str.split(2);
for (String s:strings) {
System.out.println(s);
}
}
那当我们要对一个IP地址进行分割,还能和上边的一样吗? 都这样问了肯定是不会的啦!!!
public static void main(String[] args) {
String str = "19.15.5.4";
String []strings = str.split("\\.");
for (String s:strings) {
System.out.println(s);
}
}
注意事项: 当分割符为**"+","*","|“都要在前面加上转移字符”/";** 如果是"",就在前面加上"//"; 如果有多个分割符就要用"|"分开 。
字符串中有多个分割符
public static void main(String[] args) {
String str = "19&18%14#13";
String []strings = str.split("&|%|#");
for (String s:strings) {
System.out.println(s);
}
}
实现多组分割:
public static void main(String[] args) {
String str = "18#15#16&11&100";
String []strings = str.split("#");
for(String s1 : strings){
String []s = s1.split("&");
for(String ss: s){
System.out.println(ss);
}
}
}
字符串替换:
使用一个特定新的字符串来替换里一个字符串。
No | 方法名称 | 类型 | 作用 |
---|
1 | public String replace(String regex,String replacement) | 普通 | 替换字符串 | 2 | public String replaceAll( String regex,String replacement) | 普通 | 替换字符串中的所用特定的字符串 | 3 | public String replaceFirst(String regex,String replacement) | 普通 | 替换首个内容 |
方法一: 替换字符串
public static void main(String[] args) {
String str = "a b c d ";
String str1 = str.replace(" ","1");
System.out.println(str1);
}
方法二:
public static void main(String[] args) {
String str = "a b c d ";
String str1 = str.replaceAll(" ","1");
System.out.println(str1);
}
方法三: 替换首个内容
public static void main(String[] args) {
String str = "a b c d e";
String str1 = str.replaceFirst(" ","1");
System.out.println(str1);
}
字符串查找:
从一个完整的字符串当中,判断指定内容是否存在。
No | 方法名称 | 类型 | 作用 |
---|
1 | public boolean contains( CharSequence str) | 普通方法 | 在一个字符串中查找另一个字符串 | 2 | public int indexOf(String str) | 普通方法 | 在一个字符串中查找一个字符串,找到了就返回它的下标,如果没有找到就返回-1 | 3 | public int indexOf(String str,int fromIndex) | 普通方法 | 从指定位置开始查找字符串,找到返回索引,找不到返回-1 | 4 | public int lastIndexOf(String str) | 普通方法 | 从后向前查找指定字符串 | 5 | public int lastIndexOf(String str,int formIndex) | 普通方法 | 从指定位置从后向前查找字符串 | 6 | public boolean startsWith(String prefix) | 普通方法 | 看是否已指定字符串开头 | 7 | public boolean startWith(String prefix,int formIndex) | 普通方法 | 从指定的位置开始判断是否已特定的字符串开头 | 8 | public boolean endsWith(String suffix) | 普通方法 | 看是否以特定的字符串结尾 |
方法一: 在一个字符串中查找另一个字符串
public static void main(String[] args) {
String str = "abcdef";
String str1 = "ab";
System.out.println(str.contains(str1));
}
方法二: 在一个字符串中查找一个字符串,找到了就返回它的下标,如果没有找到就返回-1
public static void main(String[] args) {
String str = "ancdef";
String srr1 = "cd";
System.out.println(str.indexOf(srr1));
}
方法三: 从指定位置开始查找字符串,找到返回索引,找不到返回-1
public static void main(String[] args) {
String str = "decfgh";
String str1 = "de";
System.out.println(str.indexOf(str1,2));
}
方法四: 从后向前查找指定字符串
public static void main(String[] args) {
String str = "abndefg";
String str1 = "nd";
System.out.println(str.lastIndexOf(str1));
}
方法五: 从指定位置从后向前查找字符串
public static void main(String[] args) {
String str = "ababrag";
String str1 = "ab";
System.out.println(str.lastIndexOf(str1,2));
}
方法六: 看是否已指定字符串开头
public static void main(String[] args) {
String str = "abcdefgt";
String str1 = "abc";
System.out.println(str.startsWith(str1));
}
方法七: 从指定的位置开始判断是否已特定的字符串开头
public static void main(String[] args) {
String str = "abcdef";
String str1 = "a";
System.out.println(str.startsWith(str1,1));
}
方法八: 看是否以特定的字符串结尾
public static void main(String[] args) {
String str = "abcde";
String str1 = "de";
System.out.println(str.endsWith(str1));
}
字符串截取:
截取字符串中的一部分。
No | 方法名称 | 类型 | 作用 |
---|
1 | public String subString(int index) | 普通 | 从index处截取字符串 | 2 | public String subString(int index,int count) | 普通 | 截取index和count之间的字符串,注意前闭后开区间 |
方法一: 从index处截取字符串
public static void main(String[] args) {
String str = "abcdef";
String str1 = str.substring(2);
System.out.println(str1);
}
方法二: 截取index和count之间的字符串
public static void main(String[] args) {
String str = "abcdef";
String str1 = str.substring(2,5);
System.out.println(str1);
}
其他操作方法:
No | 方法名称 | 类型 | 作用 |
---|
1 | public String trim() | 普通 | 去除字符串的左右空格,中间空格不变 | 2 | public String toUpperCase() | 普通 | 把字符串中的字符全部变为大写 | 3 | public String toLowerCase() | 普通 | 把字符串中的字符全部变为小写 | 4 | public native iterm() | 普通 | 把字符串入池 | 5 | public String concat(String str) | 普通 | 字符串连接,类似于"+" | 6 | public int length() | 普通 | 计算字符串的长度 | 7 | public boolean IsEmpty() | 普通 | 判断当前字符串是否为空 |
**这下方法博主就不一一介绍了,大家自己在idea上独自完成。每个方法的具体功能已经说得很清楚。**在我们之后写代码的过程中也会时常用到。
4.String,StringBuilder和StringBuffer之间的区别:
我们知道了String 是用来描述字符串的,并且String 类型的变量指向的内容是不能修改的,虽然这样会使程序变得安全,但是为我们在平时利用字符串解题时带来了诸多不便,在这是就衍生出了两个StringBuilder和StringBuffer关键字,这两个关键字所修饰的是可以字符串改变的.并且StringBuilder和StringBuffer的方法基本相同。 我们知道String类型定义变量的方法有两种,一种是直接赋值即(String str = “abc”),另一种是new一个对象,这个对象中的value数组指向要添加进来的字符串即(String str = new String(“abc”));但是在StringBuffer和StringBuilder中就只有一种赋值方法,就是进行构造。
StringBuffer sb = new StringBuffer("abc"); 我们知道在String中连接字符串的时候会在堆上产生一个新的对象,这个对象中的value数组指向拼接好的字符串。然而在被StringBuffer和StringBuilder所修饰的时候,如果要拼接字符串,那么直接就在一个字符串的末尾直接添加。不会产生新的对象,这样就不会利用更多的空间去申请对象,耗费空间。 在StringBuffer和StringBuider中用来拼接字符串的方法时append()方法 这里就利用StringBuffer介绍一下,为什么被他修饰字符串就可以改变呢? 我们一起康康它的源码: 我们可以看到在append()方法所对应的源码中,最后返回的是this.即当前的对象,所以我们就不用在对上重复的申请空间了。
请看一下代码
public static void main(String[] args) {
String str = new String("abc");
for(int i = 0;i<10;i++){
str += i;
}
System.out.println(str);
}
请看源码: 虽然StringBuilder在这里实现了优化,但是我们每次循环,都要new一个对象,所以我们以后千万不要这样写代码。 String和StringBuilder,StringBuffer之间的相互转化
Stringhe StringBuilder,Stringbuffer之间不能进行直接转化, String变为StringBuffer:利用StringBuffer的构造方法或append()方法 StringBuffer变为String:调用toString()方法。
public static void main(String[] args) {
String str = "hello";
StringBuffer stringBuffer = new StringBuffer(str);
System.out.println(stringBuffer);
System.out.println(str1);
}
public static void main(String[] args) {
StringBuffer stringBuffer = new StringBuffer("abc");
String str = stringBuffer.toString();
System.out.println(str);
}
除了append()方法外,StringBuffer也有一些String没有的方法,比如字符串翻转`。
public static void main(String[] args) {
StringBuffer stringBuffer = new StringBuffer();
StringBuffer str = stringBuffer.append("abcdef");
StringBuffer str1 = str.reverse();
System.out.println(str1);
}
规定字符串删除
public static void main(String[] args) {
StringBuffer stringBuffer = new StringBuffer();
StringBuffer str = stringBuffer.append("abcdef");
StringBuffer str1 = str.delete(2,4);
System.out.println(str1);
}
向字符串中插入字符
public static void main(String[] args) {
StringBuffer stringBuffer = new StringBuffer();
StringBuffer str = stringBuffer.append("abcd");
StringBuffer str1 = str.insert(2,"dd");
System.out.println(str1);
}
快接近尾声了,那今天就利用一道经典的面试题来结束今天的博客吧!!
面试题:String,StringBuffer,StringBuilder之间的差别 String修饰的字符串,不能被修改,而被StringBuffer和StringBuilder修饰的符串是可以进行修改的。 StringBuffer和StringBuilder之间的比较:StringBuffer采用同步处理,属于线程安全操作;而StringBuilder未采用同步处理,属于线程不安全操作 StringBuffer和StringBuilder所利用的大部分方法基本相同。 让我们分别看一下StringBuffer和StringBuilder的源码!! StringBuffer的源码: StringBuilder的源码: 我们可以从StringBuffer和StringBuilder的源码比较可以看出,关于StringBuffer的方法,都被synchronized 所修饰 那么我们就知道了synchronized的作用是什么了,他就是保证线程的安全。 StringBuffer一般用于多线程,StringBuilder一般用于单线程。 那有些童鞋就会问什么是线程安全,什么又是线程不安全呢? 博主在这里举一个尴尬但是有助于理解的例子。
如果你在上厕所,厕所门外面有着一群人都等着你,大家都要上厕所,如果你没有一把锁把门锁住,大家蜂拥的挤向厕所,正在如厕的你,不是就尴尬了吗,而这个锁就是synchronized
|