av一区二区在线观看_亚洲男人的天堂网站_日韩亚洲视频_在线成人免费_欧美日韩精品免费观看视频_久草视

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

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

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

Java 是很多人一直在用的編程語言,但是有些 Java 概念是非常難以理解的,哪怕是一些多年的老手,對(duì)某些 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è)局部類一次的場(chǎng)景。

匿名內(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)常量。匿名類的典型使用場(chǎng)景

臨時(shí)使用:我們有時(shí)候需要添加一些類的臨時(shí)實(shí)現(xiàn)去修復(fù)一些問題或者添加一些功能。為了避免在項(xiàng)目里添加java文件,尤其是僅使用一次這個(gè)類的時(shí)候,我們就會(huì)使用匿名類。UI Event Listeners:在java的圖形界面編程中,匿名類最常使用的場(chǎng)景就是去創(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í)的進(jìn)程,是程序執(zhí)行的最小單元,并且線程和線程之間是共享主內(nèi)存的,而進(jìn)程不是。

線程生命周期

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

正如上圖所示,線程生命周期一共有六種狀態(tài)。我們現(xiàn)在依次對(duì)這些狀態(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)先級(jí)比當(dāng)前正在運(yùn)行的線程高的線程都可能會(huì)使得正在運(yùn)行的線程轉(zhuǎn)為 Blocked 狀態(tài)。 Waiting:在同步塊中調(diào)用被同步對(duì)象的 wait 方法,當(dāng)前線程就會(huì)進(jìn)入 Waiting 狀態(tài)。如果在另一個(gè)線程中的同一個(gè)對(duì)象被同步的同步塊中調(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)勢(shì)。

重要的優(yōu)勢(shì)

可以更好地利用 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 對(duì)象,并調(diào)用這個(gè)對(duì)象的 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)建方式對(duì)比 如果一個(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è)對(duì)象可以被多個(gè)線程訪問,為了避免對(duì)象狀態(tài)或者程序執(zhí)行出現(xiàn)錯(cuò)誤,對(duì)這個(gè)對(duì)象使用同步是非常必要的。

在深入講解同步概念之前,我們先來看看同步相關(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è)對(duì)象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)我們對(duì) printProduction() 加上了同步(synchronized)后, 已有一個(gè)線程執(zhí)行的情況下,是不會(huì)有任何一個(gè)線程可以再次執(zhí)行這個(gè)方法。這次加了同步后的輸出結(jié)果是有次序的。

Output:

5 10 15 20 25 100 200 300 400 500

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

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

序列化

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

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

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

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

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

為什么使用序列化

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

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

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

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)反序列化對(duì)象的時(shí)候,是不會(huì)調(diào)用對(duì)象的構(gòu)造函數(shù)的。 如果一個(gè)對(duì)象被一個(gè)要序列化的對(duì)象引用了,這個(gè)對(duì)象也會(huì)被序列化,并且這個(gè)對(duì)象也必須要實(shí)現(xiàn) Serializable 接口。總結(jié)

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

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

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

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

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

標(biāo)簽: Java
相關(guān)文章:
主站蜘蛛池模板: 91视频网页| 婷婷五月在线视频 | 在线观看黄网站 | 黄色片观看 | 免费看的毛片 | 成人在线视频播放 | 日韩二三区| 精品一区二区在线播放 | 一区二区三区视频在线 | 午夜国产在线观看 | 日韩1区2区 | 国产一级特黄 | 簧片在线免费观看 | 高清av在线 | 日韩女优在线观看 | av网站免费看 | 日韩视频第一页 | 国产美女自拍视频 | 亚洲 欧美 激情 另类 校园 | 五月天激情视频 | 国产精品一品二品 | 国产一级片免费 | 久久精品久久久久久久 | www.一级片 | 一级黄色网| 黄色aaa | 成人午夜在线观看 | 亚洲三级在线 | 中文字幕av一区 | 一级欧美一级日韩 | 国产网站在线 | 天天综合av | 中日韩一级片 | 国产精品偷乱一区二区三区 | 黄色影视| av毛片网站 | 懂色av成人一区二区三区 | 欧美视频亚洲视频 | 中文字字幕在线 | 毛片视频免费 | 日韩视频网 |