воскресенье, 3 июня 2012 г.

Try с ресурсами Java 7

Управление ресурсами с помощью конструкции Try-Catch-Finally, как это было раньше

До выхода Java 7 управление ресурсами требовало явного закрытия, что было несколько утомительно.

Посмотрите на этот метод, который читает данные из файла и выводит их в System.out:
private static void printFile() throws IOException {
    InputStream input = null;

    try {
        input = new FileInputStream("file.txt"); //!

        int data = input.read(); //!
        while(data != -1){
            System.out.print((char) data);
            data = input.read(); //!
        }
    } finally {
        if(input != null){
            input.close(); //!
        }
    }
}
Код, помеченный восклицательным знаком, это места где код может выбросить исключение. Как вы можете видеть это может случиться в трёх местах внутри блока try и в одном месте внутри блока finally.

Блок finally всегда выполняется вне зависимости будет выброшено исключение в блоке try или нет. Это означает, что InputStream будет закрыт вне зависимости от того, что произойдёт в блоке try. Метод close() класса InputStream так же может выбрасывать исключение, если не удалось закрыть поток.

Представте, что исключение возникает в блоке try. Блок finally будет выполнен. После чего представте, что в блоке finally так же возникнет исключение. Как вы думаете, какое из исключений будет на верху стека вызовов?

Исключение из блока finally будет на верху стека вызовов, даже не смотря на то, что исключение из блока try, вероятно,  будет более подходящим.

try с ресурсами

В Java 7 вы можете написать код из примера выше используя try c ресурсами например так:

private static void printFileJava7() throws IOException {

    try(FileInputStream input = new FileInputStream("file.txt")) {

        int data = input.read();
        while(data != -1){
            System.out.print((char) data);
            data = input.read();
        }
    }
}
Обратите на первую строку метода:
try(FileInputStream input = new FileInputStream("file.txt")) {
Это констукция try с ресурсами. FileInputStream объявлена в круглых скобках после ключевого слова try. Кроме того для класса FileInputStream создана и объявлена переменная.

В конце блока try FileInputStream будет закрыт автоматически. Это возможно потому, что FileInputStream реализует интерфейс java.lang.AutoCloseable. Все классы, реализующие этот интерфейс, могут быть использованы в конструкции try с ресурсами.

Если исключение будет выброшенно и в конструкции try с ресурсами и при закрытии FileInputStream (вызову метода close()), то исключение выброшенное в внутри блока try будет выборшенно во внешний мир. Исключение, выбрасываемое во время закрытия FileInputStream будет подавлено. Это альтернатива тому, что происходит, когда мы используем конструкцию try-finally.


Использование нескольких ресурсов

Внутри констукции try с ресурсами вы можете использовать несколько ресурсов и все они будут автоматически закрыты. Например:

private static void printFileJava7() throws IOException {

    try(  FileInputStream     input         = new FileInputStream("file.txt");
          BufferedInputStream bufferedInput = new BufferedInputStream(input)
    ) {

        int data = bufferedInput.read();
        while(data != -1){
            System.out.print((char) data);
    data = bufferedInput.read();
        }
    }
}
В этом примере создаются два ресурса внутри круглых скобок после ключевого слова try — FileInputStream  и  BufferedInputSream. Оба ресурса будут автоматически закрыты, как только код дойдёт до конца блока try

Ресурсы будут закрыты в обратном порядке тому, в котором они создавались в круглых скобках. Сначала будет закрыт BufferedInputSream, затем FileInputStream.


Создание классов реализующих интерфейс AutoClosable

Конструкция try с ресурсами может работать не только со встроенными в стандартную библиотеку языка Java классами. Вы так же можете реализовывать интерфейс java.lang.AutoCloseable в вашем собственном классе, и затем использовать его в конструкции try с ресурсами.

Интерфейс AutoCloseable описан всего один метод с именем close(). Вот как он выглядит:

public interface AutoClosable {

    public void close() throws Exception;
}
Любой класс, реализующий этот интерфейс может быть использован в конструкции  try с ресурсами. Вот пример такого класса:

public class MyAutoClosable implements AutoCloseable {

    public void doIt() {
        System.out.println("MyAutoClosable Работает!");
    }

    @Override
    public void close() throws Exception {
        System.out.println("MyAutoClosable закрыт!");
    }
}
Метод doIt() не является частью интерфейса AutoCloseable. Он присутствует в нашем классе потому, что мы хотим сделать нечто более, чем просто закрыть объект.

Приведём пример, как можно использовать созданный нами класс в конструкции try с ресурсами:

private static void myAutoClosable() throws Exception {

    try(MyAutoClosable myAutoClosable = new MyAutoClosable()){
        myAutoClosable.doIt();
    }
}
Если мы запустим этот код, то увидим строку, выведенную с помощью System.out из вызванного метода myAutoClosable():
MyAutoClosable работает!
MyAutoClosable закрыт!
Как вы можете видеть, конструкция try с ресурсами достаточно мощный способ быть уверенным в том, что ресурсы внутри try-catch блока будут закрыты корректно, вне зависимости создали мы эти ресурсы на основе своих собственных классов, или с помощью встроенных в стандартную библиотеку языка Java.

Это перевод статьи.

суббота, 2 июня 2012 г.

Вычисления в Java. Math. StrictMath. strictfp.

В стандартной библиотеке языка Java содержатся два класса для произведения базовых математических операций, таких как возведение в степень, взятие логарифма, синуса, косинуса и так далее. Эти классы отличаются между собой различной точностью представления чисел на разных архитектурах процессора. Это классы Math и StrictMath

При использовании StrictMath вы получите абсолютно одинаковый результат вычислений не зависимо от процессора, который установлен у пользователя вашей программы. Прямо говоря, эта функция задумывалась создателями языка как базовая для всех примитивных типов данных и манипуляций с ними. Но как оказалось это не такая простая задача и обеспечение одинаковых результатов на различных платформах значительно уменьшают быстродействие. Большинство задач просто не требуют такой точности в вычислениях, для них гораздо важнее скорость этих вычислений. Это привело к разделению математической части библиотеки на быструю и выдающую одинаковый результат на разных процессорах.

Таким образом если в решении вашей задачи важнее скорость — используйте класс Math, если же вам необходимо одинаковая работа (с точностью до бита) с примитивными типами данных, то ваш выбор — StrictMath. Константы и методы у них одинаковы, и более того некоторые методы класса Math просто вызывают аналогичные методы класса StrictMath.

Рассмотрим не типичные методы этих классов:

  • cbrt — возвращает кубический корень аргумента;
  • ceil — округляет значение в большую сторону;
  • copySign — возвращает первый аргумент со знаком второго;
  • floor — округляет в меньшую сторону;
  • hypot — возвращает квадратный корень из суммы аргументов: sqrt(x2+y2);
  • max — возвращает большее из двух чисел;
  • min — возвращает меньшее из двух чисел;
  • nextAfter — возвращает число с плавающей точкой, максимально приближенной к первому аргументу со стороны второго аргумента;
  • nextUp — то же самое, что и nextAfter, но только всегда в большую сторону;
  • random — возвращает случайное число от 0 до 1;
  • round — обычное округление;
  • signum — возвращает 1.0 если число больше 0 и -1.0 если число меньше нуля и ноль, если аргумент равен нулю;
  • toDegrees — переводит радианы в градусы;
  • toRadians — переводит градусы в радианы;

Пример:
  System.out.println(Math.cbrt(8)); // 2.0
  System.out.println(Math.ceil(2.1)); // 3.0
  System.out.println(Math.copySign(3.4, -11.2)); // -3.4
  System.out.println(Math.floor(9.999)); // 9.0
  System.out.println(Math.hypot(3,4)); // 5.0
  System.out.println(Math.max(3, 2)); // 3
  System.out.println(Math.min(3, 2)); // 2
  System.out.println(Math.nextAfter(4.45634D, -100)); // 4.456339999999999
  System.out.println(Math.nextUp(4.45634D)); // 4.456340000000001
  System.out.println(Math.random()); // 0.45357720245424116
  System.out.println(Math.round(4.5)); // 5
  System.out.println(Math.signum(-34.78)); // -1.0
  System.out.println(Math.toDegrees(Math.PI)); // 180.0
  System.out.println(Math.toRadians(180)); // 3.141592653589793
Остальные методы смотрите в документации: java.lang.Math, jav.lang.StrictMath.

А что нам делать, если мы хотим создать класс, в методах которого все действия с числами будут также гарантированно одинаковые на различных платформах? Или не класс, а метод? В таком случае в языке Java существует ключевое слово strictfp. Его можно добавлять к классу, интерфейсу и методам. Добавив его мы можем быть уверены, что на всех платформах при работе с числами мы получим одинаковый результат.