Чтение онлайн

ЖАНРЫ

Философия Java3

Эккель Брюс

Шрифт:

Из результата работы программы мы видим, что объект Class загружается только при непосредственной необходимости, а статическая инициализация производится при загрузке этого объекта.

Особенно интересно выглядит строка программы

Class forNameCGum"),

Все объекты Class принадлежат классу Class. Объект Class ничем принципиально не отличается от других объектов, поэтому вы можете выполнять операции со ссылкой на него (именно так и поступает загрузчик классов). Один из способов получения ссылки на объект Class заключается в вызове метода for-Name, которому передается строка (String) с именем класса (следите за правильностью написания и регистром символов!). Метод возвращает ссылку на

объект Class, которая в данном примере нам не нужна; метод Class.forName вызывался ради побочного эффекта, то есть загрузки класса Gum, если он еще не в памяти. В процессе загрузки выполняется static-инициализатор класса Gum.

Если бы в рассмотренном примере метод Class.forName сработал неудачно (не смог бы найти класс, который вы хотели загрузить), он возбудил бы исключение ClassNotFoundException. Здесь мы просто сообщаем о проблеме и двигаемся дальше, однако в более совершенной программе можно было бы попытаться исправить ошибку в обработчике исключения.

Чтобы использовать информацию RTTI на стадии исполнения, прежде всего необходимо получить ссылку на подходящий объект Class. Один из способов — вызов метода Class.forName — удобен тем, что вам не потребуется уже существующий объект нужного типа. Впрочем, если такой объект уже существует, для получения ссылки на его объект Class можно вызвать метод getClass, определенный в корневом классе Object. Метод возвращает объект Class, представляющий фактический тип объекта. Класс Class содержит немало интересных методов, продемонстрированных в следующем примере:

//• typeinfo/ToyTest.java

// Тестирование класса Class.

package typeinfo.toys;

import static net.mindview.util Print.*;

interface HasBatteries {} interface Waterproof {} interface Shoots {}

class Toy {

// Закомментируйте следующий далее конструктор по // умолчанию, тогда в строке с пометкой (*1*) // возникнет ошибка NoSuchMethodError. ТоуО {} Toy(int i) {}

}

class FancyToy extends Toy

implements HasBatteries. Waterproof, Shoots { FancyToyО { super(1); }

}

public class ToyTest {

static void printInfo(Class cc) {

print ("Имя класса: " + cc.getNameO +

" это интерфейс? [" + cc.isInterfaceO + "]"), print("Простое имя: " + cc.getSimpleName); print("Каноническое имя- " + cc.getCanonicalName).

}

public static void main(String[] args) { Class с = null. try {

с = CI ass.forName("typeinfo.toys FancyToy"); } catch(ClassNotFoundException e) {

print("He найден класс FancyToy"); System.exit(l);

}

printlnfo(c);

for(Class face : c.getlnterfaces)

printlnfo(face); Class up = c.getSuperclassO; Object obj = null, try {

// Необходим конструктор по умолчанию о = up.newInstanceO; } catchdnstantiationException е) {

printCHe удалось создать объект"). System.exit(1); } catchdllegalAccessException е) {

print("Нет доступа"); System.exit(l);

}

printlnfo(obj.getClassO); } продолжение &

} /* Output

Имя класса typeinfo toys FancyToy это интерфейс? [false]

Простое имя FancyToy

Каноническое имя- typeinfo.toys FancyToy

Имя класса typeinfo toys HasBatteries это интерфейс? [true]

Простое имя HasBatteries

Каноническое имя: typeinfo toys HasBatteries

Имя класса typeinfo toys Waterproof это интерфейс? [true]

Простое имя. Waterproof

Каноническое имя typeinfo toys Waterproof

Имя класса, typeinfo.toys Shoots это интерфейс? [true]

Простое имя: Shoots

Каноническое имя: typeinfo.toys Shoots

Имя класса: typeinfo.toys Toy это интерфейс? [false]

Простое имя: Toy

Каноническое имя typeinfo.toys.Toy *///.-

Класс FancyToy,

производный от Toy, реализует несколько интерфейсов: HasBatteries, Waterproof и Shoots. В методе main создается ссылка на объект Class для класса FancyToy, для этого в подходящем блоке try вызывается метод for-Name. Обратите внимание на необходимость использования полного имени (с именем пакета) в строке, передаваемой forName.

Метод printInfo использует getName для получения полного имени класса и методы getSimpleName и getCanonicalName (появившиеся в Java SE5), возвращающие имя без пакета и полное имя соответственно. Метод islnterface проверяет, представляет ли объект Class интерфейс. Таким образом, по объекту Class можно узнать практически все, что может потребоваться узнать о типе.

Метод Class.getlnterfaces возвращает массив объектов Class, представляющих интерфейсы, реализованные объектом Class. Метод getSuperclass возвращает непосредственный (то есть ближайший) базовый класс для объекта Class.

Метод newlnstance фактически реализует «виртуальный конструктор». Вы как бы говорите: «Я не знаю ваш точный тип, так что создайте себя самостоятельно». В рассмотренном примере ссылка up просто указывает на объект Class, больше никакой информации о типе у вас нет. Поэтому при создании нового экземпляра методом newlnstance вы получаете ссылку на обобщенный объект Object. Однако полученная ссылка на самом деле указывает на объект Toy. Следовательно, перед посылкой сообщений, характерных для класса Toy, придется провести нисходящее преобразование. Вдобавок объект, созданный с помощью метода newlnstance, обязан определить конструктор по умолчанию. Позднее в этой главе будет показано, как динамически создать объект класса любым конструктором с использованием механизма рефлексии Java.

Литералы class

В Java существует еще один способ получения ссылок на объект Class — посредством литерала class. В предыдущей программе получение ссылки выглядело бы так:

FancyToy.class:

Такой способ не только проще, но еще и безопасней, поскольку проверка осуществляется еще во время компиляции. К тому же он не требует вызова forName, а значит, является более эффективным.

Литералы class работают со всеми обычными классами, так же как и с интерфейсами, массивами и даже с примитивами. Вдобавок во всех классах-обертках для примитивных типов имеется поле с именем TYPE. Это поле содержит ссылку на объект Class для ассоциированного с ним простейшего типа, как показано в табл. 13.1.

Таблица 13.1. Альтернативное обозначение объекта Class с помощью литералов

Литерал

Ссылка на объект Class

boolean.class

Boolean.TYPE

char.class

Character.TYPE

byte.class

Byte.TYPE

short.class

Short.TYPE

int.class

Integer.TYPE

long.class

Long.TYPE

float.class

Float.TYPE

double.class

Double.TYPE

void.class

Void.TYPE

Хотя эти версии эквивалентны, я предпочитаю использовать синтасис .class, так как он лучше сочетается с обычными классами.

Интересно заметить, что создание ссылки на объект Class с использованием записи .class не приводит к автоматической инициализации объекта Class. Подготовка класса к использованию состоит из трех этапов:

Поделиться с друзьями: