Java常用类
1. String 类
-
String类是
final类, 代表不可变字符序列,无法被继承 , 不可变性 -
String类对象是常量,创建后不能修改
-
String类实现了
Serializable接口, 支持 序列化 -
String类实现了
Comparable接口, 支持 比较大小 -
String类定义了
final char[] value数组, 存储 字符串数据

-
字符串常量池中不会存储相同内容(使用String 类的 equal() 方法返回true)
1.1 不可变性
String的不可变性
通过
字面量方式,给字符串赋值,此时此字符串值 声明在常量池中字符串常量池中,不会存储相同内容的常量
当对字符串重新赋值时,需要
重新指定存储区域赋值,不能使用原有value进行赋值当对字符串改变值时 ,需要
重新指定存储区域赋值,不能在原有value进行改变
只要对字符串内容进行任何的修改,都要重新建一个对象,不能直接改变。


1.2 String对象创建
//1. 方式1 字面量
String str = "Hello";
//2. 方式2 new 对象
//2.1 this.value = new char[0];
String s1 = new String();
//2.2 this.value = original.value;
String s2 = new String(String original);
//2.3 this.value = Arrays.copyOf(value,value.length)
String s3 = new String(char[] a);
String s4 = new String(char[] , int startIndex,int count)
创建String对象的两种方式解读:
String str = "abc";声明在方法区的字符串常量池中
String str = new String("abc");str中保存的地址值,是字符串在堆空间中开辟的空间 的 对应的地址值
public void test(){
String s1 = "abc";
String s2 = "abc";
String s3 = new String("abc");
String s4 = new String("abc");
System.out.println(s1 == s2); //true
System.out.println(s1 == s3);//false
System.out.println(s1 == s4);//false
System.out.println(s3 == s4);//false
}
🔴面试题
String s = new String("abc");方式创建对象,在内存中会创建几个对象
一个或者两个
如果 常量池中没有字面量存在, 就要现在常量池中创建一个常量对象,之后在堆中new创建一个字符串对象。
如果 常量池中 有 字面量存在, 就只要在堆中开辟一个空间,创建一个字符串对象,然后将 值 赋值进去。

注意: 此处是 new Person("字符串",) 和直接 new String("字符串") 不一样
直接 new String("字符串") 如上图, 两个地址是不一样的
但是下图是直接使用的常量池中的字符串, 地址是一样的, 所有使用 == 比较 ,两个地址是相同的

1.3 拼接字符串
public void test(){
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 使用的是,常量池中已经存在的字符串
//intern() 方式是直接去常量池中找 与 s5内容相同的字符串字面量了
System.out.println(s3 == s8);//true 最终返回的是字面量和字面量比较结果
}


结论
常量与常量拼接结果在常量池中,且常量池中不会存在相同内容的常量
只要其中有一个是变量,结果就在堆中
如果拼接结果调用
intern()方法,返回值就在常量池中
🔴 面试题
public class StringTest{
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){
StringTest ex = new StringTest();
ex.change(ex.str,ex.ch);
System.out.print(ex.str);// good String 不可变
System.out.print(ex.ch);//best
}
}
1.4常用方法


字符串与char[] 数组的转换
stringname.toCharArray : 将字符串转换为char数组
String(char[]) : string构造器 将字符数组转换为 字符串
字符串与字节数组转换
String.getBytes([charsetName]): 字符串转为字节数组 编码
new String(byte[] bytes,[charsetName]): 字节数组 转为字符串 解码
编码 : 字符串 ---> 字节
节码: 字节 ---> 字符串
编码与解码时 使用的字符集都要一样, 不然会出现乱码

🔴 面试题:
StringBuffer和StringBuilder和String三者的异同
String:
不可变的字符序列
使用
char[]存储
StringBuffer: 存在多线程操作 ,涉及线程安全问题使用
可变的字符序列
使用
char[]存储
线程安全效率偏低
StringBuilder: JDK 5.0 新增
可变的字符序列
线程不安全效率较高
-
问题1: 长度问题
-
sb.length()StringBuffer长度还是原来的字符的数量,不会把后面的16算进去的
-
-
问题2: 扩容问题
-
默认情况下 扩充后长度:
2*len + 2,原有数组元素复制到新的数组 (len是原来长度)
-
-
开发建议 : 使用
StringBuffer(int capacity) / StringBuilder(int capacity)指定容量 避免扩容
| 可变与不可变 | 底层存储结构 | 线程安全 | 效率 | |
|---|---|---|---|---|
| String | 不可变 | char[] | 安全(不可变) | 低 |
| StringBuffer(jdk 1.0) | 可变 | char[] | 安全 (同步方法) | 中 |
| StringBuilder(jdk5.0新增) | 可变 | char[] | 不安全 | 高 |
源码分析 :
String str = new String();//new char[0]
String str1 = new String("abc");//new char[]{'a','b','c'};
//char[] value = super(capacity:16); new char[16] 底层创建了一个长度16的数组
StringBuffer sb1 = new StringBuffer();
sb1.append('a');//value[0] = 'a';
sb1.apppend('b');//value[1] = 'b';
//char[] value = new char["abc".length() + 16] 每次造完都会额外的空出16个位置
StringBuffer sb2 = new StringBuffer("abc");
//问题1: 长度问题
sb2.length();// StringBuffer长度还是原来的字符的数量,不会把后面的16算进去的
//问题2: 扩容问题
//默认情况下 扩充后长度: 2*len + 2 ,原有数组元素复制到新的数组 (len是原来长度)
//开发建议 : 使用 StringBuffer(int capacity) / StringBuilder(int capacity) 指定容量 避免扩容
StringBuffer类的常用方法

1.5 String StringBuffer StringBuilder 转换
String ---> StringBuffer,StringBuilder
转换方法: 调用 两者的构造器
StringBuffer/StringBuilder sb = StringBuffer/StringBuilder(String str);//调用构造器
StringBuffer,StringBuilder ---> String
String str = String(StringBuffer buffer)//还是调用构造器
toString(); 方法
字符串常量池存放位置
jdk 1.6 :方法区(永久区)
jdk 1.7: 堆空间
jdk 1.8 方法区(元空间)
2. 日期时间类
JDK8 之前的日期时间类
System 类中的
currentTimeMillis()java.util.Date 和子类 java.sql.Date
SimpleDateFormat : 对日期Date类的格式化与解析
Calendar
2.1 Date类
JDK8之前的日期和时间API
-
System.currentTimeMillis()毫秒数 时间戳 -
java.util.Date类-
两个构造器
-
Date()创建当前时间的date对象 -
Date(long date)创建指定毫秒时间的date对象
-
-
两个方法
-
toString(): 显示 当前年, 月, 日 , 时 分 秒 -
getTime(): 得到毫秒数 和 时间戳一样
-
-
java.sql.Date类 (子类)-
构造器java.sql.Date date3 = new java.sql.Date(毫秒时间戳); -
util.Date-->sql.Date
-
-
2.2 SimpleDateFormat类
java.text.SimpleDateFormat类
两个操作
-
格式化: 日期 ---> 字符串
-
解析 : 格式化的逆过程,字符串 ---> 日期
//1. 使用默认构造器 默认的模式 和语言环境 创建对象
SimpleDateFormat sdf = new SimpleDateFormat();
Date date = new Date();
//将日期格式化为字符串
String format = sdf.format(date);
System.out.println(format);
//将字符串解析为日期
Strint str = "22-3-24 晚上20:08";//默认只能解析这个字符串
Date date = sdf.parse(str);
System.out.println(date);
// 使用指定方式格式化和解析
//2.使用指定格式构造器
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
//格式化
String format1 = sdf1.format(date);
System.out.println(format1);
//解析 从字符串到日期
Date date2 = sdf1.parse("2022-03-24 20:14:00");
2.3 Calendar 日历类使用
抽象类
-
实例化
-
方式1: 创建 Calendar 的子类
GregorianCalendar的对象 -
方式2: 调用静态方法
getInstance()
-
Calendar calendar = Calendar.getInstance();
-
常用方法
-
get() : 获取常用信息
-
set() : 设置天数/年数/月数
-
add() :增加天数/年数/月数
-
getTime() : 从日历对象获取 Date类对象
-
setTime(): 将Date类对象 转换成 日历类对象
-
//get() 方法
int days = calendar.get(Calendar.DAY_OF_MONTH);//获取一些常用信息
//set() 方法
calendar.set(Calendar.DAY_OF_MONTH,22);//设置信息
//add() 方法
calendar.add(Calendar.DAY_OF_MONTH,3);//在当前属性上增加信息
//getTime() 从日历对象 获取 Date
Date date = calendar.getTime();
//setTime() 将 Date 改变为 日历类
Date date1 = new Date();
Calendar.setTime(date1);
3. 日期时间类 JDK8
可变性: 日期时间应该是不可变的,但是
Calendar可变偏移量:
Date从1900年0月开始, 日期出现了 偏移量, 需要减去偏移量格式化: 格式化只对 Date有关
线程安全: Date 不是线程安全的
不支持闰秒
java.time API 纠正了过去缺陷:
-
本地日期
LocalDate -
本地时间
LocalTime -
本地日期时间
LocalDateTime -
时区
ZonedDateTime -
持续时间
Duration -
计算机记录时间,通过距离
1970开始以毫秒为单位Instant
1. 新时间日期API
-
java.time包含值对象的基础包 -
java.time.chrono 提供不同的日历系统的访问
-
java.time.format格式化和解析时间和日期 -
java.time.temporal 包括底层框架和扩展特性
-
java.time.zone 包含时区支持的类
package datetimetest;
import org.junit.jupiter.api.Test;
import java.time.*;
/**
* @author ZhongweiLeex
* @Date 2022-03-25 13:43
*/
public class JDK8DateTimeTest {
