Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Язык программирования JAVA.pdf
Скачиваний:
373
Добавлен:
02.05.2014
Размер:
2.57 Mб
Скачать

converted to PDF by BoJIoc

Глава 14 СИСТЕМНОЕ ПРОГРАММИРОВАНИЕ

Глендаур: Я духов вызывать могу из бездны. Хотспер: И я могу, и каждый это может, Вопрос лишь, явятся ль они на зов.

Вильям Шекспир, “Генрих IV”, перевод Е. Бируковой

В этой главе рассказано, как работать с общими функциями runtime-системы Java и операционной системы. К ним относятся: определение системных свойств, математические вычисления, запуск других программ, управление памятью, отсчет времени. Эти функции предоставляются четырьмя классами Java:

Класс Runtime описывает состояние runtime-системы Java. Объект этого класса отвечает за доступ к функциям времени выполнения например, управлению сборщиком мусора и прекращением работы.

Класс Process представляет выполняемый процесс, созданный вызовом метода

Runtime.exec.

Класс System содержит статические методы для работы с состоянием системы в целом. Некоторые методы System оперируют с текущим runtime-контекстом.

Класс Math содержит статические методы для выполнения многих стандартных математических вычислений например, для определения значения тригонометрических функций и логарифмов.

14.1. Стандартный поток ввода/вывода

Вы можете осуществлять стандартные операции ввода/вывода с помощью трех системных потоков, которые являются статическими полями класса System,— System.in, System.out

и System.err:

public static InputStream in

Стандартный входной поток для чтения символьных данных.

public static OutputStream out

Стандартный выходной поток для вывода сообщений.

public static PrintStream err

Стандартный поток для вывода сообщений об ошибках. Пользователи часто перенаправляют стандартный вывод программы в файл, однако приложение при этом должно иметь возможность вывести сообщение об ошибке так, чтобы пользователь его увидел. Поток err предназначен для тех сообщений об ошибках, которые не перенаправляются вместе со стандартным выводом. Потоки out и err являются объектами класса PrintStream, поэтому для вывода сообщений в err используются те же методы, что и для out.

converted to PDF by BoJIoc

14.2. Управление памятью

Хотя Java не позволяет явно уничтожать ненужные объекты, вы можете непосредственно вызвать сборщик мусора, используя метод gc класса Runtime. Класс Runtime также содержит метод runFinalization для вызова ожидающих блоков завершения (finalizers). Класс Runtime содержит два метода для вывода информации о состоянии памяти:

public long freeMemory()

Возвращает примерное количество свободных байтов системной памяти.

public long totalMemory()

Возвращает общее количество байтов системной памяти.

Класс System содержит статические методы gc и runFinalization, которые вызывают соответствующий метод для текущего runtime-контекста.

Не исключено, что метод Runtime.gc не сможет освободить дополнительную память за счет избавления от мусора” — его может и не быть, к тому же не все сборщики мусора могут находить ненужные объекты по требованию. Тем не менее перед созданием большого количества объектов (особенно в приложениях, критических по времени, на работе которых могут отрицательно сказаться накладные расходы по сборке мусора) все же стоит вызвать метод gc. Он приносит двойную пользу: вы начинаете работу с

максимальным объемом свободной памяти и сокращаете вероятность вызова сборщика мусора во время выполнения программы. Следующий метод освобождает всю возможную память:

public static void fullGC() {

Runtime rt = Runtime.getRuntime(); long isFree = rt.freeMemory(); long wasFree;

do {

wasFree = isFree; rt.gc();

isFree = rt.freeMemory(); } while (isFree >> wasFree); rt.runFinalization();

}

Данный метод в цикле вызывает gc, при этом объем свободной памяти freeMemory увеличивается до определенного предела, после достижения которого дальнейшие вызовы gc, скорее всего, ни к чему не приведут. Затем мы обращаемся к runFinalization, чтобы немедленно выполнить все завершающие действия, не позволяя сборщику мусора отложить их на потом.

Обычно вам незачем использовать runFinalization, поскольку методы finalize вызываются сборщиком мусора асинхронно. Однако при некоторых обстоятельствах (например, при нехватке ресурса, освобождаемого методом finalize) вынужденное исполнение всех возможных завершающих действий способно принести пользу. Конечно, нет никакой гарантии, что ожида-ющие завершения объекты используют данный ресурс, так что вызов run Finalization может оказаться бесполезным.

14.3. Системные свойства

Существует ряд системных свойств, которые хранятся внутри класса System в виде объекта класса Properties. Они определяют системное окружение и используются классами, которым необходима соответствующая информация. Например, приведем распечатку свойств одной системы:

converted to PDF by BoJIoc

#System properties

#Tue Feb 27 19:45:22 EST 1996 java.home=/lab/east/tools/java/java java.version=1.0.1 file.separator=/

line.separator=\n java.vendor=Sun Microsystem Inc. user.name=arnold

os.arch=sparc

os.name=Solaris java.vendor.url=http://www.sun.com/ user.dir=/vob/java_prog/src java.class.path=.:./classes:/home/arnold/java/lib/ classes.zip:/home/arnold/java/classes java.class.version=45.3

os.version=2.x

path.separator=:

user.home=/home/arnold

Все перечисленные выше свойства определены во всех системах, хотя их значения, конечно же, меняются. Некоторые из них применяются стандартными пакетами Java. Например, класс File использует свойство file.separator для построения и анализа путей к файлам. Программисты тоже могут задействовать эти свойства. Следующий метод ищет файл конфигурации в личной папке пользователя:

public static File personalConfig(String fileName) { String home = System.getPropety("user.home"); if (home == null)

return null;

else

return new File(home, fileName);

}

Ниже перечислены все методы класса System, которые служат для работы с системными свойствами:

public static Properties getProperties()

Возвращает объект класса Properties, представляющий системные свойства.

public static void setProperties(Properties props)

Задает системные свойства, используя для этого заданный объект класса Properties.

public static String getProperty(String key)

Возвращает текущее значение системного свойства, заданного в виде строки key.

Эквивалентно

System.getProperties().getProperty(key);

public static String getProperty(String key,

String defaultValue)

Возвращает текущее значение системного свойства, заданного в виде строки key. Если оно не определено, возвращается defaultValue. Эквивалентно

System.getProperties().getProperty(key, def);

converted to PDF by BoJIoc

Значения свойств хранятся в виде строк, однако некоторые строки представляют другие типы например, целые или логические. Существуют специальные методы, которые являются статическими методами соответствующих классов-оболочек, для чтения свойств и преобразования их в значения примитивных типов. Каждый из таких методов получает строковый параметр с именем свойства, интересующего программиста. Некоторые методы имеют и второй параметр (обозначенный ниже как def) со значением по умолчанию, которое возвращается в том случае, если свойство с данным именем не найдено. Методы, в которых этот параметр отсутствует, в этом случае возвращают объект, содержащий 0 для числового типа или false — для логического. Все эти методы преобразуют значения в стандартный для Java формат примитивного типа.

public static boolean Boolean.getBoolean(String name) public static Integer Integer.getInteger(String name) public static Integer Integer.getInteger(String name, int def) public static Long Long.getLong(String nm)

public static Long Long.getLong(String nm, long def)

Метод getBoolean отличается от других тем, что он возвращает логическое значение (boolean) вместо объекта класса Boolean. Если свойство не найдено, getBoolean передает false.

14.4. Создание процессов

Как упоминалось выше, в программах Java могут одновременно выполняться несколько потоков. Большинство систем, на которых функционирует среда Java, также поддерживают запуск нескольких программ. Приложения Java могут вызывать новые программы, обращаясь к одной из двух форм метода System.exec. Каждый успешный вызов exec создает новый объект Process, который представляет собой работающую программу. Вы можете запросить информацию о состоянии процесса и вызвать методы, управляющие его ходом. Существуют две основные формы метода exec:

public Process exec(String[] cmdarray) throws IOException

Выполняет в текущей системе команду, заданную в объекте cmdarray, и возвращает объект Process (описанный ниже), который представляет запущенный процесс. В cmdarray[0] задается имя команды, а во всех последующих строках массива аргументы.

public Process exec(String command) throws IOException

Эквивалентен первой форме exec, в которой элементы массива собраны в одну строку и разделены символами-разделителями. Приведем пример использования этой формы exec:

String cmd = “/bin/ls” + opts + “ ” + dir;

Process child = Runtime.getRuntime().exec(cmd);

Порожденный процесс носит название процесса-потомка (child process ). По аналогии, создающий процесс называется родителем. Метод exec возвращает объект Process для каждого запущенного процесса-потомка. Этот объект обладает двумя важными аспектами по отношению к процессу-потомку: во-первых, он содержит методы для обращения ко входному и выходному потоку процесса-потомка, а также к его потоку ошибок:

public abstract OutputStream getOutputStream()

converted to PDF by BoJIoc

Возвращает поток OutputStream, соединенный со входным потоком процесса-потомка. Данные, записанные в этот поток, считываются процессом-потомком в качестве ввода. Поток является буферизованным.

public abstract InputStream getInputStream()

Возвращает поток InputStream, соединенный с выходным потоком процесса-потомка. Когда процесс-потомок записывает выходные данные, они могут читаться из этого потока. Поток является буферизованным.

public abstract InputStream getErrorStream()

Возвращает поток InputStream, соединенный с выходным потоком ошибок процесса- потомка. Когда процесс-потомок выдает сообщения об ошибках, они могут читаться из этого потока. Поток является небуферизованным, чтобы гарантировать немедленное поступление информации об ошибках .

Ниже в качестве примера приводится программа, которая соединяет стандартные потоки Java с потоками нового процесса, чтобы весь ввод пользователя поступал в указанную программу, а весь ее вывод был виден пользователю:

public static Process UserProg(String cmd) throws IOException

{

Process proc = Runtime.getRuntime().exec(cmd); plugTogether(System.in, proc.getOutputStream()); plugTogether(System.out, proc.getInputStream()); plugTogether(System.err, proc.getErrorStream()); return proc;

}

Предполагается, что метод plugTogether соединяет два потока, читая данные из одного из них и записывая их в другой.

Упражнение 14.1

Напишите метод plugTogether. Подсказка: воспользуйтесь многопоточной моделью.

Второй аспект взаимоотношений между процессом-родителем и процессом-потомком заключается в том, что объект Process содержит методы для управления процессом и определения его статуса:

public abstract int waitFor() throws InterruptedException

Сколь угодно долго ожидает завершения процесса-потомка и возвращает значение, переданное им методу System.exit или его аналогу (ноль означает успешное завершение, все остальное неудачу). Если процесс уже завершился, то просто возвращается значение.

public abstract int exitValue()

Возвращает завершающий код данного процесса. Если процесс еще активен, то exitValue

возбуждает исключение IllegalStateException.

public abstract void destroy()

Уничтожает процесс. Ничего не делает, если процесс уже завершился. Если объект Process будет уничтожен сборщиком мусора, это не означает уничтожения самого процесса; просто в дальнейшем вы не сможете контролировать его.

converted to PDF by BoJIoc

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

// java.io.* и java.util.* импортированы public String[] ls(String dir, String opts)

throws LSFailedException

{

try {

// запустить процесс

String[] cmdArray = { "/bin/ls", opts, dir }; Process child = Runtime.getRuntime().exec(cmdArray); DataInputStream

in = new DataInputStream(child.getInputStream());

// прочитать выходные данные

Vector lines = new Vector(); String line;

while ((line = in.readLine()) != null) lines.addElement(line);

if (child.waitFor() != 0) // если выполнение ls // было неудачным

throw new LSFailedException(child.exitValue()); String[] retval = new String{lines.size()]; lines.copyInto(retval);

return retval;

}catch (LSFailedException e) { throw e;

}catch (Exception e) {

throw new LSFailedException(e.toString());

}

}

Класс Process является абстрактным. Каждая реализация Java должна содержать один или несколько классов, расширяющих Process, которые могут взаимодействовать с процессами на уровне операционной системы. Такие классы могут обладать расширенным набором функций, полезных для программирования в данной среде. Информация о расширенных функциях должна содержаться в документации.

Две другие формы exec позволяют задать набор переменных среды (environment variables ) — системно-зависимых переменных, доступных для нового процесса. Переменные среды передаются методу exec в виде строкового массива, каждый элемент которого задает имя и значение переменной среды в форме имя = значение. Имя не может содержать пробелов, хотя значение может быть произвольным. Переменные среды передаются вторым параметром:

public Process exec(String command, String[] env)

throws IOException

public Process exec(String[] command, String[] env)

throws IOException

Способ интерпретации переменных среды процессом-потомком является системно- зависимым. Механизм переменных среды поддерживается потому, что он существует на самых разных платформах. Для передачи информации между Java-программами следует пользоваться свойствами, а не переменными среды.

Упражнение 14.2