欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時(shí)間:2021年04月25日 10:01:45   作者:四猿外  
這篇文章主要介紹了匿名內(nèi)部類、多線程、如何實(shí)現(xiàn)同步、序列化,這四個(gè)難理解的概念,同學(xué)們一定要仔細(xì)看看

前言

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è)局部類一次的場景。

匿名內(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() {
        @Override
        public 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í)的進(jìn)程,是程序執(zhí)行的最小單元,并且線程和線程之間是共享主內(nèi)存的,而進(jìn)程不是。

線程生命周期

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

  1. New:當(dāng)我們構(gòu)造出一個(gè)線程實(shí)例的時(shí)候, 這個(gè)線程就擁有了 New 狀態(tài)。這個(gè)狀態(tài)是線程的第一個(gè)狀態(tài)。此時(shí),線程并沒有準(zhǔn)備運(yùn)行。
  2. 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í)行的。
  3. 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)。
  4. 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)。
  5. Timed_Waiting:同 Waiting 狀態(tài),只是會(huì)有個(gè)時(shí)間限制,當(dāng)超時(shí)了,線程會(huì)自動(dòng)進(jìn)入 Runnable 狀態(tài)。
  6. 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 對(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é)果非?;靵y。
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ì)象,這叫做反序列化。

為什么使用序列化

如果我們創(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)鍵特性

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

總結(jié)

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

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

再次,我們了解了同步,知道同步后,僅同時(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)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • java多線程實(shí)現(xiàn)下載圖片并壓縮

    java多線程實(shí)現(xiàn)下載圖片并壓縮

    這篇文章主要為大家詳細(xì)介紹了java多線程實(shí)現(xiàn)下載圖片并壓縮,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-05-05
  • Java實(shí)現(xiàn)按鍵精靈的示例代碼

    Java實(shí)現(xiàn)按鍵精靈的示例代碼

    這篇文章主要為大家詳細(xì)介紹了如何利用Java語言實(shí)現(xiàn)按鍵精靈,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)或工作有一定的參考價(jià)值,感興趣的可以學(xué)習(xí)一下
    2022-05-05
  • Intellij IDEA 閱讀源碼的 4 個(gè)絕技(必看)

    Intellij IDEA 閱讀源碼的 4 個(gè)絕技(必看)

    今天小編給大家分享Intellij IDEA 閱讀源碼的 4 個(gè)絕技,熟練的運(yùn)用 IDEA 中各個(gè)小技巧,讓閱讀跟蹤源碼變得更輕松,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2020-04-04
  • 搭建JavaWeb服務(wù)器步驟詳解

    搭建JavaWeb服務(wù)器步驟詳解

    本篇文章主要給大家詳細(xì)分享了搭建JavaWeb服務(wù)器的詳細(xì)步驟以及用到的代碼,對(duì)此有需要的朋友可以跟著學(xué)習(xí)下。
    2018-02-02
  • SpringBoot整合JWT的實(shí)現(xiàn)示例

    SpringBoot整合JWT的實(shí)現(xiàn)示例

    JWT是目前比較流行的跨域認(rèn)證解決方案,本文主要介紹了SpringBoot整合JWT的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • 如何用好Java枚舉讓你的工作效率飛起來

    如何用好Java枚舉讓你的工作效率飛起來

    在JDK1.5之前沒有枚舉類型,那時(shí)候一般用接口常量來替代,而使用Java枚舉類型enum可以更貼近地表示這種常量,下面這篇文章主要給大家介紹了關(guān)于如何用好Java枚舉讓你的工作效率飛起來的相關(guān)資料,需要的朋友可以參考下
    2021-09-09
  • Java getParameter()獲取數(shù)據(jù)為空的問題

    Java getParameter()獲取數(shù)據(jù)為空的問題

    這篇文章主要介紹了Java getParameter()獲取數(shù)據(jù)為空的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Java實(shí)現(xiàn)導(dǎo)入csv的示例代碼

    Java實(shí)現(xiàn)導(dǎo)入csv的示例代碼

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)導(dǎo)入csv的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,有需要的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-03-03
  • Java處理圖片實(shí)現(xiàn)base64編碼轉(zhuǎn)換

    Java處理圖片實(shí)現(xiàn)base64編碼轉(zhuǎn)換

    這篇文章主要介紹了Java處理圖片實(shí)現(xiàn)base64編碼轉(zhuǎn)換,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-02-02
  • Java 裝箱與拆箱詳解及實(shí)例代碼

    Java 裝箱與拆箱詳解及實(shí)例代碼

    這篇文章主要介紹了Java 裝箱與拆箱詳解及實(shí)例代碼的相關(guān)資料,這里對(duì)java 的裝箱及拆箱進(jìn)行了基本概念詳解及簡單使用,需要的朋友可以參考下
    2017-01-01

最新評(píng)論