国产成人精品久久免费动漫-国产成人精品天堂-国产成人精品区在线观看-国产成人精品日本-a级毛片无码免费真人-a级毛片毛片免费观看久潮喷

您的位置:首頁技術(shù)文章
文章詳情頁

Java中難理解的四個(gè)概念

瀏覽:79日期:2022-08-13 16:16:35
前言

Java 是很多人一直在用的編程語言,但是有些 Java 概念是非常難以理解的,哪怕是一些多年的老手,對某些 Java 概念也存在一些混淆和困惑。

所以,在這篇文章里,會(huì)介紹四個(gè) Java 中最難理解的四個(gè)概念,去幫助開發(fā)者更清晰的理解這些概念:

匿名內(nèi)部類的用法 多線程 如何實(shí)現(xiàn)同步 序列化匿名內(nèi)部類

匿名內(nèi)部類又叫匿名類,它有點(diǎn)像局部類(Local Class)或者內(nèi)部類(Inner Class),只是匿名內(nèi)部類沒有名字,我們可以同時(shí)聲明并實(shí)例化一個(gè)匿名內(nèi)部類。

一個(gè)匿名內(nèi)部類僅適用在想使用一個(gè)局部類并且只會(huì)使用這個(gè)局部類一次的場景。

匿名內(nèi)部類是沒有需要明確聲明的構(gòu)造函數(shù)的,但是會(huì)有一個(gè)隱藏的自動(dòng)聲明的構(gòu)造函數(shù)。

創(chuàng)建匿名內(nèi)部類有兩種辦法 通過繼承一個(gè)類(具體或者抽象都可以)去創(chuàng)建出匿名內(nèi)部類 通過實(shí)現(xiàn)一個(gè)接口創(chuàng)建出匿名內(nèi)部類

咱們看看下面的例子:

// 接口:程序員interface Programmer { void develop();}public class TestAnonymousClass { public static Programmer programmer = new Programmer() {@Overridepublic void develop() { System.out.println('我是在類中實(shí)現(xiàn)了接口的匿名內(nèi)部類');} }; public static void main(String[] args) {Programmer anotherProgrammer = new Programmer() { @Override public void develop() {System.out.println('我是在方法中實(shí)現(xiàn)了接口的匿名內(nèi)部類'); }};TestAnonymousClass.programmer.develop();anotherProgrammer.develop(); }}

從上面的例子可以看出,匿名類既可以在類中也可以在方法中被創(chuàng)建。

之前我們也提及匿名類既可以繼承一個(gè)具體類或者抽象類,也可以實(shí)現(xiàn)一個(gè)接口。所以在上面的代碼里,我創(chuàng)建了一個(gè)叫做 Programmer 的接口,并在 TestAnonymousClass 這個(gè)類中和 main() 方法中分別實(shí)現(xiàn)了接口。

Programmer除了接口以外既可以是一個(gè)抽象類也可以是一個(gè)具體類。

抽象類,像下面的代碼一樣:

public abstract class Programmer { public abstract void develop();}

具體類代碼如下:

public class Programmer { public void develop() {System.out.println('我是一個(gè)具體類'); }}

OK,繼續(xù)深入,那么如果 Programmer 這個(gè)類沒有無參構(gòu)造函數(shù)怎么辦?我們可以在匿名類中訪問類變量嗎?我們?nèi)绻^承一個(gè)類,需要在匿名類中實(shí)現(xiàn)所有方法嗎?

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('開發(fā)中……除了異性,他人勿擾'); } public static void main(String[] args) {Programmer programmer = new Programmer(38) { @Override public void showAge() {System.out.println('在匿名類中的showAge方法:' + age); }};programmer.showAge(); }} 構(gòu)造匿名類時(shí),我們可以使用任何構(gòu)造函數(shù)。上面的代碼可以看到我們使用了帶參數(shù)的構(gòu)造函數(shù)。 匿名類可以繼承具體類或者抽象類,也能實(shí)現(xiàn)接口。所以訪問修飾符規(guī)則同普通類是一樣的。子類可以訪問父類中的 protected 限制的屬性,但是無法訪問 private 限制的屬性。 如果匿名類繼承了具體類,比如上面代碼中的 Programmer 類,那么就不必重寫所有方法。但是如果匿名類繼承了一個(gè)抽象類或者實(shí)現(xiàn)了一個(gè)接口,那么這個(gè)匿名類就必須實(shí)現(xiàn)所有沒有實(shí)現(xiàn)的抽象方法。 在一個(gè)匿名內(nèi)部類中你不能使用靜態(tài)初始化,也沒辦法添加靜態(tài)變量。 匿名內(nèi)部類中可以有被 final 修飾的靜態(tài)常量。匿名類的典型使用場景

臨時(shí)使用:我們有時(shí)候需要添加一些類的臨時(shí)實(shí)現(xiàn)去修復(fù)一些問題或者添加一些功能。為了避免在項(xiàng)目里添加java文件,尤其是僅使用一次這個(gè)類的時(shí)候,我們就會(huì)使用匿名類。UI Event Listeners:在java的圖形界面編程中,匿名類最常使用的場景就是去創(chuàng)建一個(gè)事件監(jiān)聽器。比如:

button.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { }});

上面的代碼中,我們通過匿名類實(shí)現(xiàn)了 setOnClickListener 接口,當(dāng)用戶點(diǎn)擊按鈕的時(shí)候,就會(huì)觸發(fā)我們實(shí)現(xiàn)的 onClick 方法。

多線程

Java 中的多線程就是利用多個(gè)線程共同完成一個(gè)大任務(wù)的運(yùn)行過程,使用多線程可以最大程度的利用CPU。

使用多線程的使用線程而不是進(jìn)程來做任務(wù)處理,是因?yàn)榫€程比進(jìn)程更加輕量,線程是一個(gè)輕量級的進(jìn)程,是程序執(zhí)行的最小單元,并且線程和線程之間是共享主內(nèi)存的,而進(jìn)程不是。

線程生命周期

Java中難理解的四個(gè)概念

正如上圖所示,線程生命周期一共有六種狀態(tài)。我們現(xiàn)在依次對這些狀態(tài)進(jìn)行介紹。

New:當(dāng)我們構(gòu)造出一個(gè)線程實(shí)例的時(shí)候, 這個(gè)線程就擁有了 New 狀態(tài)。這個(gè)狀態(tài)是線程的第一個(gè)狀態(tài)。此時(shí),線程并沒有準(zhǔn)備運(yùn)行。 Runnable:當(dāng)調(diào)用了線程類的 start() 方法, 那么這個(gè)線程就會(huì)從 New 狀態(tài)轉(zhuǎn)換到 Runnable 狀態(tài)。這就意味著這個(gè)線程要準(zhǔn)備運(yùn)行了。但是,如果線程真的要運(yùn)行起來,就需要線程調(diào)度器來調(diào)度執(zhí)行這個(gè)線程。但是線程調(diào)度器可能忙于在執(zhí)行其他的線程,從而不能及時(shí)去調(diào)度執(zhí)行這個(gè)線程。線程調(diào)度器是基于 FIFO 策略去從線程池中挑出一個(gè)線程來執(zhí)行的。 Blocked:線程可能會(huì)因?yàn)椴煌那闆r自動(dòng)的轉(zhuǎn)為 Blocked 狀態(tài)。比如,等候 I/O 操作,等候網(wǎng)絡(luò)連接等等。除此之外,任意的優(yōu)先級比當(dāng)前正在運(yùn)行的線程高的線程都可能會(huì)使得正在運(yùn)行的線程轉(zhuǎn)為 Blocked 狀態(tài)。 Waiting:在同步塊中調(diào)用被同步對象的 wait 方法,當(dāng)前線程就會(huì)進(jìn)入 Waiting 狀態(tài)。如果在另一個(gè)線程中的同一個(gè)對象被同步的同步塊中調(diào)用 notify()/notifyAll(),就可能使得在 Waiting 的線程轉(zhuǎn)入 Runnable 狀態(tài)。 Timed_Waiting:同 Waiting 狀態(tài),只是會(huì)有個(gè)時(shí)間限制,當(dāng)超時(shí)了,線程會(huì)自動(dòng)進(jìn)入 Runnable 狀態(tài)。 Terminated:線程在線程的 run() 方法執(zhí)行完畢后或者異常退出run()方法后,就會(huì)進(jìn)入 Terminated 狀態(tài)。為什么要使用多線程

大白話講就是通過多線程同時(shí)做多件事情讓 Java 應(yīng)用程序跑的更快,使用線程來實(shí)行并行和并發(fā)。如今的 CPU 都是多核并且頻率很高,如果單獨(dú)一個(gè)線程,并沒有充分利用多核 CPU 的優(yōu)勢。

重要的優(yōu)勢

可以更好地利用 CPU 可以更好地提升和響應(yīng)性相關(guān)的用戶體驗(yàn) 可以減少響應(yīng)時(shí)間 可以同時(shí)服務(wù)多個(gè)客戶端創(chuàng)建線程有兩種方式

1.通過繼承Thread類創(chuàng)建線程

這個(gè)繼承類會(huì)重寫 Thread 類的 run() 方法。一個(gè)線程的真正運(yùn)行是從 run() 方法內(nèi)部開始的,通過 start() 方法會(huì)去調(diào)用這個(gè)線程的 run() 方法。

public class MultithreadDemo extends Thread { @Override public void run() {try { System.out.println('線程 ' + Thread.currentThread().getName() + ' 現(xiàn)在正在運(yùn)行');} 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.通過實(shí)現(xiàn)Runnable接口創(chuàng)建線程

我們創(chuàng)建一個(gè)實(shí)現(xiàn)了 java.lang.Runnable 接口的新類,并實(shí)現(xiàn)其 run() 方法。然后我們會(huì)實(shí)例化一個(gè) Thread 對象,并調(diào)用這個(gè)對象的 start() 方法。

public class MultithreadDemo implements Runnable { @Override public void run() {try { System.out.println('線程 ' + Thread.currentThread().getName() + ' 現(xiàn)在正在運(yùn)行');} 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();} }}兩種創(chuàng)建方式對比 如果一個(gè)類繼承了 Thread 類,那么這個(gè)類就沒辦法繼承別的任何類了。因?yàn)?Java 是單繼承,不允許同時(shí)繼承多個(gè)類。多繼承只能采用接口的方式,一個(gè)類可以實(shí)現(xiàn)多個(gè)接口。所以,使用實(shí)現(xiàn) Runnable 接口在實(shí)踐中比繼承 Thread 類更好一些。 第一種創(chuàng)建方式,可以重寫 yield()、interrupt() 等一些可能不太常用的方法。但是如果我們使用第二種方式去創(chuàng)建線程,則 yield() 等方法就無法重寫了。同步

同步只有在多線程條件下才有意義,一次只能有一個(gè)線程執(zhí)行同步塊。

在 Java 中,同步這個(gè)概念非常重要,因?yàn)?Java 本身就是一門多線程語言,在多線程環(huán)境中,做合適的同步是極度重要的。

為什么要使用同步

在多線程環(huán)境中執(zhí)行代碼,如果一個(gè)對象可以被多個(gè)線程訪問,為了避免對象狀態(tài)或者程序執(zhí)行出現(xiàn)錯(cuò)誤,對這個(gè)對象使用同步是非常必要的。

在深入講解同步概念之前,我們先來看看同步相關(guān)的問題。

class Production { //沒有做方法同步 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(); //多線程共享同一個(gè)對象MyThread1 t1 = new MyThread1(obj);MyThread2 t2 = new MyThread2(obj);t1.start();t2.start(); }}

運(yùn)行上面的代碼后,由于我們沒有加同步,可以看到運(yùn)行結(jié)果非常混亂。Output:

100 5 10 200 15 300 20 400 25 500

接下來,我們給 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); }} }}

當(dāng)我們對 printProduction() 加上了同步(synchronized)后, 已有一個(gè)線程執(zhí)行的情況下,是不會(huì)有任何一個(gè)線程可以再次執(zhí)行這個(gè)方法。這次加了同步后的輸出結(jié)果是有次序的。

Output:

5 10 15 20 25 100 200 300 400 500

類似于對方法做同步,你也可以去同步 Java 類和對象。

注意:其實(shí)有時(shí)候我們可以不必去同步整個(gè)方法。出于性能原因,我們其實(shí)可以僅同步方法中我們需要同步的部分代碼。被同步的這部分代碼就是方法中的同步塊。

序列化

Java 的序列化就是將一個(gè) Java 對象轉(zhuǎn)化為一個(gè)字節(jié)流的一種機(jī)制。從字節(jié)流再轉(zhuǎn)回 Java 對象叫做反序列化,是序列化的反向操作。

序列化和反序列化是和平臺無關(guān)的,也就是說你可以在 Linux 系統(tǒng)序列化,然后在 Windows 操作系統(tǒng)做反序列化。

如果要序列化對象,需要使用 ObjectOutputStream 類的 writeObject() 方法。如果要做反序列化,則要使用 ObjectOutputStream 類的 readObject() 方法。

如下圖所示,對象被轉(zhuǎn)化為字節(jié)流后,被儲存在了不同的介質(zhì)中。這個(gè)流程就是序列化。在圖的右邊,也可以看到從不同的介質(zhì)中,比如內(nèi)存,獲得字節(jié)流并轉(zhuǎn)化為對象,這叫做反序列化。

Java中難理解的四個(gè)概念

為什么使用序列化

如果我們創(chuàng)建了一個(gè) Java 對象,這個(gè)對象的狀態(tài)在程序執(zhí)行完畢或者退出后就消失了,不會(huì)得到保存。

所以,為了能解決這類問題,Java 提供了序列化機(jī)制。這樣,我們就能把對象的狀態(tài)做臨時(shí)儲存或者進(jìn)行持久化,以供后續(xù)當(dāng)我們需要這個(gè)對象時(shí),可以通過反序列化把對象還原回來。

下面給出一些代碼看看我們是怎么來做序列化的。

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('序列化數(shù)據(jù)被存放至Player.ser文件'); oos.close(); fos.close();} catch (IOException e) { e.printStackTrace();} }}

Output:

序列化數(shù)據(jù)被存放至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文件中恢復(fù)'); 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文件中恢復(fù)

player名字為 : niubi

player位置為:null

關(guān)鍵特性 如果父類實(shí)現(xiàn)了 Serializable 接口那么子類就不必再實(shí)現(xiàn) Serializable 接口了。但是反過來不行。 序列化只支持非 static 的成員變量 static 修飾的變量和常量以及被 transient 修飾的變量是不會(huì)被序列化的。所以,如果我們不想要序列化某些非 static 的成員變量,直接用 transient 修飾它們就好了。 當(dāng)反序列化對象的時(shí)候,是不會(huì)調(diào)用對象的構(gòu)造函數(shù)的。 如果一個(gè)對象被一個(gè)要序列化的對象引用了,這個(gè)對象也會(huì)被序列化,并且這個(gè)對象也必須要實(shí)現(xiàn) Serializable 接口。總結(jié)

首先,我們介紹了匿名類的定義,使用場景和使用方式。

其次,我們討論了多線程和其生命周期以及多線程的使用場景。

再次,我們了解了同步,知道同步后,僅同時(shí)允許一個(gè)線程執(zhí)行被同步的方法或者代碼塊。當(dāng)一個(gè)線程在執(zhí)行被同步的代碼時(shí),別的線程只能在隊(duì)列中等待直到執(zhí)行同步代碼的線程釋放資源。

最后,我們知道了序列化就是把對象狀態(tài)儲存起來以供后續(xù)使用。

以上就是Java中難理解的四個(gè)概念的詳細(xì)內(nèi)容,更多關(guān)于Java的資料請關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標(biāo)簽: Java
相關(guān)文章:
主站蜘蛛池模板: chinese多姿势videos | 波多野结衣在线视频观看 | 欧美jizzhd欧美精品 | 亚洲欧美日本综合一区二区三区 | 国产成人精品曰本亚洲77美色 | 香蕉久久高清国产精品免费 | 91大神在线精品视频一区 | 国产精品拍拍拍福利在线观看 | 韩国一级毛片视频 | 精品亚洲一区二区 | 亚州综合| 国产一区二区三区视频在线观看 | 日韩精品一区二区三区免费观看 | 最新版天堂资源中文官网 | 日韩成人免费在线 | 国产99视频精品免费观看9e | 亚洲欧美日韩国产精品一区 | 成人小视频在线观看免费 | 亚洲国产夜色在线观看 | 日本乱人伦片中文三区 | 免费看欧美毛片大片免费看 | 国产成人麻豆精品 | 国产欧美日韩视频在线观看 | 亚洲在成人网在线看 | 亚洲国产一区二区三区a毛片 | 亚洲女视频 | 99re6热视频精品免费观看 | 成人 在线欧美亚洲 | 国产高清精品自在久久 | 日产国产精品久久久久久 | 欧美高清视频在线 | 亚洲一在线 | 天堂精品高清1区2区3区 | 特黄视频 | 精品亚洲成a人在线播放 | 99热久久国产精品一区 | 国产永久免费高清动作片www | 国产精品99久久久 | 美国三级大片 | 欧美日韩国产亚洲一区二区三区 | 成人偷拍视频 |