Java基礎09 Java常用關鍵字
Java關鍵字是電腦語言裡事先定義的,有特別意義的識別符號,有時又叫保留字,還有特別意義的變數。Java的關鍵字對Java的編譯器有特殊的意義,他們用來表示一種資料型別,或者表示程式的結構等,關鍵字不能用作變數名、方法名、類名、包名和引數。
Java語言共定義瞭如下所示的關鍵字:
- 用於資料型別:boolean、byte、char、 double、 false、float、int、long、new、short、true、void、instanceof
- 用於語句:break、case、 catch、 continue、 default 、do、 else、 for、 if、return、switch、try、 while、 finally、 throw、this、 super
- 用於修飾:abstract、final、native、private、 protected、public、static、synchronized、transient、 volatile
- 用於方法、類、介面、包和異常:class、 extends、 implements、interface、 package、import、throws
- Java保留的沒有意義的關鍵字:cat、 future、 generic、innerr、 operator、 outer、rest、var
- 3個保留字(不是關鍵字,但和關鍵字一樣,作為識別符號):true、false、null
這裡只針對性的講解幾個比較常用、值得深究的關鍵字,其他的關鍵字,有興趣的可以去
transient
初識transient
transient關鍵字主要作用是可以用一句話來總結:讓物件的某個被 transient 修飾的屬性不被系列化。
示例:
定義一個學生類
@Data @Accessors(chain = true) public class Student implements Serializable { private static final long serialVersionUID = 123456L; private String name; private transient int age; // 使用transient修飾 }
測試demo:
public class transientTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
serialDemo();
deSerialDemo();
}
// 序列化
private static void serialDemo() throws IOException {
Student student = new Student();
student.setName("李四").setAge(22);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D://my//ideaWorkSpace//reStudy//javaBaseStudy//javaBase//src//main//java//com//davidkity//javaBasicStudy//keyWords/template"));
objectOutputStream.writeObject(student); // 使用流模擬網路上的資料交替
objectOutputStream.close();
System.out.println("添加了 transient 關鍵字的序列化:age = " + student.getAge());
}
// 反序列化
private static void deSerialDemo() throws IOException, ClassNotFoundException {
File file = new File("D://my//ideaWorkSpace//reStudy//javaBaseStudy//javaBase//src//main//java//com//davidkity//javaBasicStudy//keyWords/template");
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file));
Student student = (Student) objectInputStream.readObject();
System.out.println("添加了 transient 關鍵字的反序列化:age = " + student.getAge());
}
}
執行結果:
添加了 transient 關鍵字的序列化:age = 22
添加了 transient 關鍵字的反序列化:age = 0
通過執行結果,可以得出 age 欄位沒有被序列化,所以在反序列化的時候,由於沒有儲存 age 欄位的值,才會使用預設值,int 的預設值為 0
深入理解 transient
transient 實現原理
所謂的序列化就是把物件的狀態存入到硬碟中(持久化),等需要的時候從硬碟中讀取;而 transient 的作用就是讓物件的某個屬性的生命週期僅存於呼叫者的記憶體中而不會被寫入到磁碟中。
被 transient 修飾的變數就一定不能變序列化嗎?
Java中有兩種序列化介面,一種就是常見的 Serializable,另一種是 Externalizable,看名字我們可以猜測出Externalizable是一個擴充套件的序列化介面,看過原始碼的人一定知道,Externalizable 是 Serializable 子類,其介面中有 writeExternal(ObjectOutput out) 和 readExternal(ObjectInput in) 兩個方法,返回值都是 void ,這兩個方法是用來指定物件中的那個屬性會被序列化;哪怕改屬性是被 transient 修飾,也會被系列化
。下面我們來看一個示例:
實現介面為Externalizable的學生類
@Data
@Accessors(chain = true)
public class Student2 implements Externalizable {
private static final long serialVersionUID = 123L;
private String name;
private transient int age;
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(age); // 指定 age 欄位會被序列化
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
age = (int) in.readObject(); // 讀取被序列化的欄位
}
}
測試類
public class transientTest2 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
serialDemo();
deSerialDemo();
}
// 序列化
private static void serialDemo() throws IOException {
Student2 student = new Student2();
student.setName("李四").setAge(22);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D://my//ideaWorkSpace//reStudy//javaBaseStudy//javaBase//src//main//java//com//davidkity//javaBasicStudy//keyWords/template2"));
objectOutputStream.writeObject(student);
objectOutputStream.close();
System.out.println("添加了 transient 關鍵字的序列化:" + student.toString());
}
// 反序列化
private static void deSerialDemo() throws IOException, ClassNotFoundException {
File file = new File("D://my//ideaWorkSpace//reStudy//javaBaseStudy//javaBase//src//main//java//com//davidkity//javaBasicStudy//keyWords/template2");
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file));
Student2 student = (Student2) objectInputStream.readObject();
System.out.println("添加了 transient 關鍵字的反序列化:" + student.toString());
}
}
輸出結果:
添加了 transient 關鍵字的序列化:Student2(name=李四, age=22)
添加了 transient 關鍵字的反序列化:Student2(name=null, age=22)
通過上面的示例可知,Student2類中的age屬性被transient修飾,而name屬性為普通屬性,實現序列化的介面為Externalizable,age被指定為要序列化的屬性,最終的輸出結果表明即使是被transient修飾的屬性,也不一定不能被序列化,還要看實現序列化的介面是什麼
。
instanceof
instanceof是 Java 的一個嚴格二元操作符,類似於 == ,>,< 等操作符。它的作用就是用於測試左邊的物件是否是它右邊的類的例項,返回 boolean 的資料型別。
注意:編譯器會檢查 obj 是否能轉換成右邊的class型別,如果不能轉換則直接報錯,如果不能確定型別,則通過編譯,具體看執行時決定
基本語法:
boolean result = obj instanceof Class
下面我們來看看instanceof的集中情況
instanceof 使用注意事項
1、obj 必須是引用型別,不能為基本型別
示例:
int i = 1;
System.out.println(i instanceof Integer); // 編譯的時候就會報錯
2、obj 為null時,返回的結果就是false
示例:
public class instanceofTest {
public static void main(String[] args) {
objIsNull(); // 輸出:object 為 null 時,是 Object 型別嗎? 不是
}
private static void objIsNull(){
System.out.println("object 為 null 時,是 Object 型別嗎? " + ((null instanceof Object) ? "是" : "不是"));
}
}
3、obj 為 class 類例項物件
示例:
public class instanceofTest {
public static void main(String[] args) {
objIsClass(); // 輸出:obj 為 類的例項化物件是,是String型別嗎? 是
}
private static void objIsClass(){
String str = "ss";
System.out.println("obj 為 類的例項化物件是,是String型別嗎? " + ((str instanceof String) ? "是" : "不是"));
}
}
obj 為 class 的介面實現類
示例:
public class instanceofTest {
public static void main(String[] args) {
objIsInterface(); // 輸出:obj為介面的實現類,是List型別嗎? 是
}
private static void objIsInterface(){
ArrayList<String> arrayList = new ArrayList<>();
System.out.println("obj為介面的實現類,是List型別嗎? " + ((arrayList instanceof List) ? "是" : "不是"));
}
}
obj 為 class 類的直接或間接子類
示例:
public class instanceofTest {
public static void main(String[] args) {
objIsParentOrSon();
}
private static void objIsParentOrSon(){
Animal animal = new Animal();
Animal cat1 = new Cat();
Cat cat2 = new Cat();
System.out.println(animal instanceof Cat); // false
System.out.println(cat1 instanceof Cat); // true
System.out.println(cat2 instanceof Cat); // true
System.out.println(animal instanceof Animal); // false
System.out.println(cat1 instanceof Animal); // true
System.out.println(cat2 instanceof Animal); // true
}
}
從輸出的結果可以看出,父類的型別不等於子類的型別,子類的可以等於父類的型別(instanceof),簡單的說,只能向上,不能向下。
final
final關鍵字可以修飾類、變數、方法,一但被final修飾,那麼被修飾的引用將不能改變,若檢視讓其改變,編輯器會報編譯錯誤。
修飾變數
在開發中,修飾變數的時候,final常和static關鍵字一起使用,用於宣告一個常量。final修飾基本資料型別的時候,必須賦初始值且不能改變;修飾引用型別時,引用變數不能再指向其他的物件。
示例:
public class FinalTest {
// public final String s; // 沒有初始化,編譯報錯
public final String str = "sss";
public static void main(String[] args) {
FinalTest finalTest = new FinalTest();
// finalTest.str = "abc"; // 修改被 final 修飾的變數,編譯報錯
}
}
修飾方法
final修飾方法的時候,代表這個不可以被子類重寫。當我們認為這個方法已經很完善,不需要子類去重寫,可以使用 final 修飾。這樣的好處就是執行速度會比較快,原因在於被final修飾的方法在類的初始化就已經靜態綁定了,不需要在執行時再動態繫結,所以執行的速度會比較快。
修飾類
和修飾方法一樣,被final修飾的類是不能被子類繼承的。只有當我們認為這個類已經很完善了,才會去用final修飾,就像String類一樣。
static
static關鍵字,在之前的文章中也講到了一些,這裡稍微詳細的講解一下 static 關鍵字。static 關鍵字的基本概念可以用一句話來概括:方便在沒有建立物件的情況下來進行呼叫
。static 能修飾的物件有類、變數、程式碼塊、方法,下面就分別對其進行一定的解析。
修飾內部類
static不能修飾普通的類,只能修飾內部類。
public class StaticTest {
public static class StaticInnerClass{
public StaticInnerClass(){
System.out.println("靜態內部類的無參構造方法");
}
public void staticInnerMethod(){
System.out.println("靜態內部類的方法");
}
}
public static void main(String[] args) {
StaticInnerClass staticInnerClass = new StaticInnerClass();
staticInnerClass.staticInnerMethod();
}
}
修飾方法
示例:
public class StaticMethodTest {
public static void staticMethod(){
System.out.println("靜態方法");
}
public static void main(String[] args) {
StaticMethodTest.staticMethod();
}
}
修飾變數
public class StaticVarTest {
private static String str = "hello";
public static void main(String[] args) {
System.out.println(StaticVarTest.str);
}
}