시도 -과 - 자원 자바

배경

우리 모두 알다시피, 등, 스트리밍 파일 또는 소켓 연결과 같은 모든 시스템 리소스에 열려 개발자는 수동으로 종료해야하거나 프로그램의 지속적인 운영과, 자원 누수 주요 사고에 축적됩니다.

자바의 강과 호수에서, 마지막으로, 당신은 무술의 악마에 의해 소유 될 때, 당신은 또한 자신을 저장하는 작업의 숫자를 할 수 있다고 보장 할 수 있습니다라고하는 노력이있다. 고대에는, 처리 자원 가까운 코드는 일반적으로 finally 블록에 기록됩니다. 동시에 여러 자원을 열 경우, 그것은 악몽 같은 시나리오를 나타납니다 :

public class Demo {
    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 {
            if (bin != null) {
                try {
                    bin.close();
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
                finally {
                    if (bout != null) {
                        try {
                            bout.close();
                        }
                        catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}

오, 맙소사! ! ! 비즈니스 코드보다 더 많은 자원 코드를 닫습니다! ! ! 우리뿐만 아니라 폐쇄해야하기 때문이다 BufferedInputStream, 또한 당신이 끌 경우 있는지 확인해야합니다 BufferedInputStream이상이 나타나  BufferedOutputStream제대로 닫아야합니다. 그래서 우리는 마침내 마지막으로 대법의 지원을 중첩했다. 더 개방 자원, 마지막으로 깊은 중첩 될 것으로 예상된다! ! !

"것은 우리가 르 생각하지 않는다 ~"더욱 소름이 끼치는 그, 파이썬 프로그래머는이 문제에 직면, 실제로 말을 아주 작은 미소를 지었다입니다 :

그러나 형제 모 패닉! 우리는 코드를 닫 농업 자원의 자신의 코드를 작성하지 않고 리소스를 열려면 새로운 시도 -과 - 자원 구문 설탕에 자바 1.7를 사용할 수 있습니다. 나는 더 이상 내 어머니를 가지고 가지 않았다 손에 대해 걱정할 필요가 끊어! 우리는 시도 -과 - 자원 이전 예제를 다시 작성 :

public class TryWithResource {
    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();
        }
    }
}

그것은 간단하지 않다? 그것은 흥분 아닌가요? 파이썬 프로그래머는 더 이상 멸시 할 필요가 없습니다! 그럼, 다음은 구체적인 구현 원리와 내부 메커니즘을 설명합니다.

순서는 시도 -과 - 자원을 조정 할 수 있도록에서 자원이 구현해야하는 AutoClosable인터페이스를. 인터페이스의 구현 클래스의 요구를 다시 작성하는 close방법 :

public class Connection implements AutoCloseable {
    public void sendData() {
        System.out.println("正在发送数据");
    }
    @Override
    public void close() throws Exception {
        System.out.println("正在关闭连接");
    }
}

클래스를 호출합니다 :

public class TryWithResource {
    public static void main(String[] args) {
        try (Connection conn = new Connection()) {
            conn.sendData();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}

출력을 실행 한 후 :

正在发送数据
正在关闭连接

우리는 결과로 볼 수있다, close 메소드가 자동으로 호출된다.

원칙

그래서 그것을 수행하는 방법인가? 나는 사실,이 모든 컴파일러 위대하신 하나님 유령에서, 당신이 짐작해야 스마트 믿습니다. 우리는 클래스 파일의 단지 예를 디 컴파일 :

public class TryWithResource {
    public TryWithResource() {
    }
    public static void main(String[] args) {
        try {
            Connection e = new Connection();
            Throwable var2 = null;
            try {
                e.sendData();
            } catch (Throwable var12) {
                var2 = var12;
                throw var12;
            } finally {
                if(e != null) {
                    if(var2 != null) {
                        try {
                            e.close();
                        } catch (Throwable var11) {
                            var2.addSuppressed(var11);
                        }
                    } else {
                        e.close();
                    }
                }
            }
        } catch (Exception var14) {
            var14.printStackTrace();
        }
    }
}

아니, 처음 15 27 라인, 컴파일러가 자동으로 마침내 일발 방법 자원을 우리를 도와 차단하고, 그것에서 발생했다는 것을, 방법의 너무 가까이 예는 실행시에 실행됩니다.

예외 마스크

난 그렇게 믿어 당신이 확실히 발견주의, 더 고대보다 단지 디 컴파일 코드 (21 행) 코드를 작성합니다 addSuppressed. 이 코드의 의도를 이해하기 위해, 우리는 약간 이전 예제를 수정 : 우리 것 고대 수동으로 특이한 방법으로 폐쇄하고있는 단지 코드를 다시 sendDataclose던져 예외 방법 :

public class Connection implements AutoCloseable {
    public void sendData() throws Exception {
        throw new Exception("send data");
    }
    @Override
    public void close() throws Exception {
        throw new MyException("close");
    }
}

주요 방법을 수정합니다 :

public class TryWithResource {
    public static void main(String[] args) {
        try {
            test();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
    private static void test() throws Exception {
        Connection conn = null;
        try {
            conn = new Connection();
            conn.sendData();
        }
        finally {
            if (conn != null) {
                conn.close();
            }
        }
    }
}

작업 후 우리는 발견 :

basic.exception.MyException: close
	at basic.exception.Connection.close(Connection.java:10)
	at basic.exception.TryWithResource.test(TryWithResource.java:82)
	at basic.exception.TryWithResource.main(TryWithResource.java:7)
	......

그런데, 문제는 우리는 그렇게 볼 수있는 상단에 예외를 던질 수 있기 때문에, 마지막으로 발생한 예외이다 - 즉, close메소드가 발생 MyException하고 sendData던져 Exception무시했다. 이것은 비정상적인 화면이라고합니다. 정보의 이상 손실 때문에, 예외 마스크 작업 초과 근무로했다 프로그래머는 우리가 제거되지 수있는 방법, 버그, 그래서 악성 종양을 찾기 위해, 몇 가지 버그를 발견하기가 매우 어려워진다 될 수 있습니다! 다행스럽게도, 자바 1.7부터이 문제를 해결하기 위해, 대한 거물 Throwable클래스가 추가하는 addSuppressed방법을 추가로 지원하기 위해 비정상적인 방패를 피하기 위해, 또 다른 예외에 대한 예외가 될 것입니다. 그런 다음 예외 정보는 어떤 형식으로 출력 그것에 의해 마스크 것인가? 우리는 포장의 시도 -과 - 자원의 주요 방법으로 다시 실행 :

java.lang.Exception: send data

	at basic.exception.Connection.sendData(Connection.java:5)
	at basic.exception.TryWithResource.main(TryWithResource.java:14)
	......
	Suppressed: basic.exception.MyException: close
		at basic.exception.Connection.close(Connection.java:10)
		at basic.exception.TryWithResource.main(TryWithResource.java:15)
		... 5 more

그것은 이상에 이상을 볼 수있는 Suppressed힌트,이 예외가 실제로 두 가지 예외로 구성되어 있음을 알 MyException억제 비정상이다. 축하합니다!

작은 문제

그 과정에서 시도 -과 - 자원 사용, 우리는 자원을 이해하는 데 필요한 close방법의 내부 논리를 달성하기 위해. 그렇지 않으면, 그냥 누수를 자원으로 이어질 수 있습니다.

예를 들어, 자바 BIO에서 데코레이터 패턴의 큰 숫자를 사용하여. 장식을 호출 할 때 close이 방법은 기본적으로 흐름 패키지 인테리어 데코레이터를 호출 할 때 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판독 바이트 내로 기입된다 GZIPOutputStream. GZIPOutputStream그것은 사실입니다 FileOutputStream장식. 때문에 시도 -과 - 자원의 특성으로 fin.close 코드가 실제로 후 마지막으로 컴파일 블록이 다시 나타납니다, 그리고 () 메서드를 호출하고 내부의 out.close () 메소드. 우리는 보는 GZIPOutputStream클래스의 가까운 방법 :

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

변수가 실제로 장식 표현에서 우리는 볼 수 있습니다 FileOutputStream클래스를. 변수 부르고에 close있어서 전을 GZIPOutputStream또한 제조 finish계속 동작 FileOutputStream되며, 예외가 발생하면,이 때의 압축 정보를 쓰기 out.close()방법의 BE는 생략하지만, 이것은 자원의 낮은 수준 확대 방식이다. 올바른 접근 방식은 자원의 낮은 수준은 시도 -과 - 자원에 개별적으로 선언해야하고, 보증 해당 close메서드를 호출 할 필요가 있습니다. 이전 예제에서, 우리는 개별적으로 선언 할 필요 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()코드를이 확실히 실제 스트림이 닫혀 보장 할 수있을 것입니다.

개요

당신이 단어를 배우면, 그들을 사용하는 것은 매우 쉬운 일이 아닙니다 방법

추천

출처www.cnblogs.com/kakaisgood/p/12186217.html