- 字符串相关的类
- jdk8之前的日期时间API
- jdk8新增日期时间API
- Java比较器
- System类
- Math类
- BigInteger与BigDecimal
一. 字符串相关的类
1.1 String类
- String类:代表字符串。Java程序中的所有字符串字面值(如"abc")都作为此类的实例实现
- String是一个final类,代表不可变的字符序列。
- 字符串是常量,用双引号引起来表示,其值在创建后不可更改。
- String对象的字符内容是存储在一个字符数组value[]中的。
/*
String的使用
*/
public class StringTest {
/*
String: 字符串,使用一对""引起来表示
1. String声明为final的,不可被继承
2. String实现了Serializable接口:表示字符串是支持序列化的
实现了Comparable接口: 表示String可以比较大小
3. String内部定义了final char[] value 用于存储字符串数据
4. String:代表不可变的字符序列:简称, 不可变性
体现:①当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value赋值
②当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值
③当调用String的replace()方法修改指定字符或字符串时,也需要重新制定内存区域赋值
5. 通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中
6. 字符串常量池中是不会存储相同的字符串
*/
@Test
public void test1(){
String s1 = "abc"; // 字面量 只有它类直接赋值定义 在栈里
String s2 = "abc"; // 在栈里 二者地址都在方法区常量池同一地址
s1 = "hello"; // 并不是改变之前的值,而是常量池中重新开辟空间,新建一个,地址给s1
System.out.println(s1 == s2); //true
System.out.println(s1); // hello
System.out.println(s2); // abc
System.out.println("********************");
String s3 = "abc";
s3 += "def"; // 也不能在原有字符串拼接新的串
System.out.println(s3); // abcdef
System.out.println(s2); //abc
System.out.println("********************");
String s4 = "abc";
String s5 = s4.replace('a', 'm');
System.out.println(s4); // abc
System.out.println(s5); // mbc
}
}
1.2 String不同实例化方式的对比
- 字面量方式(在常量池中)
String str = "hello";
- new对象的方式(在堆中)
//本质上this.value = new char[0]
String s1 = new String();
//本质上this.value = original.value
String s2 = new String(String.original);
//this.value = original.value
String s3 = new String(char[] a);
//this.value = Arrays.copyOf(value, value.length)
String s4 = new String(char[] a,int startIndex, int count);
问: String str1 = “abc”; 与 String str2 = new String(“abc”); 的区别?
- 字符串常量存储在字符串常量池中,目的是共享
- 字符串非常量对象存储在堆中(堆中地址指向常量池)
/*
String的使用
*/
public class StringTest {
/*
String的实例化方式
一:通过字面量
二.通过new + 构造器方式
*/
@Test
public void test2() {
String s1 = "javaEE";
String s2 = "javaEE";//此时的s1和s2的数据生命在方法区中的字符串常量池中
String s3 = new String("javaEE"); //堆空间中的地址值给了s3,s4
String s4 = new String("javaEE");
System.out.println(s1 == s2); //true
System.out.println(s1 == s3); //false
System.out.println(s2 == s3); // false
System.out.println(s3 == s4); //false 新的对象
}
}
面试题: String s = new String("abc")创建对象,内存中创建了几个对象
两个:一个是堆空间中的new结构,另一个是char[]对应的常量池中的数据:"abc"
public class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(){
}
}
...
Person p1 = new Person("Tom", 12);
Person p2 = new Person("Tom", 12);
System.out.println(p1.name.equals(p2.name)); //true equals重写,比较内容
System.out.println(p1.name == p2.name); //true name是字面量创建,比较,地址值是一致的
p1.name = "jerry";
System.out.println(p2.name); // Tom 不可变性
...
1.3 String不同拼接操作的对比
/*
字面量赋值及字面量拼接时,内容一样在常量池中地址也一样,true
有变量名参与赋值及拼接时,都在堆空间中开辟,所以比较地址为false
拼接的结果只要调用了intern()方法,就会强制要求内容在常量池中声明。返回值就在常量池中
*/
@Test
public void test3(){
String s1 = "javaEE";
String s2 = "hadoop";
String s3 = "javaEEhadoop"; // 字面量
String s4 = "javaEE" + "hadoop"; //字面量
String s5 = s1 + "hadoop";
String s6 = "javaEE" + s2;
String s7 = s1 + s2;
//看地址
System.out.println(s3 == s4); //true
System.out.println(s3 == s5); // false
System.out.println(s3 == s6); //false
System.out.println(s5 == s6); //false
System.out.println(s3 == s7); //false
System.out.println(s5 == s7); //false
System.out.println(s6 == s7); //false
String s8 = s5.intern(); //此时返回得到的s8在常量池中已经存在的"javaEEhadoop"
System.out.println(s3 == s8); // true 两个地址一样
}
1.4 面试题
/**
* 面试题:
* Created by caiyunlai on 2021/4/15
*/
public class StringTest01 {
String str = new String("good");
char[] ch = {'t', 'e', 's', 't'};
public void change(String str, char[] ch){
str = "test ok";
ch[0] = 'b';
}
public static void main(String[] args) {
StringTest01 ex = new StringTest01();
ex.change(ex.str, ex.ch);
System.out.println(ex.str + " and "); // good and 不可变性
System.out.println(ex.ch); // best
}
}
1.5 jvm中涉及字符串的内存结构
JVM运行时数据区:
堆 和 方法区并列
Heap 堆
一个JVM实例只存在一个堆内存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、长变量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行,堆内存分为三部分:
- 新生区 Young (Young Generation Space)
- 养老区 Old (Tenure generation space)
- 永久存储区 Perm (Permanent Space)
永久区(方法区)是非堆,和堆分开
1.6 String 的常用方法1
public class StringMethodTest {
/*
int length(): 返回字符串的长度:return value.length
char charAt(int index):返回某索引处的字符return value[index]
boolean isEmpty():判断是否是空字符串:return value.length == 0
String toLowerCase():使用默认语言环境,将String中的所有字符串转换为小写
String toUpperCase():使用默认语言环境,将String中的所有字符转换为大写
String trim():返回字符串的副本,忽略前导和尾部空白
String concat(String str):将指定字符串连接到此字符串的结尾。等价于用“+”
boolean equals(Object obj):比较字符串的内容是否相同
boolean equalsIgnoreCase(String anotherString):忽略大小写,与上一方法类似
int compareTo(String anotherString):比较两个字符串的大小
String substring(int beginIndex):返回一个新的字符串,它是此字符串的从beginIndex开始直到末尾截取的一段
String substring(int beginIndex, int endIndex):返回一个新的字符串,它是此字符串的从beginIndex开始到endIndex结束截取的一段
*/
@Test
public void tes1(){
String s1 = "helloWORLD";
System.out.println(s1.length()); // 10
System.out.println(s1.charAt(5)); // w
System.out.println(s1.isEmpty()); // false
System.out.println(s1.toUpperCase()); // HELLOWORLD
System.out.println(s1.toLowerCase()); // helloworld
String s2 = " he llo wor ld";
String s3 = s2.trim();
System.out.println("----" + s2 + "----"); // ---- he llo wor ld----
System.out.println("---" + s3 + "----"); // ---he llo wor ld----
}
@Test
public void tes2(){
String s1 = "HELLO world";
String s2 = "hello World";
System.out.println(s1.equals(s2)); // false
System.out.println(s1.equalsIgnoreCase(s2)); // true
String s3 = "abcd";
String s4 = s3.concat("defg");
System.out.println(s4); // abcddefg
String s5 = "abc";
String s6 = new String("abd");
String s7 = new String("abf");
System.out.println(s5.compareTo(s6)); // -1
System.out.println(s5.compareTo(s7)); // -3
String s8 = "北京尚硅谷教育";
String s9 = s8.substring(2); // 尚硅谷教育
System.out.println(s9);
String s10 = s8.substring(2, 5); // 尚硅谷 左闭右开
System.out.println(s10);
}
}
1.7 String的常用方法2
/*
boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束
boolean startsWith(String prefix):测试此字符串是否以指定的前缀开始
boolean startsWith(String prefix, int toffset):测试此字符串从指定索引开始的
子字符串是否以指定前缀开始
boolean contains(CharSequence s):当且仅当此字符串包含指定的 char 值序列
时,返回 true
int indexOf(String str):返回指定子字符串在此字符串中第一次出现处的索引
int indexOf(String str, int fromIndex):返回指定子字符串在此字符串中第一次出
现处的索引,从指定的索引开始
int lastIndexOf(String str):返回指定子字符串在此字符串中最右边出现处的索引
int lastIndexOf(String str, int fromIndex):返回指定子字符串在此字符串中最后
一次出现处的索引,从指定的索引开始反向搜索
注:indexOf和lastIndexOf方法如果未找到都是返回-1
*/
@Test
public void test2(){
String str1 = "hello world";
boolean b1 = str1.endsWith("ld"); // 任意几个字符结尾
System.out.println(b1); // true
System.out.println(str1.startsWith("He")); //false 区分大小写
System.out.println(str1.startsWith("ll", 2)); // true 从第二个位置开始
String str2 = "wo";
System.out.println(str1.contains(str2)); // true 区分大小写
String str3 = "l";
String str4 = "lol";
System.out.println(str1.indexOf(str3)); // 2
System.out.println(str1.indexOf(str4)); // -1 不存在
System.out.println(str1.indexOf(str3, 6)); // 9
System.out.println(str1.lastIndexOf(str3)); // 9 反向搜索,但还是正向索引
System.out.println(str1.lastIndexOf(str3, 6)); // 3
}
什么情况下,indexOf和lastIndexOf(str)返回值相同?
答:只存在一个或0个时。
1.8 String的常用方法3
/*
String replace(char oldChar, char newChar):返回一个新的字符串,它是
通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。
String replace(CharSequence target, CharSequence replacement):使
用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。
String replaceAll(String regex, String replacement) : 使 用 给 定 的
replacement 替换此字符串所有匹配给定的正则表达式的子字符串。
String replaceFirst(String regex, String replacement) : 使 用 给 定 的
replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。
boolean matches(String regex):告知此字符串是否匹配给定的正则表达式。
String[] split(String regex):根据给定正则表达式的匹配拆分此字符串。
String[] split(String regex, int limit):根据匹配给定的正则表达式来拆分此
字符串,最多不超过limit个,如果超过了,剩下的全部都放到最后一个元素中。
*/
@Test
public void test3(){
// 替换
String s1 = "北京尚硅谷教育";
String s2 = s1.replace('北', '东');
System.out.println(s1);
System.out.println(s2);
String s3 = s1.replace("北京", "上海");
System.out.println(s3); // 上海尚硅谷教育
System.out.println("============================");
String str = "12hello34world5java7891mysql456";
//把字符串中的数字替换成,,如果结果中开头和结尾有,的话去掉
String string = str.replaceAll("\\d+", ",").replaceAll("^,|,$", "");
System.out.println(string);
// 匹配
String str4 = "12345";
//判断str字符串中是否全部有数字组成,即有1-n个数字组成
boolean matches = str4.matches("\\d+");
System.out.println(matches); // true
String tel = "0571-4534289";
//判断这是否是一个杭州的固定电话
boolean result = tel.matches("0571-\\d{7,8}");
System.out.println(result); // true
// 切片
String str5 = "hello|world|java";
String[] str6 = str5.split("\\|");
for (int i = 0; i < str6.length; i++) {
System.out.println(str6[i]);
}
System.out.println();
String str7 = "hello.world.java";
String[] strs7 = str7.split("\\.");
for (int i = 0; i < strs7.length; i++) {
System.out.println(strs7[i]);
}
}
1.9 回顾String与基本数据类型、包装类的转换
/*
涉及到String类与其他结构之间的转换
*/
public class StringChangeTest {
/*
String --> 基本数据类型、包装类:调用包装类的静态方法:parseXxx(str)
String <-- 基本数据类型、包装类:调用String重载的valueOf(xxx)或连接符
*/
@Test
public void test(){
String s1 = "123";
int num = Integer.parseInt(s1);
String s2 = String.valueOf(num); // "123"
String s3 = num + "";
System.out.println(s1 == s3); // false 有变量参与就在堆中
}
1.10 String与char[]之间的转换
/*
String与字符数组转换
String --> char[] : 调用toCharArray()
char[] --> String:调用String的构造器
*/
@Test
public void tes2(){
String s1 = "abc123"; // 题目: a21cb3
char[] charArray = s1.toCharArray();
for (int i = 0; i < charArray.length; i++) {
System.out.println(charArray[i]);
}
char[] arr = new char[]{'h', 'e', 'l', 'l', 'o'};
String s2 = new String(arr);
System.out.println(s2); // hello
}
1.11 String与byte[]之间的转换
/*
String 与 byte[]字节数组的转换
String --> byte[]:调用String的getBytes()
byte[] --> String:调用String的构造器
编码: 字符串String --> 字节byte (二进制数据)
解码: 字节byte --> 字符串String
*/
@Test
public void test3() throws UnsupportedEncodingException {
String s1 = "abc123";
byte[] bytes = s1.getBytes();
System.out.println(Arrays.toString(bytes)); // ascii值
// [97, 98, 99, 49, 50, 51]
String s2 = "abc123中国";
byte[] bytes2 = s2.getBytes(); // 汉字占3个字节 默认字符集utf-8进行编码
System.out.println(Arrays.toString(bytes2)); // ascii值
// [97, 98, 99, 49, 50, 51, -28, -72, -83, -27, -101, -67]
byte[] gbks = s2.getBytes("gbk");
System.out.println(Arrays.toString(gbks)); // 使用gbk字符集编码,每个汉字占两个字节
// [97, 98, 99, 49, 50, 51, -42, -48, -71, -6]
// 解码,调用String构造器 编码解码必须同种字符集
String s3 = new String(bytes); // 解码
System.out.println(s3);
}
1.12 解决一个拼接问题
@Test
public void test4(){
String s1 = "javaEE";
String s2 = "hadoop";
final String s4 = "javaEE";
String s5 = s4 + "hadoop";
System.out.println(s1 == s5); // true
}
结果是true,因为final修饰变为常量。
1.13 面试中String算法考察的说明
- 模拟一个trim方法,去除字符串两端的空格
// 1.模拟一个trim方法,去除字符串两端的空格
// 思路:先转为字符数组,遍历找出非空格字符组成新的数组;最后将新数组转为字符串
String s1 = " javaEE ";
char[] arr = s1.toCharArray();
String s2 = "";
for (int i = 0; i < arr.length; i++) {
if (arr[i] != ' '){
s2 += arr[i];
}
}
System.out.println("---" + s1 + "---"); // --- javaEE ---
System.out.println("---" + s2 + "---"); // ---javaEE---
-
建一个字符串进行翻转,将字符串中指定部分反转,比如"abcdefg"反转为"abfedcg"
-
获取一个字符串在另一个字符串中出现的次数
-
获取两个字符串中最大相同子串。提示:将短的那个串进行长度依次递减的字串与较长的串比较
-
对字符串中字符进行自然顺序排序。
提示:字符串变成字符数组;对数组排序,选择,冒泡,Arrays.sort();将排序后的数组变成字符串
二. StringBuffer和StringBuilder
可变的字符序列。
/*
关于StringBuffer和StringBuilder的使用
*/
public class StringBufferBuilderTest {
/*
String、StringBuffer、StringBuilder的异同
String:不可变的字符序列:底层结构使用char[]存储,但加了final修饰,故不可变
StringBuffer:可变的字符序列:线程安全的,效率偏低(方法都是同步方法)底层结构使用char[]存储
StringBuilder:可变的字符序列:线程不安全,效率高一些,jdk5.0新增 底层结构使用char[]存储,数组长度可变
源码分析:
String str = new String(); // new char[0]
String str1 = new String("abc"); // new char[]{'a', 'b', 'c'};
StringBuffer sb1 = new StringBuffer(); //new char[16]; 底层创建了一个长度是16的数组。
sb1.append('a'); //value[0] = 'a';
sb1.append('b'); // value[1] = 'b';
StringBuffer sb2 = new StringBuffer("abc"); //char[] value = new char["abc".length() + 16)]
// 问题1:System.out.println(sb2.length()); // 3
// 问题2:扩容问题:如果要添加的数据底层数组盛不下了,就需要扩容。
默认情况下,扩容为原来容量的2倍 + 2,同时将原来数组中的元素复制到新的数组中。
指导意义:开发中建议大家使用StringBuffer(int capacity)带参数的构造器,定长数组。避免出现扩容问题,复制数组效率降低。
*/
@Test
public void test1(){
StringBuffer sb1 = new StringBuffer("abc");
sb1.setCharAt(0, 'm');
System.out.println(sb1); // mbc
StringBuffer sb2 = new StringBuffer();
System.out.println(sb2.length()); // 0 返回的是字符计数
}
}
2.2 StringBuffer中的常用方法
StringBuffer append(xxx):提供了很多的append()方法,用于进行字符串拼接
StringBuffer delete(int start,int end):删除指定位置的内容
StringBuffer replace(int start, int end, String str):把[start,end)位置替换为str
StringBuffer insert(int offset, xxx):在指定位置插入xxx
StringBuffer reverse() :把当前字符序列逆转
public int indexOf(String str)
public String substring(int start,int end)
public int length()
public char charAt(int n )
public void setCharAt(int n ,char ch)
/*
StringBuffer append(xxx):提供了很多的append()方法,用于进行字符串拼接
StringBuffer delete(int start,int end):删除指定位置的内容
StringBuffer replace(int start, int end, String str):把[start,end)位置替换为str
StringBuffer insert(int offset, xxx):在指定位置插入xxx
StringBuffer reverse() :把当前字符序列逆转
public int indexOf(String str)
public String substring(int start,int end) 截取子字符串,新的,原先字符串不变
public int length()
public char charAt(int n )
public void setCharAt(int n ,char ch) 指定位置字符替换
总结:
增: append(xxx) 方法链原理可以不断.append
删: delete
改: setCharAt
查: charAt
长度:length()
遍历:for + charAt() / toString()
*/
@Test
public void test2(){
StringBuffer s1 = new StringBuffer("abc");
s1.append(1);
s1.append('1');
System.out.println(s1); // abc11
//s1.delete(2, 4); // 删除c1 左闭右开
//s1.replace(2, 4, "hello");
//System.out.println(s1); // abhello1
//s1.insert(2, false);
//System.out.println(s1); // abfalsec11 在2位置处插入字符串
//System.out.println(s1.length()); // 10
System.out.println(s1.reverse()); // 11cba
}
2.3 String、StringBuffer、StringBuilder效率对比
@Test
public void test3(){
//初始设置
long startTime = 0L;
long endTime = 0L;
String text = "";
StringBuffer buffer = new StringBuffer("");
StringBuilder builder = new StringBuilder("");
//开始对比
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
buffer.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuffer的执行时间:" + (endTime - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
builder.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuilder的执行时间:" + (endTime - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
text = text + i;
}
endTime = System.currentTimeMillis();
System.out.println("String的执行时间:" + (endTime - startTime));
}
StringBuffer的执行时间:5
StringBuilder的执行时间:2
String的执行时间:219
StringBuilder > StringBuffer > String
常用类第二部分——>:
常用类(2)时间相关的类等