Для иллюстрации приведем пример, как может выглядеть простая реализация загрузчика классов, использующего сетевое соединение:
class NetworkClassLoader extends ClassLoader {
String host; int port;
public NetworkClassLoader(String host, int port) {
this.host = host; this.port = port;
}
public Class findClass(String className) {
byte[] bytes = loadClassData(className);
return defineClass(className, bytes, 0,
bytes.length);
}
private byte[] loadClassData(
String className) {
byte[] result = null;
// open connection, load the class data
return result;
}
}
В этом примере только показано, что наследник загрузчика классов должен определить и реализовать методы findClass() и loadClassData() для загрузки описания класса. Когда описание получено, массив байт передается в метод defineClass() для создания экземпляра Class. Для простоты в примере приведен только шаблонный код, без реализации получения байт из сетевого соединения.
Для получения экземпляров классов, загруженных с помощью этого загрузчика, можно воспользоваться методом loadClass():
try {
ClassLoader loader =
new NetworkClassLoader(host, port);
Object main = loader.loadClass(
"Main").newInstance();
}
catch(ClassNotFoundException e) {
e.printStackTrace();
}
catch(InstantiationException e) {
e.printStackTrace();
}
catch(IllegalAccessException e) {
e.printStackTrace();
}
Если такой класс не будет найден, будет брошено исключение ClassNotFoundException, если класс будет найден, но произойдет какая-либо ошибка при создании объекта этого класса – будет брошено исключение InstantiationException, и, наконец, если у вызывающего потока не имеется соответствующих прав для создания экземпляров этого класса (что проверяется менеджером безопасности), будет брошено исключение IllegalAccessException.
SecurityManager – менеджер безопасности
С помощью методов этого класса приложения перед выполнением потенциально опасных операций проверяют, является ли операция допустимой в данном контексте.
Класс SecurityManager содержит много методов с именами, начинающимися с приставки check ("проверить"). Эти методы вызываются из стандартных классов библиотек Java перед тем, как в них будут выполнены потенциально опасные операции. Типичный вызов выглядит примерно следующим образом:
SecurityManager security =
System.getSecurityManager();
if(security != null) {
security.checkX(…);
}
где X – название потенциально опасной операции: Access, Read, Write, Connect, Delete, Exec, Listen и т.д.
Предотвращение вызова производится путем бросания исключения – SecurityException, если вызов операции не разрешен (кроме метода checkTopLevelWindow, который возвращает boolean значение).
Для установки менеджера безопасности в качестве текущего вызывается метод setSecurityManager() в классе System. Соответственно, для его получения нужно вызвать метод getSecurityManager().
В большинстве случаев, если приложение запускается локально, будут разрешены все действия, поскольку в системе SecurityManager отсутствует. Предполагается, что запускаемому локально приложению можно полностью доверять. Если же приложение может быть опасно (например, его код был загружен из сети, как это происходит в случае апплетов), то менеджер безопасности выставляется и его уже нельзя убрать или заменить (попытки вызовут SecurityException ). Он контролирует работу с локальной файловой системой, сетевыми соединениями, потоками исполнения и т.д.
System
Класс System содержит набор полезных статических методов и полей. Экземпляр этого класса не может быть создан или получен.
Пожалуй, наиболее широко используемой возможностью, предоставляемой System, является стандартный вывод, доступный через переменную System.out. Ее тип – PrintStream (потоки данных будут подробно рассматриваться в лекции 15). Стандартный вывод можно перенаправить в другой поток (файл, массив байт и т.д., главное, чтобы это был объект PrintStream ):
public static void main(String[] args) {
System.out.println("Study Java");
try {
PrintStream print = new PrintStream(new
FileOutputStream("d:\\file2.txt"));
System.setOut(print);
System.out.println("Study well");
}
catch(FileNotFoundException e) {
e.printStackTrace();
}
}
При запуске этого кода на экран будет выведено только
Study Java
И в файл "d:\file2.txt" будет записано
Study well
Аналогично могут быть перенаправлены стандартный ввод System.in – вызовом System.setIn(InputStream) и поток вывода сообщений об ошибках System.err – вызовом System.setErr(PrintStream) (по умолчанию все потоки – in, out, err – работают с консолью приложения).
Следующие методы класса System позволяют работать с некоторыми параметрами системы:
* public static void runFinalizersOnExit(boolean value) – определяет, будет ли производиться вызов метода finalize() у всех объектов (у кого еще не вызывался), когда выполнение программы будет окончено (по умолчанию выставлено значение false );