day13_String_StringBuilder
课前回顾:
1.异常:Exception
a.编译时期异常:语法没问题,但是调用了某个方法,底层给咱们抛了一个编译时期异常,导致咱们一调用就爆红
Exception以及子类(除了RuntimeException以及子类之外)
b.运行时期异常:语法没问题,运行的时候就会出现问题
RuntimeException以及子类
2.创建异常对象:
throw new 异常
3.异常的处理:
a.throws 异常1,异常2...
b.try...catch
4.finally:不管异常有么有捕获到,都会执行的代码,都是配合try...catch使用
使用场景:关闭资源
5.三个输出异常信息的方法:
a.toString()输出异常类型+原因
b.getMessage()输出异常类型
c.printStackTrace()输出最详细的异常信息
6.Object:所有类的父类,所有的类都会直接或者间接继承Object类
a.toString():
没有重写,直接输出对象名会默认调用Object中的toString,会输出地址值
重写了,直接输出对象名会默认调用重写的toString方法,输出的是对象内容
b.equals():
没有重写,直接回比较对象的地址值
重写了,会比较对象的内容
==:
针对于基本类型,比较的是值
针对于引用类型,比较的是地址值
今日重点:
all
第一章.Object 类
1.toString 方法
1.Object类中的toString:返回该对象的字符串表示
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
2.结论:
a.直接输出对对象名,默认调用toString方法,如果没有重写Object中的toString,那么就会默认调用Object中的toString方法,输出的是地址值
b.如果重写了Object中的toString方法,默认就会调用重写的toString方法,重写了toString方法,应该返回对象的内容
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
/* public String toString(){
return name+","+age;
}*/
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Demo01Object {
public static void main(String[] args) {
Person p1 = new Person("张三",10);
System.out.println(p1);//地址值
System.out.println(p1.toString());//地址值
System.out.println("=======================");
ArrayList<String> list = new ArrayList<>();
list.add("张三");
list.add("李四");
list.add("王五");
System.out.println(list);
}
}
小结:如果直接输出对象名,不想输出地址值,就重写 toString 方法
2.equals 方法
1.Object中的equals方法:指示其他某个对象是否与此对象“相等”。
public boolean equals(Object obj) {
return (this == obj);
}
针对于基本类型:==比较的是值
针对于引用类型:==比较的是地址值
2.结论:
a.如果没有重写Object中的equals方法,比较的是地址值
b.如果重写了Object中的equals方法,应该比较对象的内容

public class Demo02Object {
public static void main(String[] args) {
Person p1 = new Person("涛哥", 12);
Person p2 = new Person("涛哥", 12);
System.out.println(p1.equals(p2));
ArrayList<String> list = new ArrayList<>();
System.out.println(p1.equals(p1));
System.out.println("============================");
String s1 = new String("abc");
String s2 = new String("abc");
System.out.println(s1==s2);
System.out.println(s1.equals(s2));
}
}
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
/* public String toString(){
return name+","+age;
}*/
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
/**
* 问题1:我们用obj调用name和age操作是为啥?
* name和age是Person中的属性,不是Object中的属性,用Object类型调用name和age
* 由于多态,父类不能直接调用子类特有成员,所以需要向下转型
*
* 问题2:如果传递的不是Person类型,那么强转之后,就会出现类型转换异常
* 所以我们需要先判断一下类型再强转
*
* 问题3:如果传递null,我们直接返回false即可,不用做类型判断
*
* 问题4:如果传递自己呢?我们直接返回true即可,没必要做类型判断,强转等操作,直接返回true
* @param obj
* @return
*/
/* public boolean equals(Object obj){
if (this==obj){
return true;
}
if (obj==null){
return false;
}
if (obj instanceof Person){
Person p = (Person) obj;
return this.name.equals(p.name) && this.age == p.age;
}
return false;
}*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
}
小结:如果调用 equals 想比较对象的内容,那么就重写 equals 方法
总结:
1.直接输出对象名不想输出地址值,而是想输出对象的内容(属性值)就重写 toString 方法
2.如果比较对象时不想比较地址值,而是比较对象的内容(属性值),就重写 equals 方法
第二章.String
1.String 介绍
1.概述:String 类代表字符串,属于字符串的类型
2.特点:
a.Java 程序中的所有字符串字面值(如 "abc" )都作为此类的实例(对象)实现
凡是带双引号的都是String的对象
比如: String s = "abc" -> String是数据类型;s是对象名;"abc"是对象
b.字符串是常量(底层是被final修饰的数组);它们的值在创建之后不能更改
c.因为 String 对象是不可变的,所以可以共享


2.String 的实现原理
1.底层就是一个被final修饰的数组
a.jdk8: 被final修饰的一个char数组
b.jdk8之后: 被final修饰的一个byte数组
private final byte[] value;
byte数组被final修饰了,所以数组的地址值不能改变,直接定死
3.String 的创建
1.String() 利用空参构造创建String对象
2.String(String s) 利用字符串创建String对象
3.String(char[] chars) 根据char数组创建String对象
4.String(byte[] bytes)通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。
5.简化: String 变量名 = ""
public class Demo01String {
public static void main(String[] args) {
//1.String() 利用空参构造创建String对象
String s1 = new String();
System.out.println(s1);
//2.String(String s) 利用字符串创建String对象
String s2 = new String("abc");
System.out.println(s2);
//3.String(char[] chars) 根据char数组创建String对象
char[] chars = {'a', 'b', 'c'};
String s3 = new String(chars);
System.out.println(s3);
/*
4.String(byte[] bytes)通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。
a.如果字节是正数,会直接转成对应的字符
b.如果是负数,就涉及到中文了,涉及到中文了,就涉及到编码问题了
中文对应的字节都是负数
GBK:一个汉字占2个字节
UTF-8:一个汉字占3个字节
c.注意:
平台:操作系统
咱们得操作系统默认字符集:GBK
但是我们的代码是在idea中写的,idea在代码启动的时候,会给代码添加
一个启动参数:-Dfile.encoding=UTF-8
所以我们认为idea就是按照UTF-8来编码解码
*/
byte[] bytes = {97,98,99};
String s4 = new String(bytes);
System.out.println(s4);
byte[] bytes1 = {-27,-101,-67};
String s5 = new String(bytes1);
System.out.println(s5);
}
}
扩展构造:
1.String(char[] chars,int offset,int length)->将字符数组的一部分转成String
chars:代表的是被转的数组
offset:代表的是从数组的哪个索引开始转
length:转多少个
2.String(byte[] bytes,int offset,int count) -> 将字节数组的一部分转成String
bytes:被转的数组
offset:从数组的哪个索引开始转
count:转多少个
public class Demo02String {
public static void main(String[] args) {
/*1.String(char[] chars,int offset,int length)->将字符数组的一部分转成String
chars:代表的是被转的数组
offset:代表的是从数组的哪个索引开始转
length:转多少个*/
char[] chars = {'a','b','c','d','e','f'};
String s = new String(chars, 2, 3);
System.out.println("s = " + s);
System.out.println("=======================");
/*2.String(byte[] bytes,init offset,int count) -> 将字节数组的一部分转成String
bytes:被转的数组
offset:从数组的哪个索引开始转
count:转多少个*/
byte[] bytes = {97,98,99,100,101,102};
String s1 = new String(bytes, 2, 3);
System.out.println("s1 = " + s1);
}
}
4.String 面试题
public class Demo03String {
public static void main(String[] args) {
String s1 = "abc";
String s2 = "abc";
String s3 = new String("abc");
System.out.println(s1==s2);//true
System.out.println(s1==s3);//false
}
}

问题1: String s = new String("abc") 共有几个对象 -> 2个
问题2: String s = new String("abc") 共创建了几个对象 -> 1个或2个

5.字符串常见问题
public class Demo04String {
public static void main(String[] args) {
String s1 = "hello";
String s2 = "world";
String s3 = "helloworld";
String s4 = "hello"+"world";
String s5 = s1+s2;
String s6 = s1+"world";
System.out.println(s3==s4);//true
System.out.println(s3==s5);//false
System.out.println(s5==s6);//false
}
}
如果拼接字符串时,等号右边没有变量参与,都是字符串字面值,那么不会随意产生新对象(假如之前有这个拼接好的串儿)
如果等号右边有变量参与拼接 ,即使之前有这个字符串内容,也会产生新对象
第三章.String 的方法
1.判断方法
1.boolean equals(Object obj) ->比较字符串内容
2.boolean equalsIgnoreCase(String s) -> 比较字符串内容,忽略大小写 -> 常见于验证码对比使用
public class Demo05String {
public static void main(String[] args) {
String s1 = "abc";
String s2 = new String("abc");
System.out.println(s1.equals(s2));
String s3 = "ABC";
System.out.println(s1.equalsIgnoreCase(s3));
//String s4 = "1234";
//System.out.println("一二三四".equalsIgnoreCase(s4));
//System.out.println("壹贰叁肆".equalsIgnoreCase("一二三四"));
}
}
2.练习 1
已知用户名和密码,请用程序实现模拟用户登录。总共给三次机会,登录成功与否,给出相应的提示
public class Demo06String {
public static void main(String[] args) {
//1.定义两个字符串,代表已经注册过的用户名和密码
String username = "admin";
String password = "123456";
//2.创建Scanner对象
Scanner sc = new Scanner(System.in);
//3.循环录入用户名和密码,给3次机会
for (int i = 0; i < 3; i++) {
System.out.println("请您输入用户名:");
String name = sc.next();
System.out.println("请您输入密码:");
String pwd = sc.next();
if (name.equals(username) && pwd.equals(password)){
System.out.println("登录成功!");
break;
}else{
System.out.println("用户名或密码错误,您还有"+(2-i)+"次机会");
}
}
}
}
javapublic class Demo07String { public static void main(String[] args) { String s1 = "abc"; method(s1); } private static void method(String s1) { /*if (s1.equals("abc")){ System.out.println("是abc"); }else{ System.out.println("不是abc"); }*/ if ("abc".equals(s1)){ System.out.println("是abc"); }else{ System.out.println("不是abc"); } /* 工具类: Objects */ /* boolean result = Objects.equals(s1, "abc"); System.out.println(result);*/ } }
3.获取功能
1.String concat(String str) 拼接字符串,返回一个新串
2.char charAt(int index) 根据索引获取对应的字符
3.int indexOf(String str) 根据指定的字符串获取对应的第一次出现的索引
4.String substring(int beginIndex) 从指定的索引位置开始截取字符串到最后,返回一个新串儿
5.String substring(int beginIndex,int endIndex)从beginIndex截取到endIndex,返回一个新串儿
含头不含尾
6.int length() 获取字符串长度
public class Demo08String {
public static void main(String[] args) {
String s = "abcdefg";
//1.String concat(String str) 拼接字符串,返回一个新串
String str1 = s.concat("哈哈哈哈");
System.out.println("str1 = " + str1);
//2.char charAt(int index) 根据索引获取对应的字符
System.out.println(s.charAt(0));
//3.int indexOf(String str) 根据指定的字符串获取对应的第一次出现的索引
System.out.println(s.indexOf("c"));
//4.String substring(int beginIndex) 从指定的索引位置开始截取字符串到最后,返回一个新串儿
System.out.println(s.substring(2));
//5.String substring(int beginIndex,int endIndex)从beginIndex截取到endIndex,返回一个新串儿,含头不含尾
System.out.println(s.substring(2,4));
//6.int length() 获取字符串长度
System.out.println(s.length());
}
}
4.练习
遍历字符串
public class Demo09String {
public static void main(String[] args) {
String s = "abcdefg";
for (int i = 0;i<s.length();i++){
System.out.println(s.charAt(i));
}
}
}
5.转换功能
1.char[] toCharArray() -> 将字符串转成char数组
2.byte[] getBytes() -> 将字符串转成byte数组
3.String replace(CharSequence target, CharSequence replacement)->将target指定的字符串替换成replacement指定的串儿
String类是CharSequence接口的实现类
4.byte[] getBytes(String charsetName)->将字符串按照指定的编码规则去转成byte数组
charsetName:不区分大小写
public class Demo10String {
public static void main(String[] args) throws UnsupportedEncodingException {
String s = "aabcdefg";
///1.char[] toCharArray() -> 将字符串转成char数组
char[] chars = s.toCharArray();
System.out.println(chars);
///2.byte[] getBytes() -> 将字符串转成byte数组
byte[] bytes = s.getBytes();
System.out.println(Arrays.toString(bytes));
/*
3.String replace(CharSequence target, CharSequence replacement)->将target指定的字符串替换成replacement指定的串儿
String类是CharSequence接口的实现类
*/
String str = s.replace("a", "z");
System.out.println("str = " + str);
/* 4.byte[] getBytes(String charsetName)->将字符串按照指定的编码规则去转成byte数组
charsetName:不区分大小写
*/
byte[] bytes1 = "你".getBytes("UTF-8");
System.out.println(Arrays.toString(bytes1));
}
}
7.练习 4
键盘录入一个字符串,统计该字符串中大写字母字符,小写字母字符,数字字符出现的次数(不考虑其他字符)
1.创建Scanner对象,录入一个字符串-> data
2.定义三个变量,分别统计大写字母字符,小写字母字符,数字字符的个数
int big = 0;
int small = 0;
int number = 0;
3.遍历data字符串,将每一个字符都获取出来
4.在遍历的过程中是否是大写字母,小写字母,数字,分别让三个变量++
a.大写字母范围65-90 -> B 66 ->66在65-90之间,证明是大写字母
b.小写字母范围97-122 -> b 98 -> 98在97-122之间,证明是小写字母
c.数字范围48-57 -> 1 49 -> 49在48-57之间,证明是数字
5.输出三个变量
public class Demo11String {
public static void main(String[] args) {
//1.创建Scanner对象,录入一个字符串-> data
Scanner scanner = new Scanner(System.in);
System.out.println("请您输入一个字符串:");
String data = scanner.next();
/*2.定义三个变量,分别统计大写字母字符,小写字母字符,数字字符的个数
int big = 0;
int small = 0;
int number = 0;*/
int big = 0;
int small = 0;
int number = 0;
//3.遍历data字符串,将每一个字符都获取出来
char[] chars = data.toCharArray();
//4.在遍历的过程中是否是大写字母,小写字母,数字,分别让三个变量++
for (int i = 0; i < chars.length; i++) {
char key = chars[i];
//a.大写字母范围65-90 -> B 66 ->66在65-90之间,证明是大写字母
if (key >= 'A' && key <= 'Z') {
big++;
//b.小写字母范围97-122 -> b 98 -> 98在97-122之间,证明是小写字母
} else if (key >= 'a' && key <= 'z') {
small++;
//c.数字范围48-57 -> 1 49 -> 49在48-57之间,证明是数字
} else if (key >= '0' && key <= '9') {
number++;
}
}
//5.输出三个变量
System.out.println("大写字母个数:" + big);
System.out.println("小写字母个数:" + small);
System.out.println("数字个数:" + number);
}
}
8.分割功能
String[] split(String regex) -> 按照指定的规则切割字符串
public class Demo12String {
public static void main(String[] args) {
String s = "abc,java,test";
String[] arr = s.split(",");
System.out.println(Arrays.toString(arr));
System.out.println("==================");
String s1 = "abc.java";
String[] arr1 = s1.split("\\.");
System.out.println(Arrays.toString(arr1));
}
}
第四章.其他方法
boolean contains(CharSequence s) -> 判断字符串中是否包含指定的串儿
boolean endsWith(String suffix) -> 判断字符串是否以指定的串儿结尾
boolean startsWith(String prefix) -> 判断字符串是否以指定的串儿开头
String toLowerCase() -> 将大写字母转成小写
String toUpperCase() -> 将小写字母转成大写
String trim() -> 去掉字符串两端空格
public class Demo13String {
public static void main(String[] args) {
//boolean contains(CharSequence s) -> 判断字符串中是否包含指定的串儿
System.out.println("abcdefg".contains("abc"));
//boolean endsWith(String suffix) -> 判断字符串是否以指定的串儿结尾
System.out.println("abcdefg".endsWith("g"));
//boolean startsWith(String prefix) -> 判断字符串是否以指定的串儿开头
System.out.println("abcdefg".startsWith("a"));
//String toLowerCase() -> 将大写字母转成小写
System.out.println("ABCdefg".toLowerCase());
//String toUpperCase() -> 将小写字母转成大写
System.out.println("abcdefg".toUpperCase());
//String trim() -> 去掉字符串两端空格
System.out.println(" abc defg ".trim());
String str = " adfa dsfads dsfasd ".replace(" ", "");
System.out.println(str);
}
}
第五章. String 新特性_文本块
预览的新特性文本块在 Java 15 中被最终确定下来,Java 15 之后我们就可以放心使用该文本块了。
JDK 12 引入了 Raw String Literals 特性,但在其发布之前就放弃了这个特性。这个 JEP 与引入多行字符串文字(文本块)在意义上是类似的。Java 13 中引入了文本块(预览特性),这个新特性跟 Kotlin 中的文本块是类似的。
现实问题
在 Java 中,通常需要使用 String 类型表达 HTML,XML,SQL 或 JSON 等格式的字符串,在进行字符串赋值时需要进行转义和连接操作,然后才能编译该代码,这种表达方式难以阅读并且难以维护。
文本块就是指多行字符串,例如一段格式化后的 XML、JSON 等。而有了文本块以后,用户不需要转义,Java 能自动搞定。因此,文本块将提高 Java 程序的可读性和可写性。
目标
- 简化跨越多行的字符串,避免对换行等特殊字符进行转义,简化编写 Java 程序。
- 增强 Java 程序中字符串的可读性。
举例
会被自动转义,如有一段以下字符串:
<html>
<body>
<p>Hello, 尚硅谷</p>
</body>
</html>
将其复制到 Java 的字符串中,会展示成以下内容:
"<html>\n" +
" <body>\n" +
" <p>Hello, 尚硅谷</p>\n" +
" </body>\n" +
"</html>\n";
即被自动进行了转义,这样的字符串看起来不是很直观,在 JDK 13 中,就可以使用以下语法了:
"""
<html>
<body>
<p>Hello, world</p>
</body>
</html>
""";
使用“”“作为文本块的开始符和结束符,在其中就可以放置多行的字符串,不需要进行任何转义。看起来就十分清爽了。
文本块是 Java 中的一种新形式,它可以用来表示任何字符串,并且提供更多的表现力和更少的复杂性。
(1)文本块由零个或多个字符组成,由开始和结束分隔符括起来。
- 开始分隔符由三个双引号字符表示,后面可以跟零个或多个空格,最终以行终止符结束。
- 文本块内容以开始分隔符的行终止符后的第一个字符开始。
- 结束分隔符也由三个双引号字符表示,文本块内容以结束分隔符的第一个双引号之前的最后一个字符结束。
以下示例代码是错误格式的文本块:
String err1 = """""";//开始分隔符后没有行终止符,六个双引号最中间必须换行
String err2 = """ """;//开始分隔符后没有行终止符,六个双引号最中间必须换行
如果要表示空字符串需要以下示例代码表示:
String emp1 = "";//推荐
String emp2 = """
""";//第二种需要两行,更麻烦了
(2)允许开发人员使用“\n”“\f”和“\r”来进行字符串的垂直格式化,使用“\b”“\t”进行水平格式化。如以下示例代码就是合法的。
String html = """
<html>\n
<body>\n
<p>Hello, world</p>\n
</body>\n
</html>\n
""";
(3)在文本块中自由使用双引号是合法的。
String story = """
Elly said,"Maybe I was a bird in another life."
Noah said,"If you're a bird , I'm a bird."
""";
public class Demo14String {
public static void main(String[] args) {
String s = "<!DOCTYPE html>\n" +
"<html lang=\"en\">\n" +
"<head>\n" +
" <meta charset=\"UTF-8\">\n" +
" <title>性感涛哥,在线发牌</title>\n" +
"</head>\n" +
"<body>\n" +
" 哈哈哈\n" +
"</body>\n" +
"</html>";
System.out.println(s);
System.out.println("================================");
String s1 = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>性感涛哥,在线发牌</title>
</head>
<body>
哈哈哈
</body>
</html>
""";
System.out.println(s1);
System.out.println("================================");
}
}
第六章.StringBuilder 类
1.StringBuilder 的介绍
1.概述:一个可变的字符序列(底层数组没有被final修饰,数组地址值可以改变)。此类提供一个与 StringBuffer 兼容的 API(StringBuilder和StringBuffer使用一毛一样)
2.作用:主要是拼接字符串
3.问题:String就可以拼接字符串,那为啥还要学StringBuilder呢?
a.String拼接字符串的时候,每拼接一次,都会产生一个新的字符串对象,如果拼接过多,会占用内存,而且拼接效率低
b.StringBuilder底层自带缓冲区,我们所有拼接的字符串内容,都会保存到这个缓冲区中,而且不会随意创建新对象
所以,用StringBuilder拼接字符串省内存,拼接效率高
4.StringBuilder的特点:
a.底层自带缓冲区,拼接的过程中不会随意产生新对象,缓冲区默认是长度为16的数组
b.拼接如果超出数组容量,会自动扩容(因为底层数组或者叫缓冲区,没有被final修饰,数组地址值可以改变)
c.怎么扩容的:创建新数组,指明新数组容量,将老数组元素复制到新数组中去,然后将新数组地址值赋值给老数组
d.默认扩容容量: 2倍+2
如果存储的数据没有超过默认扩容容量,就按照2倍+2扩容
如果存储的数据超过了默认扩容容量,那么就直接按照实际存的数据来扩容

2.StringBuilder 的使用
练习:键盘录入一个字符串,判断此字符串是否为"回文内容"
3.练习
定义一个数组,以[元素1, 元素2, 元素3..]的形式输出,用StringBuilder拼接
自己玩儿
String,StringBuilder,StringBuffer 区别:
1.相同点:都可以字符串拼接
2.不同点:
a.String:每拼接一次都会产生新的对象,占用内存,拼接效率低
b.StringBuilder:不会随意产生新对象,效率高,线程不安全
c.StringBuffer:不会随意产生新对象,效率低,线程安全
3.从拼接效率来说:
StringBuilder>StringBuffer>String