1 API概述
1.1 API概述
1.1.1 什么是API
API(Application Programming Interface),意为:应用程序接口。API就是已经写好的的程序或功能,程序要需要时可以直接调用,无需再次编写。
API可以大致分为如下几类:
- 编程语言API:编程语言中内置的API
- 方法库或框架API:第三方方法库或框架提供的API
- 操作系统API:由操作系统提供的应用程序和操作系统之间的接口
- 远程API:远程 API 允许开发人员通过协议操作远程资源,特定的通信标准允许不同的技术协同工作
- Web API:是一种使用超文本传输协议 (HTTP) 从客户端设备(手机、笔记本电脑等)访问 Web 服务器的服务
1.1.2 为什么需要API
编程界一句名言:"不要重复的发明轮子(Don't Reinvent the Wheel)",意思就是别人已经设计好的工具可以直接使用。
我们日常生活中也会使用很多工具,我们需要时拿过来用就好了,不需要知道这个工具的运作原理。
Java的设计者已经将很多常用的功能写好,形成了API(或称类库)。Java程序要需要这些功能时,直接调用API即可,方便了软件开发,提高了开发速度,也降低了开发难度。
1.1.3 常用API包结构
下面的图中列出了部分常用的API所在的包和功能概述
1.1.4 API规范文档
描述如何构建或使用API的文档或标准称为 API 规范(API specification)。Java提供了完善的API文档。
Java 8的API文档访问网址为 https://docs.oracle.com/javase/8/docs/api/index.html
Java 17的API文档访问网址为 https://docs.oracle.com/en/java/javase/17/docs/api/index.html
1.1.5 API的学习方法
API方法的数量众多,一次性记住它们可能会很困难,但通过利用在线工具,我们可以更好地学习和掌握这些API。
以下是三种常见的在线工具分类:
- 官方手册:使用Java官方手册是学习API的常用方式。官方手册提供了完整的API文档和示例代码,我们可以查阅官方提供的信息来深入了解每个方法的功能、参数和用法。如果官方手册不是我们的母语,我们可以结合翻译工具,帮助我们理解官方文档中的内容。
- ChatGPT:将ChatGPT作为工具,可以快速获取生成的信息,帮助我们学习API。我们可以向ChatGPT提出关于特定方法或概念的问题,并获得相应的解答和示例代码。ChatGPT可以为我们提供实时的帮助和指导,使我们能够更迅速地理解和学习API。
- 搜索引擎:利用搜索引擎可以获取大量的在线文章、教程和博客,涵盖了各种API的用法和示例。我们可以通过搜索关键词和API的名称,来获取与特定API相关的资源。通过阅读这些在线文章,我们可以学习其他开发者的经验和见解,从而更好地理解和掌握API的使用。
记住所有API方法确实是一项挑战,但通过结合官方手册、ChatGPT和搜索引擎,我们可以逐步学习和掌握所需的API方法。持续的实践和编写代码也是加深对API理解的重要方式。在实际项目中应用API,并通过查阅文档和资源来解决具体问题,可以帮助我们更好地掌握和熟悉API的使用。
2 Object
2.1 Object类概述
2.1.1 什么是Object类
Object类是Java中所有类的父类,位于java.lang包下。
我们编写的类如果不主动继承任何类,则默认继承Object类。
Object类承载了Java中所有类的共性内容,为Java程序提供了最基本的结构支撑,是所有类的根类。
2.1.2 Object类中的方法
Object类中定义了多个开发中非常常用的方法:
- toString:用于返回该对象的字符串表示,常用于在调试中输出对象的属性值
- equals:用于判断两个对象逻辑相等
- finalize:用于释放资源,因无法确定该方法什么时候被调用,所以很少使用
- hashCode:为对象生成一个hash值,用于哈希查找,后续介绍
- wait和notify相关:线程中使用,后续介绍
- getClass:反射中使用,后续介绍
- clone:克隆对象的方法,后续介绍
2.2 toString方法
2.2.1 什么是toString方法
toString方法,顾名思义,是返回一个可以代表该对象的字符串,该字符串中一般包含的是该对象的具体信息。Object类中的toString方法实现如下图所示。
如果不重写 toString 方法,默认逻辑是返回对象的类型+@+hashCode值,其中hashCode值默认是将对象的内存地址转换为整型表示。
Java 语言中很多地方会默认调用对象的 toString 方法。比如System.out.print()方法:打印一个引用类型变量时,如果该变量的值不为null,则直接调用该对象的toString()方法,获取该对象的字符串表示,再打印该字符串。
2.2.2 重写toString方法
在实际开发中,经常需要按照对象的属性得到相应的字符串表现形式,因此子类需要重写toString方法。主流的IDE均提供了自动生成toString方法的功能,开发者可以使用该功能快速生成toString方法的内容。
toString 方法也是非常有用的调试工具,JDK中的标准类库中,许多类都定义了toString方法,方便用户获得有关对象状态的必要信息。在实际开发中,建议为自定义的每一个类重写 toString 方法。
2.2.3【案例】toString示例
定义类,并重写该类的toString() 方法,返回记载对象信息的字符串;并测试。
案例示意代码如下:
package api_01;
public class Person {String name;int age;public Person(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}
}
package api_01;
public class Student {String name;int age;public Student(String name, int age) {this.name = name;this.age = age;}
}
package api_01;
public class ToStringDemo {public static void main(String[] args) {Student s1 = new Student("Jerry",18);// 未重写时调用Object中toString方法的逻辑System.out.println(s1);Person p1 = new Person("Tom",22);// 重写后调用toString方法System.out.println(p1);}
}
2.3 equals方法
2.3.1 equals方法
在Java中,equals()方法是用于比较两个对象是否相等的方法。这个方法定义在Object类中,因此所有的Java类都继承了equals()方法。
方法签名:public boolean equals(Object obj) 这个方法接受一个Object类型的参数,并返回一个布尔值,表示两个对象是否相等。
在Object类中,默认的equals()方法实现是比较两个对象的引用是否相等,即比较对象的内存地址。这相当于使用==运算符进行比较。
很多情况下,我们需要自定义类的equals()方法来比较对象的内容是否相等,而不仅仅是比较引用。为了实现自定义的相等比较逻辑,通常需要重写equals()方法。
2.3.2 重写equals方法
默认的equals方法比较规则是两个对象内存地址相同。这样的规则并不能适合所有情况。
比如你有一个Person类,它有两个属性:name(姓名)和age(年龄)。如果两个人的姓名和年龄都相同,它们就应该被视为相等的对象,也就是完全相同的人。
但是,默认情况下,Java的equals()方法只是比较对象的引用,也就是比较对象在内存中的位置。所以,如果你创建了两个Person对象,即使它们的姓名和年龄相同,由于它们在内存中的位置不同,equals()方法会返回false。
这时候,你需要重写equals()方法,根据你的定义来比较对象的内容。你可以在重写的equals()方法中,按照你的自定义逻辑,比较两个人的姓名和年龄是否相同。如果相同,你可以返回true,表示这两个人是相等的对象。
2.3.3【案例】重写equals方法示例
定义类,并重写该类的equals() 方法,测试效果。
可以使用IDE提供的工具快捷创建equals方法。
案例示意代码如下:
package api_01;
import java.util.Objects;
public class Person {String name;int age;public Person(String name, int age) {this.name = name;this.age = age;}@Overridepublic boolean equals(Object o) {// 如果对象地址一样,则认为相同if (this == o) return true;// 如果参数为空,或者类型信息不一样,则认为不同if (o == null || getClass() != o.getClass()) return false;// 转换为当前类型Person person = (Person) o;// 要求基本数据类型的属性的值相等// 并且将引用类型交给java.util.Objects类的equals静态方法判断return age == person.age && Objects.equals(name, person.name);}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}
}
package api_01;
public class EqualsDemo {public static void main(String[] args) {// 未重写equals方法的示例Student s1 = new Student("Jerry",18);Student s2 = new Student("Jerry",18);System.out.println("s1 equals s2 ? "+s1.equals(s2));// 重写equals方法后的示例Person p1 = new Person("Tom",22);Person p2 = new Person("Tom",22);System.out.println("p1 equals p2 ? "+p1.equals(p2));}
}
2.3.4 equals 和 ==
"equals"和"=="是在Java中经常被讨论的两个关键概念。它们用于比较对象的相等性,但在使用和含义上有所不同。
"=="操作符:
- "=="操作符用于比较两个对象的引用是否相同,即比较对象在内存中的地址。当两个对象引用指向同一个内存地址时,"=="操作符返回true,表示这两个引用是相等的。
- 对于基本数据类型(如int、boolean等),"=="操作符比较的是它们的值是否相等。
"equals"方法:
- "equals"方法是用于比较两个对象是否相等的方法。这个方法定义在Object类中,可以被所有的Java类继承和重写。
- 默认情况下,"equals"方法比较的是对象的引用是否相等,类似于"=="操作符。然而,通常情况下,我们需要重写"equals"方法来根据自定义的逻辑比较对象的内容是否相等。
简洁版本回答:
- "=="操作符用于比较引用是否相等,比较的是对象在内存中的地址。
- "equals"方法用于比较对象的内容是否相等,通常需要重写以满足自定义的相等性标准。
- 在比较对象时,通常应该使用"equals"方法来比较内容相等性,而不是使用"=="操作符来比较引用相等性。但对于基本数据类型,可以使用"=="操作符比较它们的值。
3 String
3.1 String概述
3.1.1 String类型的重要性
String类型用于处理和存储文本数据,String类型在Java中具有重要性和广泛的应用领域。下面是关于String类型的重要性和应用领域的概述:
重要性:
- 数据处理和存储:String类型用于处理和存储文本数据。它是一种数据类型,被广泛用于表示和操作字符串。
- 用户界面:在用户界面开发中,String类型常用于显示文本内容、标签、按钮等。它是构建用户友好的界面的基础。
- 数据传输和通信:在网络通信中,String类型常用于表示和传输文本数据。例如,在Web开发中,表单数据、URL参数、响应内容等都以字符串的形式进行传输。
应用领域:
- 文本处理:String类型被广泛用于处理和操作文本数据。它提供了丰富的方法和功能,例如字符串连接、截取、查找、替换、分割等,使得文本处理变得更加方便和灵活。
- 文件操作:在文件操作中,String类型用于表示文件路径、文件名、文件内容等。通过String类型,我们可以方便地读取、写入和操作文件的文本数据。
- 字符串处理算法:许多算法和数据结构涉及字符串处理,例如字符串匹配、编辑距离、正则表达式匹配等。String类型是实现这些算法和数据结构的基础。
String类型在Java中具有重要性和广泛的应用领域。它是处理和操作文本数据的基础,并在用户界面、数据传输、文件操作等方面发挥着关键作用。
3.1.2 String类的位置和特点
String类在Java中位于java.lang包下,因此无需导入即可使用。它具有以下特点:
- 不可变性:String对象一旦创建,其值无法修改。每次对String对象的操作都会创建一个新的String对象,而不是修改原始对象。
- 常量池:Java通过字符串常量池(String Pool)来重用相同值的String对象,以节省内存和提高性能。
- 丰富的方法:String类提供了许多方法来操作和处理字符串,如连接、截取、比较、搜索、替换、格式化等。
3.2 String的创建和初始化
3.2.1创建字符串
使用双引号创建字符串是一种常用的方式,在Java中可以直接使用双引号将字符序列括起来来创建一个String对象。
String str1 = "Hello, World!";
在上述示例中,使用双引号将字符序列"Hello, World!"括起来,创建了一个名为str1的String对象,它包含了该字符序列作为其值。
另一种创建字符串的方式是使用String构造函数。String类提供了多个构造函数,其中一个可以接受一个字符串参数来创建String对象。
String str2 = new String("Hello, World!");
在上述示例中,通过String构造函数传入"Hello, World!"字符串作为参数,创建了一个名为str2的String对象,它的值与传入的字符串相同。
在Java 13中,通过使用三个双引号(""")来创建字符串字面量,可以更方便地创建包含换行符的多行字符串。这种方式在处理长文本、SQL查询、HTML模板等情况下非常有用。
String str = """Hello, World!""";
无论是使用那种方式创建字符串,它们都会在内存中创建一个String对象没有本质没有区别。
根据实际需求,可以选择适合的方式来创建字符串。使用引号创建字符串简洁明了,而使用String构造函数创建字符串则更灵活,可以根据需要进行动态创建。
3.2.2 字符串常量池和字符串字面量
字符串常量池(String Pool)是Java中的一个特性,它是存储字符串常量的内存区域。字符串常量池的目的是重用具有相同值的字符串对象,以节省内存和提高性能。
当使用双引号创建字符串时,Java会首先检查字符串常量池中是否已经存在相同值的字符串对象。如果存在,则直接返回常量池中的实例,而不会创建新的对象。这种重用机制使得字符串常量具有唯一性,相同的字符串字面量在内存中只有一个实例。要注意的是,字符串常量池中存储的是字符串的引用,而字符串对象本身是存储在堆内存中的。
String s1= "Apple"; // 字符串常量池中创建了一个"Apple"的实例
String s2 = "Mango"; // 字符串常量池中创建了一个" Mango"的实例
String s3 = "Apple"; // 直接使用常量池中的"Apple"实例
System.out.println(s1 == s3); // 输出结果为 true
System.out.println(s1 == s2); // 输出结果为 false
在上述示例中,通过双引号创建了两个值相同的字符串对象s1和s3。由于字符串常量池的存在,它们实际上引用的是同一个字符串对象,所以s1 == s3的比较结果为true。
字符串字面量是指直接在代码中使用的字符串值。在Java中,使用双引号括起来的字符序列即为字符串字面量。字符串字面量会被Java编译器自动放入字符串常量池中,以便进行重用。
字符串常量池是Java中存储字符串常量的内存区域,用于重用具有相同值的字符串对象。字符串字面量是直接在代码中使用的字符串值,它们会被自动放入字符串常量池中。通过重用字符串对象,字符串常量池提供了内存和性能优化的好处。
3.2.3 new String("Hello World!") 创建了几个对象
当使用new String("Hello World!")时,实际上会创建两个String对象:
首先,在堆内存中创建一个新的String对象作为传入的字符串参数,存储字符串的"Hello World!"值,并且存储在常量池中复用。
然后,使用new String构造器创建的第二个String对象。这个对象是独立的,存储在堆内存中,不会复用字符串常量池中的字符串。
所以,new String("Hello World!")操作实际上创建了两个String对象,两个都在内存堆中,其中作为参数的"Hello World!"缓存在字符串常量池。
这个例子也说明了使用双引号创建的字符串可以通过字符串常量池复用,具有更好的效率。
3.2.4 String对象不变性
String对象的不变性指的是一旦创建了String对象,其值无法被修改。换句话说,String对象是不可变的。任何对String对象的修改操作都不会改变原始的String对象,而是创建一个新的String对象来存储修改后的值。
String对象的不变性带来了许多好处:
- 线程安全性:由于String对象是不可变的,多个线程可以同时访问和共享相同的String对象,而无需担心并发修改导致的竞争条件和数据不一致性。
- 缓存利用:由于String对象的不可变性,Java可以对字符串进行缓存和重用。相同的字符串字面量(例如"Hello")在内存中只会存在一个实例,这可以节省内存并提高性能。
String对象的不可变性原理:
- String对象是通过final关键字修饰的,即不可被继承,避免了子类对其进行修改。
- 在创建String对象时,它的值被保存在内部的字符数组(char[])中(Java 11 以后更换为byte[]),并且该数组被声明为final和private,无法被外部修改。
- String提供的方法,如拼接、替换等,并不修改原始的String对象,而是创建一个新的String对象并返回。这种设计保证了原始String对象的不可变性。
总结:String对象的不可变性具有重要的意义和优势,包括线程安全、安全性、缓存和性能优化以及参数传递安全。通过final关键字和内部字符数组的不可修改性,String对象实现了不可变性。这种设计决策在Java中广泛应用于字符串的处理,带来了多方面的好处。
3.2.5字符串的底层数据结构
字符串的底层数据结构是一个字符数组或者字节数组,具体取决于Java的版本。
在Java 8及之前的版本中,字符串以字符数组的形式存储和操作,底层数据结构是一个字符数组(char[])。每个字符占据16位(2字节)的内存空间,并使用Unicode字符编码。
然而,从Java 9开始,引入了一种新的字符串实现方式,称为"Compact Strings"。这种实现方式使用字节数组(byte[])来存储字符串。"Compact Strings"在存储ASCII字符(字符代码在0-127之间)的字符串时,使用单字节存储;而在存储包含非ASCII字符的字符串时,采用双字节存储字符。
这种优化方式可以在存储大量ASCII字符的字符串时节省内存空间,并提高内存使用效率和性能。
需要注意的是,无论是使用字符数组还是字节数组来表示字符串,对外界来说,字符串的操作和使用方式是一致的。这些实现方式只是在内部存储和处理字符串时有所不同,旨在提供更好的内存利用和性能优化。
综上所述,字符串的底层数据结构可以是字符数组或字节数组,具体取决于Java的版本和字符串的内容。这些实现方式都旨在提供高效、可靠的字符串处理能力。
3.3 String的常用方法
3.3.1 String的常用方法
String类提供了许多常用的方法,用于处理和操作字符串。下面列出了一些常用的String方法:
- length():返回字符串的长度(字符个数)。
- charAt(int index):返回指定索引位置的字符。
- substring(int beginIndex):返回从指定索引开始到字符串末尾的子字符串。
- substring(int beginIndex, int endIndex):返回指定索引范围内的子字符串。
- concat(String str):将指定字符串连接到原始字符串的末尾。
- equals(Object obj):比较字符串是否与指定对象相等。
- equalsIgnoreCase(String str):比较字符串是否与指定字符串相等,忽略大小写。
- compareTo(String str):按字典顺序比较字符串。
- indexOf(String str):返回指定字符串在原始字符串中第一次出现的索引位置。
- lastIndexOf(String str):返回指定字符串在原始字符串中最后一次出现的索引位置。
- startsWith(String prefix):判断字符串是否以指定的前缀开头。
- endsWith(String suffix):判断字符串是否以指定的后缀结尾。
- toUpperCase():将字符串转换为大写字母形式。
- toLowerCase():将字符串转换为小写字母形式。
- trim():去除字符串两端的空格。
- replace(char oldChar, char newChar):替换字符串中的字符。
- split(String regex):将字符串按指定的正则表达式拆分为字符串数组。
- contains(CharSequence sequence):判断字符串是否包含指定的字符序列。
- isEmpty():判断字符串是否为空字符串。
- valueOf():将其他类型的数据转换为字符串。
这只是一小部分String类提供的方法,还有许多其他有用的方法可供使用。通过使用这些方法,可以对字符串进行截取、拼接、查找、替换等操作,以满足不同的字符串处理需求。
需要注意的是,由于字符串的不可变性,String类中的大多数方法并不会修改原始字符串,而是返回一个新的字符串对象。因此,在对字符串进行操作时,需要将返回的新字符串赋值给一个新的变量或原始变量以保存结果。
3.3.2【案例】charAt和length方法示例
charAt()和length()是String类提供的两个常用方法,用于处理字符串的字符和长度。
charAt(int index):该方法返回指定索引位置的字符。索引从0开始,表示字符串中的第一个字符。如果指定的索引超出了字符串的范围,将会抛出IndexOutOfBoundsException异常。
String str = "Hello, World!";
char firstChar = str.charAt(0); // 获取第一个字符 'H'
char lastChar = str.charAt(str.length() - 1); // 获取最后一个字符 '!'
在上述示例中,我们定义了一个字符串str,使用charAt()方法获取了第一个字符和最后一个字符。
length():该方法返回字符串的长度,即字符串中字符的个数。
String str = "Hello, World!";
int length = str.length(); // 获取字符串的长度,结果为 13
在上述示例中,我们定义了一个字符串str,使用length()方法获取了字符串的长度。
通过使用charAt()方法,我们可以获取字符串中特定索引位置的字符。而使用length()方法,我们可以获取字符串的长度,即字符串中字符的个数。这两个方法在字符串处理中非常常用,可以帮助我们对字符串进行具体的字符操作和长度计算。
下面是一个示例代码,演示了如何使用charAt()和length()方法的组合来遍历字符串中的每个字符:
String str = "Hello, World!";
for (int i = 0; i < str.length(); i++) {char c = str.charAt(i);System.out.println("Character at index " + i + ": " + c);
}
在上述示例中,我们首先创建了一个名为str的字符串,其值为"Hello, World!"。然后,我们使用一个for循环来遍历字符串中的每个字符。
3.3.3【案例】使用在线工具了解indexOf和lastIndexOf方法
当需要了解Java中的indexOf()和lastIndexOf()方法时,可以使用以下在线工具之一来获取相关信息:
- 官方手册:使用Java官方手册是一种常用的方式。您可以查阅Java官方文档,找到关于String类的相关部分。在官方手册中,您将找到关于indexOf()和lastIndexOf()方法的详细说明,包括方法的功能、参数和返回值。您还可以查看官方示例代码来了解这些方法的使用方式。
- ChatGPT:使用像ChatGPT这样的在线工具,您可以直接向它提问关于indexOf()和lastIndexOf()方法的问题。ChatGPT可以为您提供生成的信息和示例代码,以帮助您快速了解这两个方法的作用和用法。您可以向ChatGPT提出类似于以下问题的查询:" Java中的indexOf()方法的目的是什么?" 或 "你能提供使用lastIndexOf()方法的例子吗?"
- 搜索引擎:使用搜索引擎(如Baidu、Bing等)来搜索关于indexOf()和lastIndexOf()方法的在线文章、教程和博客。通过在搜索引擎中输入关键词,如 "Java indexOf 方法案例" 或 "Java lastIndexOf 方法教程",您将找到很多相关资源。这些资源通常包含了对这两个方法的解释、示例代码和用法说明。
通过使用这些在线工具,您可以获得关于indexOf()和lastIndexOf()方法的详细信息和示例代码。获得案例后,请务必进行模拟演练,您就能更好地了解这两个方法的功能和用法,并能在自己的代码中正确应用它们。
案例示意代码如下:
package api_01;
public class StringDemo2 {public static void main(String[] args) {// indexOfString str = "abcabcabc";int index1 = str.indexOf("b");System.out.println("index1="+index1); // index1=1// indexOf重载方法int index2 = str.indexOf("b",2); // 从下标为2的字符开始查找System.out.println("index2="+index2); // index2=4// lastIndexOfint index3 = str.lastIndexOf("b");System.out.println("index3="+index3); // index3=7// lastIndexOf重载方法int index4 = str.lastIndexOf("b",6); // 从下标为6的字符开始查找System.out.println("index4="+index4); // index4=4}
}
3.3.4【案例】replace方法的使用
定义类,测试String 的replace方法。
案例示意代码如下:
package api_01;
public class StringDemo3 {public static void main(String[] args) {String str1 = "abcabc";// replaceString str2 = str1.replace("abc", "A");System.out.println("str1="+str1); // str1=abcabcSystem.out.println("str2="+str2); // str2=AA// replaceFirstString str3 = str1.replaceFirst("abc", "A");System.out.println("str3="+str3); // str3=Aabc}
}
3.3.5【案例】startsWith和endsWith方法的使用
定义类,测试String 的 startsWith和endsWith方法,判断字符串是否以指定字符开始或结束。
案例示意代码如下:
package api_01;
public class StringDemo4 {public static void main(String[] args) {String str = "abc";// startsWithboolean flag1 = str.startsWith("a");System.out.println("flag1 = " + flag1); // flag1 = trueboolean flag2 = str.startsWith("ac");System.out.println("flag2 = " + flag2); // flag2 = false// endsWithboolean flag3 = str.endsWith("c");System.out.println("flag3 = " + flag3); // flag3 = trueboolean flag4 = str.endsWith("bc");System.out.println("flag4 = " + flag4); // flag4 = true}
}
3.3.6【案例】split和join方法的使用
定义类,测试String 的split和join方法,实现字符串的拆分和拼接。
案例示意代码如下:
package api_01;
public class StringDemo5 {public static void main(String[] args) {// splitString str = "id,name,age,gender";// {"id", "name", "age", "gender"}String[] columns = str.split(",");for(int i = 0; i < columns.length; i++) {System.out.println(columns[i]);}// {"id,name,age,gender"}String[] columns2 = str.split("#");System.out.println(columns2.length +" "+columns2[0]);// joinString[] words = {"Hello","World","Java"};String newWord = String.join(",", words);System.out.println(newWord); // Hello,World,Java}
}
3.3.7【案例】substring和trim方法的使用
定义类,测试String 的substring和trim方法。
案例示意代码如下:
package api_01;
public class StringDemo6 {public static void main(String[] args) {// subStringString name = "李雷";String lastName = name.substring(1);System.out.println("lastName="+lastName);// subString重载String id = "xxxxxx19860810xxxx";String birthDate = id.substring(6,14);System.out.println("birthDate="+birthDate);// trimString username = " Tom ";String trimmed = username.trim();System.out.println("username: " + username);System.out.println("trimmed: " + trimmed);}
}
3.3.8【案例】valueOf方法的使用
定义类,测试String 的svalueOf方法。
案例示意代码如下:
package api_01;
public class StringDemo6 {public static void main(String[] args) {int a = 123;String s1 = String.valueOf(a); //将int型变量a转换为String类型并赋值给s1System.out.println(s1); //123---字符串类型double b = 123.456;String s2 = String.valueOf(b); //将double型变量b转换为String类型并赋值给s2System.out.println(s2); //123.456---字符串类型String s3 = b+""; //任何类型与字符串相连,结果都变为字符串类型,效率低System.out.println(s3); //123.456---字符串类型}
}
3.3.9 字符串的比较
在Java中,字符串的相等比较是一个常见的操作,用于确定两个字符串是否相等。有两种常用的方法可以进行字符串的相等比较:
使用equals()方法:equals()方法用于比较两个字符串的内容是否相等。它比较字符串的每个字符是否一致,如果字符序列完全相同,则返回true,否则返回false。
String str1 = "Hello";
String str2 = "hello";
boolean isEqual = str1.equals(str2); // 比较两个字符串的内容是否相等
System.out.println(isEqual); // 输出结果为 false
使用equalsIgnoreCase()方法:equalsIgnoreCase()方法也用于比较两个字符串的内容是否相等,但不考虑字符的大小写。它会忽略字符的大小写差异,只比较字符序列是否相同。
String str1 = "Hello";
String str2 = "hello";
boolean isEqualIgnoreCase = str1.equalsIgnoreCase(str2); // 忽略大小写比较两个字符串的内容是否相等
System.out.println(isEqualIgnoreCase); // 输出结果为 true
使用运算符进行比较:可以使用==运算符或!=运算符直接比较两个字符串的引用是否相等。这种方式比较的是字符串对象在内存中的地址,而不是字符串的内容。
String str1 = "Hello";
String str2 = "Hello";
boolean isSameReference = str1 == str2; // 比较两个字符串的引用是否相等
System.out.println(isSameReference); // 输出结果为 true
需要注意的是,使用equals()方法进行内容比较是最常用的方式,因为它比较的是字符串的实际内容而不是引用。而使用运算符进行引用比较只适用于特定的场景。
3.3.10 说出String的5个常用API方法
以下是String类的五个常用API方法,不包含equals()方法:
- length():该方法返回字符串的长度,即字符串中字符的个数。使用方式:int length = str.length();
- charAt(int index):该方法返回指定索引位置的字符。索引从0开始,可以用于遍历字符串中的每个字符。使用方式:char ch = str.charAt(index);
- substring(int beginIndex, int endIndex):该方法返回指定索引范围内的子字符串。其中,beginIndex表示子字符串的起始索引(包括),endIndex表示子字符串的结束索引(不包括)。使用方式:String subStr = str.substring(beginIndex, endIndex);
- indexOf(String str):该方法用于查找指定字符串在原字符串中的索引位置。如果找到了指定字符串,则返回第一次出现的索引值;如果未找到,则返回-1。使用方式:int index = str.indexOf(subStr);
- toUpperCase():该方法将字符串中的所有字符转换为大写形式。使用方式:String upperCaseStr = str.toUpperCase();
这些方法在字符串的处理和操作中非常常用,能够帮助我们获取字符串的长度、访问指定位置的字符、提取子字符串、查找指定字符串的位置以及将字符串转换为大写形式。
3.3.11 字符串的"=="和equals()的区别是:
"=="运算符用于比较两个字符串对象的引用是否相同,即比较它们在内存中的存储地址。如果两个字符串对象的引用地址相同,则返回true;否则返回false。它不比较字符串对象的内容。
equals()方法用于比较两个字符串对象的内容是否相同。它比较字符串对象的每个字符是否相等,并返回比较结果的布尔值。如果两个字符串的内容相同,则返回true;否则返回false。
在字符串比较时,一般推荐使用equals()方法来比较字符串的内容是否相等。因为"=="比较的是引用地址,而equals()比较的是字符串的内容,更符合我们对字符串相等性的定义。
3.4 可变长度字符串
3.4.1【案例】字符串操作性能
在字符串拼接的过程中,特别是在大量拼接操作时,性能是一个需要考虑的因素。原因是字符串是不可变的,每次拼接操作都会创建一个新的字符串对象,导致内存的频繁分配和回收,影响性能和资源消耗。
例如,下面是一个案例来比较字符串拼接的性能:
int n = 10000; // 连接次数
// 使用String进行连接
long startTime = System.currentTimeMillis();
String result = "";
for (int i = 0; i < n; i++) {result += "a";
}
long endTime = System.currentTimeMillis();
long stringTime = endTime - startTime;
System.out.println("String concatenation time: " + stringTime + "ms");
输出结果可能是:String concatenation time: 13ms
对于计算机来说,在内存中处理数据的速度通常是纳秒级别的,然而链接10000个字符却需要大约13毫秒的时间,这是非常慢的操作!
这是由于每次使用+=操作符进行字符串拼接时,都会创建一个新的字符串对象。在循环中,每次拼接都会导致创建新的字符串对象和内存分配,这是一种低效的操作。当连接的次数增加时,内存分配和回收的开销也会增加,导致性能下降。
为了提升性能,可以使用StringBuilder或StringBuffer类来执行字符串拼接操作。这些类使用可变的字符序列,避免了频繁的对象创建和内存分配,从而提高了性能。
通过优化字符串拼接操作,可以显著提升程序的执行效率,并减少资源消耗。
3.4.2 StringBuilder
StringBuilder是Java中的一个类,用于高效地进行字符串操作。它属于可变字符串类,用于处理频繁的字符串拼接、插入和修改操作。与不可变的String类不同,StringBuilder允许对字符串进行原地修改,避免了频繁创建新的字符串对象,从而提升了性能。
以下是一些关于StringBuilder的重要特点和用法:
- 可变性:StringBuilder对象的值可以被修改。可以通过添加、插入、替换和删除字符来修改字符串的内容,而不会创建新的对象。这使得StringBuilder在需要频繁修改字符串的场景中非常有用。
- 高效的字符串拼接:通过append()方法,可以将字符串片段追加到StringBuilder对象的末尾。这种方式避免了使用+操作符进行字符串拼接时创建大量中间字符串的问题,从而提高了性能。
StringBuilder是一个灵活且高效的类,适用于需要频繁进行字符串操作的场景。它提供了可变性和高效的字符串拼接能力,可以帮助提升字符串处理的性能。通过使用StringBuilder,可以避免频繁的字符串对象创建和拷贝操作,从而提高程序的执行效率。
3.4.3【案例】使用StringBuilder提升字符串处理效率
使用StringBuilder处理字符串连接可以大大提升效率:
int n = 10000; // 连接次数
// 使用StringBuilder进行连接
startTime = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++) {sb.append("a");
}
result = sb.toString();
endTime = System.currentTimeMillis();
long stringBuilderTime = endTime - startTime;
System.out.println("StringBuilder concatenation time: " + stringBuilderTime + "ms");
输出结果大概是:StringBuilder concatenation time: 1ms
这个结果显然比使用String进行连接的操作快得多。
使用StringBuilder进行字符串拼接时,每次追加字符到内部的可变字符序列,避免了创建新的字符串对象和频繁的内存分配。相比之下,使用String进行字符串拼接每次都会创建新的字符串对象,导致内存的频繁分配和回收,性能较低。
因此,当需要频繁进行字符串拼接操作时,建议使用StringBuilder来提升字符串处理的效率。通过在可变字符序列中追加字符,最后再将结果转换为String类型,可以得到更高效的字符串拼接操作。
3.4.4 StringBuilder常用方法
首先需要创建StringBuilder对象:
public StringBuilder(): 创建一个空的StringBuilder对象。
public StringBuilder(String str): 使用指定的字符串初始化StringBuilder对象。
StringBuilder的常用方法有:
append(): 在尾部追加内容。
delete(): 删除指定范围内的内容。
replace(): 替换指定范围内的内容。
insert(): 在指定位置插入内容。
reverse(): 反转字符串。
StringBuilder的许多方法返回值都是StringBuilder类型。由于方法返回了对象本身的引用,可以通过链式调用方式简洁地书写代码:
StringBuilder buf = new StringBuilder();
buf.append("ibm").append("java").insert(3, "oracle").replace(9, 13, "JAVA");
System.out.println(buf.toString());
通过链式调用,可以连续执行多个方法操作,从而实现对字符串的快速处理。最后通过toString()方法获取最终的字符串结果,并进行输出。
3.4.5【案例】StringBuilder方法
定义类,在main方法中添加代码,使用StringBuilder操作字符串。
案例示意代码如下:
package api_01;
public class StringBuilderDemo2 {public static void main(String[] args) {String str = "好好学习Java";//复制str的内容到builder中--------好好学习JavaStringBuilder builder = new StringBuilder(str);//append():追加内容-----在末尾追加builder.append(",为了找个好工作");System.out.println(builder); //好好学习Java,为了找个好工作//replace():替换部分内容(含头不含尾)//将下标为9到15的内容替换为---就是为了改变世界builder.replace(9,16,"就是为了改变世界");System.out.println(builder); //好好学习Java,就是为了改变世界//delete():删除部分内容(含头不含尾)builder.delete(0,8); //删除下标为0到7的System.out.println(builder); //,就是为了改变世界//insert():插入内容builder.insert(0,"活着"); //在下标为0的位置插入活着System.out.println(builder); //活着,就是为了改变世界}
}
在上述示例中,通过StringBuilder对象对字符串进行追加、替换、删除和插入操作。使用append()方法在末尾追加内容,replace()方法替换指定范围内的内容,delete()方法删除指定范围内的内容,insert()方法在指定位置插入内容。每次操作后,通过println()方法输出结果。
通过使用StringBuilder的各种方法,可以方便地对字符串进行修改和操作,而无需创建新的字符串对象。这种可变性和灵活性使得StringBuilder在处理字符串时非常有用。
3.4.6 String和StringBuilder区别
String和StringBuilder是Java中用于处理字符串的两个类,它们在以下几个方面有所区别:
可变性:String是不可变类,即一旦创建,其值不可更改。每次对String进行修改时,都会创建一个新的String对象。而StringBuilder是可变类,允许对字符串进行原地修改,避免了频繁创建新的对象。
性能:由于String的不可变性,每次对String进行拼接、替换或删除操作时,都会创建新的String对象,导致内存的频繁分配和回收,性能较低。而StringBuilder允许原地修改字符串,避免了这种性能损耗,在大量字符串操作时通常比String效率更高。
使用场景:String适用于不需要频繁修改字符串内容的情况,例如字符串的比较、提取子串等。StringBuilder适用于需要频繁进行字符串拼接、插入和修改的情况,例如动态生成字符串、字符串的动态拼接等。
总结起来,String适用于不需要修改的字符串操作,而StringBuilder适用于需要频繁修改字符串的操作,根据具体的需求选择合适的类可以提高代码的性能和效率。
4 正则表达式
4.1 正则表达式概述
4.1.1 什么是正则表达式
正则表达式是指定文本搜索模式的字符序列,也就是能够描述字符串的格式。
正则表达式是强大而灵活的文本处理工具。利用正则表达式,我们可以通过编程的方式,构建复杂的文本模式,从而在输入的字符串中进行查找。一旦发现了这些匹配的模式,就可以进行相应的处理。它提供了一种紧凑且动态的语言,可以用完全通用的方式来解决字符串处理、匹配和选择,以及编辑、验证等各种问题。
通过使用正则表达式,可以实现以下功能:
- 匹配:确定一个字符串是否与某个模式匹配。
- 搜索:查找符合特定模式的字符串。
- 替换:将符合特定模式的字符串替换为指定的内容。
- 提取:从文本中提取符合特定模式的部分。
正则表达式在文本处理、数据验证、搜索引擎、文本编辑器等领域都得到广泛应用。它提供了一种灵活且强大的方式来处理和操作字符串,使得字符串处理任务更加便捷和高效。
比如:
4.1.2 正则表达式的语法
1、[ ]:表示一个字符,该字符可以是[ ]中指定的内容一个字符。规则如下:
2、预定义字符。规则如下:
3、数量词。规则如下:
4、分组“( )”:将括号内的内容看作是一个整体。
分组时,可使用“|”表示“或”关系。比如:
- (abc){3} :表示abc整体出现3次。可以匹配abcabcabc,但是不能匹配aaa或abcabc
- (abc|def){3}:表示abc或def整体出现3次。可以匹配: abcabcabc 或 defdefdef 或 abcdefabc,但是不能匹配abcdef 或abcdfbdef
5、边界匹配: “ ^”和“ $”
边界匹配规则中:
- ^ 代表字符串开始
- $ 代表字符串结束
例如,需要匹配用户名规则 – 从头到尾连续8~10个单词字符:
- \w { 8,10 } : “abcd1234_abcd”是可以验证通过的
- ^\w{ 8,10 }$:有从头到尾整体的限定, “abcd1234_abcd”验证不能通过
4.1.3 正则表达式示例
为了更好的理解正则表达式的语法,查看一个示例:验证邮政编码,其规则为“6位数字”。正确的正则表达式如下:
如果需要检索手机号码,形如: +86 13838389438。其规则为:
- +86 可有可无
- +86与后面的号码之间空格可以没有或者有多个
- 电话号码为11位数字
正则表达式如下:
4.1.4使用ChatGPT帮助撰写正则表达式
当使用ChatGPT帮助撰写正则表达式时,可以按照以下步骤进行:
1. 描述要匹配的模式:首先,描述你希望匹配的字符串模式。例如,你想匹配以大写字母开头,后跟一些小写字母和数字的单词。
2. 请求ChatGPT生成正则表达式:向ChatGPT提出请求,让它生成一个正则表达式来匹配描述的模式。你可以提问如下:
"请生成一个正则表达式,用于匹配以大写字母开头,后跟小写字母和数字的单词。"
"生成一个用于匹配电话号码的正则表达式。"
"请提供一个匹配电子邮件地址的正则表达式。"
3. 测试正则表达式:使用生成的正则表达式在测试工具中测试它是否能够正确匹配你期望的字符串。检查是否匹配预期的模式,并排除可能的错误。
4. 进一步优化:如果生成的正则表达式无法满足你的需求,你可以通过与ChatGPT的进一步交互来优化它。
使用ChatGPT作为辅助工具可以帮助你生成和优化正则表达式,提供创造性和多样化的解决方案。同时,确保对生成的正则表达式进行测试和验证,以确保它能够正确地匹配所需的字符串模式。
4.2 正则表达式API
4.2.1 String正则API
String类中有多个方法支持正则表达式,分别是:
- matches: 判断当前字符串是否与指定的正则表达式匹配
- replaceFirst/replaceAll:替换当前字符串中与正则表达式匹配的内容
- split:基于正则表达式匹配的内容将当前字符串拆分成字符串数组
4.2.2 matches方法及示例
matches 方法需要一个正则表达式作为参数,将一个字符串与正则表达式进行匹配:如果匹配成功就返回true,否则返回false。
查看如下代码,验证字符串是否为合格的邮箱地址:
package api_01;
public class RegexDemo1 {public static void main(String[] args) {String email = "abc@tedu.cn";/** [a-zA-Z0-9_]+@[a-zA-Z0-9]+(\.[a-zA-Z]+)+*/String regex = "[a-zA-Z0-9_]+@[a-zA-Z0-9]+(\\.[a-zA-Z]+)+";boolean match = email.matches(regex);if(match) {System.out.println("是邮箱");}else {System.out.println("不是邮箱");}}
}
4.2.3 split 方法及示例
split 方法的语法如下:
String[ ] split( String regex )
将字符串按照特定的分隔符拆分成字符串数组,其中,参数regex为正则表达式。
查看如下代码,获取字符串中的数字串:
package api_01;
import java.util.Arrays;
public class RegexDemo2 {public static void main(String[] args) {String str = "abc123def456ghi111";/** split方法如果连续匹配到了拆分的内容则* 在中间会拆分出空字符串.但是如果是字符串* 末尾连续匹配,则所有空字符串都会被忽略*/String[] arr = str.split("[0-9]+");System.out.println(arr.length);System.out.println(Arrays.toString(arr));}
}
4.2.4 replaceAll方法及示例
replaceAll方法的语法如下:
String replaceAll (String regex, String replacement)
将字符串中匹配正则表达式regex的字符串替换成replacement。
查看如下代码,将字符串中的数字部分替换成其他文本:
package api_01;
public class RegexDemo3 {public static void main(String[] args) {String s1 = "abc123def456ghi";/** 将数字部分替换为#NUMBER#*/String s2 = s1.replaceAll("[0-9]+", "#NUMBER#");System.out.println(s2);/** 将第一个数字部分替换为#NUMBER#*/String s3 = s1.replaceFirst("[0-9]+", "#NUMBER#");System.out.println(s3);}
}