Все ли после моего оператора try должно быть включено в этот оператор try для доступа к переменным внутри него? - PullRequest
6 голосов
/ 30 апреля 2010

Я изучаю Java, и одна вещь, которую я нашел, что мне не нравится, - обычно, когда у меня есть такой код:

import java.util.*;
import java.io.*;

public class GraphProblem
{
    public static void main(String[] args)
    {
        if (args.length < 2)
        {
            System.out.println("Error: Please specify a graph file!");
            return;
        }


        FileReader in = new FileReader(args[1]);
        Scanner input = new Scanner(in);

        int size = input.nextInt();
        WeightedGraph graph = new WeightedGraph(size);

        for(int i = 0; i < size; i++)
        {
            graph.setLabel(i,Character.toString((char)('A' + i)));
        }

        for(int i = 0; i < size; i++)
        {
            for(int j = 0; j < size; j++)
            {
                graph.addEdge(i, j, input.nextInt());
            }
        }

        // .. lots more code

    }
}

У меня есть необработанное исключение вокруг моего FileReader.

Итак, я должен обернуть его в попытку, чтобы поймать это конкретное исключение. Мой вопрос заключается в том, что try { } должно охватывать все после этого в моем методе, который хочет использовать либо мой FileReader (in), либо мой сканер (input)?

Если я не закрою всю оставшуюся часть программы в этом операторе try, то все, что находится за ее пределами, не сможет получить доступ к входу / входу, поскольку оно может быть не инициализировано или инициализировано вне его области действия. Поэтому я не могу изолировать try-catch, чтобы просто сказать часть, которая инициализирует FileReader, и сразу после этого закрыть оператор try.

Итак, является ли "наилучшей практикой" использование оператора try, охватывающего все части кода, которые будут обращаться к переменным, инициализированным в нем? *

Спасибо!

Ответы [ 7 ]

9 голосов
/ 30 апреля 2010

Если вас устраивает , а не , заключающий код после конструктора FileReader, вы можете объявить FileReader вне блока try / catch, например:

FileReader fr = null;
try
{
    fr = new FileReader(args[1]);
}
catch (IOException e)
{
    // handle
}
// code that uses fr

Это разумный дизайн, и я им часто пользуюсь. Убедитесь, что вы правильно обрабатываете в следующем коде возможность того, что fr равно нулю (то есть конструктор выдал исключение).

3 голосов
/ 30 апреля 2010

Это не проблема с блоками try / catch как таковыми. Проблема связана с областью видимости переменной, и вам необходимо иметь блок try / catch из-за проверенного исключения и, следовательно, установить новую область видимости.

У вас также есть другая опция - объявите отмеченное исключение (я) как throws из вашего метода.

public static void main(String[] args) throws IOException {
    // ...code here...
}

Это совершенно законно для main метода.

Если вы хотите обработать исключение, как в большой программе. Вы можете определить конкретную попытку / отловить проблемный блок кода и использовать переменную вне области видимости, объявив переменную вне этой области, как многие ответили:

FileReader fr = null; // must be initialized here if the exception handling code 
                      // does not exit the method
try {
    fr = new FileReader(fileName);
} catch (IOException ex) {
    // log, print, and/or return
    // if you return or exit here then subsequent code can assume that fr is valid
}

Вы также можете поместить код перемещения в другой метод, который работает с исключением:

private static FileReader openReader(String fileName) {
    try {
        return new FileReader(fileName);
    } catch (IOException ex) {
        // log/print exception
        return null; // caller must then expect a null
        // or
        throw new RuntimeException(...); // throw a RuntimeException of some kind (may not be good practice either)
    }
}

Вы также можете переместить код обработки файла в другой метод. Это может быть лучше и позволит вам более правильно следовать принципам открытия / закрытия в окончательной идиоме:

FileReader fr = null;
try {
    fr = new FileReader(fileName);
    Scanner input = new Scanner(fr);

    processInput(input);
} catch (IOException ex) {
    // log/print exception
} finally {
    if (fr != null) {
        try {
            fr.close();
        } catch (IOException ex) {
            // empty
        }
    }
}

private static void processInput(Scanner in) throws IOException {
    // ...processing here
}

Для закрытой части вы можете использовать стороннюю библиотеку (Apache File Utils) или написать простой метод для обеспечения статического безопасного метода закрытия, который не вызывает исключений.

Вам действительно стоит взглянуть на разбиение больших методов на более мелкие единицы, и это часто даст вам гораздо более четкий способ обработки исключений.

2 голосов
/ 30 апреля 2010

Нет. Вы можете объявить это так:

FileReader in = null;
Scanner input = null;

try {
   in = new FileReader(args[1]);
   input = new Scanner(in);
} catch(IOException ioe) {
   //
}
0 голосов
/ 01 мая 2010

Локальные переменные, объявленные в блоке , ограничены для этого блока.

Итак, является ли "наилучшей практикой" использование оператора try, охватывающего все части кода, которые будут обращаться к инициализированным в нем переменным?

В общем, вы должны стараться минимизировать область действия ваших переменных, чтобы сделать их как можно более узкими. Это описано в Effective Java и Code Complete , среди прочих.

Этот код является рецептом для NullPointerExceptions:

FileReader in = null;
try {
  in = new FileReader(filename);
} catch(IOException e) {
  //handle error, but now what?
}
// Code that needs "in", but what if it is null
// because of a FileNotFoundException?
// This code is junk.

Практически все объявления локальных переменных должны включать присваивание.

FileReader in = new FileReader(filename);

Если вы будете следовать этому правилу, у вас получится лучший код.


// .. lots more code

Если у вас гигантские операторы try / catch, похоже, ваши методы слишком велики. Это проблема организации кода, а не то, что связано именно с try / catch.

  public static void main(String[] args) {
    if (args.length < 1) {
      System.out.println("Error: Please specify a graph file!");
      return;
    }
    try {
      processGraphFile(args[0]);
    } catch (IOException e) {
      // Deal with error
    }
  }

  private static void processGraphFile(String filename) throws IOException {
    FileReader in = new FileReader(filename);
    try {
      Scanner scanner = new Scanner(in);
      processGraph(scanner);
      if (scanner.ioException() != null) {
        throw scanner.ioException();
      }
    } finally {
      in.close();
    }
  }

  //other methods

В сторону:

  • Обратите внимание, что массивы Java начинаются с индекса 0, а не 1
  • FileReader удобен во время обучения, но обычно его следует избегать из-за проблем с кодированием; это вряд ли будет проблемой, пока код не покинет ваш компьютер
0 голосов
/ 30 апреля 2010

Если вы только учитесь, ваш метод может вызвать исключение.Однако это не лучший способ обработки исключений: если вы только учитесь, вы, вероятно, хотите больше сосредоточиться на логике того, что делает ваш код.Как только вы освоитесь с этим, тогда вы можете рассмотреть правильную обработку исключений.

Таким образом, ваш код должен выглядеть следующим образом, и вам не придется беспокоиться об операторах try / catch:

public class GraphProblem {
    public static void main(String[] args) throws Exception {
        //your code
    }
}

Опять же, это не лучшая практика, но она позволяет вам сосредоточиться на своей бизнес-логике, а не на обработке ошибок.Когда вы освоитесь с бизнес-логикой, вы сможете углубиться в обработку исключений, которая является темой для себя.

0 голосов
/ 30 апреля 2010

Вы обязательно должны следовать шаблону выше. Если я помню, как я изучал SCJP, компилятор не пытается оптимизировать что-либо, находящееся в блоке try, поэтому вы должны попытаться перехватить его на самом низком уровне.

0 голосов
/ 30 апреля 2010

Да и нет. Try запускает собственную локальную область видимости, поэтому, когда вы делаете это:

try
{
   FileReader reader = new FileReader(args[1]);

   // Stuff
}
catch(Exception e) { }

Переменная reader видна только в пределах ограничений блока try {}. Это предпочтительное поведение. Если вы хотите использовать его снаружи (в случае файлового ввода-вывода, он не рекомендуется), вам нужно объявить его вне try / catch Подходящим примером будет флаг:

boolean isValidFile = false;
try
{
   FileReader reader = new FileReader(args[1]);

   // Do Stuff

   isValidFile = true;
}
catch(Exception e)
{
   // Handle Errors
}

System.out.print("Valid File: ");
System.out.println(isValidFile);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...