国产成人精品18p,天天干成人网,无码专区狠狠躁天天躁,美女脱精光隐私扒开免费观看

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

發(fā)布時(shí)間:2021-07-06 11:12 來(lái)源:腳本之家 閱讀:0 作者:四猿外 欄目: 開(kāi)發(fā)技術(shù)

目錄

    前言

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

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

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

    匿名內部類(lèi)

    匿名內部類(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)聲明的構造函數。

    創(chuàng )建匿名內部類(lèi)有兩種辦法

    • 通過(guò)繼承一個(gè)類(lèi)(具體或者抽象都可以)去創(chuàng )建出匿名內部類(lèi)
    • 通過(guò)實(shí)現一個(gè)接口創(chuàng )建出匿名內部類(lèi)

    咱們看看下面的例子:

    // 接口:程序員
    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();
        }
    }
    • 構造匿名類(lèi)時(shí),我們可以使用任何構造函數。上面的代碼可以看到我們使用了帶參數的構造函數。
    • 匿名類(lèi)可以繼承具體類(lèi)或者抽象類(lèi),也能實(shí)現接口。所以訪(fǎng)問(wèn)修飾符規則同普通類(lèi)是一樣的。子類(lèi)可以訪(fǎng)問(wèn)父類(lèi)中的 protected 限制的屬性,但是無(wú)法訪(fǎng)問(wèn) private 限制的屬性。
    • 如果匿名類(lèi)繼承了具體類(lèi),比如上面代碼中的 Programmer 類(lèi),那么就不必重寫(xiě)所有方法。但是如果匿名類(lèi)繼承了一個(gè)抽象類(lèi)或者實(shí)現了一個(gè)接口,那么這個(gè)匿名類(lèi)就必須實(shí)現所有沒(méi)有實(shí)現的抽象方法。
    • 在一個(gè)匿名內部類(lèi)中你不能使用靜態(tài)初始化,也沒(méi)辦法添加靜態(tài)變量。
    • 匿名內部類(lèi)中可以有被 final 修飾的靜態(tài)常量。

    匿名類(lèi)的典型使用場(chǎng)景

    臨時(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 方法。

    多線(xiàn)程

    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)程生命周期

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

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

    為什么要使用多線(xiàn)程

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

    重要的優(yōu)勢

    • 可以更好地利用 CPU
    • 可以更好地提升和響應性相關(guān)的用戶(hù)體驗
    • 可以減少響應時(shí)間
    • 可以同時(shí)服務(wù)多個(gè)客戶(hù)端

    創(chuàng )建線(xiàn)程有兩種方式

    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();
            }
        }
    }

    兩種創(chuàng )建方式對比

    • 如果一個(gè)類(lèi)繼承了 Thread 類(lèi),那么這個(gè)類(lèi)就沒(méi)辦法繼承別的任何類(lèi)了。因為 Java 是單繼承,不允許同時(shí)繼承多個(gè)類(lèi)。多繼承只能采用接口的方式,一個(gè)類(lèi)可以實(shí)現多個(gè)接口。所以,使用實(shí)現 Runnable 接口在實(shí)踐中比繼承 Thread 類(lèi)更好一些。
    • 第一種創(chuàng )建方式,可以重寫(xiě) yield()、interrupt() 等一些可能不太常用的方法。但是如果我們使用第二種方式去創(chuàng )建線(xiàn)程,則 yield() 等方法就無(wú)法重寫(xiě)了。

    同步

    同步只有在多線(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

    關(guān)鍵特性

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

    總結

    首先,我們介紹了匿名類(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í),將立刻刪除涉嫌侵權內容。

    国产成人精品无码一区二区| 国产女人高潮抽搐喷水免费视频| 国产日产高清欧美一区| 欧美精品中文字幕亚洲专区| AI迪丽热巴喷水视频在线观看| 亚洲成AV人片在一线观看|