字符串
1 API
1.1 概述
API(Application Programming Interface):应用程序编程接口
String 和StringBuilder都是属于API中的内容
Java API:指的是JDK中提供各种功能的Java类
这些类将底层的实现封装了起来,我们不需要关心这些类是如何实现的,只需要学习这些类如何使用即可,可以通过帮助文档来学习这些API如何使用
1.2 API 帮助使用文档
1.2.1 使用步骤
1、打开后点显示搜索相应的类的名称
2、看类在哪个包下,导包需要使用
3、看类的描述,知道类是干什么用的
4、看构造方法
5、看成员方法
1.2.2 使用示例
package com.huly.ApI;
import java.util.Scanner;
public class Demo2Scanner {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int a = sc.nextInt();//存入回车结束符前的所有数据,但不存入回车
System.out.println(a);
String b = sc.nextLine();//存入回车前的数据包含回车本身
System.out.println(b);
}
}
/*
程序运行结果:输入22,输出结果22,sc.nextLine的语句被跳过
原因分析:在sc.nextInt键入结束后以回车键结尾,回车前的数据存入a中在栈内存中消失,回车数据却依然在内存中,在执行sc.nextLine时,会将之前没取走的数据继续扫描,而nextLine的结束标志是回车,所以扫描到回车就直接结束录入了
*/
解决方法:
使用next()方法代替nextLine()
package com.huly.ApI;
import java.util.Scanner;
public class Demo2Scanner {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int a = sc.nextInt();
System.out.println(a);
String b = sc.next();
System.out.println(b);
String c = sc.nextLine();
System.out.println(c);
}
}
/*
程序运行结果:第一次输入22回车结束,输出结果22,第二次sc.next键入3空格3,输出结果3,空格和空格后的数据被跳过,第三次录入过程没显示,直接输出空格3
原因分析:next();语句遇到空格就不再扫描数据,只记录空格前的数据,然后遇到第二次回车后结束录入,空格3和回车没被存入变量,在遇到nextLine时自动被扫描上,遇到回车直接结束扫描输出空格3
*/
2 String
Java程序中,所有的双引号字符串都是String这个类的对象
String
类代表字符串。Java 程序中的所有字符串字面值(如 "abc"
)都作为此类的实例实现。
即:
String创建对象时是不用使用new关键字的,直接使用双引号代替。
String类通过查API文档是在java.lang这个包下,而之前使用String类中成员方法的时候没有导包,所以使用String类时不用导包
字符串数据是一个常量,其值在创建之后不能更改。
2.1 String 常见构造方法
2.1.1 常见String构造方法
2.1.2 常见构造方法的使用方法
package com.huly.ApI;
public class Demo3StringConstructor {
public static void main(String[] args){
String s1 = new String();//创建一个空白字符对象,不含任何内容
System.out.println(s1);//输出结果空白
//String类打印其对象名的时候,不会打印内存地址,而是打印对象所记录的真实内容
char[] arr = {'a','b','c'};
String s2 = new String(arr);//根据字符数组的内容,来创建字符串对象
System.out.println(s2);//输出结果:abc
//new String(数组名);会将数组内的内容连起来作为一个字符串
String s3 = new String("134");//敲出""后,第一个"前会出现original:字样
//new String("134")创建了两个对象,
System.out.println(s3);//打印结果:134
//根据传入的字符串内容来创建字符串对象
}
}
2.2 创建字符串对象的区别对比
2.2.1 字符串常量池
- 字符串常量池:当使用双引号创建字符串对象的时候,系统会检查该字符串是否在字符串常量池中存在,不存在会创建,存在不会重新创建而是直接复用,字符串常量池在JDK7版本开始从方法区挪到了堆内存,相当于在内存中有专门的空间来放字符串常量
2.2.2 区别
- 通过双引号方式创建字符串对象:以双引号方式给出的字符串,只要字符序列相同(顺序和大小写),无论程序代码中出现几次,JVM都只会建立一个String对象,并在字符串常量池中维护
- 通过new关键字创建字符串对象:每一次new都会申请一个内存空间,虽然内容相同,但是地址值不同
即:双引号创建的字符串对象,在字符串常量池中存储,通过构造方法创建的字符串对象,在堆内存中存储
2.2.3 代码示例
package com.huly.ApI;
public class Demo4StringDifference {
public static void main(String[] args){
String s1 = "abc";
String s2 = "abc";//==号作比较,基本数据类型比较的是具体的值,引用数据类型比较的是地址值
System.out.println(s1==s2);//比较对象s1和s2的内存地址,输出结果:true
/*
程序运行,先将类的字节码文件加载如方法区,类中的主函数会被虚拟机自动调入栈内
存中运行,运行String s1 = "abc";后先检查abc在字符串常量池中是否存在,检测到常量池中
没有会创建一个字符串常量对象,之后运行String s2 = "abc";时检测到常量池中有就会直接使用
不在重新创建对象,因此两个对象的地址值都相同,输出结果为true
*/
char[] arr = {'a','b','c'};
String s3 = new String(arr);
String s4 = new String(arr);
System.out.println(s3==s4);
/*
char[] arr = {'a','b','c'};创建字符型数组,在堆内存中开辟空间,将记录的地址值赋给字符型数组变量arr
在堆内存开辟的空间中存入字符型变量a,b,c,String s3 = new String(arr);
String s4 = new String(arr);运行后会在堆内存中开辟两个不同的空间,根据传入数组的地址值将地址值对应的空间中
的常量连起来存入new关键字开辟的空间中,因为数组的地址值相同,所以new开辟的空间中记录的字符串变量是相同的,因为
对象s3,s4开辟了两个空间,所以System.out.println(s3==s4);中对比地址值结果为false
*/
}
}
}
2.3 String常见面试题
public class Test{
publi"c static void main(String[] args){
String s1 = "abc";
String s2 = "ab";
String s3 = s2+"c";
System.out.println(s1==s3); //输出结果:false;
}
}
程序执行过程:
- 字节码文件加载入方法区,jvm自动将main方法调入栈内存中执行,执行String s1 = “abc”;String s2 = “ab”;这两句代码用”“创建的对象进入在方法区的字符串常量池中;
- String s3 = s2+“c”;这句代码先创建了一个对象”c“放入了字符串常量池中了,然后字符串”c“和s2进行拼接,进行拼接时java底层会自动在堆内存中创建一个StringBuilder对象,开辟空间产生地址,StringBulider会自动调用本对象中的方法,将字符串拼接在一起放在开辟的内存空间中,此时空间中存放的字符串是”abc“,但因为类型是StringBuilder,和s3的String类型不匹配,所以需要再调用该对象的一个toString();方法,将StringBuilder类型转化为String类型,存放在一个新的内存空间中
- 当String s3 = ”a“+“b”+"c"时,由于Java中存在常量优化机制在编译的时候会自动将 ”a“+“b”+“c"拼接为"abc”;此时的输出结果将为true;原语句s2是变量所以没有触发常量优化机制;
2.4 字符串的比较
使用==作比较
基本类型:比较的是数据值是否相同
引用类型:比较的是地址值是否相同
字符串是对象,他比较内容是否相同,是通过equals()方法来实现的;
public boolean equals(Object anObject):此方法将字符串与指定对象进行比较,由于我们比较的是字符串对象,所以参数直接传递一个字符串。此方法返回一个boolean类型的值
public boolean equalsIgnoreCase(String anotherString):将此字符串与另一个字符串比较,不考虑大小写
2.4.1 格式
对象名.equals(对象名);
对象名.equalsIgnoreCase(对象名);
2.4.2 示例
package com.huly.stringmethod;
public class Dome1Equals {
public static void main(String[] args){
String s1 = "abc";
String s2 = "ABC";
String s3 = "abc";
System.out.println(s1.equals(s2));//字符串s1和s2比较
System.out.println(s1.equalsIgnoreCase(s2));//字符串s1和s2比较不计大小写
}
}
输出结果:
false
true
package com.huly.stringmethod;
import java.util.Scanner;
public class Dome2Equals {
public static void main(String[] args){
String name = "abc";
String password = "123";
scycle:for(int i = 1;i<=3;i++) {
Scanner sc1 = new Scanner(System.in);
System.out.println("请输入账户");
String s1 = sc1.next();
System.out.println("请输入密码");
//Scanner sc2 = new Scanner(System.in);
String s2 = sc1.next();
if (s1.equalsIgnoreCase(name)) {
if (s2.equals(password)) { //两个if语句可以用逻辑符简化成y
System.out.println("登录成功");
break scycle;
}else{
System.out.println("输入错误请重新输入");
}
}else{
System.out.println("输入错误请重新输入");
}
if(i==3){
System.out.println("错误已达上限");
}
}
}
}
2.5 字符串遍历
字符串遍历(traversal)先要知道字符串的长度
public int length () : 返回字符串的长度
调用格式:
对象名.length();
2.5.1 字符串遍历方法
A. public char charAt(int index)
返回指定索引处的char值,字符串的索引也是从0开始的
方法调用格式:
char 变量名 = 对象名.charAt(索引号);
此方法一次只能调出一个字符,需要加上循环语句才能将字符串遍历完整;
示例:
package com.huly.stringtraversal;
import java.util.Scanner;
public class test1Straversal {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
String s1 = sc.next();
for (int i = 0; i <s1.length(); i++) {
char a = s1.charAt(i);
System.out.println(a);
}
}
}
B. public char[] toCharArray():
将字符串拆分为字符数组并返回
调用格式:
对象名.toCharArray()
此方法可以将字符串拆分为单个字符并存放在字符型数组中
调用示例:
package com.huly.stringtraversal;
import java.util.Scanner;
public class test1Straversal {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
String s1 = sc.next();
char arr[] = s1.toCharArray();
for (int i = 0; i < arr.length ; i++) {
System.out.println(arr[i]);
}
}
}
2.5.2 统计字符次数
判断字符属于大写字母、小写字母还是数字,直接判断是否在对应的范围即可,如:
ch>=‘A’&&ch<=‘Z’,满足则ch属于大写字母;
ch>=‘a’&&ch<=‘z’,满足则ch属于小写字母;
ch>=‘0’&&ch<=‘9’,满足则ch属于数字;
字符的底层是由数字代替,比较大小即是判断底层所对应的数字是否属于某一范围;
示例:
package com.huly.stringtraversal;
import java.util.Scanner;
public class test1Straversal {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
String s1 = sc.next();
char arr[] = s1.toCharArray();
int bigCount = 0;
int smallCount = 0;
for (int i = 0; i <arr.length ; i++) {
if(arr[i]>='A'&&arr[i]<='Z'){
bigCount++;
}else{
if(arr[i]>='a'&&arr[i]<='z'){
smallCount++;
}
}
}
System.out.println(bigCount+"---"+smallCount);
}
}
2.5.3 字符串截取
A. public String substring(int beginIndex)
从传入索引位置处开始向后截取,一直截取到末尾,得到新的字符串并返回
调用格式:
对象名.substring(int beginIndex);
B. public String substring(int beginIndex,int endIndex)
从beginIndex处索引位置开始截取,截取到endIndex处索引位置(不包含endIndex索引处的字符,包含头不包含尾),得到新字符串并返回
调用格式:
对象名.substring(int beginIndex,int endIndex);
示例:
package com.huly.stringtraversal;
import java.util.Scanner;
public class test2intercept {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
String id = sc.next();
System.out.println("请输入手机号");
System.out.println(id);
String start = id.substring(0,3);
String end = id.substring(7);
System.out.println(start+"****"+end);
}
}
2.5.4 字符串替换 String replace(CharSequence target,CharSequence replacement)
将当前字符串中的target(被替换的旧值)使用replacement(替换的新值)进行替换,并返回替换后的字符串
调用格式:
对象名.replace();
示例:
package com.huly.stringtraversal;
import java.util.Scanner;
public class test3intercept {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String s = sc.next();
String result = s.replace("TMD","***");
System.out.println(result);
}
}
2.5.5 切割字符串
String[] split(String regex)
根据传入的字符串作为规则进行切割,将切割后的内容存入字符串数组中,并将字符串数组返回
注意:封装时,类一般存放在domain包内;
调用格式:
对象名.split("截取符号")
示例:
package com.huly.domain;
public class student {
private String name;
private String age;
public student(){}
public student(String name, String age) {
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(String age) {
this.age = age;
}
public String getName() {
return name;
}
public String getAge() {
return age;
}
}
package com.huly.stringtraversal;
import java.util.Scanner;
import com.huly.domain.student;
public class test4 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String stuInfo = sc.next();
String[] arr = stuInfo.split(",");
System.out.println(arr[0]);
System.out.println(arr[1]);
student st1 = new student(arr[0],arr[1]);
System.out.println(st1.getName()+st1.getAge());
}
}
2.6 String方法小节
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NIoIu9Jf-1630851398903)(C:\Users\13943\AppData\Roaming\Typora\typora-user-images\image-20210831221052900.png)]
3.StringBuilder
- StringBuilder是一个字符串类,其存储的内容可以更改,我们可以把它看成是一个容器,作用是提高字符串的操作效率,原理是StringBuilder中存储的是变量,可在内存中直接更改,而String类型的字符串在内存中无法更改,只能通过创建新的对象然后把地址赋给原对象型变量达到一种看似更改的状态,原来的对象因为没有东西存储他的地址,无法再程序中找到,所以会被jvm的垃圾处理系统清理掉
- StringBuilder在java.lang路径下故不需要导包
- StringBuilder可翻译为字符串生成器
3.1 常用构造方法
public StringBuilder():StringBuilder的无参构造方法,创建一个空容器用来存储字符串
public StringBuilder(String型变量名):StringBuilder的有参构造方法,在创建的容器中直接放入一个初始值
3.1.1 格式
StringBuilder 对象名 = new StringBuilder();
//创建一个空白可变字符串对象,不含有任何内容
StringBuilder 对象名 = new StringBUilder("字符串");
//根据字符串的内容创建一个可变的字符串对象,根据字符串是String类型的
3.1.2 特点
package com.huly.stringbuilder;
public class demo2stringBulider {
public static void main(String[] args) {
StringBuilder sb1 = new StringBuilder();
System.out.println(sb1);//打印空白
StringBuilder sb2 = new StringBuilder("avf");
System.out.println(sb2);//输出结果:avf
//StringBuilder类和String一样,打印对象名输出的时对象所代表的值而不是内尺寸地址
}
}
3.2 StringBuilder常用成员方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5muWarUB-1630851398906)(C:\Users\13943\AppData\Roaming\Typora\typora-user-images\image-20210901213507650.png)]
3.2.1 publid StringBuilder append(任意类型)
在对象中添加数据并返回对象本身,此方法可以使用任意类型的参数是因为StringBuilder成员方法append把所有的数据类型都定义了,每一种类型的数据都能找到对应方法追加。
append可翻译为追加、附加
A.调用格式
对象名.append(任意类型数据)
B.使用实例
package com.huly.stringbuilder;
import com.sun.corba.se.impl.protocol.AddressingDispositionException;
public class demo3append {
public static void main(String[] args){
StringBuilder sb1 = new StringBuilder();
System.out.println(sb1);//运行结果:空白;
// 说明StringBuilder所创建的对象存储的不是内存地址
sb1.append("aaa");
System.out.println(sb1);//运行结果:aaa
//说明StringBuilder创建的对象存储的相当于一个容器,调用方法可以直接更改容器中的内容
sb1.append("bbb");
sb1.append("ccc");
System.out.println(sb1);//运行结果:aaabbbccc
//说明成员方法append()的功能是将传入的数据加入对象中,每次加入内容都可能会使对象的数据改变
StringBuilder sb2 = sb1.append("ddd");//将ddd附加到aaabbbccc后面,并将自己的地址值也就是sb1的地址值赋值给sb2
StringBuilder sb3 = sb2.append("fff");//将fff附加到aaabbbcccddd后面,并将自己的地址值传给sb3
System.out.println(sb1);//输出结果:aaabbbcccdddfff
System.out.println(sb2);//输出结果:aaabbbcccdddfff
System.out.println(sb3);//输出结果:aaabbbcccdddfff
System.out.println(sb1==sb2);//输出结果:true
System.out.println(sb3==sb2);//输出结果:true
//说明调用append();方法是返回的就是方法自己,调用时先将要附加的值通过append方法附加到被调用的对象,再将自身的地址值赋值给新创建的对象
//等于说三个对象所的地址值指向同一处内存空间,数据一进行变动三个对象都会变动
}
}
C. 拓展延申:链式编程
如果一个方法返回的是对象类型,那对象就可以继续向下调用方法,添加多个数据时可以不用创建多个对象,节省时间;
示例:
package com.huly.stringbuilder;
public class dome3append2 {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
sb.append("a").append("b").append("c");
System.out.println(sb);//输出结果:abc
}
}
3.2.2 public StringBulider reverse()
更改对象中的数据为相反序列并返回,返回的也是对象本身
reverse可翻译为逆转
A. 调用格式
对象名.reverse();
B.示例
package com.huly.stringbuilder;
public class reverse {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
sb.append("ab");//在对象型变量sb这个空白容器中加入"ab"这个字符串型变量
System.out.println(sb);//运行结果:ab
sb.reverse();//将内容更改为相反顺序的内容并返回
System.out.println(sb);//运行结果:ba
}
}
3.2.3 public int length()
返回对象中内容的长度(字符的个数)
A. 调用格式
对象名.length();
B. 使用示例
package com.huly.stringbuilder;
public class reverse {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
sb.append("ab");
System.out.println(sb);
int l =sb.length();
System.out.println(l);//运行结果:2
System.out.println(sb.length());//运行结果:2
}
}
3.2.4 public String toString()
通过toString()就可以实现把StringBuilder类型转化为String类型
A. 调用格式
对象名.toString();
B. 使用示例
package com.huly.stringbuilder;
public class reverse {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
sb.append("ab");
System.out.println(sb);
String s1 = sb.toString();
//将StringBuilder类型的数据变为String类型的数据,并将值赋予String型变量s1
System.out.println(s1);//运行结果:ab
}
}
4 StringBuilder提升效率的原理
String:内容是不可以更改的
StringBuilder:内容是可以更改的
4.1 如何判断StringBuilder能提高效率
package com.huly.stringbuilder;
public class demo1stringbuilder {
public static void main(String[] args){
long start = System.currentTimeMillis();
//获取从1970年1月1日0时0分0秒到当前时间所经历的毫秒值
StringBuilder s1 = new StringBuilder();
for (int i = 0; i <=50000 ; i++) {
s1.append(i);//获取累加值
}
System.out.println(s1);
long end = System.currentTimeMillis();
//获取从1970年1月1日0时0分0秒到当前时间所经历的毫秒值
System.out.println(end-start);//输出结果:16ms
//程序结束后获取的毫秒值减去程序开始前的毫秒值等于程序运行时间
method();
}
public static void method(){
long start = System.currentTimeMillis();
String s = "";
for (int i = 0; i <=50000 ; i++) {
s+=i;
}
System.out.println(s);
long end = System.currentTimeMillis();
System.out.println(end-start);//输出结果:6257
}
}
4.2 StringBuilder提升效率内存原理
4.2.1 String在内存中拼接的运行原理
package com.huly.contrast;
public class test1string {
public static void main(String[] args) {
String s1 = "a";
String s2 = s1+"b";
String s3 = s2+"c";
System.out.println(s3);
}
}
- 字节码文件先加载入方法区,虚拟机将主方法自动调入栈内存中执行
- 执行String s1 = “a”; 使用双引号创建对象,先检查字符串常量池中有没创建过,没有会在字符串常量池中创建一个对象"a",然后将s1将会记录常量池中"a"的地址
- 执行String s2 = s1+“b”; 又有双引号创建对象,在常量池中创建一个对象"b",此句程序有个加号拼接,系统底层会自动在堆内存中创建一个StringBuilder对象,即new StringBuilder(); 然后自动调用该对象的append方法完成拼接,得到StringBuilder类型的字符串"ab",为了让String类型的s2可以接受,需要调用StringBuilder类的toString方法将StringBuilder类型的数据转化为String类型,这一步需要在堆内存中再开辟一个空间存放数据"ab",然后将该空间的地址赋值给s2
- 执行String s3 = s2+“c”; 步骤和3相同,得到String 型变量s3记录String型字符串"abc"的地址
- 打印s3记录地址的对饮字符串abc
4.2.2 StringBuilder再内存中拼接的运行原理
package com.huly.contrast;
public class test2stringbuilder {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
sb.append("a");
sb.append("b");
sb.append("c");
System.out.println(sb);
}
}
- 字节码文件先加载入方法区,虚拟机将主方法自动调入栈内存中执行
- 执行StringBuilder sb = new StringBuilder();new会在堆内存中开辟空间,然后将地址传递给StringBuilder型变量sb
- sb调用apeend方法将"a",“b”,"c"拼接在一起前先检查字符串常量池中有没有相同的字符串,没有的话需要创建,创建好之后在sb记录的地址空间中append依次将abc拼接在一起
- 打印拼接后的字符串
4.2.3 StringBuilder提升效率原理分析
String类型变量在进行拼接时,每拼接一次都需要在堆内存中创建一个StringBuilder类型的对象,而StringBuilder类型的变量只用在堆内存中创建一个StringBuilder类型的变量就可以连续调用append方法将需要拼接的字符串拼接在一起。
总的来说就是StringBuilder类型的字符串在内存中可以更改,而String类型的变量不能变每次只能创建新对象替换内存地址来达到更改记录的字符的目的
5 案例
5.1 对称字符串案例
要求:键盘录入一个字符串,程序判断这个字符串是不是对称字符串
package com.huly.case1;
import java.util.Scanner;
public class symmtric {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个字符串");
String s1 = sc.next();
StringBuilder sb1 = new StringBuilder(s1);
//sb1.append(s1);
//可以在构造方式时直接将字符串内容赋值给StringBuilder类型的变量
sb1.reverse();
String s2 = sb1.toString();
//toString可以将StringBuilder型字符串转化为String型
//System.out.println(s1.equals(s2));
//这种对比只能显示true或false
if(s1.equals(s2)){
System.out.println("是对称字符串");
}else{
System.out.println("不是对称字符串");
}
}
}
5.2 拼接字符串
需求:定义一个方法,把int数组中的数据按照指定的格式拼接成一个字符串返回,调用该方法,并在控制台输出结果,例如int[] arr={1,2,3},执行方法后输出结果为:[1,2,3]
package com.huly.stringSplicing;
public class splicing {
public static void main(String[] args) {
int arr[] = {1,2,3};
String s1 = zhuanhuan(arr);
System.out.println(s1);
}
private static String zhuanhuan(int arr[]) {
StringBuilder sb1 = new StringBuilder();
for (int i = 0; i < 5; i++) {
if(i==0){
sb1.append("[");
}else if(i==arr.length+1){
sb1.append("]");
}else if(i==arr.length){
sb1.append(arr[i-1]);
}else{
sb1.append(arr[i-1]).append(",");
}
}
//System.out.println(sb1);
return sb1.toString();
}
}