Каков наилучший/безопасный способ закрыть «ресурс ввода-вывода» в Java?
Исходя из фона python, который использует with, который создает оператор Context Manager для
Чтение и запись файлов, это гарантирует, что ресурс контекста, если он закрыт / очищен (технически это может быть что угодно, но встроенные операции ввода-вывода, например, будут закрываться, когда он выходит за пределы области видимости, даже сокеты)
Итак, взяв этот код:
file = open('workfile', encoding = "utf-8")
read_data = f.read()
file.close()
Рекомендуемый способ - использовать оператор with
with open('workfile', encoding = "utf-8") as file:
read_data = file.read()
К тому времени, когда мы выйдем из области видимости (что буквально означает предыдущий уровень отступа), даже если произошла ошибка, файл всегда будет закрыт.
Конечно, мы могли бы сделать это
try:
file = open('workfile', encoding = "utf-8")
read_data = f.read()
except Exception as _:
pass
finally:
file.close()
Но в большинстве случаев контекстный менеджер лучше. Одна из причин заключается в том, что просто кто-то может забыть записать file.close(), но это просто более чисто и безопасно.
Теперь в java, что рекомендуется, похоже, зачем с этим справляться?
Возьмем, к примеру, Scanner:
public class App
{
public static void main( String... args )
{
Scanner reader = new Scanner(System.in);
try {
System.out.print(">>> username: ");
if (reader.hasNextLine()) {
String username = reader.nextLine();
System.out.format("Hello %s", username);
}
} catch (Exception error) {
System.out.format("Error while reading scanner %s", error);
} finally {
reader.close();
}
}
}
Как видите, вот как я с этим справляюсь,
java способ?Хорошо спасибо. Но я не понимаю одного. Вы говорите о System.in, но я закрываю сканер. Я что-то пропустил ?
Ах я вижу. Я добавляю System.in в сканер.
Ваш сканер сканирует поток — какой поток он сканирует?
Когда вы закрываете этот сканер, вы приближаете поток.
Это действительно хороший момент. Но в некотором смысле хорошо, что я написал это так, так как это дает пример плохого, почему нужно закрывать его слишком рано (я не знал. Просто так получилось)






Я думаю, вы можете использовать try(...){...}catch{} утверждение (try with resource statement).
например:
try (
Scanner reader = new Scanner(System.in);
// other resource initalization
) {
System.out.print(">>> username: ");
if (reader.hasNextLine()) {
String username = reader.nextLine();
System.out.format("Hello %s", username);
}
} catch (Exception error) {
System.out.format("Error while reading scanner %s", error);
}
Начиная с JDK 1.7, () после try используется для инициализации одного или нескольких ресурсов, которые должны быть закрыты после завершения программы. Этот подход не требует блока finally
Итак, это оказывается довольно старой и хорошо документированной функцией, я просто не видел ее. Эта функция называется Заявление о попытке использования ресурсов.
Я сохраняю @shmily как принятый ответ, поскольку он правильный и отвечает на вопрос (а также был первым), я просто добавлю сюда немного, так как это может помочь, и особенно я добавлю код для моего варианта использования что мне это было нужно.
Кроме того, прочитайте последнюю заметку о том, что пользователь «Hovercraft Full Of Eels» сказал о моей реализации.
Итак, в более простой форме мы можем использовать его как
public static void main( String... args )
{
try (Scanner reader = new Scanner(System.in)) {
System.out.print(">>> username: ");
if (reader.hasNextLine()) {
String username = reader.nextLine();
System.out.format("Hello %s", username);
}
} catch (NoSuchElementException | IllegalStateException error) {
System.out.println("Error while reading scanner");
error.printStackTrace();
}
}
Теперь, следуя этим документам baeldung.com/java-try-with-resources, он показывает, что вы действительно можете объявить его снаружи, если long является либо окончательным.
final Scanner scanner = new Scanner(new File("testRead.txt"));
PrintWriter writer = new PrintWriter(new File("testWrite.txt"))
try (scanner;writer) {
// omitted
}
Теперь, для моего конкретного случая использования и первой оценки, я бы не рекомендовал всегда (поскольку на самом деле даже в их примере PrintWriter может выдать ошибку FileNotFoundException), поэтому имеет смысл, если эта ошибка улавливается заранее или ожидается . иначе я вижу, что вы можете сделать вариацию
public static void main( String... args )
{
String fileName = "output.txt";
final Scanner readerTerminal = new Scanner(System.in);
try (
readerTerminal;
PrintWriter writer = new PrintWriter("output.txt");
) {
System.out.print(">>> username: ");
if (readerTerminal.hasNextLine()) {
String username = readerTerminal.nextLine();
System.out.format("Hello %s", username);
writer.write(username);
}
} catch (FileNotFoundException error) {
System.out.format("File %s not found, reason: %s", fileName, error);
error.printStackTrace();
} catch (NoSuchElementException | IllegalStateException error) {
System.out.format("Error while reading scanner, reason: %s", error);
error.printStackTrace();
} catch (Exception error) {
System.out.format("Something went wrong while reading scanner, reason: %s", error);
error.printStackTrace();
}
}
Это особенно имеет смысл, если reader исходит из параметров функции.
Сказав это, эта функция очень и очень похожа на менеджер контекста Python, вы можете определить/настроить свой собственный класс, который реализует AutoCloseable.
public class MyResource implements AutoCloseable {
@Override
public void close() throws Exception {
System.out.println("Closed MyResource");
}
}
Пока он вызывает close, вы можете делать с ним все, что хотите (любая очистка, закрытие ресурсов, закрытие API и многое другое).
Единственная разница, которую я вижу (по крайней мере, с точки зрения пользователя, очевидно, что реализация может сильно отличаться на каждом языке), заключается в том, что для включения этой функции в java вам действительно нужно поместить ее в блок try/catch, тогда как в python это полностью независимый от ошибки - сценарий перехвата.
Почему это актуально?
Как я вижу, использование его в качестве сценария try-catch может ограничить то, что вы на самом деле можете с ним делать, и особенно вы хотите закрыть этот ресурс не потому, что возникла ошибка, а потому, что он существовал в области!, которая находится в на самом деле, как это работает, но это может заставить вас думать, что он закрывается только тогда, когда поднят.
В следующем примере я покажу, как использовать его без необходимости выдавать исключение, но, если честно, это не выглядит так очевидно, как в python (что не является концом света).
Нормальный AutoCloseable
public class Main
{
public static class MyResource implements AutoCloseable {
@Override
public void close() throws Exception {
System.out.println("Resource closed, let's call some resource ... ");
}
}
public static void main( String[] args )
{
final MyResource resource1 = new MyResource();
try (resource1; ) {
System.out.println("Inside Try - catch block");
} catch (Exception error) {
error.printStackTrace();
}
}
}
Ммм, а что, если я не хочу выдавать ошибку? И я просто хочу вызвать какую-то другую вещь, которую я хочу убедиться, что она вызывается, когда этот объект закрывается.
public class Main
{
public static class MyResource implements AutoCloseable {
@Override
public void close() {
System.out.println("Resource closed, let's call some resource ... ");
}
}
public static void main( String[] args )
{
final MyResource resource1 = new MyResource();
try (resource1; ) {
System.out.println("Inside Try - catch block");
}
}
}
Сделайте это так, это выглядит очень похоже на версию Python, мне не нужно ничего ловить (и выбрасывать), так как это не точка моего конкретного автозакрытия,
Круто то, что любые необнаруженные ошибки, которые произошли с этим try-with-resource, все равно вызовут метод close
public class Main
{
public static class MyResource implements AutoCloseable {
@Override
public void close() {
System.out.println("Resource closed, let's call some resource ... ");
}
}
public static void main( String[] args ) throws FileNotFoundException {
final MyResource resource1 = new MyResource();
try (resource1; ) {
System.out.println("Inside Try - catch block");
final Scanner readerTerminal = new Scanner(new File("dont_exits.txt"));
}
}
}
Какие выходы
Inside Try - catch block
Resource closed, let's call some resource ...
Exception in thread "main" java.io.FileNotFoundException: dont_exits.txt (No such file or directory)
at java.base/java.io.FileInputStream.open0(Native Method)
at java.base/java.io.FileInputStream.open(FileInputStream.java:216)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
at java.base/java.util.Scanner.<init>(Scanner.java:639)
at com.koubae.app1.Main.main(Main.java:30)
Process finished with exit code 1
В Java есть try-with-resources, что, вероятно, является лучшим способом, за исключением определенных обстоятельств, один из которых вы используете System.in (как вы это делаете) и хотите избежать его преждевременного закрытия. После закрытия этот поток остается закрытым.
Итак, несмотря на весь приведенный выше пример, я не думаю, что вам следует закрывать сканер, который сканирует System.in (но могут быть причины)
public static void main( String[] args )
{
final Scanner readerUserTerminal = new Scanner(System.in);
try (
readerUserTerminal;
) {
System.out.print(">>> username: ");
if (readerUserTerminal.hasNextLine()) {
String username = readerUserTerminal.nextLine();
System.out.format("Hello %s", username);
}
} catch (NoSuchElementException | IllegalStateException error) {
System.out.format("Error while reading scanner, reason: %s", error);
error.printStackTrace();
} catch (Exception error) {
System.out.format("Something went wrong while reading scanner, reason: %s", error);
error.printStackTrace();
}
// yea no ... will break...
Scanner second = new Scanner(System.in);
System.out.print(">>> username: ");
String username2 = second.nextLine();
System.out.format("Hello %s", username2);
}
выход
Hello fede>>> username: Exception in thread "main" java.util.NoSuchElementException: No line found
at java.base/java.util.Scanner.nextLine(Scanner.java:1651)
at com.koubae.app1.Main.main(Main.java:39)
Так что будьте осторожны! Я на самом деле удивлен, так как во многих учебниках они используют сканер для чтения пользовательского ввода с терминала, а затем показывают, чтобы закрыть его, который не является файлом, а System.in (для меня это похоже на stdin как в C или python sys.stdin Ужасный уровень туториалов. Осторожно! :D
В Java есть try-with-resources, что, вероятно, является лучшим способом, за исключением определенных обстоятельств, один из которых вы используете System.in (как вы это делаете) и хотите избежать его преждевременного закрытия. После закрытия этот поток остается закрытым.