Сначала JVM проверяет, загружен ли объект Class для этого нового класса. При отрицательном результате JVM ищет подходящий файл .class и подгружает его (а дополнительный загрузчик, например, может подгружать байт-код из базы данных). Загруженный код класса проходит проверку на целостность и на отсутствие некорректного байт-кода Java (одна из «линий защиты» в Java).
После того как объект Class для определенного типа будет помещен в память, в дальнейшем он используется при создании всех объектов этого типа. Следующая программа проясняет сказанное:
//• typei nfo/SweetShop.java
// Проверка процесса загрузки классов.
import static net.mindview util.Print *.
class Candy {
static { print("3arpy3Ka класса Candy"), }
}
class Gum {
static { print("Загрузка класса Gum"), }
}
class Cookie {
static { System out println("3arpy3Ka класса Cookie"), }
}
public class SweetShop {
public static void main(String[] args) { printC'B методе mainO"), new CandyO;
print("После создания объекта Candy");
try {
Class forName("Gum"). } catchCClassNotFoundException e) {
pri nt("Класс Gum не найден").
}
print( "После вызова метода Class forName(\"Gum\")"), new CookieO.
print("После создания объекта Cookie").
}
} /* Output в методе mainO Загрузка класса Candy После создания объекта Candy Загрузка класса Gum
После вызова метода Class forNameCGum") Загрузка класса Cookie После создания объекта Cookie */// ~
В каждом из классов Candy, Gum и Cookie присутствует статический блок, который отрабатывает один раз, при первой загрузке класса. При выполнении этого блока выводится сообщение, говорящее о том, какой класс загружается. В методе main() создание объектов классов Candy, Gum и Cookie чередуется с выводом на экран вспомогательных сообщений, по которым можно оценить, в какой момент загружается тот или иной класс.
Из результата работы программы мы видим, что объект 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