implementuje Closeable lub implementuje AutoCloseable

128

Jestem w trakcie nauki języka Java i nie mogę znaleźć dobrego wyjaśnienia na implements Closeabletemat implements AutoCloseableinterfejsów i.

Kiedy zaimplementowałem interface Closeable, moje Eclipse IDE utworzyło metodę public void close() throws IOException.

Mogę zamknąć strumień pw.close();bez interfejsu. Ale nie mogę zrozumieć, jak mogę zaimplementować close()metodę za pomocą interfejsu. Jaki jest cel tego interfejsu?

Chciałbym również wiedzieć: jak mogę sprawdzić, czy IOstreamrzeczywiście był zamknięty?

Użyłem podstawowego kodu poniżej

import java.io.*;

public class IOtest implements AutoCloseable {

public static void main(String[] args) throws IOException  {

    File file = new File("C:\\test.txt");
    PrintWriter pw = new PrintWriter(file);

    System.out.println("file has been created");

    pw.println("file has been created");

}

@Override
public void close() throws IOException {


}
malas
źródło
2
Myślę, że wszystko już zostało powiedziane, ale może interesuje Cię następujący artykuł o przymierzaniu zasobów: docs.oracle.com/javase/tutorial/essential/exceptions/… . Może to być również pomocne w zrozumieniu udzielonych odpowiedzi.
crusam

Odpowiedzi:

40

Wydaje mi się, że nie znasz zbyt dobrze interfejsów. W opublikowanym kodzie nie musisz implementować AutoCloseable.

Musisz (lub powinieneś) wdrożyć Closeablelub AutoCloseablejeśli masz zamiar zaimplementować własne PrintWriter, które obsługuje pliki lub inne zasoby, które należy zamknąć.

W Twojej realizacji wystarczy zadzwonić pw.close(). Powinieneś to zrobić w ostatnim bloku:

PrintWriter pw = null;
try {
   File file = new File("C:\\test.txt");
   pw = new PrintWriter(file);
} catch (IOException e) {
   System.out.println("bad things happen");
} finally {
   if (pw != null) {
      try {
         pw.close();
      } catch (IOException e) {
      }
   }
}

Powyższy kod jest związany z Javą 6. W Javie 7 można to zrobić bardziej elegancko (zobacz tę odpowiedź ).

Kai
źródło
3
Dlaczego tylko z PrintWriter? Zwłaszcza AutoClosableprzedmioty mogą być używane w wielu innych okolicznościach niż tylko PrintWriter...
glglgl
3
Masz absolutną rację. Pytanie dotyczyło, PrintWriterwięc wspomniałem o nim bardziej szczegółowo.
Kai
7
Po co opisywać sytuację dotyczącą języka Java 6 w kontekście AutoCloseable? Lepiej pokaż try-with-resourceszamiast tego…
ᴠɪɴᴄᴇɴᴛ
191

AutoCloseable(wprowadzone w Javie 7) umożliwia użycie idiomu try-with-resources :

public class MyResource implements AutoCloseable {

    public void close() throws Exception {
        System.out.println("Closing!");
    }

}

Teraz możesz powiedzieć:

try (MyResource res = new MyResource()) {
    // use resource here
}

a JVM zadzwoni close()automatycznie.

Closeable to starszy interfejs. Z jakiegoś powoduAby zachować zgodność z poprzednimi wersjami, projektanci języków postanowili stworzyć oddzielny. Pozwala to nie tylko na użycie wszystkich Closeableklas (takich jak rzucanie strumieni IOException) w próbach z zasobami, ale także pozwala na rzucanie bardziej ogólnych sprawdzonych wyjątków z close().

W razie wątpliwości użyj AutoCloseable, użytkownicy Twojej klasy będą wdzięczni.

Tomasz Nurkiewicz
źródło
107
Powód jest prosty: Closeable.close()rzuty IOException. Wiele close()metod, które mogłyby skorzystać z try-with-resources, rzucają inne sprawdzone wyjątki (np. java.sql.Connection.close()Tak AutoCloseable.close()rzuca Exception. Zmiana istniejącego Closeablekontraktu spowodowałaby zerwanie wszystkich istniejących aplikacji / bibliotek opierających się na kontrakcie, który close()tylko generuje, IOExceptiona nie wszystkie (zaznaczone) wyjątki.
Zaznacz Rotteveel
4
@MarkRotteveel: +1, dzięki. Poprawiłem swoją odpowiedź, aby odzwierciedlić Twoje sugestie i komentarze.
Tomasz Nurkiewicz
9
A także: Closeable.close()musi być idempotentny. AutoCloseable.close()nie jest, chociaż nadal jest to zdecydowanie zalecane.
Lukas Eder
2
Nie używaj też wartości domyślnych public void close( ) throws Exception- jeśli możesz, użyj bardziej szczegółowego wyjątku (np. IOException)
gerardw
3
Closeablenie gwarantuje idempotencji. To wymaga idempotentność w realizacji użytkownika o close()metodzie. A IOExceptionto, czy jest bardziej szczegółowe / odpowiednie, zależy od przypadku użycia.
xdhmoore
71

Closeableextends AutoCloseablei jest specjalnie dedykowany dla strumieni IO: zgłasza IOException zamiast Exception i jest idempotentny, podczas gdy AutoCloseable nie zapewnia tej gwarancji.

Wszystko to jest wyjaśnione w javadoc obu interfejsów.

Zaimplementowanie AutoCloseable (lub Closeable) pozwala na użycie klasy jako zasobu konstrukcji try-with-resources wprowadzonej w Javie 7, co umożliwia automatyczne zamykanie takich zasobów na końcu bloku, bez konieczności dodawania końcowego bloku, który zamyka zasób jawnie.

Twoja klasa nie stanowi zamykalnego zasobu i nie ma absolutnie żadnego sensu w implementowaniu tego interfejsu: IOTest nie może zostać zamknięty. Nie powinno być nawet możliwe utworzenie jej wystąpienia, ponieważ nie ma żadnej metody instancji. Pamiętaj, że implementacja interfejsu oznacza, że ​​istnieje plik relacja is-a między klasą a interfejsem. Nie masz tutaj takiego związku.

JB Nizet
źródło
5
Po prostu zaimplementuj Closable dla klas związanych ze strumieniami i AutoClosable dla innych, które wymagają funkcji autoclose .
lospejos
7

Oto mały przykład

public class TryWithResource {

    public static void main(String[] args) {
        try (TestMe r = new TestMe()) {
            r.generalTest();
        } catch(Exception e) {
            System.out.println("From Exception Block");
        } finally {
            System.out.println("From Final Block");
        }
    }
}



public class TestMe implements AutoCloseable {

    @Override
    public void close() throws Exception {
        System.out.println(" From Close -  AutoCloseable  ");
    }

    public void generalTest() {
        System.out.println(" GeneralTest ");
    }
}

Oto wynik:

GeneralTest 
From Close -  AutoCloseable  
From Final Block
Lova Chittumuri
źródło
Lepiej też napisać wyjście, żeby nie było potrzeby tworzenia projektu próbnego dla tak krótkiego kodu.
raxetul
Czy w metodzie close () nie musimy jawnie zamykać zasobu? Być może jest tylko oświadczenie drukowane.
Shailesh Waghmare
@ShaileshWaghmare tak, dokładnie. ale dla celów testowych wspomniałem we fragmencie kodu.
Lova Chittumuri
@LovaChittumuri Czy to będzie jak this.close()czy coś w kodzie ?, ponieważ jest wywoływane automatycznie. (Dla pewności)
Shailesh Waghmare
@shailesh Waghmare Chcesz mnie przetestować.
Lova Chittumuri
6

try-with-resourcesKomunikat.

try-with-resources statementJest trystwierdzenie, że deklaruje jeden lub więcej zasobów. A resourceto obiekt, który musi zostać zamknięty po zakończeniu programu. try-with-resources statementGwarantuje, że każdy zasób jest zamknięty na końcu instrukcji. Każdy obiekt, który implementuje java.lang.AutoCloseable, który zawiera wszystkie implementowane obiekty java.io.Closeable, może być używany jako zasób.

Poniższy przykład odczytuje pierwszy wiersz z pliku. Używa instancji BufferedReaderdo odczytywania danych z pliku. BufferedReaderto zasób, który musi zostać zamknięty po zakończeniu programu z nim:

static String readFirstLineFromFile(String path) throws IOException {
    try (BufferedReader br =
                   new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }
}

W tym przykładzie zasób zadeklarowany w instrukcji try-with-resources to BufferedReader. Instrukcja deklaracji pojawia się w nawiasach bezpośrednio po słowie kluczowym try. Klasa BufferedReaderw języku Java SE 7 i nowszych implementuje interfejs java.lang.AutoCloseable. Ponieważ BufferedReaderinstancja jest zadeklarowana w instrukcji try-with-resource, zostanie zamknięta niezależnie od tego, czy instrukcja try zakończy się normalnie, czy nagle (w wyniku BufferedReader.readLinewyrzucenia metody IOException).

W wersjach starszych niż Java SE 7 można użyć finallybloku, aby upewnić się, że zasób zostanie zamknięty, niezależnie od tego, czy instrukcja try zakończy się normalnie, czy nagle. Poniższy przykład używa finallybloku zamiast try-with-resourcesinstrukcji:

static String readFirstLineFromFileWithFinallyBlock(String path)
                                                     throws IOException {
    BufferedReader br = new BufferedReader(new FileReader(path));
    try {
        return br.readLine();
    } finally {
        if (br != null) br.close();
    }

}

Zapoznaj się z dokumentacją .

inder
źródło
6

Niedawno przeczytałem podręcznik Java SE 8 Programmer Guide ii.

Znalazłem coś na temat różnicy między AutoCloseablevsCloseable .

AutoCloseableInterfejs został wprowadzony w Javie 7. Wcześniej inny interfejs o nazwie istniały Closeable. Był podobny do tego, czego chcieli projektanci języka, z następującymi wyjątkami:

  • Closeableogranicza typ zgłaszanego wyjątku IOException.
  • Closeable wymaga, aby implementacje były idempotentne.

Projektanci języka kładą nacisk na kompatybilność wsteczną. Ponieważ zmiana istniejącego interfejsu była niepożądana, stworzyli nowy o nazwie AutoCloseable. Ten nowy interfejs jest mniej rygorystyczny niż Closeable. Ponieważ Closeablespełnia wymagania AutoCloseable, zaczął wdrażać, AutoCloseablegdy ten ostatni został wprowadzony.

Arvind Katte
źródło
1
Zamiast mówić, że „Ten nowy interfejs jest mniej rygorystyczny niż Closeable”, proponuję powiedzieć „Ten nowy interfejs może być używany w bardziej ogólnych kontekstach, w których wyjątek zgłaszany podczas zamykania niekoniecznie jest IOException”. W świecie Java bycie „mniej rygorystycznym” ma negatywny wpływ na to.
fontanna