Платформа Java SE и ее составляющие

1 Теоретическая часть

1.1 Общая характеристика платформы Java SE

Java Platform, Standard Edition, (Java SE) - стандартная версия платформы Java, предназначенная для создания и выполнения апплетов и приложений, рассчитанных на индивидуальное пользование или на использование в масштабах малого предприятия. Java SE определяется спецификацией пакетов и классов, которые обеспечивают решение задач по следующим направлениям:

  • работа с математическими функциями
  • работа с контейнерными классами
  • работа с таймером и календарем
  • работа с текстом
  • интернационализация и локализация
  • работа с регулярными выражениями
  • работа с потоками ввода-вывода и файловой системой
  • работа с XML
  • cериализация и десериализация
  • разработка приложений AWT и апплетов
  • создание программ графического интерфейса с помощью библиотеки javax.swing
  • использование графических средств
  • поддержка печати
  • поддержка работы со звуком
  • использование RTTI, рефлексии и загрузчиков классов
  • использование потоков управления
  • работа с базами данных
  • Java Native Interface
  • средства выполнения сценариев (скриптов)
  • поддержка сетевого взаимодействия
  • взаимодействие с программной средой
  • обеспечение безопасности приложений
  • поддержка ведения системного журнала
  • развертывание Java-приложений

Ниже рассматриваются некоторые возможности Java SE, которые не будут освещены в отдельных темах.

1.2 Особенности работы с математическими функциями

Как уже говорилось раньше, в Java реализован класс Math. Этот класс предоставляет более 50 математических функций (с учетом реализации для разных типов). Их можно сгруппировать следующим образом:

  • определение абсолютной величины (abs()), знака (signum()), округления (round(), rint()), ближайших целых сверху и снизу (ceil(), floor());
  • присваивание знака одного числа другому числу (copySign());
  • получение минимального и максимального из двух значений (min(), max());
  • определение ближайшего различимого вещественного значения (nextUp(), nextAfter());
  • вычисление степени (pow()), квадратного (sqrt()) и кубического (cbrt()) корня;
  • вычисление гипотенузы по двум катетам (hypot())
  • вычисление ex (exp()), ex - 1 (expm1()), натурального и десятичного логарифмов (log(), log10()), а также ln(x + 1) (log1p());
  • вычисление тригонометрических (sin(), cos(), tan()) и обратных тригонометрических (asin(), acos(), atan(), atan2()) функций;
  • перевод из градусов в радианы и обратно (toRadians(), toDegrees());
  • вычисление гиперболических функций (sinh(), cosh(), tanh())
  • получение степени в двоичном представлении числа (getExponent()), умножение числа на степень двойки (scalb());
  • специальные функции внутреннего использования (ulp(), IEEEremainder()).

В большом количестве приложений используются случайные и псевдослучайные числа. Например, они нужны в задачах математической статистики, численного моделирования, в компьютерных играх и т. д. Но чаще всего они применяются для тестирования программного обеспечения. Получение случайных значений может быть осуществлено как с помощью функции random() класса Math и с помощью специального класса java.util.Random. Первый вариант дает псевдослучайное число в диапазоне от 0 до 1. Использование класса Random позволяет получить более разнообразные результаты. Конструктор класса Random без параметров инициализирует датчик псевдослучайных чисел так, что последовательности случайных значений практически не повторяются. Если для отладки нам необходимо каждый раз получать одни и те же случайные значения, следует воспользоваться конструктором с целым параметром. Параметром может быть любое целое, которое инициализирует собой датчик случайных чисел.

В следующей таблице представлены функции класса java.util.Random, позволяющие получить различные псевдослучайные значения.

Функция
Описание
nextBoolean() возвращает следующее равномерно распределенное значение типа boolean
nextDouble() возвращает следующее значение типа double, равномерно распределенное на интервале от 0 до 1
nextFloat() возвращает следующее значение типа float, равномерно распределенное на интервале от 0 до 1
nextInt() возвращает следующее равномерно распределенное значение типа int
nextInt(int n) возвращает следующее значение типа int , равномерно распределенное от 0 до n (не включая n)
nextLong() возвращает следующее равномерно распределенное значение типа long
nextBytes(byte[] bytes) заполняет массив целых типа byte случайными значениями
nextGaussian() возвращает следующее значение типа double, распределенное на интервале от 0 до 1 по нормальному закону

При работе с числами с плавающей точкой иногда необходимо обеспечить гарантированно одинаковый результат для всех платформ. Такую возможность обеспечивает модификатор strictfp - ключевое слово языка программирования Java, позволяющее произвести вычисления с плавающей точкой одинаково, независимо от операционной системы и аппаратной платформы, даже если они позволяют получить большую точность. Введено в Java, начиная с версии JVM 1.2.

До JVM 1.2, все промежуточные вычисления с плавающей точкой производились только с одинарной или двойной точностью. Вследствие этого, ошибки расчета (ошибки округления, арифметическое переполнение и антипереполнение - ситуация, когда результат операции с плавающей точкой становится настолько близким к нулю, что порядок числа выходит за пределы разрядной сетки) происходили чаще, чем в архитектурах, которые осуществляли промежуточные вычисления с большей точностью.

Начиная с версии JVM 1.2, промежуточные вычисления не ограничиваются стандартной точностью 32 и 64 битами. На платформах, которые могут производить вычисления с другой точностью (например, с 80-битной двойной расширенной точностью на x86 или x86-64 платформах), JVM использует их возможности (по умолчанию), что помогает предотвратить ошибки округления и переполнения и тем самым повысить точность.

Однако бывает необходимо, чтобы вычисления с плавающей точкой производились одинаково на всех платформах. Этого позволяет достичь модификатор strictfp, ограничивающий значения промежуточных вычислений одинарной и двойной точностью (точно так же, как в более ранних версиях JVM). Модификатор strictfp может быть использован перед именами классов, интерфейсов и неабстрактных методов.

Для математических вычислений кроме встроенных типов-значений можно использовать объекты классов, производных от java.lang.Number. Ранее были рассмотрены классы Byte, Double, Float, Integer, Long и Short, производные от Number. Существуют также классы java.math.BigInteger и java.math.BigDecimal, позволяющие работать с числами произвольной точности.

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

BigInteger number1 = new BigInteger(100, new Random()); // 100 - количество бит
BigInteger number2 = new BigInteger("12345678901234567890");
int n = 100000;
// Целое значение нужно преобразовать в строку:
BigInteger number3 = new BigInteger(n + "");

В отличие от традиционных операций, необходимо использовать методы add(), subtract(), multiply() и divide() с параметром типа BigInteger (второй операнд). Существует также целый ряд дополнительных функций для реализации различных операций и преобразования типов.

Класс BigDecimal создается и работает аналогично. Кроме того, имеется ряд дополнительных конструкторов. В частности, для создания объекта можно использовать BigInteger. Поддерживается также большое количество методов, реализующих различные операции.

Для удобства работы в классе BigInteger определены константы BigInteger.ZERO, BigInteger.ONE и BigInteger.TEN. Аналогичные константы определены в классе BigDecimal.

1.3 Работа с календарем

Классы Calendar и Date из пакета java.util предоставляют методы работы с датой и временем. Объект класса Date хранит число миллисекунд, прошедших с 1 января 1970 г. 00:00:00 по Гринвичу. Это "день рождения" UNIX, он называется "Epoch".

Класс Date реализует два конструктора. Конструктор Date() заносит в создаваемый объект текущую дату и время в формате текущих региональных настроек:

Date aDate = new Date();

Конструктор Date(long millisec) заносит в создаваемый объект указанное число. Например, получить число миллисекунд, прошедших с момента Epoch, можно статическим методом System.currentTimeMillis():

Date date = new Date(System.currentTimeMillis());

Многие конструкторы и методы класса Date() считаются устаревшими, так как класс не обеспечивает возможности интернационализации приложений. Для задания дат следует использовать класс Calendar.

Методы класса Date:

  • long getTime() - получить значение, хранящееся в объекте
  • void setTime(long newTime) - установить новое значение
  • boolean after(long when) - возвращает true, если время when больше данного;
  • boolean before(long when) - возвращает true, если время when меньше данного.

Преобразование миллисекунд, хранящихся в объектах класса Date, в текущее время и дату производится методами класса Calendar. Класс Calendar является абстрактным классом, который предоставляет методы для преобразования определенного момента времени в набор полей календаря (год, месяц, день месяца, час и т. д.), а также для работы с полями календаря (например, получить дату следующей недели).

В Java есть только одна реализация класса Calendar - класс GregorianCalendar, производный от Calendar. Создать экземпляр класса GregorianCalendar можно с помощью статического метода getInstance():

Calendar gregorianCalendar = Calendar.getInstance(); // или Calendar gregorianCalendar = GregorianCalendar.getInstance();

В классе Calendar определены целочисленные константы JANUARYDECEMBER (месяца) , константы MONDAYSUNDAY (дни недели) , а также методы, позволяющие прочитать или установить первый день недели, время, часовой пояс и др.

Дочерний класс GregorianCalendar имеет конструкторы, позволяющие определить календарь по времени:

GregorianCalendar()
GregorianCalendar(int year, int month, int date) 
GregorianCalendar(int year, int month, int date, int hour, int minute) 
GregorianCalendar(int year, int month, int date, int hour, int minute, int second)

Можно привести пример работы с классами Date и Calendar:

package ua.inf.iwanoff.calendar;

import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Date;

public class DateTest {

    public static void main(String[] args) {
        //Установка времени с использованием экземпляра Date:
        Date date = new Date(System.currentTimeMillis());
        Calendar calendar = GregorianCalendar.getInstance();
        calendar.setTime(date);
        System.out.println(calendar.getTime());

        //Вызов методов getter и setter объекта Calendar:
        calendar.set(Calendar.MONTH, Calendar.JULY);
        calendar.set(Calendar.DAY_OF_MONTH, 15);
        calendar.set(Calendar.YEAR, 1978);
        calendar.set(Calendar.HOUR, 2);
        calendar.set(Calendar.MINUTE, 15);
        calendar.set(Calendar.SECOND, 37);
        System.out.println(calendar.getTime());
        System.out.println("The YEAR is: " + calendar.get(Calendar.YEAR));
        System.out.println("The MONTH is: " + calendar.get(Calendar.MONTH));
        System.out.println("The DAY is: " + calendar.get(Calendar.DATE));
        System.out.println("The HOUR is: " + calendar.get(Calendar.HOUR));
        System.out.println("The MINUTE is: " + calendar.get(Calendar.MINUTE));
        System.out.println("The SECOND is: " + calendar.get(Calendar.SECOND));
        System.out.println("The AM_PM indicator is: " + calendar.get(Calendar.AM_PM));
    }

}

Стандартные средства Java (до JDK 7) для работы с датами и временем имеют целый ряд недостатков, связанных с неудобством их использования, что привело к появлению альтернативных (нестандартных) библиотек, таких как популярная среди Java-программистов библиотека Joda-Time. Для того, чтобы исправить это положение, новые классы и средства поддержки работы с датами и календарем реализованы в Java 8. Библиотека даты и времени по своему использованию во многом аналогична Joda-Time.

В пакете java.time предложены классы для представления даты и времени:

  • LocalDate (день, месяц, год)
  • LocalTime (только время суток)
  • LocalDateTime (дата и время).

Эти три класса используются в тех случаях, когда не учитывается поясное время. Создать объекты можно с помощью статических функций now(), возвращающих текущее значение времени (даты):

LocalDate date = LocalDate.now();
LocalTime time = LocalTime.now();
LocalDateTime dateTime = LocalDateTime.now();

Другие способы создания объекта - использование статической функции of(), принимающей в качестве параметров значения часов, минут и секунд:

time = LocalTime.of(11, 15, 0);
date = LocalDate.of(2015, 3, 8);
dateTime = LocalDateTime.of(2015, Month.MARCH, 8, 11, 15, 0);
dateTime = LocalDateTime.of(date, time); // другой вариант

Как видно из примера, для задания месяцев можно использовать как целые значения, так и константы перечисления java.time.Month. Из имеющегося объекта типа LocalDate можно создать объект типа LocalDateTime с помощью метода atTime():

dateTime = date.atTime(time);

С помощью методов plus() и minus() можно изменять значения даты и времени существующих объектов. При этом можно задавать единицу измерения с помощью констант перечисления java.time.temporal.ChronoUnit. В следующем примере определяется время на два часа позже текущего:

LocalTime now = LocalTime.now();
LocalTime later = now.plus(2, ChronoUnit.HOURS);
System.out.println(later);

Есть также методы добавления и вычитания plusHours(), minusHours(), plusMinutes(), minusMinutes(), plusSeconds(), minusSeconds(), plusNanos() и minusNanos() с целыми параметрами. Аналогично класс LocalDate предоставляет методы plusDays, plusMonths, minusDays и minusMonths. Например:

LocalDate today = LocalDate.now();
LocalDate thirtyDaysFromNow = today.plusDays(30);
LocalDate nextMonth = today.plusMonths(1);
LocalDate aMonthAgo = today.minusMonths(1);

Идентификация часовых поясов осуществляется средствами класса ZoneId. Простейший способ получения идентификатора часового пояса, установленного в системе, - использование статической функции systemDefault():

ZoneId myZone = ZoneId.systemDefault();

Можно получить идентификатор часового пояса, используя строковую константу. Список возможных констант можно получить с помощью статического метода ZoneId.getAvailableZoneIds(). Перечисленные константы могут быть использованы в методе of():

ZoneId germanZone = ZoneId.of("Europe/Berlin");

Для работы со временем с учетом часовых поясов используется класс ZonedDateTime. Его использование аналогично LocalTime, однако если в качестве параметра метода now() задать объект типа ZoneId, можно работать с данными о времени соответствующего часового пояса:

ZonedDateTime germanTime = ZonedDateTime.now(germanZone); 
System.out.println(germanTime);

Для работы с поясным временем можно также использовать класс Clock.

Clock clock = Clock.system(ZoneId.of("Europe/Moscow"));
LocalTime moscowTime = LocalTime.now(clock);
System.out.println(moscowTime);

Java 8 определяет два класса, Period и Duration, позволяющие определять промежутки между датами и временами соответственно. Для этого используются методы between():

Period p = Period.between(date1, date2);
Duration d = Duration.between(time1, time2);

Объекты этих классов также могут быть созданы с помощью статических методов, например:

Duration twoHours = Duration.ofHours(2);
Duration tenMinutes = Duration.ofMinutes(10);
Duration thirtySecs = Duration.ofSeconds(30);

Класс java.time.temporal.TemporalAdjusters содержит такие полезные методы, как firstDayOfMonth(), firstDayOfNextMonth(), firstInMonth(DayOfWeek), lastDayOfMont(), next(DayOfWeek), nextOrSame(DayOfWeek), previous(DayOfWeek), previousOrSame(DayOfWeek) и т. д.

Класс Instant представляет момент времени, измеренный в наносекундах. С использованием представления в наносекундах новая библиотека позволяет обеспечить совместимость с ранее использовавшимися средствами. Классы Date и Calendar предыдущих версий Java поддерживают метод toInstant(), результат которого может быть использован для создания объектов LocalDateTime или ZonedDateTime:

java.util.Date date = new java.util.Date();
Instant moment = date.toInstant();
ZoneId myZone = ZoneId.systemDefault();
LocalDateTime localDateTime = LocalDateTime.ofInstant(moment, myZone);
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(moment, myZone);

2 Примеры программ

2.1 Использование класса BigInteger

Следующая программа осуществляет вычисление факториала целых чисел в диапазоне от 0 до 1676.

package ua.inf.iwanoff.bigint;

import java.math.BigInteger;
import java.util.Scanner;

public class Factorial {

    public static void main(String[] args) {
        BigInteger result = BigInteger.ONE;
        int n = new Scanner(System.in).nextInt();
        for (int i = 1; i <= n; i++) {
            result = result.multiply(new BigInteger(i + ""));
        }
        System.out.println(result);
    }

}

2.2 Работа с календарем

Допустим, необходимо создать класс "Состязание" (Competition) с полями "Место проведения" типа String и "Дата" типа Calendar. Необходимо реализовать статическую функцию определения времени между двумя состязаниями. Программа будет иметь следующий вид:

package ua.inf.iwanoff.calendar;

import java.util.Calendar;
import java.util.GregorianCalendar;

public class Competition {
    private String place;
    private Calendar date;

    public Competition(String place, Calendar date) {
        this.place = place;
        this.date = date;
    }
 
    public static String between(Competition c1, Competition c2) {
        int years = c2.date.get(Calendar.YEAR) - c1.date.get(Calendar.YEAR);
        int months = c2.date.get(Calendar.MONTH) - c1.date.get(Calendar.MONTH);
        int day = c2.date.get(Calendar.DAY_OF_MONTH) - c1.date.get(Calendar.DAY_OF_MONTH);
        return years + " год (года, лет) " + months + " мес. " + day + " дн."; 
    }

    public static void main(String[] args) {
        Competition c1 = new Competition("Харьков", new GregorianCalendar(2012, 02, 11));
        Competition c2 = new Competition("Киев", new GregorianCalendar(2013, 06, 20));
        System.out.println(between(c1, c2));
    }

}

3 Задания на самостоятельную работу

3.1 Использование класса BigInteger*

Напишите программу, осуществляющую заполнение числа типа BigInteger случайными цифрами и вычисление целой степени этого числа. Для результата используйте BigInteger. Реализовать два варианта - с использованием функции pow класса и функции, обеспечивающей умножение длинных целых. Сравнить результаты.

3.2 Работа с календарем*

В классе Person ранее созданной иерархии классов создать поле типа Calendar и добавить функцию вычисления возраста.

4 Контрольные вопросы

  1. Назовите основные составляющие платформы Java SE.
  2. Для чего могут быть использованы случайные числа?
  3. Как обеспечить получение одинаковой последовательности псевдослучайных чисел?
  4. Как получить вещественные случайные числа в заданном диапазоне?
  5. В чем предназначение модификатора strictfp?
  6. Для чего используются классы BigInteger и BigDecimal?
  7. Как можно создать число типа BigInteger?
  8. Можно ли применять математические операции к числам типов BigInteger и BigDecimal?
  9. В чем различие между классами Calendar и Date?
  10. Как создать экземпляр класса GregorianCalendar?
  11. Какие средства для работы с календарем предоставляет Java 8?
  12. Какие средства для работы с часовыми поясами предоставляет Java 8?

 

up