Главная > Java сниппеты > Использование readResolve

Тема Зацепин
268

Java-разработчик 🧩
511
2 минуты

Использование readResolve

1 Введение 2 Десериализация без введения новых объектов 3 Интерфейс RandomAccess 4 Производительность для разных типов списков 5 Ссылки

Добавлено : 27 Mar 2009, 19:28

Содержание

Введение

Одним из стандартных методов, определенных в java.lang.Object является toString. Этот метод используется для получения строкового представления объекта. Вы можете (и обычно должны) переопределить этот метод для записываемых вами классов. В этой статье рассматриваются некоторые вопросы использования toString.

Для начала рассмотрим следующий пример программы:

class MyPoint {
private final int x, y;

public MyPoint(int x, int y) {
this.x = x;
this.y = y;
}
}

public class TSDemo1 {
public static void main(String args[]) {
MyPoint mp = new MyPoint(37, 47);

// использовать Object.toString() по умолчанию

System.out.println(mp);

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

System.out.println(mp.getClass().getName() + "@"
+ Integer.toHexString(mp.hashCode()));

// неявный вызов toString() объекта
// как часть соединения строк

String s = mp + " testing";
System.out.println
(s);

// аналогично предыдущему, за исключением того,
// что ссылка на объект равна null

mp = null;
s = mp +
" testing";
System.out.println
(s);
}
}

В программе TSDemo1 определяется класс MyPoint для представления точек X,Y. В ней не определяется метод toString для класса. Создается экземпляр класса и затем распечатывается. После выполнения программы TSDemo1 вы должны получить примерно следующий результат:

MyPoint@111f71
MyPoint@111f71
MyPoint@111f71 testing
null testing

Десериализация без введения новых объектов

Вы можете удивиться, как можно распечатать произвольный объект класса. Такие методы библиотеки, как System.out.println, не знают ничего о классе MyPoint или его объектах. Поэтому возникает вопрос, каким образом возможно преобразовать такой объект в строковую форму и вывести на печать, как это делает первый оператор вывода в TSDemo1?

Ответ заключается в том, что println вызывает метод java.io.PrintStream.print(Object), который затем вызывает метод String.valueOf. Метод String.valueOf является очень простым:

public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}

При вызове println со ссылкой на объект MyPoint, метод String.valueOf преобразует объект в строку. String.valueOf сначала проверяет ссылку, чтобы убедиться, что она не равна null. Затем он вызывает метод toString для объекта. Поскольку класс MyPoint не имеет метода toString, используется метод в java.lang.Object по умолчанию.

Что же возвращает метод toString по умолчанию в качестве строкового значения? Формат демонстрируется во втором операторе print приведенного выше примера. Возвращаются объединенные в одну строку название класса, "@" и шестнадцатиричное представление хэшкода объекта. В методе hashCode в Object по умолчанию обычно реализуется преобразование адреса памяти объекта в числовое значение. Поэтому ваш результат может отличаться от приведенного выше.

Третья и четвертая части примера TSDemo1 демонстрируют ту же идею: при использовании "+" для соединения строки с объектом, вызывается toString для преобразования объекта в строку. Чтобы увидеть это, вы должны взглянуть на байткод для TSDemo. Байткод для TSDemo1 (в подходящей для чтения форме) можно получить, выполнив следующую команду:

javap -c . TSDemo1

Если вы посмотрите на байткод, вы увидите, что часть его содержит создание объекта StringBuffer и использование StringBuffer.append(Object) для добавления к нему объекта mp. StringBuffer.append(Object) реализуется очень просто:

public synchronized StringBuffer append(Object obj) {
return append(String.valueOf(obj));
}

Как упоминалось ранее, String.valueOf вызывает toString с объектом для получения его строкового значения.

Интерфейс RandomAccess

Хорошо, достаточно о вызове метода toString по умолчанию. Как можно написать свои собственные методы toString? Это действительно очень просто. Вот пример:

class MyPoint {
private final int x, y;

public MyPoint(int x, int y) {
this.x = x;
this.y = y;
}

public String toString() {
return x + " " + y;
}
}

public class TSDemo2 {
public static void main(String args[]) {
MyPoint mp = new MyPoint(37, 47);

// вызвать MyPoint.toString()

System.out.println(mp);

// вызвать toString() и
// получить из него значение X

String s = mp.toString();
String t = s.substring
(0, s.indexOf(' '));
int x = Integer.parseInt(t);
System.out.println
(t);
}
}

Результат выполнения этой программы:

37 47
37

Метод toString в этом примере действительно работает, но есть парочка проблем. Одной из них является то, что здесь нет никакого описательного текста. Все что вы видите - это непонятная строка "37 47". Вторая проблема - значения X,Y в объектах MyPoint имеют спецификатор доступа private. Не существует другого способа получить к ним доступ кроме выборки из строки, возвращенной из toString. Вторая часть примера TSDemo2 демонстрирует код, требуемый для извлечения значения X из строки. Этот способ является неэффективным и подверженным ошибкам.

Вот другой подход для записи метода toString, который устраняет проблемы предыдущего примера:

class MyPoint {
private final int x, y;

public MyPoint(int x, int y) {
this.x = x;
this.y = y;
}

public String toString() {
return "X=" + x + " " + "Y=" + y;
}

public int getX() {
return x;
}

public int getY() {
return y;
}
}

public class TSDemo3 {
public static void main(String args[]) {
MyPoint mp = new MyPoint(37, 47);

// вызвать MyPoint.toString()

System.out.println(mp);

// получить значения X,Y при помощи методов доступа

int x = mp.getX();
int y = mp.getY();
System.out.println
(x);
System.out.println
(y);
}
}

Результат работы программы:

X=37 Y=47
37
47

В этом примере в вывод добавляется некоторый описательный текст и определяются два метода доступа для получения значений X,Y. В общем случае при написании метода toString формат возвращаемой строки должен содержать все содержимое объекта. Ваш метод toString должен, также, содержать описание для каждого поля. Также должен иметься способ получения значений полей объекта без извлечения из строки. Обратите внимание, что использование "+" в toString для построения возвращаемого значения не всегда является самым эффективным подходом. Возможно, вы захотите использовать вместо этого StringBuffer.

Производительность для разных типов списков

Элементарные типы языка программирования Java, такие как int, также имеют методы toString, например, Integer.toString(int). Как насчет массивов? Как преобразовать массив в строку? Вы можете присвоить ссылку на массив ссылке на Object, но массивы не являются реальными классами. Программа выглядит следующим образом:

import java.lang.reflect.*;

public class TSDemo4 {
public static String toString(Object arr) {

// если ссылка на объект равна null или не является
// массивом, вызвать String.valueOf()

if (arr == null || !arr.getClass().isArray()) {
return String.valueOf(arr);
}

// создать строковый буфер и
// получить длину массива

StringBuffer sb = new StringBuffer();
int len = Array.getLength(arr);

sb.append
('[');

// выполнить итерацию по элементам массива

for (int i = 0; i < len; i++) {
if (i > 0) {
sb.append(',');
}

// получить i-й элемент

Object obj = Array.get(arr, i);

// преобразовать его в строку путем
// рекурсивного вызова toString()

sb.append(toString(obj));
}
sb.append(']');

return sb.toString();
}

public static void main(String args[]) {

// пример #1

System.out.println(toString("testing"));

// пример #2

System.out.println(toString(null));

// пример #3

int arr3[] = new int[] { 1, 2, 3 };
System.out.println
(toString(arr3));

// пример #4

long arr4[][] = new long[][] { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
System.out.println
(toString(arr4));

// пример #5

double arr5[] = new double[0];
System.out.println
(toString(arr5));

// пример #6

String arr6[] = new String[] { "testing", null, "123" };
System.out.println
(toString(arr6));

// пример #7

Object arr7[] = new Object[] {
new Object[] { null, new Object(), null },
new int[] { 1, 2, 3 }, null };
System.out.println
(toString(arr7));
}
}

В программе TSDemo4 создается метод toString, а затем в метод toString передается ссылка на произвольный Object. Если эта ссылка не равна null или не относится к массиву, вызывается метод String.valueOf. В противном случае, если Object ссылается на массив, TSDemo4 использует отображение для доступа к элементам массива. Array.getLength и Array.get являются основными методами, работающими с массивом. После извлечения элемента в программе рекурсивно вызывается toString для получения строки для элемента. Этот способ гарантирует корректную обработку многомерных массивов.

Вот результат работы программы:

testing
null
[1,2,3]
[[
1,2,3],[4,5,6],[7,8,9]]
[]
[
testing,null,123]
[[
null,java.lang.Object@111f71,null],[1,2,3],null]

Естественно, если вы имеете очень большой массив и вызываете toString, будет использоваться много памяти и результирующая строка может быть не особенно полезна или доступна для чтения человеком.

Ссылки

Дополнительная информация по использованию методов toString находится в разделе 2.6.2 "Вызовы метода" в книге "Язык программирования Java(tm), третье издание" Arnold, Gosling и Holmes на странице http://java.sun.com/docs/books/javaprog/thirdedition/.

Просмотрите также пункт 9 "Всегда переопределяйте toString" в "Руководстве по эффективному программированию в Java" Джошуа Блоха (Joshua Bloch) (http://java.sun.com/docs/books/effective/).

Теги: java

Еще от автора

Применение WeakHashmap для списков слушателей

В статье от 11мая 1999 года Reference Objects были описаны основные идеи применения ссылочных объектов, но не приводилось детального описания. Данная статья позволит вам получить больше сведений, касающихся данной темы. В основном ссылочные объекты применяются для косвенных ссылок на память необходимую объектам. Ссылочные объекты хранятся в очереди (класс ReferenceQueue), в которой отслеживается доступность ссылочных объектов. Исходя из типа ссылочного объекта, сборщик мусора может освобождать память даже тогда, когда обычные ссылки не могут быть освобождены.

Заставки в Mustang

Согласно определению, данному в Wikipedia, заставка - это компьютерный термин, обозначающий рисунок, появляющийся во время загрузки программы или операционной системы. Заставка для пользователя является визуальным отображением инициализации программы. До выхода версии Java SE 6 (кодовое название Mustang) единственной возможностью применения заставки было создание окна, во время запуска метода main, и размещение в нем картинки. Хотя данный способ и работал, но он требовал полной инициализации исполняемой Java среды до появления окна заставки. При инициализации загружались библиотеки AWT и Swing, таким образом, появление заставки задерживалось. В Mustang появился новый аргумент командной строки, значительно облегчающий использование заставок. Этот способ позволяет выводить заставку значительно быстрее до запуска исполняемой Java среды. Окончательное добавление данной функциональности находится на рассмотрении в JCP.

Анонимные классы

1 Введение 2 Типичный пример применения 3 Сортировка списка с использованием анонимных классов 4 Примеры использования 5 Ссылки

Еще по теме

Гибкое журналирование с помощью log4j

Log4j – это инструмент для журналирования с открытым исходным кодом, разработанный под эгидой глобального проекта Jakarta Apache. Он представляет собой набор API с помощью которых, разработчики могут вставлять в свой код выражения, которые выводят некоторую информацию (отладочную, информационную, сообщения об ошибках и т.д.), и конфигурировать этот вывод с помощью внешний конфигурационных файлов. В этой статье рассматриваются основные идеи, положенные в данный инструмент, а также будут затронуты некоторые интересные моменты, касающиеся написания демонстрационного web-приложения.

Аннотации в Java (java annotation types). Пример 1

Продолжаю серию статей о нововведениях в Java (начиная с версии 1.5). На этот раз разговор пойдет об аннотациях (annotation type).

Указатели и виртуальные функции в Java

В настоящее время в Интернете можно найти множество статей как о перспективности платформы Java, так и об её ограниченности. Многих программистов, только присматривающихся к Яве, могут отпугнуть частые заявления, типа: «низкое быстродействие», «отсутствие указателей» и т.д.

Блокировки

Одной из популярных функциональных возможностей библиотек J2SE 5.0 является добавление средств обеспечения параллельной работы. Предоставленные как часть JSR 166 эти средства обеспечивают развитые возможности программирования параллельных процессов, устраняющие необходимость использования разработчиками ключевого слова synchronized и связанных с ним блокировок. Среди предлагаемых ими функциональных возможностей присутствуют: поддержка блокировочных таймаутов, множественные переменные условия для одной блокировки, блокировки чтения/записи и способность прерывать поток, ожидающий снятия блокировки. Более подробную информацию по дополнительной поддержке блокировок можно найти в документации по пакету java.util.concurrent.locks.