字符串、日期
Java筆記目錄可以點這裏:Java 強化筆記
字符串(String)
Java 中用 java.lang.String
類代表字符串:
- Java 9 以前,底層使用
char[]
存儲字符數據
從 Java 9 開始,底層使用byte[]
存儲字符數據 - 所有字符串字面量(比如
"abc"
)都是 String 類的實例 - String 對象一旦創建完畢,它的字符內容不可以修改、
首先來看一段代碼:並思考一下這段代碼的輸出是否與你想的不一樣。
public class Main {
public static void main(String[] args) {
String s = "555";
s += "555";
s = "666";
test(s);
System.out.println(s); // 666
}
static void test(String str) {
str += "555";
}
}
String s = "555";
、s += "555";
、s = "666"
等操作都會在堆空間開闢一塊新的內存空間,因爲 String 是不可變字符串,每次要修改棧空間的變量(s
、str
)的值,只能改變它的指針指向,令指針指向堆空間新的內存。
我們知道,成員變量、函數形參等都是開闢在棧空間的(因爲隨時可能銷燬),而上面代碼中的 s
與 str
實際上在棧空間不是同一塊佈局,test
方法修改的是 str
的指向的值,所以 s
的值不會變。
字符串常量池(String Constant Pool)
Java 中有個字符串常量池(String Constant Pool,簡稱 SCP)
- 從 Java 7 開始屬於堆空間的一部分(以前放在方法區)
當遇到字符串字面量時,會去查看 SCP
- 如果 SCP 中存在與字面量內容一樣的字符串對象 A 時,就返回 A
- 否則,創建一個新的字符串對象 D,並加入到 SCP 中,返回 D
String s1 = "mj"; // SCP中不存在字符串對象"mj"
String s2 = "mj"; // SCP中存在字符串對象"mj", 直接返回SCP中的"mj"
// 因此上面兩個字符串對象是同一個對象
System.out.println(s1 == s2); // true
字符串的初始化
在 C語言裏,字符數組就是字符串;在 Java 中,String
底層是由 char[]
組成的,但是他們不完全是一個東西。
利用 Java 的斷點調試功能來驗證上圖:右半部分中的表示不是真實的內存地址,但是可以理解爲標識不同則內存地址不同,相同則內存地址相同。可以看到 s1
、s2
、s3
、s4
、cs
、s5
、s6
的標識都是不同的;但是 s1
、s2
、s3
、s4
指向的value 的標識都是30,與上圖展示的一致;s5
、s6
指向的value 的標識都是31。
intern 方法
A.intern
方法的作用:
- 如果 SCP 中存在與 A 內容一樣的字符串對象 C 時,就返回 C
- 否則,將 A 加入到 SCP 中,返回 A
int a = 1, b = 2, c = 3;
String s1 = String.format("%d%d%d", a, b, c);
String s2 = String.format("%d%d%d", a, b, c);
// 去常量池中尋找"123", 發現沒有, 就將s1的值"123"放入常量池
String s3 = s1.intern(); // s3、s1都指向了常量池中的"123"
String s4 = s2.intern(); // 返回常量池的"123"
String s5 = "123"; // s5指向常量池中的"123"
System.out.println(s1 == s2); // false
System.out.println(s1 == s3); // true
System.out.println(s1 == s4); // true
System.out.println(s1 == s5); // true
字符串的常用方法(截取)
// 去除左右的空格: "123 456"
" 123 456 ".trim();
// 轉爲大寫字母: "ABC"
"abc".toUpperCase();
// 轉爲小寫字母: "abc"
"ABC".toLowerCase();
// 是否包含某個字符串: true
"123456".contains("34");
// 是否以某個字符串開頭: true
"123456".startsWith("123");
// 是否以某個字符串結尾: true
"123456".endsWith("456");
// 將字符串分隔爲數組: [1, 2, 3, 4]
"1_2_3_4".split("_");
// 比較字符串的大小: <
"abc".compareTo("adc");
// 忽略大小寫比較字符串的大小: <
"abc".compareTo("ADC");
String s1 = "abc";
String s2 = new String("abc");
// 查看字符串的內容是否相等: true
s1.equals(s2);
// 忽略大小寫查看字符串的內容是否相等: true
"abc".equalsIgnoreCase("ABC");
// 字符串的替換
String str = "hello123";
// 由於String是不可變字符串, 必須用變量接收
str = str.replace("123", "666"); // hello666
穿插個知識點,Eclipse 中 抽取代碼塊的快捷鍵:先選中代碼塊,ALT + SHIFT + M
字符串截取:
String str = "abcdea";
// 字符串的截取區間: [2, 4)
str.substring(2, 4); // cd
str.substring(2); // cdea
str.indexOf("a"); // 0
str.lastIndexOf("a"); // 5
str.indexOf("z"); // -1
可變字符串(StringBuilder)
在進行大量字符串的改動操作時(比如拼接、替換)
- 使用
String
會非常消耗內存、降低程序性能 - 使用
StringBuilder
可以節省內存、提高程序性能
String s1 = "";
s1 += "123";
s1 += "456";
StringBuilder s2 = new StringBuilder();
s2.append("123").append("456");
String
與 StringBuilder
性能對比:50000 次字符串拼接操作
public static void main(String[] args) {
testString(); // 2511ms
testStringBuilder(); // 2ms
}
static void testString() {
long begin = System.currentTimeMillis();
String str = "";
for (int i = 0; i < 50000; i++) {
str += i;
}
long end = System.currentTimeMillis();
System.out.println("String ->" + (end - begin) + "ms");
}
static void testStringBuilder() {
long begin = System.currentTimeMillis();
StringBuilder str = new StringBuilder();
for (int i = 0; i < 50000; i++) {
str.append(i);
}
long end = System.currentTimeMillis();
System.out.println("StringBuilder ->" + (end - begin) + "ms");
}
String -> 2511ms
StringBuilder -> 2ms
StringBuilder
的常用方法有:append
、insert
、delete
、replace
、reverse
等;
注意:
StringBuilder
並不是String
的子類 或者 父類StringBuilder
、String
都實現了CharSequence
接口
StringBuilder 的 append 原理
StringBuilder
的原理類似動態數組 ArrayList
:底層會自動擴容;
源碼中可以看到擴容的算法:(value.length << 1) + 2
即 新容量是原來容量的 2 倍 + 2
日期
Date
java.util.Date
是開發中經常用到的日期處理類(注意:不是 java.sql.Date
)
- 包含了年、月、日、時、分、秒等信息
// date1 代表當前時間
Date date1 = new Date();
// System.currentTimeMillis 表示 1970-01-01 00:00:00 GMT 到現在時間的毫秒數
// date2 也代表當前時間
Date date2 = new Date(System.currentTimeMillis());
// date3 代表 1970-01-01 00:00:00 GMT
Date date3 = new Date(0);
// Mon Apr 20 13:44:49 CST 2020
System.out.println(date1);
// Mon Apr 20 13:44:49 CST 2020
System.out.println(date2);
// Thu Jan 01 08:00:00 CST 1970
System.out.println(date3);
System.currentTimeMillis
返回的是從 1970-01-01 00:00:00 GMT 開始到現在經歷的毫秒數
- 1970-01-01 00:00:00 GMT、1970-01-01 08:00:00 CST 代表的是同一個時間
- GMT(Greenwich Mean Time):格林尼治時間
- CST(China Standard Time UT+8:00):中國標準時間
Date
常用方法:
Date d1 = new Date();
Date d2 = new Date();
// 設置毫秒數, d1比d2早(d2經歷的時間比d1大)
d1.setTime(1000);
d2.setTime(2000);
// 獲取毫秒數
d1.getTime(); // 1000
d2.getTime(); // 2000
// 比較時間
d2.after(d1); // true
d1.before(d2); // true
d1.compareTo(d2); // -1(d2經歷的時間比d1大)
SimpleDateFormat
java.text.SimpleDateFormat
常用來進行日期的格式化處理
SimpleDateFormat fmt = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
// 利用日期對象生成字符串
String str = fmt.format(new Date());
// 2020年04月20日 13:57:13
System.out.println(str);
// 解析字符串爲日期對象
Date date = fmt.parse(str);
// Mon Apr 20 13:57:13 CST 2020
System.out.println(date);
SimpleDateFormat
的模式字母:
Calendar
java.util.Calendar
也是開發中經常用到的日期處理類
- 功能比
Date
更加豐富,Date
中很多過期的方法都遷移到了Calendar
中
示例:目前是 2020年4月20日 星期一 下午 14:16:33
// 表示當前時間
Calendar c = Calendar.getInstance();
c.getTime(); // Mon Apr 20 14:16:33 CST 2020
c.get(Calendar.YEAR); // 年: 2020
// 月(取值範圍[0, 11], 0是1月, 11是12月)
c.get(Calendar.MONTH); // 3
// 一月中的第幾天(取值範圍[1, 31])
c.get(Calendar.DAY_OF_MONTH); // 20
// 一週中的第幾天(取值範圍[1, 7], 1是星期天, 2是星期一...,7是星期六)
c.get(Calendar.DAY_OF_WEEK); // 2
// 一年中的第幾天(取值範圍[1, 366])
c.get(Calendar.DAY_OF_YEAR); // 111
c.get(Calendar.HOUR); // 時: 2
c.get(Calendar.MINUTE); // 分: 16
c.get(Calendar.SECOND); // 秒: 33
c.get(Calendar.MILLISECOND); // 毫秒: 330
Calendar
的常用方法:
Calendar c = Calendar.getInstance();
// 設置時間爲 2019年7月6日
c.set(2019, 06, 06);
// 2019年7月11日
c.add(Calendar.DAY_OF_MONTH, 5);
// 2019年9月11日
c.add(Calendar.MONTH, 2);
// 設置Date對象
c.setTime(new Date());
// 獲得Date對象
c.getTime();
// 設置毫秒數
c.setTimeInMillis(System.currentTimeMillis());
// 獲得毫秒數
c.getTimeInMillis();
打印格式化(很少用)
// 獲取當前時間
Calendar c = Calendar.getInstance();
Date date = new Date();
System.out.format("%tB %te, %tY%n", date, date, date); // 四月 20, 2020
System.out.format("%tl:%tM %tp%n", c, c, c); // 2:24 下午
System.out.format("%tD%n", c); // 04/20/20