1.JAVA中==和equals的區別
區別:一個是運算符,一個是方法
==比較變量的值是否相同
①如果比較的對象是基本數據類型,則比較數值是否相等
②如果比較的是引用數據類型,則比較的是對象的內存地址是否相等
equals方法比較對象的內容是否相同
equals方法存在于Object類中,而Object類定義了equals方法
public boolean equals(Object obj) {return (this == obj);}
①如果類未重寫equals方法,會調用Object父類中的equals方法(實際使用的也是==操作符)
②如果類重寫了equals方法,則調用自己的equals方法(一般是比較對象的內容是否相等)
@SpringBootApplication
public class SpringBootDemoApplication {public static void main(String[] args) {SpringApplication.run(SpringBootDemoApplication.class, args);System.out.println("Hello World");User user1 = new User("18","520");User user2 = new User("18","520");System.out.println(user1.equals(user2));}
}
實體類
@ApiModel(value = "用戶實體類")
public class User {@ApiModelProperty(value = "用戶姓名")public String userName;@ApiModelProperty(value = "用戶密碼")public String passWord;public User(String userName, String passWord) {this.userName = userName;this.passWord = passWord;}
}
比較兩個對象是否相等,結果如下
Hello World
false
雖然兩個變量的內容是一樣的,但由于User類沒有重寫equals方法,導致調用的equals是父類Object的方法,結果會返回false
重寫equals可以使用到@Data,相當于@Getter @Setter @RequiredArgsConstructor @ToString @EqualsAndHashCode這5個注解的合集, @EqualsAndHashCode默認是false,表示不調用父類的屬性
對添加了@Data的注解的,剛開始測試的操作會返回true,因為user1和user2的內容是一樣的
2.String,StringBuffer,StringBuilder區別
2.1 String
String數組是final修飾的,所以線程是安全的,但是不可變的
@SpringBootApplication
public class SpringBootDemoApplication {public static void main(String[] args) {SpringApplication.run(SpringBootDemoApplication.class, args);System.out.println("Hello World");String s1 = "Hello";String s2 = s1;System.out.println("修改前 s1 的身份哈希碼:" + System.identityHashCode(s1));System.out.println("修改前 s2 的身份哈希碼:" + System.identityHashCode(s2)); // 與 s1 相同s1 += " World";System.out.println("修改后 s1 的身份哈希碼:" + System.identityHashCode(s1)); // 新值(新對象)System.out.println("修改后 s2 的身份哈希碼:" + System.identityHashCode(s2)); // 舊值(原對象未變)}
}
結果如下,String是引用數據類型,剛開始s1和s2的指向的內容是一樣的,所以修改前的身份哈希值是相等的,當s1拼接字符后引用地址發生變化,s1地址身份也發生了變化,但是s2不變
Hello World
修改前 s1 的身份哈希碼:1988584481
修改前 s2 的身份哈希碼:1988584481
修改后 s1 的身份哈希碼:205010614
修改后 s2 的身份哈希碼:1988584481
2.2?StringBuffe
可變的,父類AbstractStringBuilder的數組是可變的
方法都用了synchronized,所以線程是安全的
2.3?StringBuilder
可變的,父類AbstractStringBuilder的數組是可變的
線程不安全,無鎖,無final修飾符
3.Java之String系列--創建對象的個數及其原理
方式 1:字面量賦值?String s = "abc";
對象個數:0 個或 1 個(取決于常量池是否已存在該字面量)。
- 存在:將s指向常量池"abc"的引用,不創建新對象(對象個數0)
- 不存在:在常量池創建一個String對象(內容為"abc"),并將s指向該對象(對象個數1)
方式 2:new String("abc")
檢查常量池
- 若常量池中不存在?
"abc"
,則先在常量池創建 1 個對象(內容為?"abc"
)。 - 若已存在,則跳過此步。
創建堆對象
無論常量池是否存在,都會在?堆內存?中創建 1 個新的?String
?對象(內容為?"abc"
),并將?s
?指向堆對象。
所以創建對象的個數為1-2,取決于是否有常量池對象
4.Java之String系列--intern方法的作用及原理
不同版本的jdk對intern方法是有差異的(待梳理)
intern() 方法行為(Java 1.8):
當調用?str.intern()?時,會先檢查常量池中是否存在與?str?內容相同的字符串:
- 存在:返回常量池中該字符串的引用。
- 不存在:將?str?的引用?添加到常量池(而非復制對象),并返回?str?本身的引用。
== 比較:
比較的是?對象引用地址,而非字符串內容(內容比較需用?equals())。
@SpringBootApplication
public class SpringBootDemoApplication {public static void main(String[] args) {SpringApplication.run(SpringBootDemoApplication.class, args);String s1 = new String("Hello World");s1.intern();String s2 = "Hello World";System.out.println(s1 == s2);System.out.println("---------------------------------------");String s3 = new String("Hello") + new String("World");s3.intern();String s4 = "HelloWorld";System.out.println(s3 == s4);}
}
false
---------------------------------------
true----- s1 == s2
String s1 = new String("Hello World")
這個操作會創建兩個對象
對象1:字符串字面量 "Hello World" 被加入 常量池(首次出現時自動入池)。
對象2:在 堆內存 中創建一個新的 String 對象(s1 指向該對象)。
此時:
常量池中存在 "Hello World"(引用地址記為 P)。
s1 指向堆中新建的對象(引用地址記為 H1)。
s1.intern()
調用intern(),常量池存在"Hello world"即地址P
itern()直接返回P,但未改變s1的指向,H1
String s2 = "Hello World"
直接使用字面量賦值,JVM會優先檢查常量池
發現常量池存在"Hello World"地址P,因為s2指向P
s1指向H1(堆中新建對象),s2指向P(常量池的引用),地址不同----- s3 == s4
+ 操作符對字符串對象進行拼接時,底層通過StringBuilder 實現,最終返回一個新的堆對象為("HelloWorld")。
關鍵的是:此時常量池不存在"HelloWorld"字面量,因為拼接是動態生成,未出現字面量"HelloWorld"
s3指向新建的堆對象(引用地址為H3,內容為"HelloWorld"),此時常量池無引用
s3的intern()
s3調用intern(),但是常量池不存在"HelloWorld",因此會將s3的引用H3添加到常量池
但s3的指向仍是堆對象H3
String s4 = "HelloWorld";
s4使用字面量賦值,此時常量池存在"HelloWorld"的引用,即H3,因此s4直接指向H3
s3和s4的指向均指向H3,結果true
5.Java之String系列--String不可變的含義、原因、好處
5.1 String不可變的含義
String不可變的含義是:將一個已有字符串"123"重新賦值成"456",不是在原內存地址上修改數據,而是重新指向一個新對象,新地址。
5.2 String為什么不可變
?String的內部數據是一個char數組,被final修飾的,創建后不可改變。
package java.lang;public final class Stringimplements java.io.Serializable, Comparable<String>, CharSequence {/** The value is used for character storage. */private final char value[];/** Cache the hash code for the string */private int hash; // Default to 0// 其他代碼
}
5.3 String不可變的好處
使多線程安全?
加快字符串的處理:這也就是一般將String作為Map的Key的原因,處理速度要快過其它的鍵對象,所以HashMap中的鍵往往都使用String。
避免安全問題等等
6.Java--static--用法/使用位置/實例
用法1:修飾成員屬性
給屬性加了static關鍵字之后,對象就不再擁有該屬性了,該屬性會由類去管理,即多個對象只對應一個屬性。一般用于定義一些常量。
用法2:修飾成員方法
static修飾成員方法的作用是可以使用"類名.方法名"的方式操作方法,避免了先要new出對象的繁瑣和資源消耗。
用法3:修飾代碼塊
在靜態代碼塊中,可以訪問靜態變量,調用靜態方法。
靜態代碼塊(static)只在類加載的時候執行一次,實例代碼塊在創建對象的時候執行,加載的時候也會執行一次。
@Data
@Component
@NoArgsConstructor
@ApiModel(value = "用戶實體類")
public class User {@ApiModelProperty(value = "用戶姓名")private String userName;@ApiModelProperty(value = "用戶密碼")private String passWord;public User(String userName, String passWord) {this.userName = userName;this.passWord = passWord;}{System.out.println("代碼塊執行了...");}static {System.out.println("靜態代碼塊執行了");}}
創建兩個User對象,觀察代碼塊的執行情況
@SpringBootApplication
public class SpringBootDemoApplication {public static void main(String[] args) {SpringApplication.run(SpringBootDemoApplication.class, args);System.out.println("第一次創建");User user1 = new User();System.out.println("第二次創建");User user2 = new User();}
}
執行結果,類第一次加載的時候會執行代碼塊(無論是否有static修飾),后續創建過程中,只會調用無static修飾的代碼塊(每次執行都會調用)
靜態代碼塊執行了
代碼塊執行了...第一次創建
代碼塊執行了...
第二次創建
代碼塊執行了...
用法4:靜態導包
7.Java--異常/Exception--類型/原理
異常的層次結構
Throwable有兩個直接的子類: Error、Exception。
Error
JVM內部的嚴重問題,比如資源不足等,無法恢復
Exception
可恢復。分RuntimeException和其他Exception
或者說分為非受檢異常(unchecked exception)和受檢異常(checked exception)。
RuntimeException(unchecked exception)
處理或者不處理都可以(不需try...catch...或在方法聲明時throws)
其他Exception(checked exception)
Java編譯器要求程序必須捕獲(try...catch)或聲明拋出(方法聲明時throws)這種異常