- 資訊首頁(yè) > 開(kāi)發(fā)技術(shù) >
- Java中難理解的四個(gè)概念
Java 是很多人一直在用的編程語(yǔ)言,但是有些 Java 概念是非常難以理解的,哪怕是一些多年的老手,對某些 Java 概念也存在一些混淆和困惑。
所以,在這篇文章里,會(huì )介紹四個(gè) Java 中最難理解的四個(gè)概念,去幫助開(kāi)發(fā)者更清晰的理解這些概念:
匿名內部類(lèi)又叫匿名類(lèi),它有點(diǎn)像局部類(lèi)(Local Class)或者內部類(lèi)(Inner Class),只是匿名內部類(lèi)沒(méi)有名字,我們可以同時(shí)聲明并實(shí)例化一個(gè)匿名內部類(lèi)。
一個(gè)匿名內部類(lèi)僅適用在想使用一個(gè)局部類(lèi)并且只會(huì )使用這個(gè)局部類(lèi)一次的場(chǎng)景。
匿名內部類(lèi)是沒(méi)有需要明確聲明的構造函數的,但是會(huì )有一個(gè)隱藏的自動(dòng)聲明的構造函數。
咱們看看下面的例子:
// 接口:程序員 interface Programmer { void develop(); } public class TestAnonymousClass { public static Programmer programmer = new Programmer() { @Override public void develop() { System.out.println("我是在類(lèi)中實(shí)現了接口的匿名內部類(lèi)"); } }; public static void main(String[] args) { Programmer anotherProgrammer = new Programmer() { @Override public void develop() { System.out.println("我是在方法中實(shí)現了接口的匿名內部類(lèi)"); } }; TestAnonymousClass.programmer.develop(); anotherProgrammer.develop(); } }
從上面的例子可以看出,匿名類(lèi)既可以在類(lèi)中也可以在方法中被創(chuàng )建。
之前我們也提及匿名類(lèi)既可以繼承一個(gè)具體類(lèi)或者抽象類(lèi),也可以實(shí)現一個(gè)接口。所以在上面的代碼里,我創(chuàng )建了一個(gè)叫做 Programmer 的接口,并在 TestAnonymousClass 這個(gè)類(lèi)中和 main() 方法中分別實(shí)現了接口。
Programmer除了接口以外既可以是一個(gè)抽象類(lèi)也可以是一個(gè)具體類(lèi)。
抽象類(lèi),像下面的代碼一樣:
public abstract class Programmer { public abstract void develop(); }
具體類(lèi)代碼如下:
public class Programmer { public void develop() { System.out.println("我是一個(gè)具體類(lèi)"); } }
OK,繼續深入,那么如果 Programmer 這個(gè)類(lèi)沒(méi)有無(wú)參構造函數怎么辦?我們可以在匿名類(lèi)中訪(fǎng)問(wèn)類(lèi)變量嗎?我們如果繼承一個(gè)類(lèi),需要在匿名類(lèi)中實(shí)現所有方法嗎?
public class Programmer { protected int age; public Programmer(int age) { this.age = age; } public void showAge() { System.out.println("年齡:" + age); } public void develop() { System.out.println("開(kāi)發(fā)中……除了異性,他人勿擾"); } public static void main(String[] args) { Programmer programmer = new Programmer(38) { @Override public void showAge() { System.out.println("在匿名類(lèi)中的showAge方法:" + age); } }; programmer.showAge(); } }
臨時(shí)使用:我們有時(shí)候需要添加一些類(lèi)的臨時(shí)實(shí)現去修復一些問(wèn)題或者添加一些功能。為了避免在項目里添加java文件,尤其是僅使用一次這個(gè)類(lèi)的時(shí)候,我們就會(huì )使用匿名類(lèi)。UI Event Listeners:在java的圖形界面編程中,匿名類(lèi)最常使用的場(chǎng)景就是去創(chuàng )建一個(gè)事件監聽(tīng)器。比如:
button.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { } });
上面的代碼中,我們通過(guò)匿名類(lèi)實(shí)現了 setOnClickListener 接口,當用戶(hù)點(diǎn)擊按鈕的時(shí)候,就會(huì )觸發(fā)我們實(shí)現的 onClick 方法。
Java 中的多線(xiàn)程就是利用多個(gè)線(xiàn)程共同完成一個(gè)大任務(wù)的運行過(guò)程,使用多線(xiàn)程可以最大程度的利用CPU。
使用多線(xiàn)程的使用線(xiàn)程而不是進(jìn)程來(lái)做任務(wù)處理,是因為線(xiàn)程比進(jìn)程更加輕量,線(xiàn)程是一個(gè)輕量級的進(jìn)程,是程序執行的最小單元,并且線(xiàn)程和線(xiàn)程之間是共享主內存的,而進(jìn)程不是。
正如上圖所示,線(xiàn)程生命周期一共有六種狀態(tài)。我們現在依次對這些狀態(tài)進(jìn)行介紹。
大白話(huà)講就是通過(guò)多線(xiàn)程同時(shí)做多件事情讓 Java 應用程序跑的更快,使用線(xiàn)程來(lái)實(shí)行并行和并發(fā)。如今的 CPU 都是多核并且頻率很高,如果單獨一個(gè)線(xiàn)程,并沒(méi)有充分利用多核 CPU 的優(yōu)勢。
重要的優(yōu)勢
1.通過(guò)繼承Thread類(lèi)創(chuàng )建線(xiàn)程
這個(gè)繼承類(lèi)會(huì )重寫(xiě) Thread 類(lèi)的 run() 方法。一個(gè)線(xiàn)程的真正運行是從 run() 方法內部開(kāi)始的,通過(guò) start() 方法會(huì )去調用這個(gè)線(xiàn)程的 run() 方法。
public class MultithreadDemo extends Thread { @Override public void run() { try { System.out.println("線(xiàn)程 " + Thread.currentThread().getName() + " 現在正在運行"); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { for (int i = 0; i < 10; i++) { MultithreadDemo multithreadDemo = new MultithreadDemo(); multithreadDemo.start(); } } }
2.通過(guò)實(shí)現Runnable接口創(chuàng )建線(xiàn)程
我們創(chuàng )建一個(gè)實(shí)現了 java.lang.Runnable 接口的新類(lèi),并實(shí)現其 run() 方法。然后我們會(huì )實(shí)例化一個(gè) Thread 對象,并調用這個(gè)對象的 start() 方法。
public class MultithreadDemo implements Runnable { @Override public void run() { try { System.out.println("線(xiàn)程 " + Thread.currentThread().getName() + " 現在正在運行"); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { for (int i = 0; i < 10; i++) { Thread thread = new Thread(new MultithreadDemo()); thread.start(); } } }
同步只有在多線(xiàn)程條件下才有意義,一次只能有一個(gè)線(xiàn)程執行同步塊。
在 Java 中,同步這個(gè)概念非常重要,因為 Java 本身就是一門(mén)多線(xiàn)程語(yǔ)言,在多線(xiàn)程環(huán)境中,做合適的同步是極度重要的。
在多線(xiàn)程環(huán)境中執行代碼,如果一個(gè)對象可以被多個(gè)線(xiàn)程訪(fǎng)問(wèn),為了避免對象狀態(tài)或者程序執行出現錯誤,對這個(gè)對象使用同步是非常必要的。
在深入講解同步概念之前,我們先來(lái)看看同步相關(guān)的問(wèn)題。
class Production { //沒(méi)有做方法同步 void printProduction(int n) { for (int i = 1; i <= 5; i++) { System.out.print(n * i+" "); try { Thread.sleep(400); } catch (Exception e) { System.out.println(e); } } } } class MyThread1 extends Thread { Production p; MyThread1(Production p) { this.p = p; } public void run() { p.printProduction(5); } } class MyThread2 extends Thread { Production p; MyThread2(Production p) { this.p = p; } public void run() { p.printProduction(100); } } public class SynchronizationTest { public static void main(String args[]) { Production obj = new Production(); //多線(xiàn)程共享同一個(gè)對象 MyThread1 t1 = new MyThread1(obj); MyThread2 t2 = new MyThread2(obj); t1.start(); t2.start(); } }
運行上面的代碼后,由于我們沒(méi)有加同步,可以看到運行結果非?;靵y。
Output:
100 5 10 200 15 300 20 400 25 500
接下來(lái),我們給 printProduction 方法加上同步:
class Production { //做了方法同步 synchronized void printProduction(int n) { for (int i = 1; i <= 5; i++) { System.out.print(n * i+" "); try { Thread.sleep(400); } catch (Exception e) { System.out.println(e); } } } }
當我們對 printProduction() 加上了同步(synchronized)后, 已有一個(gè)線(xiàn)程執行的情況下,是不會(huì )有任何一個(gè)線(xiàn)程可以再次執行這個(gè)方法。這次加了同步后的輸出結果是有次序的。
Output:
5 10 15 20 25 100 200 300 400 500
類(lèi)似于對方法做同步,你也可以去同步 Java 類(lèi)和對象。
注意:其實(shí)有時(shí)候我們可以不必去同步整個(gè)方法。出于性能原因,我們其實(shí)可以?xún)H同步方法中我們需要同步的部分代碼。被同步的這部分代碼就是方法中的同步塊。
Java 的序列化就是將一個(gè) Java 對象轉化為一個(gè)字節流的一種機制。從字節流再轉回 Java 對象叫做反序列化,是序列化的反向操作。
序列化和反序列化是和平臺無(wú)關(guān)的,也就是說(shuō)你可以在 Linux 系統序列化,然后在 Windows 操作系統做反序列化。
如果要序列化對象,需要使用 ObjectOutputStream 類(lèi)的 writeObject() 方法。如果要做反序列化,則要使用 ObjectOutputStream 類(lèi)的 readObject() 方法。
如下圖所示,對象被轉化為字節流后,被儲存在了不同的介質(zhì)中。這個(gè)流程就是序列化。在圖的右邊,也可以看到從不同的介質(zhì)中,比如內存,獲得字節流并轉化為對象,這叫做反序列化。
如果我們創(chuàng )建了一個(gè) Java 對象,這個(gè)對象的狀態(tài)在程序執行完畢或者退出后就消失了,不會(huì )得到保存。
所以,為了能解決這類(lèi)問(wèn)題,Java 提供了序列化機制。這樣,我們就能把對象的狀態(tài)做臨時(shí)儲存或者進(jìn)行持久化,以供后續當我們需要這個(gè)對象時(shí),可以通過(guò)反序列化把對象還原回來(lái)。
下面給出一些代碼看看我們是怎么來(lái)做序列化的。
import java.io.Serializable; public class Player implements Serializable { private static final long serialVersionUID = 1L; private String serializeValueName; private transient String nonSerializeValuePos; public String getSerializeValueName() { return serializeValueName; } public void setSerializeValueName(String serializeValueName) { this.serializeValueName = serializeValueName; } public String getNonSerializeValueSalary() { return nonSerializeValuePos; } public void setNonSerializeValuePos(String nonSerializeValuePos) { this.nonSerializeValuePos = nonSerializeValuePos; } @Override public String toString() { return "Player [serializeValueName=" + serializeValueName + "]"; } }
import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; public class SerializingObject { public static void main(String[] args) { Player playerOutput = null; FileOutputStream fos = null; ObjectOutputStream oos = null; playerOutput = new Player(); playerOutput.setSerializeValueName("niubi"); playerOutput.setNonSerializeValuePos("x:1000,y:1000"); try { fos = new FileOutputStream("Player.ser"); oos = new ObjectOutputStream(fos); oos.writeObject(playerOutput); System.out.println("序列化數據被存放至Player.ser文件"); oos.close(); fos.close(); } catch (IOException e) { e.printStackTrace(); } } }
Output:
序列化數據被存放至Player.ser文件
import java.io.FileInputStream; import java.io.IOException; import java.io.ObjectInputStream; public class DeSerializingObject { public static void main(String[] args) { Player playerInput = null; FileInputStream fis = null; ObjectInputStream ois = null; try { fis = new FileInputStream("Player.ser"); ois = new ObjectInputStream(fis); playerInput = (Player) ois.readObject(); System.out.println("從Player.ser文件中恢復"); ois.close(); fis.close(); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } System.out.println("player名字為 : " + playerInput.getSerializeValueName()); System.out.println("player位置為 : " + playerInput.getNonSerializeValuePos()); } }
Output:
從Player.ser文件中恢復
player名字為 : niubi
player位置為:null
首先,我們介紹了匿名類(lèi)的定義,使用場(chǎng)景和使用方式。
其次,我們討論了多線(xiàn)程和其生命周期以及多線(xiàn)程的使用場(chǎng)景。
再次,我們了解了同步,知道同步后,僅同時(shí)允許一個(gè)線(xiàn)程執行被同步的方法或者代碼塊。當一個(gè)線(xiàn)程在執行被同步的代碼時(shí),別的線(xiàn)程只能在隊列中等待直到執行同步代碼的線(xiàn)程釋放資源。
最后,我們知道了序列化就是把對象狀態(tài)儲存起來(lái)以供后續使用。
以上就是Java中難理解的四個(gè)概念的詳細內容,更多關(guān)于Java的資料請關(guān)注腳本之家其它相關(guān)文章!
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自互聯(lián)網(wǎng)轉載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權請聯(lián)系QQ:712375056 進(jìn)行舉報,并提供相關(guān)證據,一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容。
Copyright ? 2009-2021 56dr.com. All Rights Reserved. 特網(wǎng)科技 特網(wǎng)云 版權所有 珠海市特網(wǎng)科技有限公司 粵ICP備16109289號
域名注冊服務(wù)機構:阿里云計算有限公司(萬(wàn)網(wǎng)) 域名服務(wù)機構:煙臺帝思普網(wǎng)絡(luò )科技有限公司(DNSPod) CDN服務(wù):阿里云計算有限公司 中國互聯(lián)網(wǎng)舉報中心 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證B2
建議您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流瀏覽器瀏覽本網(wǎng)站