Java幾個重要的關鍵字詳析
1.extends
- 用於類繼承類,用法:
class
+子類名+extends
+父類名+{}
class Animal{}//父類 class cat extends Animal{}//子類用extends實現繼承
註意:一個類隻能用extends
關鍵字聲明繼承一個父類
- 用於接口繼承接口,用法:
interface
+接口名+extends
+接口名+{}
interface Clippers {} interface Warriors {} interface Lakers extends Clippers,Warriors {}//接口類用extends關鍵字繼承其他接口(可多個)
註意:
- 接口不能用
extends
聲明繼承別的類 - 接口隻能用
extends
聲明繼承別的接口,且可以繼承多個接口 - 當一個類用
implements
實現瞭一個接口時,不僅要實現該接口的方法,也要實現該接口繼承的接口的方法
2.implements
- 用於聲明一個類實現瞭一個接口類,用法:
class
+類名+implements
+接口名+{}
class Nets implements Clippers,Warriors{}//用implements關鍵字聲明實現瞭兩個接口類
註意:
- 一個普通類可以
implements
關鍵字聲明實現多個接口,但必須實現所有接口中的所有方法 - 抽象類實現接口,可以不用實現接口的方法(因為抽象類中可以有抽象方法)
- 意義:可以用
implements
關鍵字聲明實現多個接口來實現類似多繼承
3.final
使用方法:
- 修飾類,使該類不能被繼承
- 修飾方法,使該方法不能被子類重寫 (仍可以被繼承和調用)
- 修飾屬性,使該屬性的值不能被修改(使為常量)
- 修飾局部變量,使該變量不能被修改(局部常量)
使用細節:
final
修飾的屬性在定義時必須賦初值,且不能修改,可以在以下位置賦初值
- 定義時(顯示初始化)
- 在構造器中
- 在代碼塊中
static final
:全局常量
- 如果
final
修飾的屬性是靜態(static
)的,則不能在構造器中賦初值,原因:靜態屬性要求在類加載時就有初值,而構造器在創建對象時才被調用,所以可能導致調用靜態屬性時沒有創建對象而沒有給靜態屬性賦值 final
不能修飾構造方法,沒有意義- 被
final
和static
同時修飾的屬性在調用時不會導致類的加載,效率更高
4.native
基本介紹:
native
用來修飾方法,被修飾的方法即成為瞭一個Java調用但非Java代碼實現的接口(本地方法) ,該方法在外部可以用任何語言去實現
"A native method is a java method whose implementation is provided by non-java code."
使用方法:
native
修飾方法的位置必須在方法返回類型之前,和方法訪問修飾符位置沒有要求,如:public native int hashCode();
native
細節:
native
方法沒有方法體,也沒有{}
- 用
native
修飾後的方法不能用abstract
修飾,因為abstract
指明該方法無實現體,而native
方法是有實現體的,隻是用非Java
代碼實現的 native
方法的返回類型可以是任意類型- 如果一個有
native
方法的類被繼承,子類會繼承這個native
方法,並且可以用java
語言重寫
使用JNI(Java Native Interface)
與其他語言交互
JNI
是Java
平臺的一部分,它允許Java
代碼和其他語言寫的代碼進行交互。
使用步驟:
- 編寫帶有
native
方法的java
類,生成.java
文件 - 使用
javac
命令編譯生成.class
文件 - 使用
javah -jni 類名
生成.h
文件 - 使用
C/C++(或者其他編程語言)
實現native
方法,創建.cpp(或其他)
文件 - 將
C/C++
編寫的文件創建動態鏈接庫(生成DLL文件) native
方法中使用System.loadLibrary()
方法加載動態庫,將DLL文件名作為參數傳入,這時候再運行.java
程序即可實現對本地方法的調用
詳細步驟參考
native意義:
Java
無法直接訪問到操作系統底層(如系統硬件),但通過使用native
關鍵字修飾方法可以借用其他的語言來擴展Java
程序的功能,提高程序的效率
5.static
修飾變量,成為靜態變量或者類變量
- 使用方法:
訪問修飾符+``static``+數據類型+變量名
註意事項:
- 靜態變量會被類的所有對象實例所共享,並且在類加載的時候就會初始化。
- 靜態變量的訪問方法(遵守相關訪問權限):
類名.靜態變量名
或者對象名.靜態變量名
修飾方法,成為靜態方法或者類方法
- 使用方法:
訪問修飾符+``static``+返回數據類型+方法名+{}
註意事項:
- 調用方法(遵守相關訪問權限):
類名.靜態方法名
或者對象名.靜態方法名
- 靜態方法和普通方法都是隨著類加載而加載,將結構信息存儲在方法區
- 靜態方法中不允許使用
this
和super
關鍵字 - 靜態方法中隻能訪問靜態變量和靜態方法
- 普通方法可以訪問靜態成員和普通成員
- 修飾代碼塊,成為靜態代碼塊:
靜態代碼塊會在類加載時被加載,優先級和靜態屬性一樣,有多個靜態代碼塊和靜態屬性時,初始化順序按定義順序執行
好處:static
關鍵字的使用,將類中的一些成員修飾成靜態的,這樣我們不需要創建該類的對象就可以調用該成員,大大提高瞭編程效率
6.transient
基本介紹:
transient
用於修飾實現瞭Serilizable
接口的類中的成員變量,在該類的實例對象進行序列化處理時,被transient
修飾的成員變量不會進行序列化。
使用例子:
import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.Serializable; public class outStream { public static void main(String[] args) throws IOException { String filePath = "d:\Cat.txt"; ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath)); oos.writeObject(new Cat("小花貓", 3)); oos.close(); } } class Cat implements Serializable { private String name; private int age; //沒有用transient修飾 public Cat(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Car{" + "name='" + name + ''' + ", age=" + age + '}'; } } public class inStream { public static void main(String[] args) throws IOException, ClassNotFoundException { String filePath = "d:\Cat.txt"; ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath)); System.out.println(ois.readObject()); ois.close(); } }
可以在Cat.txt
文件內看到兩個成員變量都能被序列化,並且能被反序列化讀出信息。
當小花貓覺得自己的年齡是隱私不想被讀出時,用transient
修飾成員變量age
:
...... private String name; private transient int age; //使用transient修飾 ......
這時在Cat.txt
文件中可以看到隻有name
一個成員變量被序列化,反序列化後的成員變量age
讀出的是int類型的默認值,說明對於transient
修飾的成員變量,在類的實例對象序列化的過程中會被忽略
transient
細節
- 對
transient
修飾的成員變量可以理解為:不會參與進行對象的序列化和反序列化過程,生存周期僅存於調用者的內存而不會寫進磁盤裡進行持久化 static
修飾的成員變量(靜態變量)也是不可序列化的,不論被transient
修飾與否
因為序列化是保存的實例對象的狀態,而靜態變量保存的是類的狀態
transient
關鍵字隻能修飾變量,不能修飾方法和類transient
關鍵字不能修飾局部變量- 如
transient
關鍵字修飾的是自定義類的變量,則該類需要實現Serilizable
接口
註意:
實現Serilizable
接口的類的實例對象是自動進行序列化的,如果序列化對象的類實現的是Externalizable
接口,則序列化不會自動進行,需要實現接口內的方法指定要序列化的變量,這時與有無Transient
修飾無關
7.synchronized
基本介紹:
關鍵字synchronized
可以保證在同一時刻,隻有一個線程可以執行被synchronized
修飾的方法或代碼塊
線程同步:
程序中多個線程都要使用同一個方法,而這個方法用synchronized
進行瞭修飾,在多個線程調用這個方法時必須遵循同步機制
線程同步機制:
當一個線程使用synchronized
修飾的方法時,其他線程想使用這個方法時就必須等待,直到這個線程使用完synchronized
方法
synchronized使用方法:
- 普通同步方法:
public synchronized void m () {}
public class syn implements Runnable { static int i = 0; public static void main(String[] args) throws InterruptedException { syn test = new syn(); Thread t1 = new Thread(test); Thread t2 = new Thread(test); t1.start(); t2.start(); } public synchronized void increase() {//被synchronized修飾的同步方法 System.out.println(Thread.currentThread().getName() + "調用:" + i++); } @Override public void run() { for (int j = 0; j < 100; j++) { increase(); } } }
兩個線程同時調用一個對象的一個同步方法,由於一個對象隻有一把鎖,所以隻有一個線程能夠獲得該對象的鎖,另一個線程無法獲得,就不能調用該對象的synchronized
方法,需要等對象被釋放後才能調用。
從運行結果中可以證明線程1搶到瞭鎖,線程0必須等待線程1執行完畢,否則不能訪問該同步方法。
- 靜態同步方法:
public static synchronized void m () {}
public class syn implements Runnable { static int i = 0; public static void main(String[] args) throws InterruptedException { syn test = new syn(); syn test1 = new syn(); Thread t1 = new Thread(test);//傳入實例對象test Thread t2 = new Thread(test1);//傳入實例對象test1 t1.start(); t2.start(); } public static synchronized void increase() {//同步靜態方法 System.out.println(Thread.currentThread().getName() + "調用:" + i++); } @Override public void run() { for (int j = 0; j < 100; j++) { increase(); } } }
雖然兩個線程實例化瞭兩個不同的對象,但是synchronized
修飾的是靜態方法,兩個線程仍然發生瞭互斥,因為靜態方法是依附與類的而不是對象,線程1先搶到瞭類的鎖,而線程0必須等待線程1執行完畢釋放才能調用同步方法
- 同步代碼塊:
synchronized(object) {}
public class syn implements Runnable { static Object object = new Object();//共享對象 public static void main(String[] args) throws InterruptedException { syn test = new syn(); syn test1 = new syn(); Thread t1 = new Thread(test); Thread t2 = new Thread(test1); t1.start(); t2.start(); } @Override public void run() { synchronized (object) {//代碼塊用靜態成員變量上鎖 for (int j = 0; j < 100; j++) { System.out.println(Thread.currentThread().getName() + "調用第" + j + "次"); } } } }
同步代碼塊用兩個實例變量共享的靜態成員object
對象來上鎖,雖然是兩個線程實例化兩個不同的對象,但是對整個syn
類來說隻有一個共享的object
對象,所以隻有一把鎖,每當有線程來訪問代碼塊時需持有鎖,對象鎖被其他線程持有時需等待。線程1需要等線程0執行完畢才能訪問同步代碼塊
同步的局限性:
由於同步的方法或代碼塊隻能同一時間讓一個線程訪問,所以會導致程序的執行效率降低
盡可能讓synchronized
修飾的范圍最小化,來減少互斥對程序執行帶來的影響
8.volatile
基本介紹:
volatile
用於修飾變量,用volatile
修飾的變量的值被某個線程修改時,會強制將修改的值立即寫入主存中,主存中的值更新會使得緩存中的該變量的值失效,對比與非volatile
變量,可能會被其他線程讀取到更新前的值。
使用方法:
//現在有線程1和線程2同時執行下列代碼 int i = 0; i = i + 1;
執行完畢後預想的結果是 i = 2
;但是可能存在這樣一種情況:兩個線程先同時把i的值讀取到自己的工作內存中,然後再分別執行 i = i + 1
的操作,再將結果寫入主存,這樣兩個線程寫入的都是 i = 1
,最終 i
的結果是 1
,而不是 2
但如果 i
是 volatile
修飾的變量就會不一樣瞭,在一個線程修改 i
的值後,會立即強制在主存中更新 i
的值,這樣會導致另一個線程的工作內存中 i
的緩存值無效,所以另一個線程再次從主存中讀取新的 i
的值,這樣保證瞭i
的值是最新並正確的
並發編程的三大概念:
- 原子性:執行一個操作時,要麼全部步驟執行完畢且不被中斷,要麼就不執行
x = 100;//是原子性操作 y = x;//不是原子性操作,可分解為:1.先讀取x的值 2.將x的值寫入主存 x ++;//不是原子性操作,可分解為:1.讀取x的值 2.進行加一操作 3.寫入主存
- 可見性:多個線程對同一個變量進行操作時,一個線程修改瞭變量的值,其他線程能立即看到修改的值
- 有序性:程序執行的順序按照代碼的先後順序執行
volatile
的意義
- 保證瞭不同線程對變量進行修改時的可見性:因為對於
volatile
變量來說,被修改後新值對其他線程來說是立即可見的 - 保證瞭有序性:
volatile
禁止瞭指令重排,它能保證在對volatile
修飾的變量進行操作時,之前的代碼語句已經全部被執行,並且後面的語句都未執行,但是對其他語句的順序是不做保證的
註意: volatile
不能保證原子性,因為不能保證對變量的操作是原子性操作
9.this
- 在方法中修飾屬性,this理解為
當前對象
,用來區別成員方法和形參,通常省略 - 修飾方法,this理解為當前對象,通常省略;不能在靜態方法中使用
- 調用構造器,在構造器中使用
this(形參列表)
顯式調用指定的其他構造器- 必須在首行調用其他構造器
- 一個構造器中不能調用多個其他構造器
- 不能在構造器中調用遞歸調用,不能成環調用
10.super
super可以理解為:父類的
- 修飾屬性:去父類中找對應屬性,用來區分子父類重名的屬性
- 修飾方法:調用重寫之前的方法
- 調用構造器:使用
super(形參列表)
指定調用父類構造器super(形參列表)
必須放在構造器的首行super(形參列表)
和this(形參列表)
隻能二選一- 在構造器首行如果沒有顯式聲明
super(形參列表)
或this(形參列表)
則默認調用父類的空參構造器super()
(如果此時父類中沒有空參構造器就會報錯)
- 不能在靜態方法中使用
當一個方法和屬性被static屬性修飾時,這些方法和屬性是優先於對象加載進入內存的,是隨著類的加載而加載的;this是當前對象的引用,super是指父類的引用,當靜態方法加載進內存進棧時,如果在靜態方法中有this和super關鍵字時,this和super也被加載到瞭內存,但是這個時候並沒有對象的引用,this和super沒有初始化,所有編譯會報錯。
10.1.子類對象實例化的過程
11.訪問修飾符
public修飾類:
- 一個類中最多隻能有一個public類,且文件名要和public類一致
- 如果沒有public類,文件名可以任意
到此這篇關於Java幾個重要的關鍵字詳析的文章就介紹到這瞭,更多相關Java關鍵字內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Java日常練習題,每天進步一點點(63)
- 一篇文章帶你復習java知識點
- Java多線程Thread類的使用及註意事項
- Java中Volatile關鍵字能保證原子性嗎
- Java面試必備之JMM高並發編程詳解