Программирование на языке Java

         

Результат запуска этого примера :



Результат запуска этого примера :

С:\> java equalsDemo Hello equals Hello -> true Hello equals Good-bye -> false Hello equals HELLO -> false Hello equalsIgnoreCase HELLO -> true

В классе String реализована группа сервисных методов, являющихся специализированными версиями метода equals. Метод regionMatches используется для сравнения подстроки в исходной строке с подстрокой в строке-параметре. Метод startsWith проверяет, начинается ли данная подстрока фрагментом, переданным методу в качестве параметра. Метод endsWith проверяет совпадает ли с параметром конец строки.



RGBImageFilter



RGBImageFilter

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



Рисование "каракулей" с использованием встроенных классов



Рисование "каракулей" с использованием встроенных классов

Модель обработки событий Java 1.1 разработана с учетом того, чтобы хорошо сочетаться с другой новой особенностью Java 1.1: встроенными классами (глава, посвященная им, еще не написана ;-(). В следующем примере показано, как изменится данный апплет, если слушатели событий будут реализованы в виде анонимных встроенных классов. Обратите внимание на компактность данного варианта программы. Новая особенность, добавленная в апплет - кнопка Clear. Для этой кнопки зарегистрирован объект ActionListener, а сама она выполняет очистку экрана при наступлении соответствующего события. /* <applet code = "Scribble3" width=200 height=200> </applet> */ import java.applet.*; import java.awt.*; import java.awt.event.*; public class Scribble3 extends Applet { int last_x, last_y; public void init() { // Определяет, создает и регистрирует объект MouseListener. this.addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { last_x = e.getX(); last_y = e.getY(); } } ); // Определяет, создает и регистрирует объект MouseMotionListener. this.addMouseMotionListener(new MouseMotionAdapter() { public void mouseDragged(MouseEvent e) { Graphics g = getGraphics(); int x = e.getX(), y= e.getY(); g.setColor(Color.black); g.drawLine(last_x, last_y, x, y); last_x = x; last_y = y; } }); // Создает кнопку Clear. Button b = new Button("Clear"); // Определяет, создает и регистрирует объект слушателя // для обработки события, связанного с нажатием кнопки. b.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { // стирание каракулей Graphics g = getGraphics(); g.setColor(getBackground()); g.fillRect(0, 0, getSize().width, getSize().height); } }); // Добавляет кнопку в апплет. this.add(b); } }



в котором используется модель обработки



Рисование "каракулей" в Java 1.0

Классический апплет, в котором используется модель обработки событий Java 1.0. В нем методы mouseDown() и mouseDragO переопределены таким образом, чтобы пользователь имел возможность рисовать "каракули" с помощью мыши. Также переопределен метод keyDown(), чтобы при нажатии клавиши [С] экран очищался, и метод action(), чтобы экран очищался после щелчка на кнопке Clear. /* <applet code = "Scribble1" width=200 height=200> </applet> */ import java.applet.*; import java.awt.*; /** Простой апплет, в котором используется модель обработки событий 1.0 */ public class Scribble1 extends Applet { private int lastx, lasty; // Хранят координаты курсора мыши. Button clear_button; // Кнопка Clear. Graphics g; // Объект Graphics,который необходимо нарисовать. /** Инициализация кнопки и объекта Graphics */ public void init() { clear_button = new Button("Clear"); this.add(clear_button); g = this.getGraphics(); } /** Реакция на нажатие кнопки мыши */ public boolean mouseDown(Event e, int x, int y) { lastx = x; lasty = y; return true; } /** Реакция на перетаскивание с помощью мыши */ public boolean mouseDrag(Event e, int x, int y) { g.setColor(Color.black) ; g.drawLine(lastx, lasty, x, y); lastx = x; lasty = y; return true; } /** Реакция на нажатие клавиши [С] */ public boolean keyDown(Event e, int key) { if ((e.id == Event.KEY_PRESS) && (key == 'с' ) ) { clear() ; return true; } else return false; } /** Реакция на нажатие кнопки Clear */ public boolean action(Event e, Object arg) { if (e.target == clear_button) { clear(); return true; } else return false; } /** Метод для стирания каракулей */ public void clear() { g.setColor(this.getBackground()); g.fillRect(0, 0, bounds().width, bounds().height); } }


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



Рисование "каракулей" в Java 1.1

Модель обработки событий Java 1.1 является достаточно гибкой и предоставляет пользователю ряд возможностей для структуризации программы обработки событий. Первый из этих способов продемонстрирован в примере. В апплете данной версии реализованы интерфейсы MouseListener и MouseMotionListener, регистрирующие себя с помощью своих же методов addMouseListener() и addMouseMotionListener(). /* <applet code = "Scribble2" width=200 height=200> </applet> */ import java.applet.*; import java.awt.*; import java.awt.event.*; public class Scribble2 extends Applet implements MouseListener, MouseMotionListener { private int last_x, last_y; public void init() { // Сообщает данному апплету о том, какие объекты // классов MouseListener и MouseMotionListener он должен оповещать // о событиях, связанных с мышью и ее перемещением. // Поскольку интерфейс реализуется в самом апплете, // при этом будут вызываться методы апплета. this.addMouseListener(this) ; this.addMouseMotionListener(this); } // Метод интерфейса MouseListener. Вызывается при нажатии // пользователем кнопки мыши. public void mousePressed(MouseEvent e) { last_x = e.getX(); last_y = e.getY(); } // Метод интерфейса MouseMotionListener. Вызывается при // перемещении мыши с нажатой кнопкой. public void mouseDragged(MouseEvent e) { Graphics g = this.getGraphics(); int x = e.getX(), y = e.getY(); g.drawLine(last_x, last_y, x, y); last_x = x; last_y = y; } // Другие, не используемые методы интерфейса MouseListener. public void mouseReleased(MouseEvent e) {;} public void mouseClicked(MouseEvent e) {;} public void mouseEntered(MouseEvent e) {;} public void mouseExited(MouseEvent e) {;} // Другой метод интерфейса MouseMotionListener. public void mouseMoved(MouseEvent e) {;} }


Run



run

Метод run - это тело выполняющегося подпроцесса. Это - единственный метод интерфейса Runnable. Он вызывается из метода start после того, как исполняющая среда выполнит необходимые операции по инициализации нового подпроцесса. Если происходит возврат из метода run, текущий подпроцесс останавливается.



Runnable



Runnable

Не очень интересно работать только с одним подпроцессом, а как можно создать еще один? Для этого нам понадобится другой экземпляр класса Thread. При создании нового объекта Thread ему нужно указать, какой программный код он должен выполнять. Вы можете запустить подпроцесс с помощью любого объекта, реализующего интерфейс Runnable. Для того, чтобы реализовать этот интерфейс, класс должен предоставить определение метода run. Ниже приведен пример, в котором создается новый подпроцесс. class ThreadDemo implements Runnable { ThreadDemo() { Thread ct = Thread.currentThread(); System.out.println("currentThread: " + ct); Thread t = new Thread(this, "Demo Thread"); System.out.println("Thread created: " + t); t.start(); try { Thread.sleep(3000); } catch (InterruptedException e) { System.out.println("interrupted"); } System.out.println("exiting main thread"); } public void run() { try { for (int i = 5; i > 0; i--) { System.out.println("" + i); Thread.sleep(1000); } } catch (InterruptedException e) { System.out.println("child interrupted"); } System.out.println("exiting child thread"); } public static void main(String args[]) { new ThreadDemo(); } }

Обратите внимание на то, что цикл внутри метода run выглядит точно так же, как и в предыдущем примере, только на этот раз он выполняется в другом подпроцессе. Подпроцесс main с помощью оператора new Thread(this, "Demo Thread") создает новый объект класса Thread, причем первый параметр конструктора — this — указывает, что нам хочется вызвать метод run текущего объекта. Затем мы вызываем метод start, который запускает подпроцесс, выполняющий метод run. После этого основной подпроцесс (main) переводится в состояние ожидания на три секунды, затем выводит сообщение и завершает работу. Второй подпроцесс — “Demo Thread” — при этом по-прежнему выполняет итерации в цикле метода run до тех пор пока значение счетчика цикла не уменьшится до нуля. Ниже показано, как выглядит результат работы этой программы этой программы после того, как она отработает 5 секунд. С:\> java ThreadDemo Thread created: Thread[Demo Thread,5,main] 5 4 3 exiting main thread 2 1 exiting child thread

Приоритеты подпроцессов

Если вы хотите добиться от Java предсказуемого независимого от платформы поведения, вам следует проектировать свои подпроцессы таким образом, чтобы они по своей воле освобождали процессор. Ниже приведен пример с двумя подпроцессами с различными приоритетами, которые не ведут себя одинаково на различных платформах. Приоритет одного из подпроцессов с помощью вызова setPriority устанавливается на два уровня выше Thread. NORM_PRIORITY, то есть, умалчиваемого приоритета. У другого подпроцесса приоритет, наоборот, на два уровня ниже. Оба этих подпроцесса запускаются и работают в течение 10 секунд. Каждый из них выполняет цикл, в котором увеличивается значение переменной-счетчика. Через десять секунд после их запуска основной подпроцесс останавливает их работу, присваивая условию завершения цикла while значение true и выводит значения счетчиков, показывающих, сколько итераций цикла успел выполнить каждый из подпроцессов. class Clicker implements Runnable { int click = 0; private Thread t; private boolean running = true; public clicker(int p) { t = new Thread(this); t.setPriority(p); } public void run() { while (running) { click++; } } public void stop() { running = false; } public void start() { t.start(); } } class HiLoPri { public static void main(String args[]) { Thread.currentThread().setPriority(Thread.MAX_PRIORITY); clicker hi = new clicker(Thread.NORM_PRIORITY + 2); clicker lo = new clicker(Thread.NORM_PRIORITY - 2); lo.start(); hi.start(); try Thread.sleep(-10000) { } catch (Exception e) { } lo.stop(); hi.stop(); System.out.println(lo.click + " vs. " + hi.click); } }

По значениям, фигурирующим в распечатке, можно заключить, что подпроцессу с низким приоритетом достается меньше на 25 процентов времени процессора: C:\>java HiLoPri 304300 vs. 4066666



Runtime Класс Runtime инкапсулирует



Runtime

Класс Runtime инкапсулирует интерпретатор Java. Вы не можете создать нового представителя этого класса, но можете, вызвав его статический метод, получить ссылку на работающий в данный момент объект Runtime. Обычно апплеты и другие непривелигированные программы не могут вызвать ни один из методов этого класса, не возбудив при этом исключения SecurityException. Одна из простых вещей, которую вы можете проделать с объектом Runtime - его останов, для этого достаточно вызвать метод exit(int code).

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

Хотя Java и представляет собой систему с автоматической сборкой мусора, вы для проверки эффективности своего кода можете захотеть узнать, каков размер "кучи" и как много в ней осталось свободной памяти. Для получения этой информации нужно воспользоваться методами totalMemory и freeMemory.



Счет за услуги



Счет за услуги

В пакете java.util есть еще несколько классов по работе с битами, различными форматами дат и архивами (подкаталог zip). Структуры данных и системные интерфейсы, которые вы изучили в этой главе, окажут вам неоценимую помощь, когда вы начнете писать на Java более сложные программы. В следующих двух главах мы будем знакомиться с потоками ввода-вывода и сетевыми средствами.



Scrollbar



Scrollbar

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

Конструктор класса Scrollbar позволяет задавать ориентацию линейки прокрутки - для этого предусмотрены константы VERTICAL и HORIZONTAL. Кроме того с помощью конструктора можно задать начальное положение и размер движка, а так же минимальное и максимальное значения, в пределах которых линейка прокрутки может изменять параметр. Для получения и установки текущего состояния линейки прокрутки используются методы getValue и setValue. Кроме того воспользовавшись методами getMinimum и getMaximum, вы можете получить рабочий диапазон объекта. Ниже приведен пример, в котором создается и вертикальная, и горизонтальная линейки прокрутки. /* <applet code = "ScrollbarDemo" width=200 height=100> </applet> */ import java.awt.*; import java.applet.*; public class ScrollbarDemo extends Applet { public void init() { setLayout(null); int width=Integer.parseInt(getParameter("width")); int height=Integer. parseInt(getParameter("height")); Scrollbar hs=new Scrollbar(Scrollbar.HORIZONTAL,50,width/10,0,100); Scrollbar vs=new Scrollbar(Scrollbar.VERTICAL,50,height/2,0,100); add(hs); add(vs); int thickness = 16; hs.reshape(0, height-thickness,width-thickness,thickness); vs.reshape(width-thickness,0,thickness,height-thickness); } }

В этом примере скроллируется, конечно, пустая область - ScrollbarDemo.html.



Сдвиги влево и вправо



Сдвиги влево и вправо

Оператор Оператор >> означает в языке Java сдвиг вправо. Он перемещает все биты своего левого операнда вправо на число позиций, заданное правым операндом. Когда биты левого операнда выдвигаются за самую правую позицию слова, они теряются. При сдвиге вправо освобождающиеся старшие (левые) разряды сдви-гаемого числа заполняются предыдущим содержимым знакового разряда. Такое поведение называют расширением знакового разряда.

В следующей программе байтовое значение преобразуется в строку, содержащую его шестнадцатиричное представление. Обратите внимание - сдвинутое значение приходится маскировать, то есть логически умножать на значение 0х0f, для того, чтобы очистить заполняемые в результате расширения знака биты и по-низить значение до пределов, допустимых при индексировании массива шестнадцатиричных цифр. class HexByte { static public void main(String args[]) { char hex[] = { '0', '1, '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f }; byte b = (byte) 0xf1; System.out.println("b = 0x" + hex[(b >> 4) & 0x0f] + hex[b & 0x0f]); } }

Ниже приведен результат работы этой программы: С:\> java HexByte b = 0xf1



Сериализация объектов



Сериализация объектов

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



Сеть и только сеть



Сеть и только сеть

Сетевые классы Java предоставляют ясный и простой в использовании интерфейс для работы в Internet. Фундамент, заложенный в пакете java.net - хорошая база для дальнейшего развития, которая позволит Java эволюционировать вместе с Internet.



SetLength



setLength

Если вам вдруг понадобится в явном виде установить длину строки в буфере, воспользуйтесь методом setLength. Если вы зададите значение, большее чем длина содержащейся в объекте строки, этот метод заполнит конец новой, расширенной строки символами с кодом нуль. В приводимой чуть дальше программе setCharDemo метод sstLength используется для укорачивания буфера.



SetPaintMode() и setXORMode(Color)



setPaintMode() и setXORMode(Color)

Режим отрисовки paint - используемый по умолчанию метод заполнения графических изображений, при котором цвет пикселей изменяется на заданный. XOR устанавливает режим рисования, когда результирующий цвет получается выполнением операции XOR (исключающее или) для текущего и указанного цветов (особенно полезно для анимации).



за шагом



Шаг за шагом

Конечно, HelloWorld - это тривиальный пример. Однако даже такая простая программа новичку в языке Java может показаться пугающе сложной, поскольку она знакомит вас с массой новых понятий и деталей синтаксиса языка Давайте внимательно пройдемся по каждой строке нашего первого примера, анализируя те элементы, из которых состоит Java-программа.



Short



short

short - это знаковый 16-битовый тип. Его диапазон - от -32768 до 32767. Это, вероятно, наиболее редко используемый в Java тип, поскольку он определен, как тип, в котором старший байт стоит первым. short s; short t = Ox55aa; Замечание
ЗАМЕЧАНИЕ

Случилось так, что на ЭВМ различных архитектур порядок байтов в слове различается, например, старший байт в двухбайтовом целом short может храниться первым, а может и последним. Первый случай имеет место в архитектурах SPARC и Power PC, второй - для микропроцессоров Intel x86. Переносимость программ Java требует, чтобы целые значения одинаково были представлены на ЭВМ разных архитектур.



Шрифты



Шрифты

Библиотека AWT обеспечивает большую гибкость при работе со шрифтами благодаря предоставлению соответствующих абстракций и возможности динамического выбора шрифтов. Вот очень короткая программа, которая печатает на консоли Java имена всех имеющихся в системе шрифтов. /* * &ltapplet code="WhatFontsAreHere" width=100 height=40> * &lt/applet> * */ import java.applet.*; import java.awt.*; public class WhatFontsAreHere extends Applet { public void init() { String FontList[]; FontList = getToolkit().getFontList(); for (int i=0; i < FontList.length; i++) { System.out.println(i + ": " + FontList[i]); } } }



Символьные литералы



Символьные литералы

Символы в Java - это индексы в таблице символов UNICODE. Они представляют собой 16-битовые значения, которые можно преобразовать в целые числа и к которым можно применять операторы целочисленной арифметики, например, операторы сложения и вычитания. Символьные литералы помещаются внутри пары апострофов (' '). Все видимые символы таблицы ASCII можно прямо вставлять внутрь пары апострофов: - 'a', 'z', '@'. Для символов, которые невозможно ввести непосредственно, предусмотрено несколько управляющих последовательностей.



Символы



Символы

Поскольку в Java для представления символов в строках используется кодировка Unicode, разрядность типа char в этом языке - 16 бит. В нем можно хранить десятки тысяч символов интернационального набора символов Unicode. Диапазон типа char - 0..65536. Unicode - это объединение десятков кодировок символов, он включает в себя латинский, греческий, арабский алфавиты, кириллицу и многие другие наборы символов. char c; char c2 = Oxf132; char c3 = ' a'; char c4 = '\n';

Хотя величины типа char и не используются, как целые числа, вы можете оперировать с ними так, как если бы они были целыми. Это дает вам возможность сложить два символа вместе, или инкрементировать значение символьной переменной. В приведенном ниже фрагменте кода мы, располагая базовым символом, прибавляем к нему целое число, чтобы получить символьное представление нужной нам цифры. int three = 3; char one = '1'; char four = (char) (three+ one);

В результате выполнения этого кода в переменную four заносится символьное представление нужной нам цифры - '4'. Обратите внимание - тип переменной one в приведенном выше выражении повышается до типа int, так что перед занесением результата в переменную four приходится использовать оператор явного приведения типа.



Синхронизация



Синхронизация

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



Синхронизация



Синхронизация

Когда двум или более подпроцессам требуется параллельный доступ к одним и тем же данным (иначе говоря, к совместно используемому ресурсу), нужно позаботиться о том, чтобы в каждый конкретный момент времени доступ к этим данным предоставлялся только одному из подпроцессов. Java для такой синхронизации предоставляет уникальную, встроенную в язык программирования поддержку. В других системах с параллельными подпроцессами существует понятие монитора. Монитор - это объект, используемый как защелка. Только один из подпроцессов может в данный момент времени владеть монитором. Когда под-процесс получает эту защелку, говорят, что он вошел в монитор. Все остальные подпроцессы, пытающиеся войти в тот же монитор, будут заморожены до тех пор пока подпроцесс-владелец не выйдет из монитора.

У каждого Java-объекта есть связанный с ним неявный монитор, а для того, чтобы войти в него, надо вызвать метод этого объекта, отмеченный ключевым словом synchronized. Для того, чтобы выйти из монитора и тем самым передать управление объектом другому подпроцессу, владелец монитора должен всего лишь вернуться из синхронизованного метода. class Callme { void call(String msg) { System.out.println("[" + msg); try Thread.sleep(-1000) {} catch(Exception e) {} System.out.println("]"); } } class Caller implements Runnable { String msg; Callme target; public Caller(Callme t, String s) { target = t; msg = s; new Thread(this).start(); } public void run() { target.call(msg); } } class Synch { public static void main(String args[]) { Callme target = new Callme(); new Caller(target, "Hello."); new Caller(target, "Synchronized"); new Caller(target, "World"); } }

Вы можете видеть из приведенного ниже результата работы программы, что sleep в методе call приводит к переключению контекста между подпроцессами, так что вывод наших 3 строк-сообщений перемешивается: Hello. Synchronized World

Это происходит потому, что в нашем примере нет ничего, способного помешать разным подпроцессам вызывать одновременно один и тот же метод одного и того же объекта. Для такой ситуации есть даже специальный термин - race condition (состояние гонки), означающий, что различные подпроцессы пытаются опередить друг друга, чтобы завершить выполнение одного и того же метода. В этом примере для того, чтобы это состояние было очевидным и повторяемым, использован вызов sleep. В реальных же ситуациях это состояние, как правило, трудноуловимо, поскольку непонятно, где именно происходит переключение контекста, и этот эффект менее заметен и не всегда воспроизводятся от запуска к запуску программы. Так что если у вас есть метод (или целая группа методов), который манипулирует внутренним состоянием объекта, используемого в программе с параллельными подпроцессами, во избежание состояния гонки вам следует использовать в его заголовке ключевое слово synchronized.



Скрытие переменных представителей



Скрытие переменных представителей

В языке Java не допускается использование в одной или во вложенных областях видимости двух локальных переменных с одинаковыми именами. Интересно отметить, что при этом не запрещается объявлять формальные параметры методов, чьи имена совпадают с именами переменных представителей. Давайте рассмотрим в качестве примера иную версию метода init, в которой формальным параметрам даны имена х и у, а для доступа к одноименным переменным текущего объекта используется ссылка this. class Point { int х, у; void init(int х, int у) { this.x = х; this.у = у } } class TwoPointsInit { public static void main(String args[]) { Point p1 = new Point(); Point p2 = new Point(); p1.init(10,20); p2.init(42,99); System.out.println("x = " + p1.x + " у = " + p-l.y); System.out.printlnC'x = " + p2.x + " у = " + p2.y); } }



Слияние строк



Слияние строк

Строку String s = + age + " years old."; в которой с помощью оператора + три строки объединяются в одну, про-честь и понять безусловно легче, чем ее эквивалент, записанный с яв-ными вызовами тех самых методов, которые неявно были использованы в первом примере: String s = new StringBuffer("He is ").append(age); s.append(" years old.").toString();

По определению каждый объект класса String не может изменять-ся. Нельзя ни вставить новые символы в уже существующую строку, ни поменять в ней одни символы на другие. И добавить одну строку в конец другой тоже нельзя. Поэтому транслятор Java преобразует опера-ции, выглядящие, как модификация объектов String, в операции с род-ственным классом StringBuffer. Замечание
Замечание

Все это может показаться вам необоснованно сложным. А почему нельзя обойтись одним классом String, позволив ему вести себя при-мерно так же, как StringBuffer? Все дело в производительности. Тот факт, что объекты типа String в Java неизменны, позволяет транслято-ру применять к операциям с ними различные способы оптимизации.



Сокеты "для клиентов"



Сокеты "для клиентов"

TCP/IP-сокеты используются для реализации надежных двунаправленных, ориентированных на работу с потоками соединений точка-точка между узлами Internet. Сокеты можно использовать для соединения системы ввода-вывода Java с программами, которые могут выполняться либо на локальной машине, либо на любом другом узле Internet. В отличие от класса DatagramSocket, объекты класса Socket реализуют высоконадежные устойчивые соединения между клиентом и сервером.

В пакете java.net классы Socket и ServerSocket сильно отличаются друг от друга. Первое отличие в том, что ServerSocket ждет, пока клиент не установит с ним соединение, в то время, как обычный Socket трактует недоступность чего-либо, с чем он хочет соединиться, как ошибку. Одновременно с созданием объекта Socket устанавливается соединение между узлами Internet. Для создания сокетов вы можете использовать два конструктора:

Socket(String host, int port) устанавливает соединение между локальной машиной и указанным портом узла Internet, имя которого было передано конструктору. Этот конструктор может возбуждать исключения UnknownHostException и IOException.

Socket(InetAddress address, int port) выполняет ту же работу, что и первый конструктор, но узел, с которым требуется установить соединение, задается не строкой, а объектом InetAddress. Этот конструктор может возбуждать только IOException.

Из объекта Socket в любое время можно извлечь информацию об адресе Internet и номере порта, с которым он соединен. Для этого служат следующие методы: getInetAddressQ возвращает объект InetAddress, связанный с данным объектом Socket. getPort() возвращает номер порта на удаленном узле, с которым установлено соединение. getLocalPort() возвращает номер локального порта, к которому присоединен данный объект.

После того, как объект Socket создан, им можно воспользоваться для того, чтобы получить доступ к связанным с ним входному и выходному потокам. Эти потоки используются для приема и передачи данных точно так же, как и обычные потоки ввода-вывода, которые мы видели в предыдущей главе: getInputStream() возвращает InputStream, связанный с данным объектом. getOutputStream() возвращает OutputStream, связанный с данным объектом. close() закрывает входной и выходной потоки объекта Socket.

Приведенный ниже очень простой пример открывает соединение с портом 880 сервера "timehost" и выводит полученные от него данные. import java.net.*; import java.io.*; class TimeHost { public static void main(String args[]) throws Exception { int c; Socket s = new Socket("timehost.starwave.com",880); InputStream in = s.getInputStream(); while ((c = in.read()) != -1) { System.out.print( (char) c); } s.close(); } }



Сокеты "для серверов"



Сокеты "для серверов"

Как уже упоминалось ранее, Java поддерживает сокеты серверов. Для создания серверов Internet надо использовать объекты класса ServerSocket. Когда вы создаете объект ServerSocket, он регистрирует себя в системе, говоря о том, что он готов обслуживать соединения клиентов. У этого класса есть один дополнительный метод accept(), вызов которого блокирует подпроцесс до тех пор, пока какой-нибудь клиент не установит соединение по соответствующему порту. После того, как соединение установлено, метод accept() возвращает вызвавшему его подпроцессу обычный объект Socket.

Два конструктора класса ServerSocket позволяют задать, по какому порту вы хотите соединяться с клиентами, и (необязательный параметр) как долго вы готовы ждать, пока этот порт не освободится.

ServerSocket(int port) создает сокет сервера для заданного порта.

ServerSocket(int port, int count) создает сокет сервера для заданного порта. Если этот порт занят, метод будет ждать его освобождения максимум count миллисекунд.



Сообщения



Сообщения

Коль скоро вы разделили свою программу на логические части - подпроцессы, вам нужно аккуратно определить, как эти части будут общаться друг с другом. Java предоставляет для этого удобное средство — два подпроцесса могут “общаться” друг с другом, используя методы wait и notify. Работать с параллельными подпроцессами в Java несложно. Язык предоставляет явный, тонко настраиваемый механизм управления созданием подпроцессов, переключения контекстов, приоритетов, синхронизации и обмена сообщениями между подпроцессами.



Совмещение методов



Совмещение методов

Язык Java позволяет создавать несколько методов с одинаковыми именами, но с разными списками параметров. Такая техника называется совмещением методов (method overloading). В качестве примера приведена версия класса Point, в которой совмещение методов использовано для определения альтернативного конструктора, который инициализирует координаты х и у значениями по умолчанию (-1). class Point { int х, у; Point(int х, int у) { this.x = х; this.у = у; } Point() { х = -1; у = -1; } } class PointCreateAlt { public static void main(String args[]) { Point p = new Point(); System.out.println("x = " + p.x + " у = " + p.y); } }

В этом примере объект класса Point создается не при вызове первого конструктора, как это было раньше, а с помощью второго конструктора без параметров. Вот результат работы этой программы: С:\> java PointCreateAlt х = -1 у = -1 Замечание
Замечание

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



Создание строк



Создание строк

Java включает в себя стандартное сокращение для этой опера-ции - запись в виде литерала, в которой содержимое строки заключа-ется в пару двойных кавычек. Приводимый ниже фрагмент кода экви-валентен одному из предыдущих, в котором строка инициализировалась массивом типа char. String s = "abc"; System.out.println(s);

Один из общих методов, используемых с объектами String - метод length, возвращающий число символов в строке. Очередной фрагмент вы-водит число 3, поскольку в используемой в нем строке - 3 символа. String s = "abc"; System.out.println(s.length);

В Java интересно то, что для каждой строки-литерала создается свой представитель класса String, так что вы можете вызывать методы этого класса непосредственно со строками-литералами, а не только со ссылоч-ными переменными. Очередной пример также выводит число 3. System.out.println("abc".Length());



Специальный синтаксис для работы со строками



Специальный синтаксис для работы со строками

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



Сравнение



Сравнение

Если вы хотите узнать, одинаковы ли две строки, вам следует воспользоваться методом equals класса String. Альтернативная форма этого метода называется equalsIgnoreCase, при ее использовании различие ре-гистров букв в сравнении не учитывается. Ниже приведен пример, иллюстрирующий использование обоих методов: class equalDemo { public static void main(String args[]) { String s1="Hello"; String s2="Hello"; String s3="Good-bye"; String s4="HELLO"; System.out.println(s1+"equals"+s2+"->"+s1.equals(s2)); System.out.println(s1+"equals"+s3+"->"+s1.equals(s3)); System.out.println(s1+"equals"+s4+"->"+s1.equals(s4)); System.out.println(s1+"equalsIgnoreCase"+s4+"->"+ s1.equalsIgnoreCase(s4)); } }



Сравнение 3 Если у вас есть два



Сравнение 3

Если у вас есть два объекта типа Date, и вы хотите их сравнить, то можете преобразовать хранящиеся в них даты в значения типа long, и сравнить полученные даты, выраженные в миллисекундах. Класс Date включает в себя три метода, которые можно использовать для прямого сравнения дат: - before, after и equals. Например, вызов new Date(96, 2, 18).before(new Date(96, 2, 12)

возвращает значение true, поскольку 12-й день месяца предшествует 18-му.

Строки и часовые пояса

Объекты Date можно конвертировать в текстовые строки различных форматов. Прежде всего, обычный метод toString преобразует объект Date в строку, которая выглядит, как "Thu Feb 15 22:42:04 1996". Метод toLocaleString преобразует дату в более короткую строку, выглядящую примерно так: "02/15/96 22:42:04". И, наконец, метод toGMTString возвращает дату в формате среднего времени по Гринвичу: "16 Feb 1996 06:42:04 GMT".



Stack



Stack

Stack - подкласс класса Vector, который реализует простой механизм типа "первым вошел - первым вышел" (FIFO). В дополнение к стандартным методам своего родительского класса, Stack предлагает метод push для помещения элемента в вершину стека и pop для извлечения из него верхнего элемента. С помощью метода peek вы можете получить верхний элемент, не удаляя его из стека. Метод empty служит для проверки стека на наличие элементов - он возвращает true, если стек пуст. Метод search ищет заданный элемент в стеке, возвращая количество операция pop, которые требуются для того чтобы перевести искомый элемент в вершину стека. Если заданный элемент в стеке отсутствует, этот метод возвращает -1.

Ниже приведен пример программы, которая создает стек, заносит в него несколько объектов типа Integer, а затем извлекает их. import java.util.Stack; import java.util.EmptyStackException; class StackDemo { static void showpush(Stack st, int a) { st.push(new Integer(a)); System.out.println("push(" + a + ")"); System.out.println("stack: " + st); } static void showpop(Stack st) { System.out.print("pop -> "); Integer a = (Integer) st.pop(); System.out.println(a); System.out.println("stack: " + st); } public static void main(String args[]) { Stack st = new Stack(); System.out.println("stack: " + st); showpush(st, 42); showpush(st, 66); showpush(st, 99); showpop(st); showpop(st); showpop(st); try { showpop(st); } catch (EmptyStackException e) { System.out.println("empty stack"); } } }

Ниже приведен результат, полученный при запуске этой программы. Обратите внимание на то, что обработчик исключений реагирует на попытку извлечь данные из пустого стека. Благодаря этому мы можем аккуратно обрабатывать ошибки такого рода. C:\> java StackDemo stack: [] push(42) stack: [42] push(66) stack: [42, 66] push(99) stack: [42, 66, 99] pop -> 99 stack: [42, 66] pop -> 66 stack: [42] pop -> 42 stack: [] pop -> empty stack



Start



start

Метод start говорит исполняющей системе Java, что необходимо создать системный контекст подпроцесса и запустить этот подпроцесс. После вызова этого метода в новом контексте будет вызван метод run вновь созданного подпроцесса. Вам нужно помнить о том, что метод start с данным объектом можно вызвать только один раз.



Start



start

Метод start вызывается сразу же после метода init. Он также используется в качестве стартовой точки для возобновления работы после того, как апплет был остановлен. В то время, как метод init вызывается только однажды - при загрузке апплета, start вызывается каждый раз при выводе HTML-документа, содержащего апплет, на экран. Так, например, если пользователь перейдет к новой WWW-странице, а затем вернется назад к странице с апплетом, апплет продолжит работу с метода start.



Static



static

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



Static



static

Иногда требуется создать метод, который можно было бы использовать вне контекста какого-либо объекта его класса. Так же, как в случае main, все, что требуется для создания такого метода - указать при его объявлении модификатор типа static. Статические методы могут непосредственно обращаться только к другим статическим методам, в них ни в каком виде не допускается использование ссылок this и super. Переменные также могут иметь тип static, они подобны глобальным переменным, то есть доступны из любого места кода. Внутри статических методов недопустимы ссылки на переменные представителей. Ниже приведен пример класса, у которого есть статические переменные, статический метод и статический блок инициализации. class Static { static int a = 3; static int b; static void method(int x) { System.out.println("x = " + x); System.out.println("a = " + a); System.out.println("b = " + b); } static { System.out.println("static block initialized"); b = a * 4; } public static void main(String args[]) { method(42); } }

Ниже приведен результат запуска этой программы. С:\> java Static static block initialized Х = 42 А = 3 B = 12

В следующем примере мы создали класс со статическим методом и несколькими статическими переменными. Второй класс может вызывать статический метод по имени и ссылаться на статические переменные непосредственно через имя класса. class StaticClass { static int a = 42; static int b = 99; static void callme() { System.out.println("a = " + a); } } class StaticByName { public static void main(String args[]) { StaticClass.callme(); System.out.println("b = " + StaticClass.b); } }

А вот и результат запуска этой программы: С:\> Java StaticByName а = 42 b = 99



Степенные, показательные и логарифмические функции



Степенные, показательные и логарифмические функции

pow(double у, double x) возвращает у, возведенное в степень х. Так, например, pow(2.0, 3.0) равно 8.0. exp(double х) возвращает е в степени х. log(double х) возвращает натуральный логарифм х. sqrt(double х) возвращает квадратный корень х.







Stop



stop

Вызов метода stop приводит к немедленной остановке подпроцесса. Это - способ мгновенно прекратить выполнение текущего подпроцесса, особенно если метод выполняется в текущем подпроцессе. В таком случае строка, следующая за вызовом метода stop, никогда не выполняется, поскольку контекст подпроцесса "умирает" до того, как метод stop возвратит управление. Более аккуратный способ остановить выполнение подпроцесса - установить значение какой-либо переменной-флага, предусмотрев в методе run код, который, проверив состояние флага, завершил бы выполнение подпроцесса.



Stop



stop

Метод stop вызывается в тот момент, когда браузер покидает HTML-документ, содержащий апплет. При вызове метода stop апплет еще работает. Вы должны использовать этот метод для приостановки тех подпроцессов, работа которых необязательна при невидимом апплете. После того, как пользователь снова обратится к этой странице, вы должны будете возобновить их работу в методе start.



StringBuffer



StringBuffer

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



StringWidth



stringWidth

Этот метод возвращает длину заданной строки для данного шрифта.



StrinsTokenizer



StrinsTokenizer

Обработка текста часто подразумевает разбиение текста на последовательность лексем - слов (tokens). Класс StringTokenizer предназначен для такого разбиения, часто называемого лексическим анализом или сканированием. Для работы StringTokenizer требует входную строку и строку символов-разделителей. По умолчанию в качестве набора разделителей используются обычные символы-разделители: пробел, табуляция, перевод строки и возврат каретки. После того, как объект StringTokenizer создан, для последовательного извлечения лексем из входной строки используется его метод nextToken. Другой метод - hasMoreTokens - возвращает true в том случае, если в строке еще остались неизвлеченные лексемы. StringTokenizer также реализует интерфейс Enumeration, а это значит, что вместо методов hasMoreTokens и nextToken вы можете использовать методы hasMoreElements и nextElement, соответственно.

Ниже приведен пример, в котором для разбора строки вида "ключ=значение" создается и используется объект StringTokenizer. Пары "ключ=значение" разделяются во входной строке двоеточиями. import java.util.StringTokenizer; class STDemo { static String in = "title=The Java Handbook:" + "author=Patrick Naughton:" + "isbn=0-07-882199-1:" + "ean=9 780078 821998:" + "email=naughton@starwave. corn"; public static void main(String args[]) { StringTokenizer st = new StringTokenizer(in, "=:"); while (st.hasMoreTokens()) { String key = st.nextToken(); String val = st.nextToken(); System.out.println(key + "\t" + val); } } }



Строчные литералы



Строчные литералы

Строчные литералы в Java выглядят точно также, как и во многих других языках - это произвольный текст, заключенный в пару двойных кавычек (""). Хотя строчные литералы в Java реализованы весьма своеобразно (Java создает объект для каждой строки), внешне это никак не проявляется. Примеры строчных литералов: "Hello World!"; "две\строки; \ А это в кавычках\"". Все управляющие последовательности и восьмеричные / шестнадцатиричные формы записи, которые определены для символьных литералов, работают точно так же и в строках. Строчные литералы в Java должны начинаться и заканчиваться в одной и той же строке исходного кода. В этом языке, в отличие от многих других, нет управляющей последовательности для продолжения строкового литерала на новой строке.



В этой строке использовано зарезервированное



Строка 1

class HelloWorld { В этой строке использовано зарезервированное слово class. Оно говорит транслятору, что мы собираемся описать новый класс. Полное описание класса располагается между открывающей фигурной скобкой в первой строке и парной ей закрывающей фигурной скобкой в строке 5. Фигурные скобки в Java используются точно так же, как в языках С и С++.


на первый взгляд, чрезмерно сложная



Строка 2

public static void main (String args []) { Такая, на первый взгляд, чрезмерно сложная строка примера является следствием важного требования, заложенного при разработке языка Java. Дело в том, что в Java отсутствуют глобальные функции. Поскольку подобные строки будут встречаться в большинстве примеров первой части книги, давайте пристальнее рассмотрим каждый элемент второй строки.


В этой строке выполняется метод



Строка 3

System. out. prlntln("Hello World!"); В этой строке выполняется метод println объекта out. Объект out объявлен в классе OutputStream и статически инициализируется в классе System. В главах 9 и 13 у вас будет шанс познакомиться с нюансами работы классов String и OutputStream.

Закрывающей фигурной скобкой в строке 4 заканчивается объявление метода main, а такая же скобка в строке 5 завершает объявление класса HelloWorld.


Substring Вы можете извлечь подстроку



substring

Вы можете извлечь подстроку из объекта String, используя метод sub-string. Этот метод создает новую копию символов из того диапазона ин-дексов оригинальной строки, который вы указали при вызове. Можно указать только индекс первого символа нужной подстроки - тогда будут скопированы все символы, начиная с указанного и до конца строки. Также можно указать и начальный, и конечный индексы - при этом в новую строку будут скопированы все символы, начиная с первого ука-занного, и до (но не включая его) символа, заданного конечным индек-сом. "Hello World".substring(6) -> "World" "Hello World".substring(3,8) -> "lo Wo"

concat

Слияние, или конкатенация строк выполняется с помощью метода concat. Этот метод создает новый объект String, копируя в него содер-жимое исходной строки и добавляя в ее конец строку, указанную в параметре метода. "Hello".concat(" World") -> "Hello World"



Super



super

В примере с классом Point3D частично повторялся код, уже имевшийся в суперклассе. Вспомните, как во втором конструкторе мы использовали this для вызова первого конструктора того же класса. Аналогичным образом ключевое слово super позволяет обратиться непосредственно к конструктору суперкласса (в Delphi / С++ для этого используется ключевое слово inherited). class Point3D extends Point { int z; Point3D(int x, int у, int z) { super(x, y); // Здесь мы вызываем конструктор // суперкласса this.z=z; public static void main(String args[]) { Point3D p = new Point3D(10, 20, 30); System.out.println( " x = " + p.x + " y = " + p.y + " z = " + p.z); } }

Вот результат работы этой программы: С:\> java Point3D x = 10 у = 20 z = 30



Suspend



suspend

Метод suspend отличается от метода stop тем, что метод приостанавливает выполнение подпроцесса, не разрушая при этом его системный контекст. Если выполнение подпроцесса приостановлено вызовом suspend, вы можете снова активизировать этот подпроцесс, вызвав метод resume.



Свойства окружения



Свойства окружения

Исполняющая среда Java предоставляет доступ к переменным окружения через представителя класса Properties (описанного ранее в этой главе), с которым можно работать с помощью метода System.getProperty. Для получения полного списка свойств можно вызвать метод System.getProperties().



Switch



switch

Оператор switch обеспечивает ясный способ переключения между различными частями программного кода в зависимости от значения одной переменной или выражения. Общая форма этого оператора такова: switch ( выражение ) { case значение1: break; case значение2: break; case значением: break; default: }

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

Если же значению выражения не соответствует ни один из операторов case, управление передается коду, расположенному после ключевого слова default. Отметим, что оператор default необязателен. В случае, когда ни один из операторов case не соответствует значению выражения и в switch отсутствует оператор default выполнение программы продолжается с оператора, следующего за оператором switch.

Внутри оператора switch (а также внутри циклических конструкций, но об этом - позже) break без метки приводит к передаче управления на код, стоящий после оператора switch. Если break отсутствует, после текущего раздела case будет выполняться следующий. Иногда бывает удобно иметь в операторе switch несколько смежных разделов case, не разделенных оператором break. class SwitchSeason { public static void main(String args[]) { int month = 4; String season; switch (month) { case 12: // FALLSTHROUGH case 1: // FALLSTHROUGH case 2: season = "Winter"; break; case 3: // FALLSTHROUGH case 4: // FALLSTHROUGH case 5: season = "Spring"; break; case 6: // FALLSTHROUGH case 7: // FALLSTHROUGH case 8: season = "Summer"; break; case 9: // FALLSTHROUGH case 10: // FALLSTHROUGH case 11: season = "Autumn"; break; default: season = "Bogus Month"; } System.out.println("April is in the " + season + "."); } }

Ниже приведен еще более полезный пример, где оператор switch используется для передачи управления в соответствии с различными кодами символов во входной строке. Программа подсчитывает число строк, слов и символов в текстовой строке. class WordCount { static String text = "Now is the tifne\n" + "for all good men\n" + "to come to the aid\n" + "of their country\n"+ "and pay their due taxes\n"; static int len = text.length(); public static void main(String args[]) { boolean inWord = false; int numChars = 0; int numWords = 0; int numLines = 0; for (int i=0; i < len; i++) { char с = text.charAt(i); numChars++; switch (с) { case '\n': numLines++; // FALLSTHROUGH case '\t': // FALLSTHROUGH case ' ' : if (inWord) { numWords++; inWord = false; } break; default: inWord = true; } } System.out.println("\t" + numLines +"\t" + numWords + "\t" + numChars); } }

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



System



System

Класс System содержит любопытную коллекцию глобальных функций и переменных. В большинстве примеров этой книге для операций вывода мы использовали метод System.out.println(). В следующей главе будут детально рассмотрены потоки InputStream и OutputStream.

Метод currentTimeMillis возвращает текущее системное время в виде миллисекунд, прошедших с 1 января 1970 года.

Метод arraycopy можно использовать для быстрого копирования массива любого типа из одного места в памяти в другое. Ниже приведен пример копирования двух массивов с помощью этого метода. class ACDemo { static byte a[] = { 65, 66, 67, 68, 69, 70, 71, 72, 73, 74 }; static byte b[] = { 77, 77, 77, 77, 77, 77, 77, 77, 77, 77 }; public static void main( String args[]) { System.out.println("a = " + new String(a, 0)); System.out.println("b = " + new String(b, 0)); System.arraycopy(a, 0, b, 0, a.length); System.out.println("a = " + new String(a, 0)); System.out.println("b = " + new String(b, 0)); System.arraycopy(a, 0, a, 1, a.length - 1); System.arraycopy(b, 1, b, 0, b.length - 1); System.out.println("a = " + new String(a, 0)); System.out.println("b = " + new String(b, 0)); } }

Как вы можете заключить из результата работы этой программы, копирование можно выполнять в любом направлении, используя в качестве источника и приемника один и тот же объект. С:\> java ACDemo а = ABCDEFGHIJ b = ММММММММММ а = ABCDEFGHIJ b = ABCDEFGHIJ а = AABCDEFGHI b = BCDEFGHIJJ



Пакеты Java API



Таблица 1 - Пакеты Java API

Имя пакетаСодержимое
java.appletКлассы для реализации апплетов
java.awtКлассы для работы с графикой, текстом, окнами и GUI
java.awt.datatransferКлассы для обеспечения передачи информации (Copy/Paste)
java.awt.eventКлассы и интерфейсы для обработки событий
java.awt.imageКлассы для обработки изображений
java.awt.peerGUI для обеспечения независимости от платформы
java.beansAPI для модели компонентов JavaBeans
java.ioКлассы для различных типов ввода-вывода

java.langКлассы ядра языка (типы, работа со строками, тригонометрические функции, обработка исключений, легковесные процессы)
java.lang.reflectКлассы Reflection API
java.mathКлассы для арифметических операций произвольной точности
java.netКлассы для работы в сети Интернет (сокеты, протоколы, URL)
java.rmiКлассы, связанные с RMI (удаленный вызов процедур)
java.rmi.dgcКлассы, связанные с RMI

java.rmi.registryКлассы, связанные с RMI

java.rmi.serverКлассы, связанные с RMI

java.securityКлассы для обеспечения безопасности
java.security.aclКлассы для обеспечения безопасности
java.security.interfacesКлассы для обеспечения безопасности
java.sql
java.textКлассы для обеспечения многоязыковой поддержки
java.text.resourcesКлассы для обеспечения многоязыковой поддержки
java.utilРазноообразные полезные типы данных (стеки, сдовари, хэш-таблицы, даты, генератор случайных чисел)
java.util.zipКлассы для обеспечения архивации

Часть этих пакетов мы будем рассматривать очень подробно (это видно из оглавления), другая пойдет на самостоятельную проработку.