Использование готовых классов

From AsIsWiki
Jump to: navigation, search
Форум

Назад | Оглавление | Дальше


Contents

Объекты и объектные переменные

Для работы с объектом необходимо:

  1. создать объект (экземпляр класса);
  2. задать исходное состояние объекта (инициализировать);
  3. далее к объекту можно применять методы.

Constructor - специальный метод, предназначенный для создания и инициализации объекта.

В стандартной библиотеке Java имеется класс Date. С помощью объекта этого класса можно описать текущий или иной момент времени, например так:

January 20, 2011, 08:07:00 GMT

Имя конструктора всегда совпадает с именем класса. Поэтому, конструктор класса Date называется Date.
Для создания объекта, конструктор объединяется с оператором new:

new Date()

Это выражение создает новый объект, инициализируемый текущей датой и временем.

Объект можно передать методу:

System.out.println(new Date());

Класс Date содержит метод toString(), позволяющий представить дату в виде строки:

String = new Date().toString();

Для многократного использования объекта, его необходимо связать с переменной:

Date birthday = new Date();

Между объектами и объектными переменными есть существенная разница. Например, приведенное ниже выражение определяет объектную переменную deadline, которая может ссылаться (но не ссылается) на объекты типа Date:

Date deadline;  // переменная deadline не ссылается ни на один объект

На данном этапе переменная deadline объектом не является и даже не ссылается ни на один объект. Поэтому, методы класса Date с помощью этой переменной вызывать пока нельзя:

s = deadline.toString();  // Error! вызывать метод еще рано

Сначала переменную deadline нужно инициализировать вновь созданным объектом:

deadline = new Date();

Кроме того, можно скопировать ссылку на уже инициализированный объект:

deadline = birthday;

Теперь переменные deadline и birthday ссылаются на один и тот же объект.

Объектная переменная не содержит никакого объекта, она лишь ссылается на него.

Оператор new также возвращает лишь ссылку на созданный объект:

Date deadline = new Date();

Выражение new Date() создает объект типа Date, а значение переменной deadline - это ссылка на созданный объект.

Объектной переменной можно присвоить ссылку null, чтобы указать, что она не ссылается ни на один объект:

deadline = null;
...
if (deadline != null) {
    System.out.println(deadline);
}

Вызов метода объекта через переменную, значение которой равно null, приведет к ошибке:

birthday = null;

String s = birthday.toString();  // Error!

Локальные объектные переменные не инициализируются автоматически. Переменную необходимо инициализировать вызовом оператора new, или значением null.

Почему для представления даты и времени применяются классы, а не встроенные типы? Например, в Visual Basic дата задается так: #6/1/1995#. При таком подходе можно использовать встроенный тип, не заботясь о классах. Но, в некоторых странах даты записываются в виде month/day/year, а в других - year/month/day. Могут ли разработчики языка предусмотреть все возможные варианты? Даже если это удастся, то данные средства будут слишком сложны, а программисты будут вынуждены применять их. Классы позволяют переложить ответственность за решение этих проблем с разработчиков языка на создателей библиотек. Если системный класс плох, то всегда можно создать свое решение.

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

Date birthday;  // Java

Date* birthday;  // C++

Разумеется, указатель Date* не инициализируется, пока не выполнен оператор new:

Date* birthday = new Date();  // C++

При копировании одной переменной в другую, в обеих переменных появляется ссылка на один и тот же объект. Эквивалентом ссылки null в C++ является указатель NULL.

Все объекты Java располагаются в динамической памяти. Если объект A содержит объектную переменную B, то она представляет собой указатель на объект B, расположенный в динамической памяти.

Указатели C++ часто приводят к ошибкам: очень легко создать неверный указатель или потерять управление памятью. В Java такой проблемы нет: система поддержки выполнения кода обнаруживает неинициализированный указатель, сообщает об ошибке и прекращает выполнение программы. Программист не думает об управлении памятью, так как механизм "сборки мусора" делает это за него автоматически.

В С++ большое внимание уделено автоматическому копированию объектов. Например, копией связного списка является новый связный список, который, имея старое содержимое, содержит совершенно другие связи. То есть, копирование объектов осуществляется так же, как и копирование встроенных типов. В Java для получения полной копии объекта используется метод clone.


Класс GregorianCalendar из библиотеки Java

Класс Date является частью стандартной библиотеки Java. Экземпляр класса Date отражает конкретный момент времени.

В классе Date время представлено числом миллисекунд (+ или -), отсчитанным от начала эпохи:

00:00:00 UTC, 1 January 1970

UTC (Universal Coordinated Time) - научный стандарт времени.
UTC применяется наряду с более известным GMT (Greenwich Mean Time).

Класс Date не очень удобен для работы с датами. Разработчики библиотеки Java считали, что представление даты, например "December 31, 2009, 23:59:59", является произвольным и должно зависеть от календаря. Данное отображение подчинено григорианскому календарю, самому распространенному календарю в мире. Но тот же самый момент времени совершенно иначе представляется в китайском или еврейском лунном календаре.

Вся история развития человечества сопровождалась созданием календарей - систем именования различных моментов времени. Как правило, основой для календарей был солнечный или лунный цикл. В книге Nachum Dershowitz and Edward M. Reingold "Calendrical Calculations" (Cambridge University Press, 2nd ed., 2001) есть сведения о календаре французской революции, календаре Майя и др. экзотических системах.

Разработчики библиотеки решили отделить вопросы отслеживания моментов времени, от вопросов их представления. Поэтому, стандартная библиотека Java содержит два отдельных класса:

  • Date - представляющий момент времени
  • GregorianCalendar - расширяющий более общий класс Calendar, который описывает свойства календаря в целом

На основе класса Calendar можно создать практически любой существующий календарь. Но, в стандартной библиотеке, кроме григорианского календаря, пока нет никакой другой реализации.

Отделение измерения времени от календарей представляет собой хорошее решение, вполне соответствующее принципу ООП.

Класс Date содержит методы before и after, позволяющие сравнивать два момента времени:

if (today.before(birthday)) {
    System.out.println("Еще есть время купить подарок.");
}

Класс GregorianCalendar содержит гораздо больше методов, чем класс Date. В частности, в нем есть несколько полезных конструкторов:

// создает объект, представляющий дату и момент времени создания
new GregorianCalendar()

// создает объект представляет полночь даты 31/12/2009
new GregorianCalendar(2009, 11, 31)

Месяцы лежат в диапазоне: 0 ... 11. Для большей ясности можно использовать константу:

new GregorianCalendar(2009, Calendar.DECEMBER, 31)

Конструктор GregorianCalendar позволяет задать время:

new GregorianCalendar(2009, Calendar.DECEMBER, 31, 23, 59, 59)

Чаще всего ссылка на созданный объект присваивается переменной:

GregorianCalendar deadline = new GregorianCalendar(2009, 11, 31);

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

Класс Date содержит методы getDay(), getMonth() и getYear(), но использовать их без крайней необходимости не рекомендуется. Эти методы были частью класса Date до того, как разработчики библиотеки поняли, что классы, реализующие разные календари, разумнее отделить друг от друга. Сделав это, они пометили методы класса Date как deprecated (не рекомендованные к применению). Можно продолжать использовать их, получая при этом предупреждение компилятора. Однако, лучше отказаться от использования данных методов, поскольку в будущем они могут быть удалены из библиотеки.


Модифицирующие методы и методы доступа

Как получить текущий день, месяц или год на базе даты, инкапсулированной в объекте класса GregorianCalendar? Как можно изменить эти значения?

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

GregorianCalendar now = new GregorianCalendar();

int month = now.get(Calendar.MONTH);

int weekday = now.get(Calendar.DAY_OF_WEEK);

Состояние объекта можно изменить с помощью метода set():

deadline.set(Calendar.YEAR, 2009);

deadline.set(Calendar.MONTH, Calendar.APRIL);

deadline.set(Calendar.DAY_OF_MONTH, 15);

Год, месяц и день можно установить в одном вызове:

deadline.set(2009, Calendar.DECEMBER, 31);

К заданной дате можно добавить определенное количество дней, недель, месяцев и т.д.

deadline.add(Calendar.MONTH, 3);  // сдвинуть deadline на 3 месяца

Сдвиг в обратную сторону выполняется добавлением отрицательного числа.

Mutator - модифицирующий метод, способный изменить поля объекта.

Accessor - метод доступа, может лишь просматривать поля объекта.

В C++ для формирования метода доступа используется суффикс const. Если метод не объявлен с помощью const, то он модифицирующий. В Java нет синтаксических средств, позволяющих различать модифицирующие методы и методы доступа.

В имени метода доступа принято использовать префикс get(), а в имени модифицирующего метода - префикс set(). Класс GregorianCalendar обеспечивает получение и установку момента времени, представленного объектом, посредством методов getTime() и setTime():

Date time = calendar.getTime();

calendar.setTime(time);

Эти методы очень полезны для преобразований объектов GregorianCalendar в объекты Date и наоборот. Например, требуется создать объект Date, поля которого были бы заполнены известными значениями года, месяца и дня:

GregorianCalendar calendar = new GregorianCalendar(2009, 1, 1);

Date hireDay = calendar.getTime();

Получение параметров даты из экземпляра класса Date:

GregorianCalendar calendar = new GregorianCalendar();

calendar.setTime(hireDay);

int year = calendar.get(Calendar.YEAR);

Следующая программа иллюстрирует работу класса GregorianCalendar:

import java.util.*;

public class CalendarTest {

    public static void main(String[] args) {

        // инициализируем объектную переменную d значением текущей даты
        GregorianCalendar d = new GregorianCalendar();

        System.out.printf("\nToday %d day of year.\n\n", d.get(Calendar.DAY_OF_YEAR));

        int today = d.get(Calendar.DAY_OF_MONTH);
        int month = d.get(Calendar.MONTH);

        // устанавливаем d на первый день месяца
        d.set(Calendar.DAY_OF_MONTH, 1);

        int weekday = d.get(Calendar.DAY_OF_WEEK);

        // печатаем шапку таблицы
        System.out.println("Sun Mon Tue Wed Thu Fri Sat");

        // забиваем пробелами пространство до первого дня месяца
        for (int i = Calendar.SUNDAY; i < weekday; i++) {
            System.out.print("    ");
        }

        do {
            // печатаем день
            int day = d.get(Calendar.DAY_OF_MONTH);
            System.out.printf("%3d", day);

            // помечаем текущий день символом *
            if (day == today) {
                System.out.print("*");
            } else {
                System.out.print(" ");
            }

            // переводим строку после каждой субботы
            if (weekday == Calendar.SATURDAY) {
                System.out.println();
            }

            // переходим на следующий день
            d.add(Calendar.DAY_OF_MONTH, 1);
            weekday = d.get(Calendar.DAY_OF_WEEK);

        } while (d.get(Calendar.MONTH) == month);
        // крутимся в цикле, пока обрабатываемый день не перейдет в следующий месяц

        // если последняя дата цикла не воскресенье, то надо перевести строку
        if (weekday != Calendar.SUNDAY) {
            System.out.println();
        }
    }
}

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

Today 50 day of year.

Sun Mon Tue Wed Thu Fri Sat
          1   2   3   4   5 
  6   7   8   9  10  11  12 
 13  14  15  16  17  18  19*
 20  21  22  23  24  25  26 
 27  28

Класс GregorianCalendar позволяет легко отслеживать дни недели и учитывать продолжительность месяцев. Программист не обязан знать о том, как именно класс GregorianCalendar вычисляет месяцы и дни. Нужно просто использовать интерфейс класса - методы get(), set() и add().

Чтобы не усложнять программу, имена выводятся на английском языке, а первым днем недели считается воскресенье. Для вывода календаря по соглашениям другой страны, следует ознакомиться с классом DateFormatSymbols. Метод Calendar.getFirstDayOfWeek() возвращает первый день недели: для США - это воскресенье, а для Германии - понедельник.


Справочник

java.util.GregorianCalendar

Создаем объект GregorianCalendar:

// формирует текущее время для часового пояса и страны, заданных по умолчанию
GregorianCalendar()

// формирует заданную дату
GregorianCalendar(int year, int month, int date)

// формирует указанное время для часового пояса и страны, заданных по умолчанию
GregorianCalendar(int year, int month, int date, int hour, int minutes, int seconds)

Отсчет месяцев начинается с нуля: 0 - январь
int get(int field)  // возвращает значение поля

void set(int field, int value)  // устанавливает значение поля

void add(int field, int amount)  // добавляет к полю заданную величину

Значения field:

Calendar.ERA
Calendar.YEAR
Calendar.WEEK_OF_YEAR
Calendar.WEEK_OF_MONTH
Calendar.DAY_OF_MONTH
Calendar.DAY_OF_YEAR
Calendar.DAY_OF_WEEK
Calendar.DAY_OF_WEEK_IN_MONTH
Calendar.AM_PM
Calendar.HOUR
Calendar.HOUR_OF_DAY
Calendar.MINUTE
Calendar.SECOND
Calendar.MILLISECOND
Calendar.ZONE_OFFSET или Calendar.DST_OFFSET

Пример:

somedate.add(Calendar.DAY_OF_MONTH, 7);  // увеличивает текущую дату на 7
// устанавливает дату в объекте
void set(int year, int month, int day)

// устанавливает дату и время в объекте
void set(int year, int month, int day, int hour, int minutes, int seconds)

Отсчет месяцев начинается с нуля: 0 - январь
// устанавливает заданный момент времени
void setTime(Date time)

// возвращает момент времени, представленный текущим значением объекта
Date getTime()



Форум

Назад | Оглавление | Дальше

Personal tools
Namespaces

Variants
Actions
Navigation
Tools