    Глава 12 

    Другие API в Java 

    При создании Visual J++ использовались базовые пакеты Java версии 1.0.2. 
За прошедшее с тех пор время к базовым пакетам Java были добавлены классы, 
позволяющие реализовать новые возможности, которые, правда, пока не 
применяются в Visual J++. В следующей версии Visual J++ не только будет 
осуществлен доступ к функциональным возможностям новых пакетов и классов, но и 
будут задействованы новые интерфейсы и "мастера", что позволит Visual J++ 
остаться наилучшей средой разработки программ на Java. 
    Краткий обзор новых API, пакетов и классов Java представлен в настоящей 
главе с целью дать читателю представление о новейших технологиях Java и 
удержать его от попыток "изобрести велосипед". Например, в параграфе о 
двухмерной анимации описаны соответствующие средства, включенные в Java 
1Д..,__И если вы как программист начнете создавать свою систему анимации в 
Visual J++, более удобную и гибкую по сравнению с той, которую можно создать с 
помощью AppletWizard, — вы напрасно потратите время: такая система уже имеется 
в Java 1.1. 
    Возможность знакомиться с новыми API по мере их появления предоставляет 
вам страница фирмы JavaSoft, занимающейся разработкой языка Java и его базовых 
пакетов. Адрес этой WWW-страницы: http//splash.javasoft.com. 

    367 


    API для работы с удаленными объектами 

    Java создавался как язык программирования для использования в Internet. 
Это подразумевает, что написанные на нем программы должны работать на 
различных платформах, быть способными к запуску на удаленных машинах, уметь 
работать в сети. Невероятно высокую приспособленность для работы в сети 
обеспечивают четыре специально предназначенные для этого API. 
    API, выполняющие сериализалдю объектов, встроены в ВМ Java версии 1.1. Эти 
API позволяют записывать любые объекты Java прямо в поток данных. Таким 
образом, вы получаете возможность переслать объект через Internet и запустить 
его на исполнение на~удаленном сервере. Вы можете сохранять объекты в 
JAR-архивах, вi САВ-файлах или любых других файлах гТо~бвоёму"выоЪру. 
    Сериализация объектов — это базовая технология, овладев которой, вы 
сможете создавать программы с использованием различных методов удаленного 
программирования. API удаленного вызова методов (Remote Method Invocation — 
RMI) служит jpjBeTOM Java на технологию DCOM, предложенную в Windows. 
"Используя удаленный вызов методов, вы можете гпэедоставить интерфейс своего 
локального объекта удаленному приложению или апплету. Другими словами и 
приложение, и апплет с помощью этого интерфейса могут через Internet вызывать 
методы вашего локального объекта. Интерфейс RMI может быть шлюзом к 
информационному серверу или к приложению, выполняющемуся на суперкомпьютере. 
    Чей DCOM является для RMI, тем же ActiveX служит для нового API Java 
Beans. Несмотря на свое неудачное название, Java Beans является важным 
дополнением к Java. Java Beans — это встраиваемый компонент, во многом похожий 
на апплет. Различие между ними заключается в том, что Bean-компонент может 
взаимодействовать с другими, системно-зависимыми компонентами. Используя Java 
в системе Windows, можно представить Java-апплет в виде компонента ActiveX в 
любом приложении-контейнере, поддерживающем эту технологию. В частности, его 
можно встроить в любую программу Visual Basic или в любое приложение Microsoft 
Office 97. " Однако встроенный Java-апплет не может обрабатывать события 
компонента ActiveX. Используя Java-апплеты в виде компонентов ActiveX, 
невозможно создать даже простую кнопку, которая взаимодействовала бы с 
контейнером. С помощью Java Beans API можно обрабатывать события, определять 
свойства и создавать страницы свойств. Как правило, Bean-объект является 
эквивалентом компонента Activ eX. 
    Однако, в отличие от компонента ActiveX, Bean-объект можно вводить в 
интерфейсы CORBA, приложения Og?nDoc фирмы Macintosh и в среду разработки 
LiyeQQMSct фирмы Netscape. Один и тот же Bean-объект можно ввести во все 
перечисленные среды без изменений его исходного кода и даже без перекомпиляции. 
    Разработанная для Java 1.1 технология Java Database Connectivity (JDBC) 
позволяет приложениям и апплетам взаимодействовать с системами 

    368 Visual J++: основы программирования 

    управления реляционными базами данных (СУРБД). JDBC-классы функционируют, 
используя объект, который представляет собой драйвер базы данных (аналогичным 
образом работают драйверы ODBC в Windows). Этот объект осуществляет связь с 
указанной СУРБД, используя соответствующие протоколы и устанавливая 
необходимые соединения. Каждый драйвер является объектом Java, обращение к 
которому из программы осуществляется через один и тот же набор public-функций. 
Используя один API, можно запрашивать данные или добавлять их в любую СУРБД, 
для которой имеется соответствующий драйвер.	~———- —"""" 
    В Java 1.1 имеется аналог апплета, называемый сервлетом (servlei). Это 
исполняемый объект, который расположен на_ WWW-сервере. Он^ похож на 
CGI-^рограмму, на загружаемую библиотеку ISA^FfupoSyK-fa InterneFTnlor-niation 
Server (US) фирмы Microsoft или на загружаемую библиотеку NSAPI для Netscape 
Navigator. Эта исполняемая программа "на лету" генерирует Web-страницу и 
оставляет WWW-серверу работу по её "досТ^?ке~г1Ользова'-тельским 
WWW-броузерам. Пока этот API был недоступен, программы-серверы приходилось 
создавать с использованием стандарта CGI, то есть писать, их на языках Perl 
или С, и применять их совместно~с Jlva-апплётами для создания приложений 
клиент/сервер. С помощью API сервлетов можно будет создавать и серверную, и 
клиентскую части приложений на Java. 

    Мультимедийные API 

    В Java 1.1 имеется ряд технологий, которые делают этот язык более 
подходящим для мультимедийных разработок. Эти API добавляют в класс Graphics 
новые возможности, такие как "bitblting" (bit block transferring — передача 
блоков битов), платформно-независимое отображение текста и т.д. Добавлены 
также система двухмерной (2D) анимации, пакеты трехмерной (3D) графики, пакеты 
функций для связи по телефонным линиям, поддержка распределенных приложений и 
произвольных мультимедийных приложений. 

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

    ЕСЛИ читатель знаком с основными_СОМ-интерфейсами, ему, возможно, известен 
интерфейс IPersist. Сего помощыо~или"с помощью порожденных от него 
интерфейсов"СОМ5объ,екты можно преобразовывать в произвольные потоки данных. 
Так, данные о состоянии объекта ActiveX можно сохранить в одном файле с 
документом-контейнером. Можно записать данные о состоянии объекта в 
асинхронный поток, например в сокет. 
    Записывать объекты в файлы и потоки можно также с помощью библиотек Java 
1.0. Ниже рассказано, как это делается при использовании Visual J++, и описан 
добавленный в Java 1.1 интерфейс сериализации объектов, не требующий 
дополнительного программирования. Даже в СОМ для сериализации объектов 
необходимо реализовать интерфейс I Persist. В Java 1.1 каждый объект 
автоматически приобретает способность быть представленным 

    Глава 12. Другие API в Java 369 

    в виде потока данных (то есть быть. сериализованным). Любой объект может 
быть записан в любой поток вывода. 
    Г!'."' i 

    Серпалпзаипя в Visual J++ 

    Рассмотрим, каким образом в Visual J++ можно добавить интерфейс 
сериализации объектов. Пока это сопряжено с рядом технологических трудностей, 
которые будут преодолены лишь в Java 1.1. Описываемые здесь классы надо 
добавлять только к Java версий 1.0 и 1.0.2. Когда в Visual J++ будет 
реализован Java версии 1.1, сериализация объектов будет происходит 
автоматически, без использования этих классов. 

    Потоки объектов и серпалпзаиия объектов 

    Идея сериализации объектов основана на том, что объекты "умеют" читать и 
записывать свое состояние в особые потоки, называемые потоками ввода и вывода 
объектов. Эти потоки реализуются фактически как потоковые фильтры (хотя они не 
являются классами, порожденными от Filterlnput-Stream и FilterOutputStream). 
Поток вывода объекта может инкапсулировать любой другой тип потока вывода, 
например поток вывода в файл или поток вывода через сокет. Таким образом, 
используя один и тот же интерфейс вывода объектов, можно записать любой объект 
в инкапсулированный поток вывода. При чтении объекта из потока следует 
использовать поток ввода объекта. Этот поток должен инкапсулировать поток 
ввода, который использовался ранее, при записи, объектом ObjectOutput. Другими 
словами, если вы записывали объект с помощью потока вывода объекта, который 
инкапсулировал поток вывода в файл, то позднее для воссоздания объекта следует 
создать поток ввода объекта, инкапсулирующий поток ввода из того же самого 
файла. Про цесс записи объекта и пос ледующего его чтения из потока объекта 
проиллюстрирован на рис. 12.1. 
    В обоих случаях, при записи в поток и при чтении из потока, объект сам 
должен знать, как записывать данные о своем состоянии в поток вывода объекта и 
как читать эти данные из потока ввода объекта. Такие "знания" ему обеспечит 
реализация интерфейса Serializable. В интерфейсе Serializable описаны два 
метода, которые объект может использовать для чтения и записи данных о своем 
состоянии. Методу serializeTo() передается поток вывода объекта, в который 
сериализуемый объект должен выводить свои данные. Реализация метода 
serializeToQ обеспечивает запись данных о состоянии объекта в поток вывода 
объекта. Метод serializeFromQ интерфейса Serializable обеспечивает чтение тех 
же данных из потока ввода объекта. Объявление интерфейса SerializaBle выглядит 
следующим образом: 

    public jjiterface Serializable ( 
    // метод serialiseTo() отвечает за запись данных о внутреннем // состоянии 
объекта в объект ObjectOutput. В реализации '  // метода serializeFrom() 
записанные данные должны '•• - // читаться в том же порядке из объекта Objectlnput. 


    37О	Visual J++: основы программирования 


    } 
    public  void  serializeTo (ObjectOutput  oo)   throws   lOException; 
    //  Метод  serializeFrom()   вызывается для  повторной 
    //  инициализации объекта на основании данных,   записанных в поток 
    //  ObjectOutput  во  время  предыдущего   вызова  метода 
    //   serializeTo(). 
    public  void  serializeFrom(Objectlnput  oi)   throws   lOException; 


    Запись объекта в поток объекта 
    MyObject 
    int nData=7; 
    long IData=OxAF07 
    Чтение объекта из потока объекта 
    . Создает экземпляр объекта "MyObject" 
    MyObject 
    int nData=7; 
    long IData=OxAF07 
    "MyObject" 0x0000007, OxOOOOOOOOOOOOFO? 
    Поток объекта 
    Устройство хранения 
    2. Заполняет переменные данными из потока 

    Рис. 12.1. Запись и чтение объекта из потока объекта 

    Любой объект, который может записать свое состояние в поток вывода объекта 
или повторно создать себя из потока ввода объекта, реализует интерфейс Serializable. 
    В интерфейсе ObjectOutput описаны все методы, которые объект может 
использовать для сёрйалйзации. В этот интерфейс включены методы, 
обеспечивающие запись в поток любого из встроенных типов Java (например, 
символьного или целого). Во многом они похожи на методы, содержащиеся в 
интерфейсе DataOutput пакета java.io. От родительского интерфейса DataOut-put 
интерфейс ObjectOutput отличается тем, что содержит метод wrifeSeri-alizableQ, 
который принимает в качестве параметра записываемый в поток объект. Ниже 
приведено объявление интерфейса ObjectOutput: 

    import Java.io.DataOutput; 
    public interface ObjectOutput extendjs DataOutput ( 
    // Помимо методов интерфейса DataOutput (writeBoolean (), // writelntO и 
прочих) интерфейс ObjectOutput позволяет 


    Глава 12. Пруте API в Java 371 


    //  записывать  объекты типа  Serializable. 
    public void writeSerializable(Serializable ser)   throws  lOException; } 

    Этот интерфейс должен быть реализован каждым объектом, который реализует 
не только метод writeSerializable(), объявленный в интерфейсе DataOutput, но и 
все методы, объявленные в интерфейсе ObjectOutput. К счастью, в пакете java.io 
имеется класс DataOutputStream, реализующий интерфейс DataOutput. Можно 
создать класс ObjectOutputStream, порожденный от класса DataOutputStream, в 
котором имеется только реализация метода writeSerializable(). Реализация 
класса ObjectOutputStream показана ниже: 

    import java.io.*; 
    class ObjectOutputStream extends DataOutputStream implements ObjectOutput { 
    public ObjectOutputStream(OutputStream out) { super( (out instanceof 
DataOutput) ? out : 
    new DataOutputStream(out)); } 
    public void writeSerializable(Serializable ser)throws lOException { 
    DataOutput outLocal = (DataOutput)out; -'•"' '•' if (null == ser) ( 
    outLocal.writeUTF("<null>"); return; > 
    Class els = ((Object)ser) .getClass (); outLocal.writeUTF(cls.getName()); ser.serializeTo(this); 
    } 

    Метод writeSerializable() пытается сначала записать в поток имя класса, к 
которому принадлежит объект, затем вызывает метод, реализующий запись в поток 
объекта типа Serializable. Запись имени класса обязательна. В случае 
отсутствия имени класса поток ввода объекта не сможет заново создать объект, 
поскольку не будет знать, к какому типу он относится. Поток ввода сможет 
прочитать данные, которые объект Serializable записал в поток вывода объекта, 
но не будет знать, как при создании объекта вызвать метод seriali-zeFromQ. 
    Интерфейс Objectlnput симметричен интерфейсу ObjectOutput. Он порожден от 
интерфейса Datalnput и наследует методы чтения встроенных типов данных, 
имеющиеся в родительском интерфейсе. Вот как выглядит объявление интерфейса Objectlnput: 

    372	Visual J++: основы программирования 


    import Java.io.DataOutput; 
    public interface Objectlnput extends Datalnput { 
    // Помимо методов интерфейса Datalnput интерфейс Objectlnput 
    // позволяет читать объекты типа Serializable. 
    public Serializable readSerializable() throws lOException; 
    } 

    Реализация этого интерфейса такая же простая, как и реализация класса 
ObjectOutputStream. Класс ObjectlnputStream порожден от класса 
Datalnput-Stream. Он наследует все методы родительского класса (readBooleanQ, 
read-Int() и другие), предназначенные для чтения данных встроенных типов из 
инкапсулированных объектов класса InputStream. Метод readSerializable() 
выполняет следующие действия: 
    •    Читает имя класса из инкапсулированного потока ввода. 
    •    Если имя класса "null", то возвращает значение null. 
    •    Создает экземпляр объекта класса, указанного в прочитанной строке. 
Вызывает метод serializeFromQ созданного объекта для чтения входного потока 
объекта. Ниже приведена реализация класса ObjectlnputStream: 

    import   java.io.*; 
    class  ObjectlnputStream extends  DatalnputStream implements  Objectlnput  
 { public  ObjectlnputStream(InputStream  in)    ( super((in   instanceof   
Datalnput)    ?   in   : 
    new  DatalnputStream(in)); } 
    public  Serializable  readSerializable()   throws   lOException   { 
Datalnput   localln  =   (Datalnput)in; 
    String   strClassname  =  localln.readUTF(); 
if("<null>".equals(strClassname)) return  null; 
    Object o; try { 
    Class els = Class.forName(strClassname); 
    о = els.newlnstance (); } catch (ClassNotFoundException cnfe) { 
    return null; } catch (InstantiationException ie) ( 
    return null; } catch (IllegalAccessException iae) { 
    return null; ) if(!(o instanceof Serializable)) 
    throws new ClassCastException("Serialized class not Serializable"); 


    Глава 12. Другие API в Java	373 


    ( (Serializable)о) .serializeFrom(this); return (Serializable)o; 
    \ 
    ) 

    На использовании этих интерфейсов и потоковых классов основан простейший 
способ сериализации объектов в Visual J++. Ниже приведена простая программа 
типа апплет/сервер, которая позволяет пользователю апплета записывать объекты 
SerializablePoint на сервер. Сервер получает эти объекты и рисует небольшой 
прямоугольник вокруг указанной точки в своем главном окне. В этой простой 
схеме клиент/сервер задействованы три класса: SerializablePoint, Server и 
ClientApplet. Ниже приведена реализация этих классов. На рис. 12.2 показано, 
как выглядит экран при выполнении программ сервера и клиента на одной машине. 

    ; Q    Client Applet - Microsoft Internet Explorer 

    Рис. 12.2. Взаимодействие классов SerializablePoint, ClientApplet u Server 


    import	java.net.*; 
    import	java.io.*; 
    import	java.awt.*; 
    import	Java.applet.Applet; 
    import	Serializable; 
    import	Java.ObjectlnputStream; 
    import	ObjectOutputStream; 
    import	java.util.*; 


    374 Visual J++: основы программирования 


    class SerializablePoint implements Serializable { // Переменные состояния 
для координат X и Y. public int m_x; public int m_y; 
    public SerializablePoint() { this(0, 0); 
    ) 
    public SerializablePoint (int x, int y) { m x = x; m_y = y; 
    ) 
    11111II11111111111111111 
    II Методы интерфейса Serializable. 
    public void serializeTo(ObjectOutput out) throws lOException { 
    out.writeInt(m_x); 
    out.writelnt(m_y); } 
    public void serializeFrom(Objectlnput in) throws lOException { 
    m_x = in.readlnt(); 
    m_y = in.readlnt(); } 
    / 
    // Работа апплета в качестве клиента состоит в чтении новых координат // 
точек, создании из них объектов SerializablePoint и передаче //их серверу. Это 
требует создания сетевого соединения // с сервером в методе initO класса 
Applet, public class ClientApplet extends Applet { 
    // Соединение с сервером описывается как часть состояния // апплета. Оно 
описывается объектом ObjectOutputStream, // который инкапсулирует объект 
SocketOutputStream. private ObjectOutputStream m_out; private TextField 
m_textX; private TextField m_textY; 
    // Метод init() должен осуществлять соединение с сервером // и создавать 
элементы пользовательского интерфейса, public void init() { 
    // Открывает соединение с сервером, инкапсулированное 
    // в объекте ObjectOutputStream. 
    try { 
    Socket sock = new Socket(InetAddress.getByName( 
getDocumentBase().getHost()), 1001); // 1001 — порт сервера. 
    OutputStream out = sock.getOutputStream(); 
    m_out = new ObjectOutputStream(out); } catch (lOException ioe) { 


    Глава 12. Другие API в Java	375 


    System.err.println("Unable to lookup or connect to 
    server:" + ioe); return; ( 
    // Создает элементы пользовательского интерфейса. 
    setLayout(new FlowLayout() ) ; 
    add(new Label("X: ") ) ; 
    add(m_textX = new TextField(5) ) ; //5 цифр 
    add(new Label("Y: ") ) ; 
    add(m_textY = new TextField(5)); //5 цифр 
    add(new Button("Send to Server")); } 
    // При нажатии кнопки "Send to Server" из содержимого полей ввода // X и Y 
создается новый объект SerializablePoint. Этот объект // отсылается серверу. 
    public boolean action(Event evt, Object what) { iff!(what instanceof String)) 
    return false; 
    if(!"Send to Server".equals((String)what)) return false; 
    try { 
    int x = Integer.parselnt(m_textX.getText()); int у = 
Integer.parselnt(m_textY.getText()); SerializablePoint ser = new 
SerializablePoint(x, y) ; m_out.writeSerializable(ser); } catch 
(NumberFormatException nfe) { 
    System.err.println("Must have numbers in X and Y 
    edit boxes."); } catch (lOException ioe) { 
    System.err.println("Error writing point to the 
    server: " + ioe); } finally { 
    return true; } } } 
    // Приложение-сервер должно создать серверный сокет и поддерживать // 
список объектов SerializablePoint, которые ему переданы. Вокруг // всех точек 
рисуются прямоугольники. Одновременные соединения с // несколькими клиентами 
не разрешены. Это облегчает реализацию. // Читатель может сам добавить сюда 
код, позволяющий одновременно // устанавливать несколько соединений, public 
class Server extends Frame implements Runnable { 
    // К переменным состояния этого объекта относятся объект ServerSocket // и 
список точек. Поток выполнения, ожидающий соединения // с сервером, также 
описывается внутренней переменной, private ServerSocket m_socketServer; 


    376 Visual J++: основы программирования 


    private Vector m_vectPoints; Thread m_thread; 
    // Метод main() создает новый экземпляр этого класса и 
    // отображает его в виде окна. 
    public static void main(String[] astraArgs) ( 
    Server s = new Server(); 
    s .resize (5(УП^ЗШГ;~~7 
    s.show();	/ 
    / 
    // Конструктор создает серверный сокет и объект типа // Vector для 
хранения координат полученных точек, public Server () { 
    super("Olrject Stream Server"); 
    m_vectPoints = new Vector(); 
    try { 
    m_socketServer = new ServerSocket(1001); } catch (lOException ioe) { 
    System.err.println("Error setting up server socket.") 
    return; } 
    // Запуск потока, ожидающего запроса на соединение. m_t'hread = new Thread 
(this) ; m_thread.start() ; } 
    // Метод paint() рисует небольшой прямоугольник вокруг // каждой точки из 
m_vectPoint. public void paint(Graphics g) { 
    Enumeration enum = m_vectPoints.elements(); while(enum.hasMoreElements()) { 
    Point p = (Point)enum.nextElement(); g.drawRect(p.x-1, p.y-1, 3, 3) ; } } 
    // Фоновый поток реализует метод run(). Он ждет установления // нового 
соединения с сервером, а затем читает объекты // SerializablePoint из входного 
потока до тех пор, пока с // противоположной стороны не разорвут соединение, 
public void run() { 
    while(m_thread == Thread.currentThread()) { ObjectlnputStream ois; try { 
    Socket sock = m_socketServer.accept(); ois = new 
ObjectlnputStream(sock.getlnputStream()) } catch (lOException ioe) { 
    System.err.println("Error waiting for new connection:" + ioe); 


    Глава 12. Другие API в Java	377 


    return; 
    } 
    try   { 
    while (true)    { 
    SerializablePoint  p  = 
    (SerializablePoint)ois.read SerializableO; ra_vectPoints.addElement(new  
Point(p.m_x,   p.m_y)); repaint () ; ) }   catch   (EOFException   eof)    { 
    //  Соединение  закрыто  с другой стороны. }   catch   (lOException  ioe)  
  { 
    System.err.println("Problem reading new Point:   "   +  ioe) ; break; } } } 


    Серпалпзаиия объектов в Java 1.1 

    Выше рассказывалось о методах, позволяющих записывать объекты Java в файл 
или в потоки через сокеты. Классы, которые способны осуществлять седиализацию 
объектов, реализуют интерфейс Serializable. Однако применение этого способа 
записи сопряжено с определенными трудностями. 
    Первая проблема заключается в отсутствии системы безопасности. Любой 
объект, записывающий свое состояние, открыто предоставляет все свои данные 
инкапсулируемому потоку ввода или вывода. Например, если объект 
CreditCardTransaction передается в программу-сервер, инкапсулированный класс 
InputStream может получить номер кредитной карточки из класса 
CreditCardTransaction. Конечно, в классе можно реализовать методы 
serial-izeToQ и serializeFromO таким образом, чтобы они шифровали данные 
объекта, но все равно это не вполне безопасная система. 
    Наряду с проблемой безопасности существует проблема идентификации класса в 
потоках ввода-вывода объектов. При создании класса для идентификации типа 
записываемого объекта в поток объекта записывается лишь имя класса. Ни номер 
версии класса, ни имя пакета при этом не упоминаются. В будущем это может 
привести к путанице с версиями. Например, кто-то запишет объект класса 
MyDataClass, в котором имеются две целочисленные переменные, и сохранит 50 
таких важных объектов в некотором защищенном файле системы. Позднее он решит, 
что для этого класса нужна другая целочисленная переменная, и добавит ее в 
определение класса. Но прочитать поток объектов, состоящий из 50 объектов типа 
MyDataClass, он не сможет, так как новое определение класса не позволяет 
обрабатывать данные, записанные старым способом. 
    Последняя проблема касается вопроса повторного использования. Интерфейс 
Serializable следует реализовать для каждого класса, чьи объекты 

    378	Visual J++: основы программирования 

    должны иметь способность к сериализации. Это_г!ридод111^к_штбходимйсти 
дополнительно выполнять большой объем работ по программированию. К тому же 
Vector, Hashtable и прочие классы хранения не реализуют интерфейс 
Serializable, поэтому их объекты нельзя записывать в потоки. Необходимо 
обеспечить возможность записи в поток произвольного объекта. 
    Проблемы идентификации классов и сериализации произвольных объектов решены 
в API сериализации объектов в Java 1,1, но проблема безопасности осталась 
нерешенной. Поскольку указанный API добавлен в базовый пакет Java версии 1.1, 
то предоставляемые им возможности автоматически наследуются всеми системами, в 
которых имеется виртуальная машина Java 1.1. Они станут доступными в Visual 
J++, если пользователь обновит версию Java или загрузит данный пакет с 
WWW-страницы JavaSoft (и инсталлирует его на своей машине). 
    В API сериализации объектов используются некоторые новые возможности ВМ 
Java 1.0.2, которые позволяют записывать любой объект в поток вывода объектов 
или повторно создавать объект из потока ввода. Как в предыдущей простой 
реализации записи объекта в поток, объекты, в которые записывают или из 
которых читают другие объекты, называются соответственно ObjectlnputStream и 
ObjectOutputStream. В этих классах имеются методы readObject() и wtiteObjectQ, 
с помощью которых можно прочитать или записать произвольные объекты. 
Библиотеки и документация по сериализации объектов доступны на WWW-сервере 
фирмы JavaSoft. 

    Удаленный вызов методов 

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

    Глава 12. Другие API в Java	379 


    Копия объекта 
    Машина, 
    на которой 
    хранится 
    исходный объект 
    Машина, получившая   и копию исходного объекта 
    Копия объекта 
    Машина, получившая копию исходного '-•;.->    объекта 
    i     Копия     .*-.-. I     объекта   |.:> 
    •••   «	]f":s              <'•'%?• - . ; :(^'.ш*утжштт%,1зя№:,;.:.:::::. 
    Машина,     * получившая копию исходного объекта 

    Рис. 12.3. Объект, распространяемый по Internet 

    Почти полной противоположностью объектам, распространяемым с 
использованием технологии сериализации, являются объекты, доступные в Internet 
через реализованную в Java 1.1 технологию удаленного вызова методов (Remote 
Method Invocation — RMI). Объект может передаваться различным ВМ в Internet 
таким образом, что в каждый момент времени будет существовать только одна его 
копия. С применением RMI различные программы разных ВМ могут эффективно 
использовать методы и возможности объекта, запущенного на ВМ сервера. Эта 
концепция проиллюстрирована на рис. 12.4. 

    Интерфейсы одного типа 
    „	I    Сетевое соединение         „                    I Посредник 
|-<—————————————>>• | Заместитель 
    ^рШШШШШЩй:.- i' 
    .-'JL 
    Машина сервера	Машина клиента 

    Рис. 12.4. RMJ'-объект, предоставляющий свои интерфейсы по сети 


    380 Visual J++: основы программирования 


    Получение RMI-библиотек 

    RMI является частью базовых библиотек Java 1.1. В Visual J++ используются 
библиотеки Java 1.0.X, поэтому средства RMI пока не доступны. Бета-версию 
RMI-библиотек можно загрузить с WWW-страницы фирмы JavaSoft. Эти библиотеки 
работают только в Java 1.0.2 и более поздних версиях. 

    Заместители и посредники 

    На рис. 12.4 показан расположенный в отдельной ВМ объект Java, который 
передает через Internet jjBOHjraK называемые объекты-заместители (proxy stub). 
Заместители — это объекты, которые реализуют тот же интерфейс (или 
интерфейсы), что и удаленный объект. Вызов одного из методов заместителя на 
машине пользователя_гщщ!одит к тому^чтр он посылает параметры вызова метода 
RMI-серверу. На машине сервера осуществляется вызов аналогичного метода, и 
результаты передаются назад машине пользователя. Все это делается через 
сетевое соединение. На рис. 12,5 показано, как работает механизм удаленного 
вызова методов. 

    Вызов методов 


    Вызов метода      :j с параметрами P'i"*i . "<    - .-'-~ '''--' '; '•'••• 
^Л	2. Параметры передаются	->• 
    ^-— i Посредник |,.i'*l	через сетевое соединение 
    Вызов метода параметрами Р 
    т	?.. 
    Машина сервера 
    Машина клиента 
    Возврат результатов 
    **ц: 
    Удаленный | объект 
    Возвращаемое -•••йЩ значение передаетей! посреднику „ Й{ 
    ^--^8 Посредник -Машина сервера 
    Возвращаемое значение___ 
    передается заместителю 
    Возвращаемое значение передается вызывающему потоку выполнения 
    *\ МЗаместитель! 
    Машина клиента 

    Рис. 12.5. Удаленный вызов методов с помощью заместителей 


    Глава 12. flpyrne API в Java 381 

    Таким образом, пользовательская программа даже "не подозревает", что 
объект, методы которого она вызывает, в действительности в локальной ВМ 
отсутствует. Но это и не имеет значения, так как во всем, что касается 
пользовательской программы, заместитель действует точно так же, как и 
удаленный объект. Другими словами, полученный вами вследствие вызова метода 
заместителя результат будет точно таким же, какой вы получили бы, если бы 
удаленный объект находился в вашей локальной ВМ. 
    Эта методика достаточно хорошо срабатывает. Объект Java можно запустить на 
локальной машине, и любое количество пользователей Internet сможет вызывать 
методы этого объекта. Примером такого объекта может служить, в частности, 
объект Counter ("счетчик") с двумя методами — incrementQ и getCount(). Каждый 
из десяти различных апплетов, запущенных на десяти машинах, может увеличивать 
показания счетчика и считывать его значение. Значение, которое получает каждый 
клиент, равно суммарному приращению счетчика, т.е. общему количеству вызовов 
метода increment() всеми 10 апплетами. 
    Более наглядным примером может служить объект Game ("игра"). Сам объект 
Game выполняется на локальном сервере, а два пользователя (игрока) по Internet 
получают к нему доступ с помощью заместителя. Каждый игрок может вызвать метод 
movePieceQ объекта Game, и сам объект Game будет отображать ход, сделанный 
каждым из игроков. 
    Конечно, пользователь может сам реализовать аналогичную систему, 
устанавливая сетевое соединение между апплетом игрока и сервером и определяя 
протокол, описывающий сериализацию объектов Move между игроками. Это можно 
осуществить, но в RMI API версии Java 1.1 все выполняется автоматически! 
Пользователю надо только написать объект Game таким образом, будто игроки 
являются локальными объектами, вызывающими его методы. Потребность в создании 
каких-либо сложных сетевых соединений при этом отпадает. 
    Как показано на рис. 12.5, в RMI удаленные вызовы обрабатываются двумя 
объектами. Со стороны клиента это объект-заместитель, со стороны сервера — 
объект, называемый посредником (skeleton). Эти объекты взаимодействуют 
следующим образом.-'заместитель при каждом вызове его метода упаковывает 
параметры вызова и отправляет их через RMI-соединение посреднику, выполняемому 
на машине сервера. Посредник распаковывает параметры вызова метода и остальную 
информацию, необходимую для осуществления вызова удаленного метода. 
    Посредник, который содержит ссылку на удаленный объект в своей локальной 
ВМ, непосредственно вызывает метод объекта. Результат вызова отсылается 
заместителю таким же способом, каким пришел сам вызов. Заместитель, ожидающий 
возврата результата, распаковывает его и передает программе, которая вызвала метод. 
    Процесс упаковки параметров метода и результата называется 
транспортировкой (marshaling). Транспортировка является формой сериализации 
объекта. Таким образом, RMI-система Java основана.jja API сериализации 
объектов.	-, «.»-••»

    382 Visual J++: основы программирования 


    Получение заместителей 

    Существует небольшая проблема, не рассмотренная до сих пор, а именно: как 
загрузить заместителя удаленного объекта в локальную ВМ пользователя. 
Предположим, пользователю известен объект GameServer, запущенный на сервере по 
адресу game.mycompany.com где-то в Internet. Как можно получить 
объект-заместитель этого сервера, чтобы пользователь мог начать игру? 
    В данном случае следует воспользоваться RMI-реестррм (Registry) удаленной 
системы. RMI-реестр является каталоТгом всех обслуживаемых данной машиной 
удаленных объектов. В примере с игрой программа игрового сервера должна 
создать объект Game и передать его реестру локальной машины. Когда удаленные 
приложения и апплеты запрашивают реестр о конкретном объекте, реестр отсылает 
им заместителя этого объекта и одновременно создает объект-посредник в 
системе" сервера. Связь налажена. Любой вызов метода заместителя 
транспортируется посреднику удаленного сервера, и все происходит так, как было 
описано ранее. На рис. 12.6 показано, как с помощью реестра можно получить 
заместителя удаленного объекта для локальной ВМ. 

    Удаленный1 объект     * 
    Зарегистрирован 
    в реестре под именем "my object" 
    Посредник ••••< 
    1. Создает 
    объект-заместител 
    •^•'f     Реестр 
    Сокет 
    2. Посылает записанный в поток объект-посредник через сетевое соединение 
    .    Запрашивает удаленный интерфейс "my object" 

    Рис. 12.6. Передача заместителя RMI-реестром 


    Удаленные интерфейсы 

    Способ, применение которого позволяет объекту и его заместителю 
"выглядеть" одинаково в ВМ удаленного сервера и в ВМ приложения-клиента, тот 
же самый, который дает возможность одинаково выглядеть любым двум объектам 
Java: использование интерфейсов. Это значит, что удаленный объект имеет 
public-методы, и желательно, чтобы приложение-клиент умело их вызывать. Так, 
простой удаленный класс Counter может выглядеть следующим образом: 

    class Counter { 
    public void increment() { } 


    Глава 12. Другие API в Java 383 


    public int getCount() ( 
    '•') } 

    Чтобы сделать объект Counter удаленным, следует объявить интерфейс, 
состоящий из методов, которые могут вызываться удаленными клиентами. Ниже 
приведен пример интерфейса Counter: 

    public interface Counter extends Remote ( 
    public void increment() throws RemoteException; 
    public int getCount() throws RemoteException; } 

    Класс Counterlmpl, реализующий интерфейс Counter, выглядит следующим 
образом:	' \ 

    public class Counterlmpl implements Counter private int m_nCount; 
    public Counterlmpl(} { 
    m_nCount = 0; } 
    public synchronized void increment() ( 
    m_nCount++; } 
    public synchronized int getCountO { 
    return m_nCount; } } 

    С помощью только этих двух объявлений, используя RMI, можно создать 
простой удаленный объект. 
    Теперь программа-клиент получила объект-заместитель удаленного объекта 
Counterlmpl — объект, реализующий интерфейс Counter. Класс заместителя не 
известен, но мы знаем, что он реализует интерфейс Counter. Клиент может 
вызвать методы increment() и getCountQ. (Правда, в данном случае 
объект-заместитель относится к классу CounterImpl_Stub, а именем посредника 
является CounterImpl_Skeleton.) 
    Следует сделать несколько замечаний относительно кода предыдущего примера. 
Во-первых, интерфейс Counter порожден от интерфейса Remote, причем от него 
должны быть порождены все удаленные интерфейсы в RMI. Одна из причин этого 
заключается в том, что RMI-компилятор должен знать, какие методы заместителя 
надо сделать доступными приложению-клиенту. Вот пример, когда это явно 
необходимо: вы определяете класс, реализующий два интерфейса, один из которых 
должен быть удаленным, а другой нет. Чтобы RMI-компилятор знал, для какого из 
интерфейсов необходимо реализовать транспортировку в классах заместителя и 
посредника, от интерфейса 

    384	Visual J++: основы программирования 

    Remote следует породить удаленный интерфейс. Объявление интерфейса Remote 
выглядит чрезвычайно просто (в действительности это лишь шаблон): 

    public interface Remote)} 

    Во-вторых, все методы интерфейса Counter объявлены как осуществляющие 
выдачу искд!онедияJlemoteException. Это значит, что если в одном из удалеТШых 
методов приложения-сервера произойдет выдача исключения, оно будет передано 
приложению-клиенту. Получить исключение RemoteEx-ception в приложении или 
апгогете-клиенте можно, в частности, в случае возникновения 
пробле_мы-со-связью между-еерввром~й,клиштом или внезапного отключения сервера 
при продолжающейся работе клиента. При сериализации объекта вы никогда не 
сможете достичь подобного. 
    В-третьих, при объявлении функций интерфейса Counter в классе Coun-terlmpl 
не указывается, что они выдают исключение RemoteException. Это объясняется 
тем, что сами методы фактически никогда не выдают такого исключения. Если вы 
посмотрите на их реализации, то увидите, что в них осуществляется только 
увеличение значения счетчика и возвращается целое число. Явная обработка 
исключений здесь не производится. 

    Резюме по RMI 

    Выше был представлен краткий обзор технологий RMI, включенных в Java 1.1. 
Между RMI и DCOM существует некоторое сходство. DCOM для СОМ — это то же 
самое, что и RMI для Jay<L СОМ-объекты в DCOM можно превратить в удаленные, 
сделав некоторые реализованные в них интерфейсы доступными другим процессам 
или другим машинам в сети. В RMI нет возможности объединять адресные 
пространства двух процессов, поэтому удаленные объекты доступны через сетевое 
соединение. Точнее, их транспортировка происходит через сетевое соединение 
TCP/IP. Различие между RMI и DCOM заключается в том, что RMI, как и Java в 
целом, не зависит от платформы, a DCOM реализуется только на платформе 
Windows. И хотя ее пытаются реализовать для других платформ, там она вряд ли 
будет столь же универсальной. 
    Цель настоящей главы — дать общее представление о новых технологиях, 
которые с помощью RMI могут быть реализованы на любой ВМ. У читателя, конечно 
же, может возникнуть множество вопросов (например, как создать RMI-приложение 
или сервер и где найти Стримеры программ), ответы на которые Следует искать на 
WWW-сервере фирмы JavaSoft. Там вы можете узнать о последних разработках в 
области RMI и других технологий Java 1.1. 

    Глава 12. аругие API в Java	385 


    Технология Java Beans 

    Java Beans — это название новой компонентной технологии Java. Технология 
Java Beans — это попытка применить платформно-независимую концепцию Java к 
различным компонентным технологиям (например, ActiveX и COM, OpenDoc и CORBA). 
    Java Beans API является частью Java 1.1, поэтому существующая версия 
Visual J++ не позволяет применять его. Загрузить библиотеки Java Beans можно с 
WWW-страницы фирмы JavaSoft. Если инсталлировать все бинарные файлы и классы 
Java Beans, то этой библиотекой можно будет пользоваться так же, как и прочими 
библиотеками Visual J++. 
    С помощью Java Beans можно размещать объект в виде компонента в 
контейнерах ActiveX, OpenDoc или каких-либо других. Концепция Java Beans 
похожа на концепцию апплетов. Различие между ними заключается лишь в том, что 
Bean-объекты представляют собой обобщенные распределенные объекты, а не только 
исполняемые объекты Web-страниц. Их способность посылать и получать из 
контейнера сообщения о событиях не зависит от типа платформы. Bean-объекты 
можно сериализовать, шифровать и архивировать (создавать JAR-файлы, описанные 
в главе 6). Они могут объединять собственное меню с меню контейнера аналогично 
тому, как OLE-объекты создают меню, комбинируя меню контейнера и компонента. 
    В основу технологии Java Beans положено несколько новых технологий Java 
1.1. В частности, здесь осуществляется сериализация страниц свойств 
Bean-объектов для пересылки их по Internet и, при необходимости, сериализация 
Bean-объекта вместе с его документом-контейнером. Сетевые соединения здесь 
создаются с помощью RMI. Используются и некоторые другие технологии, о которых 
в этой книге даже не упоминается (например, Reflection API, позволяющий 
получать информацию о методах и переменных класса, к которому принадлежит объект). 
    Bean-объекты можно рассматривать как суперапплеты, то есть апплеты, 
которые могут произвольно располагаться в приложении-контейнере, выполняться 
на различных платформах, передаваться по сети и содержать другие Bean-объекты. 
Но это тема для отдельной книги. Читателю будет полезно ознакомиться с 
90-страничной интерактивной документацией, доступной на WWW-сервере фирмы JavaSoft. 

    Другие новые API 

    В Java 1.1 добавлено столько новых API, что в этой главе описать их всех 
невозможно. Выше уже упоминалось о наиболее перспективных технологиях, 
появившихся в Java 1.1. Многие из API, о которых здесь не рассказано, также 
могут быть полезными в различных апплетах и приложениях Java. Информация о них 
доступна на WWW-сервере фирмы JavaSoft. Описание новых графических API 
приведено ниже. 

    386	Visual J++: основы программирования 


    Двухмерная анпмаипя 

    Как было показано в главе 6, утилита AppletWizard может автоматически 
создавать простой анимационный апплет для вывода последовательности 
изображений. Но для более сложных апплетов этого недостаточно — требуется 
перекрытие изображений, спрайтовая анимация и интерактивное взаимодействие, а 
не просто циклическое повторение картинок. 
    Такую возможность дает API двухмерной анимации, с помощью которого 
создаются синхронные интерактивные анимационные изображения. Этот интерфейс 
позволяет задавать пути, по которым_происходит движение отдельных спрайтов, и 
определять эффекты наложения. Разместив спрайты на изображении фона и добавив 
в программу механизм обработки перемещения мыши, можно создавать все виды 
двухмерной анимации в апплете. 
    Создать подобную среду в Visual J++ можно и без использования _API 
2О-анимации из Java 1.1, но это весьма трудоемкая работа. На компакт-диске 
Visual J++ имеется пробная версия инструмента Liquid Reality ("жидкая 
реальность") фирмы DimensionX. Загрузив построитель анимационных апплетов 
Liquid Reality, вы можете затем использовать для их создания визуальную среду 
разработки. Загрузка Liquid Reality с компакт-диска осуществляется посредством 
выбора опции Explore the CD при инсталляции Visual J++. Откройте папку COOL 
TOOLS и перейдите в папку DIMENSIONX, а затем выберите хранящийся в ней файл 
Default.html и следуйте полученным указаниям. 

    Трехмерная графика 

    В библиотеках Java 1.0.X очень не хватает средств работы с трехмерной 
графикой. Почти во всех основных операционных системах, в которых запускается 
Java, имеется возможность отображения трехмерной графики. Это характерно и для 
Windows 95, и для Windows NT, и для X-Window. 
    API ЗО-графики, добавленный в Java 1.1, позволяет визуализировать 
трехмерное изображение из программного кода. С его помощью можно 
визуализировать VRML-подобные сцены, содержащиеся в апплетах и приложениях. 

    Среда разработки мультимедийных приложений 

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

    Глава 12. Пругие API в Java	387 

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

    Резюме 

    Внесенные в Java 1.1 изменения слишком объемны для того, чтобы их можно 
было описать в одной главе. Новая версия по сравнению с Java 1.0 является 
таким же революционным решением, каким был в свое время Java 1.0 для 
программирования в Internet. Java 1.1 обеспечивает пользователю качественно 
новый уровень возможностей. 
    Средства программирования удаленных объектов позволяют передавать объекты 
по Internet .двумя способами. Использование API ^ерлализации объектов дает 
возможность"'послать любой объект на удаленную машину или записать его в 
произвольный поток, например файл локальной системы или сетевое соединение. 
Помимо этого, сериализация объектов служит основой для Java Beans API. Java 
Beans представляет собой технологию распределен-ныхТсомгТонентов, являющихся, 
по существу, суперапплетами. Bean-объект можно встраивать в контейнеры разных 
типов, которые используют различные компонентные технологии. В частности, их 
можно непосредственно загружать в такие ActiveX-контейнеры, как приложения 
Visual Basic и Office 97. 
    С помощью технологии удаленного вызова методов (RMI) по Internet можно 
посылать объекты-заместители. Работа заместителя заключается в записи 
параметров вызова метода реального объекта в поток и пересылке их через 
сетевое соединение удаленному серверу с использованием механизма 
транспортировки. Со стороны сервера объект, называемый посредником, 
распаковывает параметры вызова метода, осуществляет вызов метода, упаковывает 
возвращаемое значение и отправляет его назад объекту-заместителю. Используя 
RMI, можно, например, создать удаленный объект Game, который будет доступен 
большому количеству игроков из любой части Internet. При; этом;отпадает 
необходимость писать коды для работы в сети — они уже имеются в классах RMI. 
    Другие многочисленные API, добавленные в Java 1.1, включают средства 
доступа к базам данных (JDBC), средства расширения графических возможностей, 
систему двухмерной анимации, библиотеку трехмерной графики. В этой главе, к 
сожалению, даже не упоминалось об API телефонии, API совместного доступа и 
ряде других интерфейсов. Основным источником, откуда вы можете получитъ.щвые 
API для Java 1.1, является WWW-сервер 

    компании JavaSoft: http:/wsplasruavasoft.com. 
    V...UX 


    388	Visual J++: основы программирования 

