- •Кен Арнольд Джеймс Гослинг Дэвид Холмс Язык программирования 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
- •Приложение в Полезные таблицы
5.13.3. Строковое приведение
Класс String отличается от остальных: это неявно используется в операторе конкатенации +, а строковые литералы ссылаются на объекты String. Примеры нам уже встречались в программах: при выполнении конкатенации Java пытается преобразовать в String все, что еще не относится к этому типу. Подобные приведения определены для всех примитивных типов и осуществляются вызовом метода toString объекта (см. раздел “Метод toString”).
Если преобразовать в String пустую ссылку, то результатом будет строка “null”. Если для данного класса метод toString не определен, то используется метод, унаследованный от класса Object и возвращающий строковое представление типа объекта.
5.14. Доступ к членам
Доступ к членам объекта осуществляется с помощью оператора .— например, obj.method(). Оператор . может применяться и для доступа к статическим членам либо по имени класса, либо по ссылке на объект. Если для доступа к статическим членам используется ссылка на объект, то выбор класса осуществляется на основании объявленного типа ссылки, а не фактического типа объекта. Длядоступа к элементам массивов служат квадратные скобки— например, array[i].
Если использовать . или [] со ссылкой, значение которой равно null, то возбуждается исключение NullPointerException (кроме того случая, когда вы используете . для вызова статического метода). Если индекс массива выходит за его пределы, возбуждается исключение IndexOutOfBounds. Проверка осуществляется при каждом обращении к элементу массива. /По крайней мере runtime-система ведет себя именно так, но компилятор часто может избежать проверки в тех случаях, когда он действительно уверен, что все в порядке, например, если значение переменной цикла всегда лежит в допустимом диапазоне./
Для правильного вызова метода необходимо предоставить аргументы в нужном количестве и правильного типа, чтобы в классе нашелся ровно один метод с совпадающей сигнатурой. Если метод не перегружался, дело обстоит просто— с именем метода ассоциируется всего один набор параметров. Выбор оказывается несложным и в том случае, если был объявлен всего один метод с заданным именем и количеством аргументов.
Если существует два и более перегруженных метода с одинаковым количеством параметров, выбор правильной версии становится несколько более сложным. Для этого Java пользуется следующим алгоритмом, называемым “алгоритмом самого точного совпадения”:
1. Найти все методы, к которым может относиться данный вызов,— то есть построить список всех перегруженных методов с нужным именем и типами параметров, которым могут быть присвоены значения всех аргументов. Если находится всего один метод со всеми подходящими аргументами, вызывается именно он.
2. У нас остался список методов, каждый из которых подходит для вызова. Проделаем следующую процедуру. Рассмотрим первый метод в списке. Если вместо параметров в первом методе могут быть использованы параметры еще какого-либо метода из списка— исключим первый метод из списка. Эта процедура повторяется до тех пор, пока остается возможность удаления методов.
3. Если остался ровно один метод, то совпадение для него оказывается самым точным, и он будет вызываться в программе. Если же осталось несколько методов, то вызов является неоднозначным; точного совпадения не существует, а вызов оказывается недопустимым.
Например, предположим, что у нас имеется усовершенствованная версия класса с десертами из раздела3.2:
Допустим также, что у нас имеется несколько перегруженных методов, которые вызываются для конкретных комбинаций параметров Dessert:
void moorge(Dessert d, Scone s) { /* Первая форма */ }
void moorge(Cake c, Dessert d) { /* Вторая форма */ }
void moorge(ChocolateCake cc,Scone s) { /* Третья форма */ }
Теперь рассмотрим следующие вызовы moorge:
moorge(dessertRef, sconeRef);
moorge(chocolateCakeRef, dessertRef);
moorge(chocolateCakeRef, butteredSconeRef);
moorge(cakeRef, sconeRef); // НЕВЕРНО
В первом вызове используется первая форма moorge, потому что типы параметров и аргументов в точности совпадают. Во втором вызове используется вторая форма, потому что только в ней переданные аргументы могут быть присвоены в соответствии с типом параметров. В обоих случаях вызываемый метод определяется после выполнения первого шага описанного выше алгоритма.
С третьим вызовом дело обстоит сложнее. Список потенциальных кандидатов включает все три формы, потому что ссылка chocolateCakeRef может быть присвоена первому параметру, а ButteredScone— второму параметру во всех трех формах, и ни для одной из сигнатур не находится точного совпадения. Следовательно, после шага 1 у нас имеется набор из трех методов-кандидатов.
На шаге 2 из набора исключаются все методы с менее точным совпадением. В нашем случае первая форма исключается из-за того, что совпадение для третьей формы оказывается более точным. Действительно, рассмотрим третий и первый методы. Ссылка на ChocolateCake (из третьей формы) может быть присвоена параметру типа Desert (из первой формы), а ссылка на Scone (из третьей формы) непосредственно присваивается параметру типа Scone (в первой форме). Вторая форма исключается из набора по аналогичным соображениям. В итоге количество возможных методов сократилось до одного (третьей формы moorge), и именно этот метод и будет вызван.
Последний вызов является недопустимым. После шага 1 набор возможных методов состоит из первой и второй форм. Поскольку параметры любой из этих форм не могут быть присвоены параметрам другой, на шаге 2 не удается исключить из набора ни одну из этих форм. Следовательно, мы имеем дело с неоднозначным вызовом, по поводу которого компилятор не может принять никакого решения, что является недопустимым.
Эти же правила относятся и к примитивным типам. Например, значение int может быть присвоено переменной float, и при рассмотрении перегруженного вызова этот факт будет принят во внимание— точно так же, как и возможность присваивания ссылки ButteredScone ссылке Scone.
Перегруженные методы не могут отличаться одним лишь типом возвращаемого значения и/или списком возбуждаемых исключений, поскольку в противном случае при выборе запускаемого метода возникало бы слишком много неоднозначностей. Например, если бы существовало два метода doppelgänger, которые бы отличались только тем, что один из них возвращает int, а другой— short, то отдать предпочтение одному из них в следующем операторе было бы невозможно:
double d = doppelgänger();
Аналогичная проблема существует и для исключений, поскольку любое их количество (а также все или ни одно) может быть перехвачено во фрагменте программы, вызывающем перегруженный метод. Вам не удастся определить, какой из двух методов должен вызываться, если они отличаются только возбуждаемыми исключениями.