- •Аннотация
- •Лекция 1. Что такое Java? История создания.
- •1. Что такое Java?
- •2. История создания Java
- •2.1. Сложности внутри Sun Microsystems
- •2.2. Проект Green
- •2.3. Компания FirstPerson
- •2.4. World Wide Web
- •2.5. Возрождение Oak
- •2.6. Java выходит в свет
- •3. История развития Java
- •3.1. Браузеры
- •3.2. Сетевые компьютеры
- •3.3. Платформа Java
- •4. Заключение
- •5. Контрольные вопросы
- •Аннотация
- •1. Основы объектно-ориентированного программирования
- •1.1. Методология процедурно-ориентированного программирования
- •1.2. Методология объектно-ориентированного программирования
- •1.3. Объекты
- •1.3.1. Состояние.
- •1.3.2. Поведение
- •1.3.3. Уникальность
- •1.4. Классы
- •1.4.1. Инкапсуляция
- •1.4.2. Полиморфизм
- •1.5. Типы отношений между классами
- •1.5.1. Агрегация
- •1.5.2. Ассоциация
- •1.5.3. Наследование
- •1.5.4. Метаклассы
- •1.6. Достоинства ООП
- •1.7. Недостатки ООП
- •1.8. Заключение
- •1.9. Контрольные вопросы
- •Аннотация
- •Лекция 3. Лексика языка
- •1. Лексика языка
- •1.1. Кодировка
- •1.2. Анализ программы
- •1.2.1. Пробелы
- •1.2.2. Комментарии
- •1.2.3. Лексемы
- •1.3. Виды лексем
- •1.3.1. Идентификаторы
- •1.3.2. Ключевые слова
- •1.3.3. Литералы
- •1.3.3.1. Целочисленные литералы
- •1.3.3.2. Дробные литералы
- •1.3.3.3. Логические литералы
- •1.3.3.4. Символьные литералы
- •1.3.3.5. Строковые литералы
- •1.3.3.6. Null литерал
- •1.3.3.7. Разделители
- •1.3.3.8. Операторы
- •1.3.3.9. Заключение
- •1.4. Дополнение: Работа с операторами
- •1.4.1. Операторы присваивания и сравнения
- •1.4.2. Арифметические операции
- •1.4.3. Логические операторы
- •1.4.4. Битовые операции
- •1.5. Заключение
- •1.6. Контрольные вопросы
- •Аннотация
- •Лекция 4. Типы данных
- •1. Введение
- •2. Переменные
- •3. Примитивные и ссылочные типы данных
- •3.1. Примитивные типы
- •3.2. Целочисленные типы
- •4. Дробные типы
- •5. Булевский тип
- •6. Ссылочные типы
- •6.1. Объекты и правила работы с ними
- •6.2. Класс Object
- •6.3. Класс String
- •6.4. Класс Class
- •7. Заключение
- •8. Заключение
- •9. Контрольные вопросы
- •Аннотация
- •Лекция 5. Имена. Пакеты
- •1. Введение
- •2. Имена
- •2.1. Простые и составные имена. Элементы.
- •2.2. Имена и идентификаторы
- •2.3. Область видимости (введение)
- •3. Пакеты
- •3.1. Элементы пакета
- •3.2. Платформенная поддержка пакетов
- •3.3. Модуль компиляции
- •3.3.1. Объявление пакета
- •3.3.2. Импорт-выражения
- •3.3.3. Объявление верхнего уровня
- •3.4. Уникальность имен пакетов
- •4. Область видимости имен
- •4.1. "Затеняющее" объявление (Shadowing)
- •4.2. "Заслоняющее" объявление (Obscuring)
- •5. Соглашения по именованию
- •6. Заключение
- •7. Контрольные вопросы
- •Аннотация
- •Лекция 6. Объявление классов
- •1. Введение
- •2. Модификаторы доступа
- •2.1. Предназначение модификаторов доступа
- •2.2. Разграничение доступа в Java
- •3. Объявление классов
- •3.1. Заголовок класса
- •3.2. Тело класса
- •3.3. Объявление полей
- •3.4. Объявление методов
- •3.5. Объявление конструкторов
- •3.6. Инициализаторы
- •4. Дополнительные свойства классов
- •4.1. Метод main
- •4.2. Параметры методов
- •4.3. Перегруженные методы
- •5. Заключение
- •6. Контрольные вопросы
- •Аннотация
- •Лекция 7. Преобразование типов
- •1. Введение
- •2. Виды приведений
- •2.1. Тождественное преобразование
- •2.2. Преобразование примитивных типов (расширение и сужение)
- •2.3. Преобразование ссылочных типов (расширение и сужение)
- •2.4. Преобразование к строке
- •2.5. Запрещенные преобразования
- •3. Применение приведений
- •3.1. Присвоение значений
- •3.2. Вызов метода
- •3.3. Явное приведение
- •3.4. Оператор конкатенации строк
- •3.5. Числовое расширение
- •3.5.1. Унарное числовое расширение
- •3.5.2. Бинарное числовое расширение
- •4. Тип переменной и тип ее значения
- •5. Заключение
- •6. Контрольные вопросы
- •Аннотация
- •1. Введение
- •2. Статические элементы
- •3. Ключевые слова this и super
- •4. Ключевое слово abstract
- •5. Интерфейсы
- •5.1. Объявление интерфейсов
- •5.2. Реализация интерфейса
- •5.3. Применение интерфейсов
- •6. Полиморфизм
- •6.1. Поля
- •6.2. Методы
- •6.3. Полиморфизм и объекты
- •7. Заключение
- •8. Контрольные вопросы
- •Аннотация
- •Лекция 9. Массивы
- •1. Введение
- •2. Массивы, как тип данных в Java
- •2.1. Объявление массивов
- •2.2. Инициализация массивов
- •2.3. Многомерные массивы
- •2.4. Класс массива
- •3. Преобразование типов для массивов
- •3.1. Ошибка ArrayStoreException
- •3.2. Переменные типа массив, и их значения
- •4. Клонирование
- •4.1. Клонирование массивов
- •5. Заключение
- •6. Контрольные вопросы
- •Аннотация
- •Лекция 10. Операторы и структура кода
- •1. Управление ходом программы
- •2. Нормальное и прерванное выполнение операторов
- •3. Блоки и локальные переменные
- •4. Пустой оператор
- •5. Метки
- •6. Оператор if
- •7. Оператор switch
- •8. Управление циклами
- •8.1. Цикл while
- •8.2. Цикл do
- •8.3. Цикл for
- •9. Операторы break и continue
- •9.1. Оператор continue
- •9.2. Оператор break
- •10. Именованные блоки
- •11. Оператор return
- •12. Оператор synchronized
- •13.1. Причины возникновения ошибок
- •13.2. Обработка исключительных ситуаций
- •13.2.1. Конструкция try-catch
- •13.2.2. Конструкция try-catch-finally
- •13.3. Использование оператора throw
- •13.4. Обрабатываемые и необрабатываемые исключения
- •13.5. Создание пользовательских классов исключений
- •13.6. Переопределение методов и исключения
- •13.7. Особые случаи
- •14. Заключение
- •15. Контрольные вопросы
- •Аннотация
- •Лекция 11. Пакет java.awt
- •1. Введение
- •2. Апплеты
- •2.1. Тег HTML <Applet>
- •2.2. Передача параметров
- •2.3. Контекст апплета
- •2.4. Отладочная печать
- •2.5. Порядок инициализации апплета
- •2.6. Перерисовка
- •2.7. Задание размеров графических изображений
- •2.8. Простые методы класса Graphics
- •2.9. Цвет
- •2.9.1. Методы класса Color
- •2.10. Шрифты
- •2.10.1. Использование шрифтов
- •2.10.2. Позиционирование и шрифты: FontMetrics
- •2.10.3. Использование FontMetrics
- •2.10.4. Центрирование текста
- •3. Базовые классы
- •4. Основные компоненты
- •5. Менеджеры компоновки
- •6. Окна
- •7. Меню
- •8. Обработка событий
- •8.1. Рисование "каракулей" в Java
- •8.2. Рисование "каракулей" с использованием встроенных классов
- •9. Заключение
- •10. Контрольные вопросы
- •Аннотация
- •Лекция 12. Потоки выполнения. Синхронизация
- •1. Введение
- •2. Многопоточная архитектура
- •3. Базовые классы для работы с потоками
- •3.1. Класс Thread
- •3.2. Интерфейс Runnable
- •3.3. Работа с приоритетами
- •3.4. Демон-потоки
- •4. Синхронизация
- •4.1. Хранение переменных в памяти
- •4.2. Модификатор volatile
- •4.3. Блокировки
- •5. Методы wait(), notify(), notifyAll() класса Object
- •6. Контрольные вопросы
- •Аннотация
- •Лекция 13. Пакет java.lang.
- •1. Введение
- •2. Object
- •3. Class
- •4. Wrapper Classes
- •4.1. Integer
- •4.2. Character
- •4.3. Boolean
- •4.4. Void
- •5. Math
- •6. Строки
- •6.1. String
- •6.2. StringBuffer
- •7. Системные классы
- •7.1. ClassLoader
- •7.2. SecurityManager - менеджер безопасности
- •7.3. System
- •7.4. Runtime
- •7.5. Process
- •8. Потоки исполнения
- •8.1. Runnable
- •8.2. Thread
- •8.3. ThreadGroup
- •9. Исключения
- •10. Заключение
- •11. Контрольные вопросы
- •Аннотация
- •Лекция 14. Пакет java.util
- •1. Введение
- •2. Работа с датами и временем
- •2.1. Класс Date
- •2.2. Классы Calendar и GregorianCalendar
- •2.3. Класс TimeZone
- •2.4. Класс SimpleTimeZone
- •3. Интерфейс Observer и класс Observable
- •4. Коллекции
- •4.1. Интерфейсы
- •4.1.1. Интерфейс Collection
- •4.1.2. Интерфейс Set
- •4.1.3. Интерфейс List
- •4.1.4. Интерфейс Map
- •4.1.5. Интерфейс SortedSet
- •4.1.6. Интерфейс SortedMap
- •4.1.7. Интерфейс Iterator
- •4.2. Aбстрактные классы используемые при работе с коллекциями.
- •4.3. Конкретные классы коллекций
- •4.4. Класс Collections
- •5. Класс Properties
- •6. Интерфейс Comparator
- •7. Класс Arrays
- •8. Класс StringTokenizer
- •9. Класс BitSet
- •10. Класс Random
- •11. Локализация
- •11.1. Класс Locale
- •11.2. Класс ResourceBundle
- •12. Заключение
- •13. Контрольные вопросы
- •Аннотация
- •Лекция 15. Пакет java.io
- •1. Система ввода/вывода. Потоки данных (stream)
- •1.1. Классы InputStream и OutputStream
- •1.2. Классы-реализации потоков данных
- •1.2.1. Классы ByteArrayInputStream и ByteArrayOutputStream
- •1.2.2. Классы FileInputStream и FileOutputStream
- •1.2.3. PipedInputStream и PipedOutputStream
- •1.2.4. StringBufferInputStream
- •1.2.5. SequenceInputStream
- •1.3. Классы FilterInputStreeam и FilterOutputStream. Их наследники.
- •1.3.1. BufferedInputStream и BufferedOutputStream
- •1.3.2. LineNumberInputStream
- •1.3.3. PushBackInputStream
- •1.3.4. PrintStream
- •1.3.5. DataInputStream и DataOutputStream
- •2. Serialization
- •2.1. Версии классов
- •3. Классы Reader и Writer. Их наследники.
- •4. Класс StreamTokenizer
- •5. Работа с файловой системой.
- •5.1. Класс File
- •5.2. Класс RandomAccessFile
- •6. Заключение
- •7. Контрольные вопросы
- •Аннотация
- •Лекция 16. Введение в сетевые протоколы
- •1. Основы модели OSI
- •2. Physical layer (layer 1)
- •3. Data layer (layer 2)
- •3.1. LLC sublayer.
- •3.2. MAC sublayer.
- •4. Network layer (layer 3)
- •4.1. Class A
- •4.2. Class B
- •4.3. Class CClass DClass E
- •5. Transport layer (layer 4)
- •6. Session layer (layer 5)
- •7. Presentation layer (layer 6)
- •8. Application layer (layer 7)
- •9. Утилиты для работы с сетью
- •9.1. IPCONFIG (IFCONFIG)
- •9.3. Ping
- •9.4. Traceroute
- •9.5. Route
- •9.6. Netstat
- •9.7. Задания для практического занятия
- •10. Пакет java.net
- •11. Заключение
- •12. Контрольные вопросы
Классы FilterInputStreeam и FilterOutputStream. Их наследники. Стр. 15 из 34
System.out.println("readInt: " + inData.readInt()); System.out.println("readLong: " + inData.readLong()); System.out.println("readDouble: " + inData.readDouble()); inData.close();
System.out.println("read the same data in other order (first byte is skipped):");
in = new ByteArrayInputStream(bytes); inData = new DataInputStream(in);
System.out.println("readInt: " + inData.readInt()); System.out.println("readDouble: " + inData.readDouble()); System.out.println("readLong: " + inData.readLong()); inData.close();
} catch (Exception e) {
System.out.println("Impossible IOException occurs: " + e.toString()); e.printStackTrace();
}
Интерфейсы DataInput и DataOutput представляют возможность записи/считывания данных примитивных типов Java. Для аналогичной работы с объектами определены унаследованные от них интерфейсы ObjectInput и ObjectOutput соответственно. В java.io имеются реализации этих интерфейсов - классы ObjectInputStream и ObkectOutputStream. Процесс превращения в байты и обратно для объектов несколько сложнее чем для примитивных типов - записываться могут объекты разных классов, они могут иметь ссылки на другие объекты и т.д. Процесс превращения объекта в набор байт носит название сериализация (Serialization) и, соответственно, для обратного процесса - то есть из набора байт в объект - десериализация.
2. Serialization
В java имеется стандартный механизм превращения объекта в набор байт - сериализации. Для того, что бы объект мог быть сериализован, он должен реализовать интерфейс java.io.Serializable(соответствующее объявление должно явно присутствовать в классе объекта или, по правилам наследования, неявно в родительском классе вверх по иерархии). Интерфейс java.io.Serializable не определяет никаких методов. Его присутствие только определяет, что объекты этого класса разрешено сериализовывать. При попытке сериализовать объект, не реализующий этот интерфейс, будет брошено
java.io.NotSerializableException
После того, как объект был сериализован, то есть превращен в последовательность байт, его по этой последовательности можно восстановить, при этом восстановление можно проводить на любой машине (вне зависимости от того, где проводилась сериализация), то есть последовательность байт можно передать на другую машину по сети или любым другим образом, и там провести десериализацию. При этом не имеет значения опереционная система под которой запущена Java - например, можно создать объект на машине с ОС Windows, превратить его в последовательность байт, после чего передать их по сети на машину с ОС Unix, где восстановить тот же объект.
Программирование на Java
Rendered by www.RenderX.com
Стр. 16 из 34 |
Serialization |
Для работы по сериализации в java.io определены интерфейсы ObjectInput, ObjectOutput
и реализующие их классы ObjectInputStream и ObjectOutputStream соответственно.
Для сериализации объекта нужен выходной поток OutputStream, который следует передать при конструировании ObjectOutputStream. После чего вызовом метода writeObject() сериализовать объект и записать его в выходной поток. Например:
ByteArrayOutputStream os = new ByteArrayOutputStream(); Object objSave = new Integer(1);
ObjectOutputStream oos = new ObjectOutputStream(os); oos.writeObject(objSave);
Что бы просмотреть, во что превратился объект objSave, можно просмотреть содержимое массива
byte[] bArray = os.toByteArray();
А чтобы получить этот объект, можно десериализовать его из этого массива:
ByteArrayInputStream is = new ByteArrayInputStream(bArray); ObjectInputStream ois = new ObjectInputStream(is);
Object objRead = ois.readObject();
Теперь можно убедиться, что считанный объект идентичен записанному:
System.out.println("readed object is: " + objRead.toString()); System.out.println("Object equality is: " + (objSave.equals(objRead))); System.out.println("Reference equality is: " + (objSave==objRead));
Результатом выполнения приведенного выше кода будет:
readed object is: 1 Object equality is: true
Reference equality is: false
Как видим, восстановленный объект не является тем же самым(что очевидно - ведь восстановление могло происходить и на другой машине), но равным сериализованому.
Сериализуемый объект может хранить ссылки на другие объекты, которые в свою очередь так же могут хранить ссылки на другие объекты. И все они тоже должны быть восстановлены при десериализации. При этом, важно, что если несколько ссылок указывают на один и тот же объект, то в восстановленных объектах эти ссылки так же указывали на один и тот же объект. Следующий пример демонстрирует это свойство:
package demo.io; import java.io;
class Point implements java.io.Serializable{ double x;
double y;
public Point(double x, double y) {
Программирование на Java
Rendered by www.RenderX.com
Стр. 17 из 34
this.x = x; this.y = y;
}
public String toString() {
return "("+x+","+y+") reference="+super.toString();
}
}
class Line implements java.io.Serializable{ Point point1;
Point point2; int index; public Line() {
System.out.println("Constructing empty line");
}
Line(Point p1, Point p2, int index) { System.out.println("Constructing line: " + index);
this.point1 = p1; this.point2 = p2; this.index = index;
}
public int getIndex() {return index;}
public void setIndex(int newIndex) {index = newIndex;} public void printInfo() {
System.out.println("Line: " + index);
System.out.println(" Object reference: " + super.toString()); System.out.println(" from point "+point1); System.out.println(" to point "+point2);
}
}
public class Main {
public static void main(java.lang.String[] args) { Point p1 = new Point(1.0,1.0);
Point p2 = new Point(2.0,2.0); Point p3 = new Point(3.0,3.0); Line line1 = new Line(p1,p2,1); Line line2 = new Line(p2,p3,2);
System.out.println("line 1 = " + line1); System.out.println("line 2 = " + line2); String fileName = "d:\\file";
try{
// write objects to file
FileOutputStream os = new FileOutputStream(fileName); ObjectOutputStream oos = new ObjectOutputStream(os); oos.writeObject(line1);
oos.writeObject(line2);
//change state of line 1 and write it again line1.setIndex(3);
//oos.reset();
oos.writeObject(line1);
Программирование на Java
Rendered by www.RenderX.com
Стр. 18 из 34 |
Serialization |
//close stream
//it is enought to close only filter stream oos.close();
os.close(); //read objects
System.out.println("Read objects:"); FileInputStream is = new FileInputStream(fileName); ObjectInputStream ois = new ObjectInputStream(is); while(is.available()>0){
Line line = (Line)ois.readObject(); line.printInfo();
}
}catch(ClassNotFoundException e){ e.printStackTrace(); }catch(IOException e){ e.printStackTrace();
}
}
}
Первым делом стоит обратить внимание, что метод available() вызывался не у ObjectInputStream, а именно у InputStream, над которым объектный поток надстроен. Сделано так потому, что вызов метода available() у ObjectOutputStream возвращает количество байт, определяемое следующим образом: если достигнут конец потока - значение (-1), если поток находится в заблокированном режиме, то есть производит непосредственное считывание байт объекта из содержащегося в нем потока, то возвращается количество - сколько еще должно быть считано для завершения десериализации объекта(например, такое возможно, если идет долгое считывание объекта по сети), если же поток не находится в режиме блокировки, возвращается 0.
Выполнение этой программы приведет к выводу на экран примерно следующего:
Constructing line: 1 Constructing line: 2 Read objects:
Line: 1
Object reference: study.java.Line@2dc5
from point (1.0,1.0) reference=study.java.Point@322b to point (2.0,2.0) reference=study.java.Point@3481 Line: 2
Object reference: study.java.Line@5214
from point (2.0,2.0) reference=study.java.Point@3481 to point (3.0,3.0) reference=study.java.Point@5435 Line: 1
Object reference: study.java.Line@2dc5
from point (1.0,1.0) reference=study.java.Point@322b to point (2.0,2.0) reference=study.java.Point@3481
Можно заметить, что объектные ссылки на Point - point2 у первой линии и point1 у второй линии совпадают - это по-прежнему один и тот же объект. Далее, третий записанный объект
Программирование на Java
Rendered by www.RenderX.com
Стр. 19 из 34
- тот же, что и первый, при этом совпадают объектные ссылки. Но, когда мы записывали его, значение index было присвоено 3, а в десериализованном объекте это значение равно 1. Так произошло потом, что объект уже был сериализован, а в таком случае, что не быть записанным дважды, механизм сериализации некоторым образом для себя помечает, что объект уже записан в граф, и когда в очередной раз попадется ссылка на него, она будет указывать на уже сериализованный объект. Такой механизм необходим, что бы иметь возможность записывать связанные объекты, которые могут иметь перекрестные ссылки. В таких случаях необходимо отслеживать был ли объект уже сериализован, то есть нужно ли его записывать или достаточно указать ссылку на него.
Если класс содержит в качестве полей другие объекты, то эти объекты так же будут сериализовываться и поэтому тоже должны быть сериализуемы. В свою очередь, сериализуемы должны быть и все объекты, содержащиеся в этих сериализуемых объектах и т.д. Полный путь ссылок объекта по всем объектным ссылкам, имеющимся у него и у всех объектов на которые у него имеются ссылки, и т.д. - называется графом исходного объекта.
Поэтому, когда мы повторно записывали line1, состояние этого объекта не было записано, поскольку этот объект уже был помечен, как записанный в граф. Что бы указать, что сеанс сериализации завершен, и мы хотим заново записывать объекты, у ObjectOutputStream нужно вызвать метод reset(). В рассматриваемом примере для этого достаточно убрать комментарий вв строке
//reset();
Если теперь запустить программу, то можно заметить, что line1 во второй раз записано полностью. Но при этом объектная ссылка на ее point2 не совпадает с объектной ссылкой point1 у line1. Этого, впрочем, и следовало ожидать - ведь во второй раз line1 была записана уже в новом сеансе сериализации, а значит, и все объекты Point были записаны заново.
Далее, в теле конструкторов Line специально были вставлены строки, выводящие на экран некоторые сообщения, сообщающие о вызове этих конструкторов. Как можно заметить, при десериализации никакие из этих конструкторов не вызывались - даже конструктор по умолчанию, а Point вообще такого не имеет. То есть при десериализации объект просто восстанавливается в том виде в каком он был, а не конструируется заново.
Однако, вопрос, на который следует обратить внимание - что происходит с состоянием объекта, унаследованным от суперкласса. Ведь состояние объекта определяется не только значениями полей, определенными в нем самом, но так же и таковыми, унаследованными от супепркласса. Сериализуемый подтип берет на себя такую ответственность, но только в том случае, если у суперкласса определен конструктор по умолчанию, объявленный с модификатором доступа таким, что будет доступен для вызова из рассматриваемого наследника. Этот конструктор будет вызван при десериализации. В противном случае, во время выполнения будет брошено исключение java.io.InvalidClassException . Что бы проверить, когда вызывается этот конструктор, можно в предыдущем примере добавить объявление класса
class AbstractEntity{ public AbstractEntity(){
System.out.println("Create Abstract Entity");
}
Программирование на Java
Rendered by www.RenderX.com
Стр. 20 из 34 |
Serialization |
}
А какому-нибудь классу, скажем Point, указать наследование от этого класса AbstractEntity.
class Point extends AbstractEntity implements java.io.Serializable
В процессе десериализации, поля НЕ сериализуемых классов (родительских классов, НЕ реализующих интерфейс Serializable) инициируются вызовом конструктора без параметров. Такой конструктор должен быть доступен из сериализуемого их подкласса. Поля сериализуемого класса будут восстановлены из потока.
При обходе графа, может попасться объект, не реализующий интерфейс Serializable. В
этом случае будет брошено java.io.NotSerializableException .
Весьма познавательно будет так же попробовать провести следующий эксперимент: выполнить программу из предыдущего примера. После ее выполнения на диске останется сохраненным файл, куда были сериализованы три объекта. Теперь удалим класс Point, то есть нужно сделать так, что бы файл Point.class не был доступен по путям, указанным
впеременной CLASSPATH. Если теперь попробовать считать объект из файла "d:\file", при вызове метода readObject() произойдет исключение ClassNotFoundException. Если исключение обработать и попытаться продолжить считывание из ObjectInputStream, будет брошено java.io.StreamCorruptedException. Но из InputStream над которой надстроен
ObjectInputStream в таком случае будет считано лишь некоторое количество байт, но это количество, спецификацией не оговаривается и может изменяться в зависимости от реализации Java-машины, поэтому лучше избегать попыток продолжать считывание из такого потока в подобных ситуациях. Получить java.io.StreamCorruptedException можно и более простым путем, для этого достаточно считать произвольное количество байт непосредственно из потока InputStream. В таком случае ObjectInputStream замечает, что окольными путями были считаны данные из потока, и будет брошено исключение java.io.StreamCorruptedException . Однако, если произвести считывание не из InputStream
а вызовом того же метода read() у ObjectInputStream, будет возвращено значение (-1), то есть как будто достигнут конец потока. В ObjectOutputStream так же могли быть записаны примитивные типы Java(ведь ObjectOutput наследуется от DataOutput). При попытке считывания объекта, когда следующим на очереди считывания идет некоторый примитивный тип Java, будет брошено исключение java.io.OptionalDataException. Можно рассмотреть еще несколько возможных вариантов некорректного использования ObjectInputStream, но совершенно ясно, что если мы хотим считать именно те данные, которые были записаны
впоток, нужно считывать их именно в том порядке, в каком были записаны.
Если классу для сериализации и десериализации требуется своеобразный подход, его можно осуществить путем реализации следующих методов, с точным сохранением сигнатуры:
private void writeObject(java.io.ObjectOutputStream out) throws IOException; private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException;
Методом writeObject записываются данные состояния объекта, и методом readObject они, соответственно, считываются. При этом можно вызвать стандартный механизм записи объекта, вызовом метода
Программирование на Java
Rendered by www.RenderX.com
Стр. 21 из 34
out.defaultWriteObject();
И стандартный механизм считывания вызовом метода
in.defaultReadObject();
Этот метод использует информацию из потока что бы присвоить значения полей сохраненного объекта, полям с соответствующими именами в текущем объекте. Использование такой комбинации методов удобно, если ожидается, что со временем классу будут добавлены новые поля.
Возможны ситуации, когда по некоторым причинам, необходимо самостоятельно управлять ходом сериализации и восстановления объекта. Например, опираясь на некоторую замысловатую логику, записывать не все поля, а только некоторые из них. Для такого гибкого управления процессом сериализации предназначен интерфейс java.io.Externalizable
.
При использовании этого интерфейса в поток автоматически записывается только идентификация класса. Сохранить и восстановить всю информацию по состоянию экземпляра, должен обеспечить сам класс. Для получения классом полного контроля за форматом и содержанием потока, для объекта и его суперклассов, должны быть реализованы методы writeExternal() и readExternal() интерфейса Externalizable. Эти методы должны полностью координировать сохранение состояния, унаследованное от суперкласса.
При сериализации, сериализуемый объект первым делом проверяется на поддержку интерфейса Externalizable. Если объект реализует Externalizable, вызывается его метод writeExternal . Если объект не поддерживает Externalizable, но реализует Serializable,
используется стандартная сериализация ObjectOutputStream. При восстановлении Externalizable объекта, экземпляр создается путем вызова public конструктора без аргументов, после чего вызывается метод readExternal . В противовес этому, объекты Serializable восстанавливаются путем считывания из потока ObjectInputStream.
Метод writeExternal имеет сигнатуру:
void writeExternal(ObjectOutput out) throws IOException;
Для сохранения состояния, вызываются методы ObjectInput для сохранения как примитивных значений так и объектов, связанных с сериализуемым. Для корректной работы, в соответствующем методе
void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
В том же самом порядке эти значения должны быть считаны.
При управлении процессом сериализации не всегда имеет смысл обращаться к реализации интерфейса Externalizable или методов readObject, writeObject. Если вопрос состоит только в том, что нежелательно сохранять и восстанавливать некоторый объекты-члены экземпляра. Обычно подобное требование возникает, когда в объекте хранится некоторая конфиденциальная информация, например пароль. Даже если поле такого объекта описано как private, оно все равно будет сериализовано, то есть записано в поток, а значит, это
Программирование на Java
Rendered by www.RenderX.com