Читаем Программирование на Java полностью

for(int i=0; i

try {

String name = vehicleNames[i];

System.out.println("look for class for: " + name);

Class aClass = Class.forName(name);

System.out.println("creating vehicle...");

vehicle = (Vehicle)aClass.newInstance();

System.out.println("create vehicle: " + vehicle.getClass());

vehicle.go();

} catch(ClassNotFoundException e) {

System.out.println("Exception: " + e);

} catch(InstantiationException e) {

System.out.println("Exception: " + e);

}

}

}

}

Пример 13.3.

Если запустить эту программу, на экран будет выведено следующее:

look for class for: demo.lang.Automobile

creating vehicle...

create vehicle: class demo.lang.Automobile

Automobile go!

look for class for: demo.lang.Truck

creating vehicle...

Exception: java.lang.InstantiationException

look for class for: demo.lang.Tank

Class not found: java.lang.ClassNotFoundException: demo.lang.Tank

Пример 13.4.

В этом примере делается попытка создать с помощью reflection три объекта. Имена классов, от которых они должны быть порождены, записаны в массив vehicleNames. Объект класса Automobile был успешно создан, причем, дальнейшая работа с ним велась через интерфейс Vehicle. Класс Truck был найден, но при попытке создания объекта было брошено, а затем обработано исключение java.lang.InstantiationException, поскольку конструктор без параметров отсутствует. Класс java.lang.Tank определен не был и поэтому при попытке получить соответствующий ему объект Class было выброшено исключение java.lang.ClassNotFoundException.

Классы-обертки

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

Для этих целей и предназначены так называемые классы-обертки. Для каждого примитивного типа Java существует свой класс-обертка . Такой класс является неизменяемым (если необходим объект, хранящий другое значение, его нужно создать заново), к тому же имеет атрибут final – от него нельзя наследовать класс. Все классы-обертки (кроме Void ) реализуют интерфейс Serializable, поэтому объекты любого (кроме Void ) класса-обертки могут быть сериализованы. Все классы-обертки содержат статическое поле TYPE, ссылающееся на объект Class, соответствующий примитивному оборачиваемому типу.

Также классы-обертки содержат статические методы для обеспечения удобного манипулирования соответствующими примитивными типами, например, преобразование к строковому виду.

В таблице 13.1 приведены примитивные типы и соответствующие им классы-обертки.

Таблица 13.1. Примитивные типы и соответствующие им классы-обертки.

Класс-обертка

Примитивный тип

Byte

byte

Short

short

Character

char

Integer

int

Long

long

Float

float

Double

double

Boolean

boolean

При этом классы-обертки числовых типов Byte, Short, Integer, Long, Float, Double наследуются от одного класса – Number. В нем объявлены методы, возвращающие числовое значение во всех числовых форматах Java ( byte, short, int, long, float и double ).

Все классы-обертки реализуют интерфейс Comparable. Все классы-обертки числовых типов имеют метод equals(Object), сравнивающий примитивные значения объектов.

Рассмотрим более подробно некоторые из классов-оберток.

Integer

Наиболее часто используемые статические методы:

* public static int parseInt(String s) – преобразует строку, представляющую десятичную запись целого числа, в int ;

* public static int parseInt(String s, int radix) – преобразует строку, представляющую запись целого числа в системе счисления radix, в int.

Оба метода могут возбуждать исключение NumberFormatException, если строка, переданная на вход, содержит нецифровые символы.

Не следует путать эти методы с другой парой похожих методов:

public static Integer valueOf(String s)

public static Integer valueOf(String s, int radix)

Данные методы выполняют аналогичную работу, только результат представляют в виде объекта-обертки.

Существует также два конструктора для создания экземпляров класса Integer:

* Integer(String s) – конструктор, принимающий в качестве параметра строку, представляющую числовое значение.

* Integer(int i) – конструктор, принимающий числовое значение.

public static String toString(int i) – используется для преобразования значения типа int в строку.

Далее перечислены методы, преобразующие int в строковое восьмеричное, двоичное и шестнадцатеричное представление:

* public static String toOctalString(int i) – восьмеричное;

* public static String toBinaryString(int i) – двоичное;

* public static String toHexString(int i) – шестнадцатеричное.

Имеется также две статические константы:

* Integer.MIN_VALUE – минимальное int значение;

* Integer.MAX_VALUE – максимальное int значение.

Аналогичные константы, описывающие границы соответствующих типов, определены и для всех остальных классов-оберток числовых примитивных типов.

public int intValue() возвращает значение примитивного типа для данного объекта Integer. Классы-обертки остальных примитивных целочисленных типов – Byte, Short, Long – содержат аналогичные методы и константы (определенные для соответствующих типов: byte, short, long ).

Рассмотрим пример:

public static void main(String[] args) {

int i = 1;

byte b = 1;

String value = "1000";

Integer iObj = new Integer(i);

Byte bObj = new Byte(b);

System.out.println("while i==b is " +

(i==b));

System.out.println("iObj.equals(bObj) is "

+ iObj.equals(bObj));

Long lObj = new Long(value);

System.out.println("lObj = " +

lObj.toString());

Long sum = new Long(lObj.longValue() +

iObj.byteValue() +

bObj.shortValue());

System.out.println("The sum = " +

sum.doubleValue());

}

В данном примере произвольным образом используются различные варианты классов-оберток и их методов. В результате выполнения на экран будет выведено следующее:

while i==b is true

iObj.equals(bObj) is false

lObj = 1000

The sum = 1002.0

Оставшиеся классы-обертки числовых типов Float и Double, помимо описанного для целочисленных примитивных типов, дополнительно содержат определения следующих констант (они подробно разбирались в лекции 4):

* NEGATIVE_INFINITY – отрицательная бесконечность;

* POSITIVE_INFINITY – положительная бесконечность;

* NaN – нечисловое значение.

Кроме того, другой смысл имеет значение MIN_VALUE – вместо наименьшего значения оно представляет минимальное положительное (строго > 0) значение, которое может быть представлено этим примитивным типом.

Кроме классов-оберток для примитивных числовых типов, таковые определены и для остальных примитивных типов Java.

Character

Реализует интерфейсы Comparable и Serializable.

Из конструкторов имеет только один, принимающий char в качестве параметра.

Кроме стандартных методов equals(), hashCode(), toString(), содержит только два нестатических метода:

* public char charValue() – возвращает обернутое значение char;

* public int compareTo(Character anotherCharacter) – сравнивает обернутые значения char как числа, то есть возвращает значение return this.value – anotherCharacter.value.

Также для совместимости с интерфейсом Comparable метод compareTo() определен с параметром Object:

* public int compareTo(Object o) – если переданный объект имеет тип Character, результат будет аналогичен вызову compareTo((Character)o), иначе будет брошено исключение ClassCastException, так как Character можно сравнивать только с Character.

Статических методов в классе Character довольно много, но все они просты и логика их работы понятна из названия. Большинство из них - это методы, принимающие char и проверяющие всевозможные свойства. Например:

public static boolean isDigit(char c)

// проверяет, является ли char цифрой.

Эти методы возвращают значение истина или ложь, в соответствии с тем, выполнен ли критерий проверки.

Boolean

Представляет класс-обертку для примитивного типа boolean.

Реализует интерфейс java.io.Serializable и во всем напоминает аналогичные классы-обертки.

Для получения примитивного значения используется метод booleanValue().

Void

Этот класс-обертка, в отличие от остальных, не реализует интерфейс java.io.Serializable. Он не имеет открытого конструктора. Более того, экземпляр этого класса вообще не может быть получен. Он нужен только для получения ссылки на объект Class, соответствующий void. Эта ссылка представлена статической константой TYPE.

Делая краткое заключение по классам-оберткам, можно сказать, что:

* каждый примитивный тип имеет соответствующий класс-обертку ;

* все классы-обертки могут быть сконструированы как с использованием примитивных типов, так и с использованием String, за исключением Character, который может быть сконструирован только по char ;

* классы-обертки могут сравниваться с использованием метода equals() ;

* примитивные типы могут быть извлечены из классов-оберток с помощью соответствующего метода xxxxValue() (например intValue() );

* классы-обертки также являются классами-утилитами, т.е. предоставляют набор статических методов для работы с примитивными типами;

* классы-обертки являются неизменяемыми.

Math

Класс Math состоит из набора статических методов, производящих наиболее популярные математические вычисления, и двух констант, имеющих особое значение в математике, – это число Пи и основание натурального логарифма. Часто этот класс еще называют классом-утилитой (Utility class). Так как все методы класса статические, нет необходимости создавать экземпляр данного класса, потому он и не имеет открытого конструктора. Нельзя также и наследоваться от этого класса, так как он объявлен с модификатором final.

Итак, константы определены следующим образом:

public static final double Math.PI – задает число π ("пи");

public static final double Math.E – основание натурального логарифма.

В таблице 13.2 приведены все методы класса и дано их краткое описание.

Таблица 13.2. Методы класса Math и их краткое описание.

Возвращаемое значение

Имя метода и параметры

Описание

abs(… a)

абсолютное значение (модуль) для типов double, float, int, long

double

acos(double a)

арккосинус

double

asin(double a)

арксинус

double

atan(double a)

арктангенс

double

ceil(double a)

наименьшее целое число, большее a

double

floor(double a)

целое число, меньшее a

double

IEEEremainder (double a, double b)

остаток по стандарту IEEE 754 (подробно рассматривался в лекции 3)

double

sin(double a)

синус (здесь и далее: аргумент должен быть в радианах)

double

cos(double a)

косинус

double

tan(double a)

тангенс

double

exp(double a)

e в степени a

double

log(double a)

натуральный логарифм a

max(… a, … b)

большее из двух чисел (для типов double, float, long, int )

min(… a, … b)

меньшее из двух чисел (для типов double, float, long, int )

double

pow(double a, double b)

a в степени b

double

random()

случайное число от 0.0 до 1.0

double

rint(double a)

значение int, ближайшее к a

round(… a)

значение long для double ( int для float ), ближайшее к a

double

sqrt(double a)

квадратный корень числа a

double

toDegrees(double a)

преобразование из радианов в градусы

double

toRadians(double a)

преобразование из градусов в радианы

Строки

String

Этот класс используется в Java для представления строк. Он обладает свойством неизменяемости. После того как создан экземпляр этого класса, его содержимое уже не может быть модифицировано.

Существует много способов создать объект String. Наиболее простой, если содержимое строки известно на этапе компиляции, – написать текст в кавычках:

String abc = "abc";

Можно использовать и различные варианты конструктора. Наиболее простой из них – конструктор, получающий на входе строковый литерал.

String s = new String("immutable");

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

public class Test {

public Test() {

}

public static void main(String[] args) {

Test t = new Test();

String s1 = "Hello world !!!";

String s2 = "Hello world !!!";

System.out.println("String`s equally = " +

(s1.equals(s2)));

System.out.println(

"Strings are the same = " + (s1==s2));

}

}

В результате на консоль будет выведено:

String`s equally = true

Strings are the same = true

Теперь несколько модифицируем код:

public class Test {

public Test() {

}

public static void main(String[] args) {

Test t = new Test();

String s1 = "Hello world !!!";

String s2 = new String("Hello world !!!");

System.out.println("String`s equally = " +

(s1.equals(s2)));

System.out.println(

"Strings are the same = " + (s1==s2));

}

}

В результате на консоль будет выведено:

String`s equally = true Strings are the same = false

Почему результат изменился? Дело в том, что создание нового объекта – это одна из самых трудоемких процедур в Java. Поэтому компилятор стремится уменьшить их количество, если это не приводит к непредсказуемому поведению программы.

В примере объявляются две переменные, которые инициализируются одинаковым значением. Поскольку класс String неизменяемый, их значения всегда будут одинаковыми. Это позволяет компилятору завести скрытую вспомогательную текстовую переменную, которая будет хранить такое значение, а все остальные переменные будут ссылаться на него же, а не порождать новые объекты. В результате в первом варианте программы создается лишь один объект String. Для большинства операций это несущественная разница. Исключение составляют действия, которые привязаны к конкретному объекту, а не к его значению. Это метод equals, методы wait/notify.

Во втором варианте указано динамическое обращение к конструктору. В этом случае компилятор уже не имеет возможности заниматься оптимизацией и JVM во время исполнения программы действительно создаст второй объект с точно таким же значением. Что мы и видим по результату выполнения примера.

В Java для строк определен оператор +. При использовании этого оператора производится конкатенация строк. В классе String также определен метод:

public String concat(String s);

Он возвращает новый объект-строку, дополненный справа строкой s.

Рассмотрим другой пример.

public class Test {

public static void main(String[] args) {

Test t = new Test();

String s = " prefix !";

System.out.println(s);

s = s.trim();

System.out.println(s);

s = s.concat(" suffix");

System.out.println(s);

}

}

prefix !

prefix !

prefix ! suffix

В данном случае может сложиться впечатление, что строку (объект String, на который ссылается переменная s ), можно изменять. В действительности это не так. В результате выполнения методов trim (отсечение пробелов в начале и конце строки) и concat создаются новые объекты-строки и ссылка s начинает указывать на новый объект-строку. Таким образом, меняется значение ссылки, объекты же неизменяемы.

Как уже отмечалось, строка состоит из двухбайтных Unicode-символов. Однако во многих случаях требуется работать со строкой как с набором байт (ввод/вывод, работа с базой данных и т.д.). Преобразование строки в последовательность байтов производится следующими методами:

* byte[] getBytes() – возвращает последовательность байтов в кодировке, принятой по умолчанию (как правило, зависит от настроек операционной системы);

* byte[] getBytes(String encoding) – возвращает последовательность байтов в указанной кодировке encoding.

Для выполнения обратной операции (преобразования байтов в строку) необходимо сконструировать новый объект-строку с помощью следующих методов:

* String(byte[] bytes) – создает строку из последовательности байтов в кодировке, принятой по умолчанию;

* String(byte[] bytes, String enc) – создает строку из последовательности байтов в указанной кодировке.

StringBuffer

Этот класс используется для создания и модификации строковых выражений, которые после можно превратить в String. Он реализован на основе массива char[], что позволяет, в отличие от String, модифицировать его значение после создания объекта.

Рассмотрим наиболее часто используемые конструкторы класса StringBuffer:

* StringBuffer() – создает пустой StringBuffer ;

* StringBuffer(String s) – буфер заполняется указанным значением s ;

* StringBuffer(int capacity) – создает экземпляр класса StringBuffer с указанным размером (длина char[] ). Задание размера не означает, что нельзя будет оперировать строками с большей длиной, чем указано в конструкторе. На самом деле этим гарантируется, что при работе со строками меньшей длины дополнительное выделение памяти не потребуется.

Разница между String и StringBuffer может быть продемонстрирована на следующем примере:

public class Test {

public static void main(String[] args) {

Test t = new Test();

String s = new String("ssssss");

StringBuffer sb =

new StringBuffer("bbbbbb");

s.concat("-aaa");

sb.append("-aaa");

System.out.println(s);

System.out.println(sb);

}

}

В результате на экран будет выведено следующее:

ssssss

bbbbbb-aaa

В данном примере можно заметить, что объект String остался неизменным, а объект StringBuffer изменился.

Основные методы, используемые для модификации StringBuffer, это:

* public StringBuffer append(String str) – добавляет переданную строку str в буфер;

* public StringBuffer insert(int offset, String str) – вставка строки, начиная с позиции offset (пропустив offset символов).

Стоит обратить внимание, что оба метода имеют варианты, принимающие в качестве параметров различные примитивные типы Java вместо String. При использовании этих методов аргумент предварительно приводится к строке (с помощью String.valueOf() ).

Еще один важный момент, связанный с этими методами, – они возвращают сам объект, у которого вызываются. Благодаря этому, возможно их использование в цепочке. Например:

public static void main(String[] args) {

StringBuffer sb = new StringBuffer("abc");

String str = sb.append("e").insert(4,

"f").insert(3,"d").toString();

System.out.println(str);

}

В результате на экран будет выведено:

abcdef

При передаче экземпляра класса StringBuffer в качестве параметра метода следует помнить, что этот класс изменяемый:

public class Test {

public static void main(String[] args) {

Test t = new Test();

StringBuffer sb = new StringBuffer("aaa");

System.out.println("Before = " + sb);

t.doTest(sb);

System.out.println("After = " + sb);

}

void doTest(StringBuffer theSb) {

theSb.append("-bbb");

}

}

В результате на экран будет выведено следующее:

Before = aaa

After = aaa-bbb

Поскольку все объекты передаются по ссылке, в методе doTest, при выполнении операций с theSB, будет модифицирован объект, на который ссылается sb.

Системные классы

Следующие классы, которые будут рассмотрены, обеспечивают взаимодействие с внутренними механизмами JVM и средой исполнения приложения:

* ClassLoader – загрузчик классов; отвечает за загрузку описания классов в память JVM;

* SecurityManager – менеджер безопасности; содержит различные методы проверки допустимости запрашиваемой операции;

* System – содержит набор полезных статических полей и методов;

* Runtime – позволяет приложению взаимодействовать со средой исполнения;

* Process – представляет интерфейс для взаимодействия с внешней программой, запущенной при помощи Runtime.

ClassLoader

Это абстрактный класс, ответственный за загрузку типов. По имени класса или интерфейса он находит и загружает в память данные, которые составляют определение типа. Обычно для этого используется простое правило: название типа преобразуется в название class -файла, из которого и считывается вся необходимая информация.

Каждый объект Class содержит ссылку на объект ClassLoader, с помощью которого он был загружен.

Для добавления альтернативного способа загрузки классов можно реализовать свой загрузчик, унаследовав его от ClassLoader. Например, описание класса может загружаться через сетевое соединение. Метод defineClass() преобразует массив байт в экземпляр класса Class. С помощью метода newInstance() могут быть получены экземпляры такого класса. В результате загруженный класс становится полноценной частью исполняемого Java-приложения.

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

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 );

* public static native long currentTimeMillis() – возвращает текущее время; это время представляется как количество миллисекунд, прошедших с 1 января 1970 года;

* public static String getProperty(String key) – возвращает значение свойства с именем key.

Чтобы получить все свойства, определенные в системе, можно воспользоваться следующим методом:

public static java.util.Properties getProperties() – возвращает объект java.util.Properties, в котором содержатся значения всех определенных системных свойств.

Метод arrayCopy(Object source, int srcPos, Object target, int trgPos, int length) предоставляет возможность быстрого копирования содержимого одного массива в другой. Первый параметр задает исходный массив, второй – номер позиции, начиная с которой брать элементы для копирования. Третий параметр – массив-"получатель", четвертый – номер позиции в нем, начиная с которого будут записываться скопированные элементы. Наконец, последний параметр задает количество элементов, которые надо скопировать. Оба массива должны быть созданы, иметь совместимые типы и достаточную длину, иначе будут сгенерированы соответствующие исключения.

Runtime

Во время исполнения приложению Java сопоставляется экземпляр класса Runtime. Этот объект позволяет взаимодействовать с окружением, в котором запущена Java-программа. Получить его можно с помощью статического метода Runtime.getRuntime().

Объект этого класса:

* public void exit(int status) – осуществляет завершение программы с кодом завершения status (при использовании этого метода особое внимание нужно уделить обработке исключений – выход будет осуществлен моментально и в конструкциях try-catch-finally управление в finally передано не будет);

* public native void gc() – сигнализирует сборщику мусора о необходимости запуска;

* public void runFinalization() – производит запуск выполнения методов finalize() у всех объектов, этого ожидающих;

* public native long freeMemory() – возвращает количество свободной памяти, доступной приложению JVM. В некоторых случаях это количество может быть увеличено, если вызвать у объекта Runtime метод gc() ;

* public native long totalMemory() – возвращает суммарное количество памяти, выделенное Java-машине. Это количество может изменяться даже в течение одного запуска, что зависит от реализации платформы, на которой запущена Java-машина. Также не стоит закладываться на объем памяти, занимаемой одним определенным объектом, – эта величина тоже зависит от реализации Java-машины;

* public void loadLibrary(String libname) – загружает библиотеку с указанным именем.

Обычно загрузка библиотек производится следующим образом: в классе, использующем native реализации методов, добавляется статический инициализатор, например:

static { System.loadLibrary("LibFile");}

Таким образом, когда класс будет загружен и инициализирован, необходимый код для реализации native методов также будет загружен. Если будет произведено несколько вызовов загрузки библиотеки с одним и тем же именем, произведен будет только первый, а все остальные будут проигнорированы.

public void load(String filename) – подгружает файл с указанным названием в качестве библиотеки. В принципе, этот метод работает так же, как и метод loadLibrary(), только принимает в качестве параметра именно название файла, а не библиотеки, тем самым позволяя загрузить любой файл с native кодом;

public Process exec(String command) – в отдельном процессе запускает команду, представленную переданной строкой. Возвращаемый объект Process может быть использован для взаимодействия с этим процессом.

Process

Объекты этого класса получаются вызовом метода exec() у объекта Runtime, запускающего отдельный процесс. Объект класса Process может использоваться для управления процессом и получения информации о нем.

Process – абстрактный класс, определяющий, какие методы должны присутствовать в реализациях для конкретных платформ. Методы класса Process:

* public InputStream getInputStream() – дает возможность получать поток ввода процесса;

* getErrorStream(), getOutputStream() – методы, аналогичные getInputStream(), но получающие, соответственно, стандартные потоки сообщений об ошибках и вывода;

* public void destroy() – уничтожает процесс; все подпроцессы, запущенные из него, также будут уничтожены;

* public int exitValue() – возвращает код завершения процесса; по соглашению, код завершения, равный 0, означает нормальное завершение;

* public int waitFor() – вынуждает текущий поток выполнения приостановиться до тех пор, пока не будет завершен процесс, представленный этим экземпляром Process; возвращает значение кода завершения процесса.

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

Потоки исполнения

Многопоточная архитектура в Java была подробно рассмотрена в лекции 12. Остановимся более подробно на методах применяемых классов.

Runnable

Runnable – это интерфейс, содержащий один-единственный метод без параметров: run().

Thread

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

Итак, для управления потоками в классе Thread предусмотрены следующие методы:

* public void start() – производит запуск нового потока;

* public final void join() – если поток A вызывает этот метод у объекта Thread, представляющего поток B ( threadB.join() ), то выполнение потока A приостанавливается до тех пор, пока не закончит выполнение поток B ;

* public static void yield() – поток, из которого вызван этот метод, временно приостанавливается, чтобы дать возможность выполняться другим потокам;

public static void sleep(long millis) – поток, из которого вызван этот метод, перейдет в состояние "сна" на указанное количество миллисекунд, после чего сможет продолжить выполнение. При этом нужно учесть, что через время millis миллисекунд этому потоку может быть выделено процессорное время, а может, ему придется и подождать немного дольше. Можно сказать, что поток продолжит выполнение не раньше, чем через время millis миллисекунд.

Существует еще несколько методов, которые объявлены deprecated и рекомендуется их избегать. Это: suspend() – временно прекратить выполнение, resume() – продолжить выполнение (приостановленное вызовом suspend()), stop() – остановить выполнение потока.

При вызове метода stop() в потоке, который представляет этот объект Thread, будет брошена ошибка ThreadDeath. Этот класс унаследован от Error. Если ошибка не будет обработана в программе и, соответственно, произойдет прекращение работы потока, сообщение о ненормальном завершении выведено не будет, так как такое завершение рассматривается как нормальное. Если же в программе эта ошибка обрабатывается (например, для проведения каких-то дополнительных действий перед закрытием потока), то очень важно позаботиться о том, чтобы эта же ошибка была брошена дальше, чтобы поток действительно закончил свое выполнение. Класс ThreadDeath специально унаследован от Error, а не от Exception, так как очень часто используется перехват всех исключений класса Exception, что не позволит корректно остановить поток.

Также Thread позволяет выставлять такие свойства потока, как:

* Name – значение типа String, которое можно использовать для более наглядного обращения с потоками в группе;

* Daemon – выполнение программы не будет прекращено до тех пор, пока выполняется хотя бы один не daemon поток;

* Priority – определяет приоритет потока. В классе Thread определены константы, задающие минимальное и максимальное значения для приоритетов потока,– MIN_PRIORITY и MAX_PRIORITY, а также значение приоритета по умолчанию – NORM_PRIORITY.

Эти свойства могут быть изменены только до того момента, когда поток будет запущен, то есть вызван метод start() объекта Thread.

Получить эти значения можно, конечно же, в любой момент жизни потока – и после его запуска, и после прекращения выполнения. Также можно узнать, в каком состоянии сейчас находится поток: вызовом методов isAlive() – выполняется ли еще, isInterrupted() – прерван ли.

ThreadGroup

Для того, чтобы отдельный поток не мог начать останавливать и прерывать все потоки подряд, введено понятие группы. Поток может оказывать влияние только на потоки, которые находятся в одной с ним группе. Группу потоков представляет класс ThreadGroup. Такая организация позволяет защитить потоки от нежелательного внешнего воздействия. Группа потоков может содержать другие группы, что позволяет организовать все потоки и группы в иерархическое дерево, в котором каждый объект ThreadGroup, за исключением корневого, имеет родителя.

Класс ThreadGroup обладает методами для изменения свойств всех входящих в него потоков, таких, как приоритет, daemon и т.д. Метод list() позволяет получить список потоков.

Исключения

Подробно механизм использования исключений описан в лекции 10. Здесь остановимся только на том, что базовым классом для всех исключений является класс Throwable. Любой класс, который планируется использовать как исключение, должен явным или неявным образом наследоваться от него. Класс Throwable, а также наиболее значимые его наследники – классы Error, Exception, RuntimeException, – содержатся именно в пакете java.lang.

Заключение

В этой лекции мы рассказали о назначении и возможностях классов, представленных в пакете java.lang. Как Вы теперь знаете, пакет java.lang автоматически импортируется во все Java программы и содержит фундаментальные классы и интерфейсы, которые составляют основу для других пакетов Java.

Были рассмотрены все наиболее важные классы пакета java.lang:

* Object, Class – основные классы, представляющие объект и класс объектов;

* классы-обертки (Wrapper классы) – служат для представления примитивных значений в виде объектов, так как многие классы работают именно с объектами;

* Math – класс, предоставляющий набор статических методов, реализующих базовые математические функции;

* String и StringBuffer – классы для работы со строками;

* System, Runtime, Process, ClassLoader, SecurityManager – системные классы, помогающие взаимодействовать с программным окружением ( System, Runtime, Process ), загружать классы в JVM ( ClassLoader ) и управлять безопасностью ( SecurityManager );

* Thread, ThreadGroup, Runnable – типы, обеспечивающие работу с потоками исполнения в Java;

* Throwable, Error, Exception, RuntimeException – базовые классы для всех исключений.

Перейти на страницу:

Похожие книги

1С: Бухгалтерия 8 с нуля
1С: Бухгалтерия 8 с нуля

Книга содержит полное описание приемов и методов работы с программой 1С:Бухгалтерия 8. Рассматривается автоматизация всех основных участков бухгалтерии: учет наличных и безналичных денежных средств, основных средств и НМА, прихода и расхода товарно-материальных ценностей, зарплаты, производства. Описано, как вводить исходные данные, заполнять справочники и каталоги, работать с первичными документами, проводить их по учету, формировать разнообразные отчеты, выводить данные на печать, настраивать программу и использовать ее сервисные функции. Каждый урок содержит подробное описание рассматриваемой темы с детальным разбором и иллюстрированием всех этапов.Для широкого круга пользователей.

Алексей Анатольевич Гладкий

Программирование, программы, базы данных / Программное обеспечение / Бухучет и аудит / Финансы и бизнес / Книги по IT / Словари и Энциклопедии
1С: Управление торговлей 8.2
1С: Управление торговлей 8.2

Современные торговые предприятия предлагают своим клиентам широчайший ассортимент товаров, который исчисляется тысячами и десятками тысяч наименований. Причем многие позиции могут реализовываться на разных условиях: предоплата, отсрочка платежи, скидка, наценка, объем партии, и т.д. Клиенты зачастую делятся на категории – VIP-клиент, обычный клиент, постоянный клиент, мелкооптовый клиент, и т.д. Товарные позиции могут комплектоваться и разукомплектовываться, многие товары подлежат обязательной сертификации и гигиеническим исследованиям, некондиционные позиции необходимо списывать, на складах периодически должна проводиться инвентаризация, каждая компания должна иметь свою маркетинговую политику и т.д., вообщем – современное торговое предприятие представляет живой организм, находящийся в постоянном движении.Очевидно, что вся эта кипучая деятельность требует автоматизации. Для решения этой задачи существуют специальные программные средства, и в этой книге мы познакомим вам с самым популярным продуктом, предназначенным для автоматизации деятельности торгового предприятия – «1С Управление торговлей», которое реализовано на новейшей технологической платформе версии 1С 8.2.

Алексей Анатольевич Гладкий

Финансы / Программирование, программы, базы данных