成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

為什么推薦大家優(yōu)先使用 try-with-resources 而非 try-finally

開發(fā) 前端
在處理必須關(guān)閉的資源時,使用try-with-resources?語句替代try-catch-finally語句,你會驚奇的發(fā)現(xiàn),編寫的代碼更簡潔,更清晰,同時也省去了手動顯式釋放資源的煩惱。

一、背景介紹

try-with-resources是 JDK 7 中引入的一個新的異常處理機制,它能讓開發(fā)人員不用顯式的釋放try-catch語句塊中使用的資源。

比如,我們以文件資源拷貝為示例,大家所熟悉的try-catch-finally寫法如下:

public class ResourceTest1 {

    public static void main(String[] args) {
        BufferedInputStream bin = null;
        BufferedOutputStream bout = null;
        try {
            bin = new BufferedInputStream(new FileInputStream(new File( "test.txt")));
            bout = new BufferedOutputStream(new FileOutputStream(new File( "out.txt")));
            int b;
            while ((b = bin.read()) != -1) {
                bout.write(b);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
         //關(guān)閉文件流
            if (bin != null) {
                try {
                    bin.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bout != null) {
                try {
                    bout.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

我們現(xiàn)在將其改成使用try-with-resources編程方式,你會驚奇的發(fā)現(xiàn)只需要簡單的幾行代碼就可以搞定,不用顯式關(guān)閉資源,方式如下:

public class ResourceTest2 {

    public static void main(String[] args) {
        try (BufferedInputStream bin = new BufferedInputStream(new FileInputStream(new File("test.txt")));
             BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(new File("out.txt")))) {
            int b;
            while ((b = bin.read()) != -1) {
                bout.write(b);
            }
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在 JDK7 之前,在處理必須關(guān)閉的資源時,開發(fā)人員必須要牢記在try-catch語句中使用finally執(zhí)行關(guān)閉資源的方法,否則隨著程序不斷運行,資源泄露將會累計成重大的生產(chǎn)事故,如果你的程序中同時打開了多個資源,你會驚奇的發(fā),關(guān)閉資源的代碼竟然比業(yè)務(wù)代碼還要多,使得代碼更加難以清晰的閱讀和管理。

因此在這樣的背景下,try-with-resources由此誕生,它的設(shè)計初衷就是旨在減輕開發(fā)人員釋放try塊中使用的資源負擔(dān)。

習(xí)慣了try-catch-finally寫法的同學(xué),可能會發(fā)出疑問,是不是所有涉及到資源的操作都可以用try-with-resources編程?使用這種編程方式有沒有坑?如果有坑,使用的時候哪些地方應(yīng)該需要注意呢?....

好吧,廢話也不多說了,今天我們就一起來看看try-with-resources編程原理。

二、實踐解說

try-with-resources語句能確保每個資源在語句結(jié)束時被關(guān)閉,但是有一個前提條件,那就是這個資源必須實現(xiàn)了java.lang.AutoCloseable接口,才可以被執(zhí)行關(guān)閉。

try-with-resources編程模式中,無需開發(fā)人員顯式關(guān)閉資源的前提是,這個資源必須實現(xiàn)java.lang.AutoCloseable接口,并且重寫close方法,否則無法在try-with-resources中進行聲明變量。

下面我們可以關(guān)閉單個資源為例,代碼如下:

public class TryResourceDemo implements AutoCloseable {

    public void doSomething(){
        System.out.println("do something");
    }

    @Override
    public void close() throws Exception {
        System.out.println("resource is closed");
    }
}
public class TryResourceTest {

    public static void main(String[] args) {
        try(TryResourceDemo res = new TryResourceDemo()) {
            res.doSomething();
        } catch(Exception ex) {
            ex.printStackTrace();
        }
    }
}

運行結(jié)果如下:

do something
resource is closed

可以很清晰的看到,close方法被調(diào)用了!

下面我們再打開反編譯后的TryResourceTest.class文件代碼,你會驚奇發(fā)現(xiàn),編譯器自動給代碼加上了finally方法,并且會調(diào)用close方法,將資源關(guān)閉!

public class TryResourceTest {

    public static void main(String[] args) {
        try {
            TryResourceDemo res = new TryResourceDemo();
            Throwable var2 = null;

            try {
                res.doSomething();
            } catch (Throwable var12) {
                var2 = var12;
                throw var12;
            } finally {
                if (res != null) {
                    if (var2 != null) {
                        try {
                            res.close();
                        } catch (Throwable var11) {
                            var2.addSuppressed(var11);
                        }
                    } else {
                        res.close();
                    }
                }

            }
        } catch (Exception var14) {
            var14.printStackTrace();
        }

    }
}

也就是說,使用try-with-resources編程,其實是編譯器顯式的給代碼了添加finally方法,省去開發(fā)人員手動關(guān)閉資源的操作!

三、資源關(guān)閉順序

上面我們只介紹了關(guān)閉單個資源的場景,假如有多個資源時,try-with-resources是如何關(guān)閉的呢?

下面還是舉例看結(jié)果。

public class TryResourceDemo1 implements AutoCloseable {

    public void doSomething(){
        System.out.println("do something 1");
    }

    @Override
    public void close() throws Exception {
        System.out.println("resource 1 is closed");
    }
}
public class TryResourceDemo2 implements AutoCloseable {

    public void doSomething(){
        System.out.println("do something 2");
    }

    @Override
    public void close() throws Exception {
        System.out.println("resource 2 is closed");
    }
}
public class TryResourceDemoTest {

    public static void main(String[] args) {
        try(TryResourceDemo1 demo1 = new TryResourceDemo1();
            TryResourceDemo2 demo2 = new TryResourceDemo2()) {
            System.out.println("do...");
            demo1.doSomething();
            demo2.doSomething();
        } catch(Exception ex) {
            ex.printStackTrace();
        }
    }
}

運行結(jié)果如下:

do...
do something 1
do something 2
resource 2 is closed
resource 1 is closed

從結(jié)果上可以看出,try語句中越是最后使用的資源,越是最早被關(guān)閉。

關(guān)于這一點,大家可以從反編譯的代碼中找到原理!

四、異常處理機制

正常的情況下,try語句結(jié)束時會關(guān)閉相關(guān)的資源,假如語句內(nèi)部執(zhí)行時發(fā)生異常,同時我們又顯式的調(diào)用了finally方法,執(zhí)行的順序又是怎樣的呢?

下面繼續(xù)舉例看結(jié)果。

public class TryThrowResourceDemoTest {

    public static void main(String[] args) {
        AutoCloseable obj1 = null;
        AutoCloseable obj2 = null;
        try (TryResourceDemo1 demo1 = new TryResourceDemo1();
             TryResourceDemo2 demo2 = new TryResourceDemo2();) {
            System.out.println("do...");
            obj1 = demo1;
            System.out.println(1 / 0);
            obj2 = demo2;
            System.out.println("over...");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                System.out.println("before finally close");
                if (obj1 != null) {
                    obj1.close();
                }
                if (obj2 != null) {
                    obj2.close();
                }
                System.out.println("after finally close");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

運行結(jié)果如下:

do...
resource 2 is closed
resource 1 is closed
before finally close
resource 1 is closed
after finally close
java.lang.ArithmeticException: / by zero
 at com.example.java.trywithresources.a.TryThrowResourceDemoTest.main(TryThrowResourceDemoTest.java:18)

可以很清晰的看到,可以得出如下結(jié)論:

  • 1.只要實現(xiàn)了AutoCloseable接口的類,并且在try里聲明了對象變量,在try結(jié)束后,不管是否發(fā)生異常,close方法都會被調(diào)用
  • 2.其次,在try里越晚聲明的對象,會越早被close掉
  • 3.try結(jié)束后自動調(diào)用的close方法,這個動作會早于finally里調(diào)用的方法

五、壓制異常處理

大部分情況,我們通常不會擔(dān)心資源的close會發(fā)生異常,現(xiàn)在假設(shè)如果try里聲明的資源對象,當(dāng)執(zhí)行close方法拋異常時,他們的執(zhí)行順序又是怎樣的呢?我們又如何獲取這種異常呢?

還是眼見為實,下面以舉例看結(jié)果。

public class TryThrowableResourceDemo1 implements AutoCloseable {

    public void doSomething(){
        System.out.println("do something 1");
        throw new NullPointerException("TryThrowableResourceDemo1: doSomething() NullPointerException");
    }

    @Override
    public void close() throws Exception {
        System.out.println("TryThrowableResourceDemo1 is closed");
        throw new NullPointerException("TryThrowableResourceDemo1: close() NullPointerException");
    }
}
public class TryThrowableResourceDemo2 implements AutoCloseable {

    public void doSomething(){
        System.out.println("do something 2");
        throw new NullPointerException("TryThrowableResourceDemo2: doSomething() NullPointerException");
    }

    @Override
    public void close() throws Exception {
        System.out.println("TryThrowableResourceDemo2 is closed");
        throw new NullPointerException("TryThrowableResourceDemo2: close() NullPointerException");
    }
}
public class TryThrowableResourceDemoTest {

    public static void main(String[] args) {
        try (TryThrowableResourceDemo1 demo1 = new TryThrowableResourceDemo1();
             TryThrowableResourceDemo2 demo2 = new TryThrowableResourceDemo2()) {
            System.out.println("do...");
            demo1.doSomething();
            demo2.doSomething();
        } catch (Exception e) {
            System.out.println("gobal: exception");
            System.out.println(e.getMessage());
            Throwable[] suppressed = e.getSuppressed();
            for (int i = 0; i < suppressed.length; i++){
                System.out.println(suppressed[i].getMessage());
            }
        }
    }
}

運行結(jié)果如下:

do...
do something 1
TryThrowableResourceDemo2 is closed
TryThrowableResourceDemo1 is closed
gobal: exception
TryThrowableResourceDemo1: doSomething() NullPointerException
TryThrowableResourceDemo2: close() NullPointerException
TryThrowableResourceDemo1: close() NullPointerException

從運行結(jié)果我們可以很清晰的看到,對于try語句塊內(nèi)的異常,我們可以通過e.getMessage()獲取,對于close()方法拋出的異常,其實編譯器對這部分的異常進行特殊處理,將其放入到集合數(shù)組中了,因此我們需要通過e.getSuppressed()方法來獲取。

具體反編譯后的代碼如下:

public class TryThrowableResourceDemoTest {
    
    public static void main(String[] args) {
        try {
            TryThrowableResourceDemo1 demo1 = new TryThrowableResourceDemo1();
            Throwable var34 = null;

            try {
                TryThrowableResourceDemo2 demo2 = new TryThrowableResourceDemo2();
                Throwable var4 = null;

                try {
                    System.out.println("do...");
                    demo1.doSomething();
                    demo2.doSomething();
                } catch (Throwable var29) {
                    var4 = var29;
                    throw var29;
                } finally {
                    if (demo2 != null) {
                        if (var4 != null) {
                            try {
                                demo2.close();
                            } catch (Throwable var28) {
                                var4.addSuppressed(var28);
                            }
                        } else {
                            demo2.close();
                        }
                    }

                }
            } catch (Throwable var31) {
                var34 = var31;
                throw var31;
            } finally {
                if (demo1 != null) {
                    if (var34 != null) {
                        try {
                            demo1.close();
                        } catch (Throwable var27) {
                            var34.addSuppressed(var27);
                        }
                    } else {
                        demo1.close();
                    }
                }

            }
        } catch (Exception var33) {
            System.out.println("gobal: exception");
            System.out.println(var33.getMessage());
            Throwable[] suppressed = var33.getSuppressed();

            for(int i = 0; i < suppressed.length; ++i) {
                System.out.println(suppressed[i].getMessage());
            }
        }

    }
}

六、關(guān)閉資源的坑

在實際的使用中,不管是使用try-with-resource編程還是使用try-catch-finally編程,一定需要了解資源的close方法內(nèi)部的實現(xiàn)邏輯,否則還是可能會導(dǎo)致資源泄露。

舉個例子,在 Java BIO 中采用了大量的裝飾器模式。當(dāng)調(diào)用裝飾器的 close 方法時,本質(zhì)上是調(diào)用了裝飾器包裝的流對象的 close 方法。比如:

public class TryWithResource {
    public static void main(String[] args) {
        try (FileInputStream fin = new FileInputStream(new File("input.txt"));
                GZIPOutputStream out = new GZIPOutputStream(new FileOutputStream(new File("out.txt")))) {
            byte[] buffer = new byte[4096];
            int read;
            while ((read = fin.read(buffer)) != -1) {
                out.write(buffer, 0, read);
            }
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在上述代碼中,我們從FileInputStream中讀取字節(jié),并且寫入到GZIPOutputStream中。GZIPOutputStream實際上是FileOutputStream的裝飾器。

由于try-with-resource的特性,實際編譯之后的代碼會在后面帶上finally代碼塊,并且在里面調(diào)用fin.close()方法和out.close()方法。

我們再來看GZIPOutputStream類的close方法。

public void close() throws IOException {
    if (!closed) {
        finish();
        if (usesDefaultDeflater)
            def.end();
        out.close();
        closed = true;
    }
}

在調(diào)用out變量的close方法之前,GZIPOutputStream還做了finish操作,該操作還會繼續(xù)往FileOutputStream中寫壓縮信息,此時如果出現(xiàn)異常,則out.close()方法會被略過,而out變量實際上代表的是被裝飾的FileOutputStream類,這個才是最底層的資源關(guān)閉方法。

正確的做法應(yīng)該是在try-with-resource中單獨聲明最底層的資源,保證對應(yīng)的close方法一定能夠被調(diào)用。在剛才的例子中,我們需要單獨聲明每個FileInputStream以及FileOutputStream,改成如下方式:

public class TryWithResource {
    public static void main(String[] args) {
        try (FileInputStream fin = new FileInputStream(new File("input.txt"));
                FileOutputStream fout = new FileOutputStream(new File("out.txt"));
                GZIPOutputStream out = new GZIPOutputStream(fout)) {
            byte[] buffer = new byte[4096];
            int read;
            while ((read = fin.read(buffer)) != -1) {
                out.write(buffer, 0, read);
            }
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }
}

編譯器會自動生成fout.close()的代碼,這樣肯定能夠保證真正的流被關(guān)閉。

七、小結(jié)

在處理必須關(guān)閉的資源時,使用try-with-resources語句替代try-catch-finally語句,你會驚奇的發(fā)現(xiàn),編寫的代碼更簡潔,更清晰,同時也省去了手動顯式釋放資源的煩惱。

因此在實際編程過程中,推薦大家采用這種方式編寫,同時要關(guān)注close方法內(nèi)部的實現(xiàn)邏輯,避免資源泄露,服務(wù)宕機!

責(zé)任編輯:武曉燕 來源: Java極客技術(shù)
相關(guān)推薦

2021-01-13 09:55:29

try-catch-fJava代碼

2025-04-08 07:30:00

前端開發(fā)JavaScript

2022-12-26 00:00:03

非繼承關(guān)系JDK

2021-03-31 11:52:24

try-catch-fJava代碼

2024-05-10 11:43:23

C#編程

2023-10-09 18:39:13

Python代碼

2025-02-12 12:00:00

前端try-catchJavaScrip

2020-06-28 09:08:08

Java語法塊開發(fā)

2021-03-10 09:40:50

Linux命令文件

2024-01-30 07:55:03

KubernetesAPI服務(wù)器

2024-09-29 08:57:25

2023-11-13 17:01:26

C++編程

2024-11-29 08:20:22

Autowired場景項目

2020-12-28 06:20:27

OptionalTryjava

2021-01-28 08:03:44

程序員 finallyreturn

2020-09-02 15:00:36

Linux命令軟件

2021-12-09 07:54:19

IDL字段兼容

2022-08-02 08:53:03

KubernetesSystemdLinux

2024-06-04 00:10:00

開發(fā)拷貝

2024-11-12 10:30:54

Docker部署數(shù)據(jù)庫
點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 久久夜色精品国产 | 91在线观看网址 | 国产一区二区日韩 | 国产一区二区三区色淫影院 | 亚洲网址在线观看 | 人人人人干| 欧美性猛片aaaaaaa做受 | 精品一区二区久久久久久久网站 | 国产成人午夜精品影院游乐网 | 九九热这里只有精品6 | 黄色一级免费看 | 一级亚洲| 六月成人网| 视频在线亚洲 | 欧美精品一级 | 特黄一级| 伊人手机在线视频 | av在线一区二区三区 | 久久久久国产精品一区二区 | 欧美日韩亚洲一区 | 日韩一二三区视频 | 逼逼视频| 国产精品午夜电影 | 欧美日韩国产一区 | 中文字幕国产 | 中文字幕日本一区二区 | 国产精品国产三级国产aⅴ原创 | 成人在线a | 一区二区三区不卡视频 | 久久久国产一区 | 国产无套一区二区三区久久 | 国产精品久久久久久久免费大片 | 一区二区av | 国产精品一区二区免费 | 美女张开腿露出尿口 | 人人性人人性碰国产 | 国产视频久久 | 日本在线网站 | 国产综合精品一区二区三区 | 羞羞涩涩在线观看 | 少妇精品久久久久久久久久 |