详解字符串(笔记)

字符串

字符串,是由多个字符拼接成的文本值。字符串在存储上类似数组,不仅字符串的长度可取,而且每一位上的字符也可取,访问和操作数组一样。同时字符串也是对象!

1.字符串声明

字符串是常量,它们可以显示任何文本信息,字符串的值在创建之后不可更改。
//java中单引号是字符,双引号是字符串 "我是字符串","我长得比较帅气","123456789","一年分为四季" 

延伸: java中字符串的变量是通过java.lang.String类来创建的,因此字符串变量也是一个对象。

注意: 声明一个字符串变量而不进行赋值的情况下,其默认值是null,在这样的情况下坚持去使用String类方法会报错。也就是空指针异常。

2.创建字符串的方式

2.1引用字符串常量,也就是字面量

//语法:直接将字符串字面量赋值给String类型变量 String a = "世界很残酷,但我不能龟缩"; String b = "今天天气",c = "很不错哦!"; String str1,str2; str1 = "今天下雨了"; str2 = "今天下雨了"; 

分析: 详解字符串(笔记)

代码执行到 String a = " 世界很残酷,但我不能龟缩
";时,JVM首先会去字符串常量池中检查有没有" 世界很残酷,但我不能龟缩
"字符串对象,如果有则不要再创建任何字符串对象,直接将池中的" 世界很残酷,但我不能龟缩
"这个对象地址返回,赋值给字符串常量。如果字符串池中没有,那么就在字符串池中创建" 世界很残酷,但我不能龟缩
"字符串对象,然后将池中的" 世界很残酷,但我不能龟缩
"这个对象的引用地址返回给字符串常量a,这样a就指向了池中" 世界很残酷,但我不能龟缩
"的字符串对象了。

当代码执行到 str1 = "今天下雨了"
时,JVM首先会去常量池检查是否  "今天下雨了"
字符串对象,如果不存在,则在字符串池中创建  "今天下雨了"
这个对象。然后将池中  "今天下雨了"
对象引用地址返回给字符串常量str1,这样str1就指向了字符串池中的  "今天下雨了"
对象了。如果存在,那么直接返回字符串池中的  "今天下雨了"
对象的引用地址给字符串常量str1,这样str1就指向了字符串池中的  "今天下雨了"
对象了。当代码执行到  str2 = "今天下雨了"
此时JVM去字符串池检索  "今天下雨了"
对象,发现已经存在了。那么直接返回  "今天下雨了"
对象的引用地址给字符串常量str2,这样一来str2就指向了字符串池中的  "今天下雨了"
的对象了。这样一来,str1和str2都指向了同一个字符串对象。

String str1,str2; str1 = "今天下雨了"; str2 = "今天下雨了"; //==判断是对象与对象的地址判断 System.out.println(str1 == str2); //true 

注意: 当我们使用字符串常量时,JVM首先会检查字符串常量池;如果该字符串存在字符串常量池中,那么就直接返回常量池的对象引用。如果字符串常量不存在常量池中,就会实例化该字符串对象并将其放入到字符串常量池中。String字符串的不可更改性我们一定牢记,同时字符串常量池中也不可以出现二个一样的字符串

2.2利用构造方法实例化

 String a = "i love java";  String b = "i love java";  String c = new String("i love java"); 

详解字符串(笔记)

通过上面的分析,我们明白字符串常量a和b都指向池中 **"i love java" 对象。这一点比较好理解,上面就强调了。字符串常量池中不可能出现一样的字符串对象。

当代码执行到 String c = new String("i love java") 这一行时,字符串对象是不通字符串常量创建的,而是通过 new 关键字创建的。

使用 new 创建字符串对象分析: 采用 new 创建字符串对象时,JVM首先会去字符串池中检查是否有这 "i love java"
字符串对象。如果有,则不在池中创建  "i love java"
对象,直接在堆中创建一个  "i love java"
字符串对象,然后将堆中  "i love java"
对象地址返回赋值给引用c,这样c就指向了堆中的  "i love java"
字符串对象了;如果没有,则首先在字符串池中创建  "i love java"
对象,然后再在堆中创建  "i love java"
字符串对象,然后将堆中的  "i love java"
字符串对象地址返回赋值给引用  c
。这样  c
就指向了堆中  "i love java"
对象了。

注意:new关键字一定会产生对象 "i love java"
,通过new关键字产生的对象是存储在堆中。上面代码应该产生了二个对象: 1.保存在堆中的  "i love java"
;2.保存在栈中的c; 。而java中根本就不存在二个一模一样的字符串对象,因此就可以分析得出堆中的  "i love java"
就是引用字符串池中的  "i love java"
对象,所以可以捋一个关系出来。c -> 堆中"i love java" -> 池中"i love java"

总结: 从上分析a、b、c、"i love java"是四个不同的对象,虽然c的创建在堆,但他的内部value是指向JVM字符串常量池中的"i love java"的value,因为最终构造字符串对象还是字符串常量"i love java"。

//new关键字新建一个字符串对象 String str1 = new String("zhangsan"); String str2 = new String("zhangsan"); System.out.println(str1 == str2); 

执行代码,结果输出为:false

分析:不管是创建字符串对象还是其它对象,只要是通过 new 关键字创建出来的对象,都会在堆中创建一个新对象。虽然我们str1和str2的字符串常量都是"zhangsan",但str1和str2在堆中存在的是二个完全不相干的新对象。所以他们的结果为false.

//代码所在编译期就确定的情况 String str1 = "zhangsan"; String str2 = "zhangsan"; String str3 = "zhang" + "san"; System.out.println(str1 == str2); System.out.println(str2 == str3); 

执行代码,输出结果为:true,true

str1和str2都是字符串常量"zhangsan"创建的,所以他们都是指向JVM字符串常量池中的同一个对象,同时他们在代码的编译期间就已经确定了。因此第一个输出一定是为true;当一个字符串由多个字符串常量连接而成,那么这个字符串一定也是一个字符串常量 ,因此str3也在编译期就已经被解析为一个字符串常量,同样str3也指向池中的"zhangsan"。那么str1=str2=str3输出为true。

//代码在编译时无法确定是否为字符串常量时 String str1 = "zhangsan"; String str2 = new String("zhangsan"); String str3 = "zhang" + new String("san"); //判断上面三个字符串对象,地址是不是一样的 System.out.println(str1 == str2); System.out.println(str2 == str3); System.out.println(str1 == str3); 

执行代码,输出结果为:false,false,false

分析: 字符串常量在编译时就确定,ok之后就放入到池中。但使用new String()创建的对象就不能在编译期确定,所以不能直接放入到常量池中,它们有自己的地址空间那就是堆。

str1是字符串常量,编译期就能确定,所以是常量池中"zhangsan"的引用。str2是使用new关键字创建的,所以在编译期无法确定,只能在代码运行期被创建的新对象的地址引用。str3后一部分是new String("san");无法在编译期就确定,所以也是一个在运行期创建"zhangsan"对象的引用。

String str1 = "zhang"; String str2 = "san"; String str3 = str1 + str2; System.out.println(str3 == "zhangsan"); 

执行代码,输出结果为:false.

分析:str3是指向堆中的"zhangsan",而"zhangsan"是指向常量池中的对象,所以输出结果为false。

  1. 栈中声明一个空间用来存放引用str1,str1指向池中常量"zhang"
  2. 栈中声明一个空间来存放引用str2,str2指向池中的"san"
  3. 栈中声明一个空间用来存放引用str3
  4. (str1 + str2)从常量池中复制到堆中,通过字符串的toString()方法得一个全新的String对象,"zhangsan",这个对象是存在堆中的。
  5. 引用str3指向堆中的(str1 + str2)创建的新对象
  6. str3指向堆,常量"zhangsan"指向常量池

创建字符串对象总结:

  1. 使用“”引用创建的字符串都是常量,编译期就已经确定存储到常量池中
  2. 使用new String()创建的对象会存储到堆中,是运行期创建的。
  3. 只包含常量的字符串连接符创建的字符串对象,编译期就能确定存储到常量池中。
  4. 包含变量的字符串连接符创建的对象,无法在编译期确定,所以存储在堆中。

3.equals和==的区别


  •  == 使用详解:

a).java中的基本数据类型变量在使用 == 时, 比较是他们存储的直接“值”,而不是什么地址。

基本数据类型一共为:[byte,short,int,char,long,float,double,boolean]

b).但如果在引用类型(String)中使用,则比较的是他们把指向的对象的地址是否相同(也就是不是同一个对象)。


  •  equals 使用详解:

equals是Object类中的方法,所以只要继承了Object类都有equals方法,但java中的基本数据类型不能使用equals进行比较,因为基本数据类型比较的还是地址是否指向同一个对象。而我们String就重新写了equals方法,主要作用是用于比较二个字符串对象所存储的字符串是否相等。当然Double,Date,Integer这一些都有重写equals方法。比较的是内容!

public static void main(String[] args){ String str1 = "zhangsan"; String str2 = "zhangsan"; String str3 = "zhang" + new String("san"); System.out.println(str1 == str2); System.out.println(str2 == str3); //String中使用equals是比较字符串是否相等 System.out.println(str2.equals(str3)); } 

执行代码,输出结果:true false true

4.java中连接符"+"分析

//分析以下代码具体执行过程 public static void main(String[] args){ String s1 = "wo"; String s2 = "zhangsan"; String s3 = "da" + "haohao" + s1 + "shi" + "en" + s2; System.out.println(s3); } 

在java中String使用 "+" 字符串连接符进行字符串连接,连接符最开始位置如果都是字符串常量,那么编译后尽可能多的将字符串常理连接起来。不管怎么样,代码是从最左边依次执行。模拟执行结果为: String s3 = new StringBuilder("dahaohao").append(s1).append("shi").append("en").append(s2).toString();

首先以"dahaohao"作为参数创建StringBuilder对象,然后依次进行append操作,最后调用toString()方法转换成String对象。记得只有最左边的字符串常量尽可能的多连接在一起,之后就没有这样的操作了。

所以看着很简单的操作其内部就完成了很多步骤。 "+" 进行多个字符串以及变量连接时,会产生一个StringBuilder对象和String对象。

5.提取字符串信息

主要是对字符串对象进行操作,长度、索引

5.1 获取字符串长度

字符串对象有一个方法可以获得字符串对象长度,也就是由多少个char字符组成。

public static void main(String[] args){ String str1 = "中华人民共和国万岁"; String str2 = "中华人民共 和国万岁"; System.out.println(str1.length()); System.out.println(str2.length()); } 

执行代码,输出结果为:9 10

注意:字符串对象中的length()方法与数组中的length属性都是用来获取长度,但String中的length()是成员方法;数组中的length属性是数组类的一个属性没有括号。

5.2 获取指定位置的字符

String类中有一个成员方法是charAt(int index)方法来获得指定索引的字符。

public static void main(String[] args){ String str = "我欲静思,佛魔无用。"; //创建String对象 //charAt(index); 语法: //str:是任意字符串对象 //index:str的对象的char索引,是int类型 char at = str.charAt(4); System.out.println("字符串中索引位置为4的字符是:" + at); } 

5.3 获取子字符串索引位置

子字符串是字符串对象组成的一部分,也就是被包含在字符串对象里。

public static void main(String[] args){ String str = "编程就像剑客,编得好叫大师。"; int size = str.indexOf('客'); System.out.println(size); //5 } 

注意:String中的indexOf()方法返回的是搜索字符或字符串在字符串中首次出现的索引位置。如果没有检索到则返回-1,有就返回所在字符串中的索引位置。

5.4 判断字符串首尾内容

startWith()和endsWith()方法分别用于判断字符串中是否以指定内容开始或结尾。这个方法返回值都是boolean类型

public static void main(String[] args){ //使用startWith和endsWith统计电器 String arr[] = {"美的电磁炉","海尔冰箱","格力空调","小米手机","海尔衣机","美的吸尘器","格力手机","海尔电热水器","海信液晶电视"}; int num1 = 0; //出现海尔数 int num2 = 0; //出现手机次数 for(int i = 0; i < arr.length; i++){ String str = arr[i]; if (str.startWith("海尔")){ num1++; } if(str.endsWith("手机")){ num2++; } } System.out.println("出现海尔次数:" + num1); System.out.println("出现手机次数:" + num2); } 

5.5 获取字符数组

将一个字符串对象转换成一个字符数组

public static void main(String[] args){ String str = "柳岸花明又一村,编程使我走向光明。"; //使用toCharArray()方法,直接把一个字符串对象转换成一个char数组 char c[] = str.toCharArray(); //循环打印出字符数组 for(int i = 0; i < c.length; i++){ System.out.println("数组的第" + i +"个元素为:"+c[i]); } } 

5.6 判断子字符串是否存在

String类有一个成员方式contains()方法,作用是用来检索字符串是否包含指定内容。如果包含就返回true,否则为false。

public static void main(String[] args){ String str = "斗罗大陆:唐三,小白,小荣"; //看你喜欢的唐三是否在其中 boolean r1 = str.contains("唐三"); boolean r2 = str.contains("唐三三"); System.out.println(r1); // true System.out.println(r2); //false } 

6.字符串操作

6.1 截取字符串

substring()方法返回一个新字符串,它是此字符串的一个字符串。该子字符串从指定beginIndex处的字符开始,直到endIndex-1处的字符。


  •  str:任意字符串对象

  •  beginIndex:起始索引(包括在内)

  •  endIndex:结束索引(不包括在内)
pubic static void main(String[] args){ String Num = "123456198002175890"; String str1 = Num.substring(6); System.out.println(str1); //198002175890 String str2 = Num.substring(6,10); System.out.println(str2); //1980 } 

注意: substring有二种用法,一是只有一个参数,二是指定开始和结束索引。

6.2 字符串替换

String字符串对象有一个成员方法replace(),是可以实现指定的字符或者字符串替换成新的字符序列。replace()用法:


  •  str:任意字符串对象

  •  oldstr:准备要替换的字符序列

  •  newstr:替换后的字符序列,也就是要换成什么样的

注意:replace()方法返回的是一个新字符串对象。如果在字符串没有找到指定内容,那么就将原来的字符串对象返回。

public static void main(String[] args){ String str = "我的帅气,就好像你永远不懂别人在想什么一样"; String restr = str.replace("就好像","就一定像"); System.out.println(str); System.out.println(restr); } 

6.3 字符串分割

split()方法可根据给定的分隔符对字符串进行拆分,最后返回一个字符串数组。


  •  str:任意字符串对象

  •  regex:分隔符
public static void main(String[] args){ String str = "蒸羊羔,蒸熊掌,烧花鸭,腊肉,松花小肝"; String[] arr = str.split(","); //循环 for(String tmp : arr){ System.out.println(tmp); } } 

6.4 大小写转换

public static void main(String[] args){ String str = "abc ABC"; //小写格式 System.out.println(str.toLowerCase()); //大写格式 System.out.println(str.topperCase()); //去除字符串二边空格 String str1 = " abc "; System.out.println(str1.trim()); } 

7.StringBuilder

这是String的加强版处理类,因为String是一个不可变常量;也就是变量被赋值创建时就不能修改。所以频繁赋值就会创建很的新字符串对象,这样很浪费内存。为了提高效率也就是拼接字符串,JAVA就有了StringBuilder这个类,它是一个可变对象,可以预分配缓冲区,这样往StringBuilder中新增字符时,就不会创建新的临时对象了。

下面列出了此类的几个常用方法: 

(1)append()方法可用来将文本或对象的字符串表示形式添加到由当前 StringBuilder对象表示的字符串的结尾处。

(2)appendformat()方法将文本添加到 StringBuilder的结尾处,而且实现了IFormattable接口,因此可接受格式化部分中描述的标准格式字符串。
以使用此方法来自定义变量的格式并将这些值追加到StringBuilder的后面。

(3)insert()方法将字符串或对象添加到当前 StringBuilder中的指定位置。

(4)可以使用delete()方法从当前StringBuilder中移除指定数量的字符,移除过程从指定的从零开始的索引处开始。

(5)使用replace()方法,可以用另一个指定的字符来替换 StringBuilder对象内的字符。

(6)setCharAt()方法是将指定索引处的字符修改为自己想要的字符

原文 

https://www.maiyewang.com/archives/88097

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » 详解字符串(笔记)

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址