- •Кен Арнольд Джеймс Гослинг Дэвид Холмс Язык программирования Java
- •Глава 1 первое знакомство с java 6
- •Глава 2 классы и объекты 29
- •Глава 3 расширение классов 47
- •Глава 4 интерфейсы 70
- •Глава 5 лексемы, операторы и выражения 78
- •Глава 6 порядок выполнения 105
- •Глава 7 исключения 113
- •Глава 8 строки 121
- •Глава 9 потоки 134
- •Глава 10 пакеты 156
- •Глава 11 пакет ввода/вывода 158
- •Глава 12 стандартные вспомогательные средства 183
- •Глава 13 применение типов в программировании 205
- •Глава 14 системное программирование 218
- •Глава1 первое знакомство сjava
- •1.1. С самого начала
- •1.2.Переменные
- •1.3. Комментарии
- •1.4.Именованные константы
- •1.4.1. Символы Unicode
- •1.5.Порядок выполнения
- •1.6.Классы и объекты
- •1.6.1.Создание объектов
- •1.6.2.Статические поля
- •1.6.3.Сборщик мусора
- •1.7.Методы и параметры
- •1.7.1.Вызов метода
- •1.7.2.Ссылка this
- •1.7.3.Статические методы
- •1.8.Массивы
- •1.9.Строковые объекты
- •1.10.Расширение класса
- •1.10.1.Класс Object
- •1.10.2.Вызов методов суперкласса
- •1.11. Интерфейсы
- •1.12.Исключения
- •1.13.Пакеты
- •1.14.Инфраструктура Java
- •1.15.Прочее
- •Глава 2 классы и объекты
- •2.1. Простой класс
- •2.2. Поля
- •2.3. Управление доступом и наследование
- •2.4. Создание объектов
- •2.5. Конструкторы
- •2.6. Методы
- •2.6.1. Значения параметров
- •2.6.2. Применение методов для ограничения доступа
- •2.7. Ссылка this
- •2.8. Перегрузка методов
- •2.9. Статические члены
- •2.9.1. Блоки статической инициализации
- •2.9.2. Статические методы
- •2.10. Сборка мусора и метод finalize
- •2.10.1. Метод finalize
- •2.10.2. Восстановление объектов в методе
- •2.11. Метод main
- •2.12. Метод toString
- •2.13. Родные методы
- •Глава 3 расширение классов
- •3.1. Расширенный класс
- •3.2. Истинное значение protected
- •3.3. Конструкторы в расширенных классах
- •3.3.1. Порядок вызова конструкторов
- •3.4. Переопределение методов и скрытие полей
- •3.4.1. Ключевое слово super
- •3.5. Объявление методов и классов с ключевым словом final
- •3.6. Класс Object
- •3.7. Абстрактные классы и методы
- •3.8. Дублирование объектов
- •3.9. Расширение классов: когда и как
- •3.10. Проектирование расширяемого класса
- •Глава 4 интерфейсы
- •4.1. Пример интерфейса
- •4.2. Одиночное и множественное наследование
- •4.3. Расширение интерфейсов
- •4.3.1. Конфликты имен
- •4.4. Реализация интерфейсов
- •4.5. Использование реализации интерфейса
- •4.6. Для чего применяются интерфейсы
- •Глава 5 лексемы, операторы и выражения
- •5.1. Набор символов
- •5.2. Комментарии
- •5.3. Лексемы
- •5.4. Идентификаторы
- •5.4.1. Зарезервированные слова Java
- •5.5. Примитивные типы
- •5.6. Литералы
- •5.6.1. Ссылки на объекты
- •5.6.2. Логические значения
- •5.6.3. Целые значения
- •5.6.4. Значения с плавающей точкой
- •5.6.5. Символы
- •5.6.6. Строки
- •5.7. Объявления переменных
- •5.7.1. Значение имени
- •5.8. Массивы
- •5.8.1. Многомерные массивы
- •5.9. Инициализация
- •5.9.1. Инициализация массивов
- •5.10. Приоритет и ассоциативность операторов
- •5.11. Порядок вычислений
- •5.12. Тип выражения
- •5.13. Приведение типов
- •5.13.1. Неявное приведение типов
- •5.13.2. Явное приведение и instanceof
- •5.13.3. Строковое приведение
- •5.14. Доступ к членам
- •5.15. Арифметические операторы
- •5.15.1. Целочисленная арифметика
- •5.15.2. Арифметика с плавающей точкой
- •5.15.3. Арифметика с плавающей точкой и стандарт ieee-754
- •5.15.4. Конкатенация строк
- •5.16. Операторы приращения и уменьшения
- •5.17. Операторы отношения и условный оператор
- •5.18. Поразрядные операции
- •5.19. Условный оператор
- •5.20. Операторы присваивания
- •5.21. Имена пакетов
- •Глава 6 порядок выполнения
- •6.1. Операторы и блоки
- •6.2. Оператор if-else
- •6.3. Оператор switch
- •6.4. Цикл while и do-while
- •6.5. Оператор for
- •6.6. Метки
- •6.7. Оператор break
- •6.8. Оператор continue
- •6.9. Оператор return
- •Глава 7 исключения
- •7.1. Создание новых типов исключений
- •7.2. Оператор throw
- •7.3. Условие throws
- •7.4. Операторы try, catch и finally
- •7.4.1. Условие finally
- •7.5. Когда применяются исключения
- •Глава 8 строки
- •8.1. Основные операции со строками
- •8.2. Сравнение строк
- •8.3. Вспомогательные методы
- •8.4. Создание производных строк
- •8.5. Преобразование строк
- •8.6. Строки и символьные массивы
- •8.7. Строки и массивы byte
- •8.8. Класс StringBuffer
- •8.8.1. Модификация буфера
- •8.8.2. Извлечение данных
- •8.8.3. Работа с емкостью буфера
- •Глава 9 потоки
- •9.1. Создание потоков
- •9.2. Синхронизация
- •9.2.1. Методы synchronized
- •9.2.2. Операторы synchronized
- •9.3. Методы wait и notify
- •9.4. Подробности, касающиеся wait и notify
- •9.5. Планирование потоков
- •9.6. Взаимная блокировка
- •9.7. Приостановка потоков
- •9.8. Прерывание потока
- •9.9. Завершение работы потока
- •9.10. Завершение приложения
- •9.11. Использование Runnable
- •9.12. Ключевое слово volatile
- •9.13. Безопасность потоков и ThreadGroup
- •9.14. Отладка потоков
- •Глава 10 пакеты
- •10.1. Имена пакетов
- •10.2. Пакетный доступ
- •10.3. Содержимое пакета
- •Глава 11 пакет ввода/вывода
- •11.1. Потоки
- •11.2. Класс InputStream
- •11.3. Класс OutputStream
- •11.4. Стандартные типы потоков
- •11.5. Фильтрующие потоки
- •11.6. Класс PrintStream
- •11.7. Буферизованные потоки
- •11.8. Байтовые потоки
- •11.9. Класс StringBufferInputStream
- •11.10. Файловые потоки и FileDescriptor
- •11.11. Конвейерные потоки
- •11.12. Класс Seq uenceInputStream
- •11.13. Класс LineNumberInputStream
- •11.14. Класс PushbackInputStream
- •11.15. Класс StreamTokenizer
- •11.16. Потоки данных
- •11.16.1. Классы потоков данных
- •11.17. Класс RandomAccessFile
- •11.18. Класс File
- •11.19. Интерфейс FilenameFilter
- •11.20. Классы ioException
- •Глава 12 стандартные вспомогательные средства
- •12.1. Класс BitSet
- •12.2. Интерфейс Enumeration
- •12.3. Реализация интерфейса Enumeration
- •12.4. Класс Vector
- •12.5. Класс Stack
- •12.6. Класс Dictionary
- •12.7. Класс Hashtable
- •12.8. Класс Properties
- •12.9. Классы Observer/Observable
- •12.10. Класс Date
- •12.11. Класс Random
- •12.12. Класс String Tokenizer
- •Глава 13 применение типов в программировании
- •13.1. Класс Class
- •13.2. Загрузка классов
- •13.3. Классы-оболочки: общий обзор
- •13.4. Класс Boolean
- •13.5. Класс Character
- •13.6. Класс Number
- •13.7. Класс Integer
- •13.8. Класс Long
- •13.9. Классы Float и Double
- •Глава 14 системное программирование
- •14.1. Стандартный поток ввода/вывода
- •14.2. Управление памятью
- •14.3. Системные свойства
- •14.4. Создание процессов
- •14.5. Класс Runtime
- •14.6. Разное
- •14.7. Безопасность
- •14.8. Класс Math
- •Приложение а Родные методы
- •А.1 Обзор
- •А.2.1 Имена
- •А.2.2 Методы
- •А.2.3 Типы
- •А.2.5 Средства безопасности
- •А.2.6 Работа с памятью
- •А.3 Пример
- •А.3.1 Внутреннее строение LockableFile
- •А.4 Строки
- •А.5 Массивы
- •А.6 Создание объектов
- •А.7 Вызов методов Java
- •А.8 Последнее предупреждение
- •Приложение б Runtime-исключения в Java
- •Б.1 Классы RuntimeException
- •Б.2 Классы Error
- •Приложение в Полезные таблицы
9.4. Подробности, касающиеся wait и notify
Существует три формы wait и две формы notify. Все они входят в класс Object и выполняются для текущего потока:
public final void wait(long timeout) throws InterruptedException
Выполнение текущего потока приостанавливается до получения извещения или до истечения заданного интервала времени timeout. Значение timeout задается в миллисекундах. Если оно равно нулю, то ожидание не прерывается по тайм-ауту, а продолжается до получения извещения.
public final void wait(long timeout, int nanos)throws InterruptedException
Аналог предыдущего метода с возможностью более точного контроля времени; интервал тайм-аута представляет собой сумму двух параметров: timeout (в миллисекундах) и nanos (в наносекундах, значение в диапазоне 0–999999).
public final void wait() throws InterruptedException
Эквивалентно wait(0).
public final void notify()
Посылает извещение ровно одному потоку, ожидающему выполнения некоторого условия. Потоки, которые возобновляются лишь после выполнения данного условия, могут вызвать одну из разновидностей wait. При этом выбрать извещаемый поток невозможно, поэтому данная форма notify используется лишь в тех случаях, когда вы точно знаете, какие потоки ожидают событий, какие это события и сколько длится ожидание. Если вы не уверены в каком-либо из этих факторов, вероятно, следует воспользоваться методом notifyAll.
public final void notifyAll()
Посылает извещения всем потокам, ожидающим выполнения некоторого условия. Обычно потоки стоят, пока какой-то другой поток не изменит некоторое условие. Используя этот метод, управляющий условием поток извещает все ожидающие потоки об изменении условия. Потоки, которые возобновляются лишь после выполнения данного условия, могут вызывать одну из разновидностей wait.
Все эти методы реализованы в классе Object. Тем не менее они могут вызываться только из синхронизированных фрагментов, с использованием блокировки объекта, в котором они применяются. Вызов может осуществляться или непосредственно из такого фрагмента, или косвенно— из метода, вызываемого в фрагменте. Любая попытка обращения к этим методам для объектовза пределами синхронизированных фрагментов, для которых действует блокировка, приведет к возбуждению исключения IllegalMonitorState Exception.
9.5. Планирование потоков
Java может работать как на однопроцессорных, так и на многопроцессорных компьютерах, в однопоточных и многопоточных системах, так что в отношении потоков даются лишь общие гарантии. Вы можете быть уверены в том, что исполнимый (runnable) поток с наивысшим приоритетом будет работать и что все потоки с тем же приоритетом получат некоторую долю процессорного времени. Функционирование потоков с низшим приоритетом гарантируется лишь в случае блокировки всех потоков с высшим приоритетом. /Читателю следует отличать блокировку объекта (lock), о которой говорилось выше, от блокировки потока (block). Терминология, сложившаяся в отечественной литературе, может стать источником недоразумений. - Примеч. перев./ На самом деле не исключено, что потоки с низшим приоритетом будут работать и без таких решительных мер, но полагаться на это нельзя.
Поток называется заблокированным, если он приостановлен или выполняет заблокированную функцию (системную или функцию потока). В случае блокировки потока Java выбирает исполнимый поток с наивысшим приоритетом (или один из таких потоков, если их несколько) и начинает его выполнение.
Runtime-система Java может приостановить поток с наивысшим приоритетом, чтобы дать поработать потоку с тем же приоритетом,— это означает, что все потоки, обладающие наивысшим приоритетом, со временем выполняются. Тем не менее это вряд ли можно считать серьезной гарантией, поскольку “со временем”— понятие растяжимое. Приоритетами следует пользоваться лишь для того, чтобы повлиять на политику планирования для повышения эффективности. Не стоит полагаться на приоритет потоков, если от этого зависит правильность работы алгоритма.
Начальный приоритет потока совпадает с приоритетом того потока, который создал его. Для установки приоритета используется метод setPriority с аргументом, значение которого лежит между константами MIN_PRIORITY и MAX_PRIORITY класса Thread. Стандартный приоритет для потока по умолчанию равен NORM_PRIORITY. Приоритет выполняемого потока может быть изменен в любой момент. Если потоку будет присвоен приоритет ниже текущего, то система может запустить другой поток, так как исходный поток может уже не обладать наивысшим приоритетом. Метод getPriority возвращает приоритет потока.
В общем случае постоянно работающая часть вашего приложения должна обладать более низким приоритетом, чем поток, занятый обработкой более редких событий— например, ввода информации пользователем. Скажем, когда пользователь нажимает кнопку с надписью STOP, он ждет, что приложение немедленно остановится. Если обновление изображения и ввод информации осуществляются с одинаковым приоритетом и во время нажатия кнопки происходит вывод, на то, чтобы поток ввода смог среагировать на нажатие кнопки, может потребоваться некоторое время. Даже несмотря на то, что поток вывода обладает более низким приоритетом, он все равно будет выполняться большую часть времени, поскольку поток пользовательского интерфейса будет заблокирован в ожидании ввода. С появлением введенной информации поток пользовательского интерфейса заставит поток вывода среагировать на запрос пользователя. По этой причине приоритет потока, который должен выполняться постоянно, устанавливается равным MIN_PRIORITY, чтобы он не поглощал все доступное процессорное время.
Несколько методов класса Thread управляют планировкой потоков в системе:
public static void sleep(long millis) throws InterruptedException
Приостанавливает работу текущего потока как минимум на указанное число миллисекунд. “Как минимум” означает, что не существует гарантий возобновления работы потока точно в указанное время. На время возобновления может повлиять планировка потоков в системе, гранулярность и точность системных часов, а также ряд других факторов.
public static void sleep(long millis, int nanos)throws InterruptedException
Приостанавливает работу текущего потока как минимум на указанное число миллисекунд и дополнительное число наносекунд. Значение интервала в наносекундах лежит в диапазоне 0–999999.
public static void yield()
Текущий поток передает управление, чтобы дать возможность работать и другим исполняемым потокам. Планировщик потоков выбирает новый поток среди исполняемых потоков в системе. При этом может быть вызван поток, только что уступивший управление, если его приоритет окажется самым высоким.
Приведенный ниже пример демонстрирует работу yield. Приложение получает список слов и создает потоки, предназначенные для вывода отдельного слова в списке. Первый параметр приложения определяет, должен ли каждый поток передавать управление после каждого вызова println; значение второго параметра равно количеству повторений слова при выводе. Остальные параметры представляют собой слова, входящие в список:
class Babble extends Thread {
static boolean doYield; // передавать управление другим потокам?
Static int howOften; // количеств повторов при выводе
String word; // слово
Babble(String whatToSay) {
word = whatToSay;
}
public void run() {
for (int i = 0; i << howOften; i++) {
System.out.println(word);
if (doYield)
yield(); // передать управление другому потоку
}
}
public static void main(String[] args) {
howOften = Integer.parseInt(args[1]);
doYield = new Boolean(args[0]).booleanValue();
// создать поток для каждого слова и присвоить ему
// максимальный приоритет
Thread cur = currentThread();
cur.setPriority(Thread.MAX_PRIORITY);
for (int i = 2; i << args.length; i++)
new babble(args[i]).start();
}
}
Когда потоки работают, не передавая управления друг другу, им отводятся большие кванты времени— обычно этого бывает достаточно, чтобы закончить вывод в монопольном режиме. Например, при запуске программы с присвоением doYield значения false:
Babble false 2 Did DidNot
результат будет выглядеть следующим образом:
Did
Did
DidNot
DidNot
Если же каждый поток передает управление после очередного println, то другие потоки также получат возможность работать. Если присвоить doYield значение true:
Babble true 2 Did DidNot
то остальные потоки также смогут выполняться между очередными выводами и, в свою очередь, будут уступать управление, что приведет к следующему:
Did
DidNot
Did
DidNot
Приведенные выше результаты являются приблизительными. При другой реализации потоков они могут быть другими, хотя даже при одинаковой реализации разные запуски программы могут дать разные результаты. Однако при любой реализации вызов yield повышает шансы других потоков в споре за процессорное время.