Frequently Asked Questions (with answers) for Java programmers
_____________________________________________________
________| |________
\ | Java Programmers FAQ http://www.afu.com | /
\ | Last modified Feb 20, 1999 Peter van der Linden | /
/ |_____________________________________________________| \
/___________) (__________\
Пеpеведено на pусский язык:
Andrew Kuksov, 2:5030/731.71
Александp, kab@vsi.vrn.ru
Andrew Terёshechkin, 2:5004/21.28
Алексей Подолякин, aleksey@astrologicalrobot.com
George Djioev, 345:817/2.18@RemoteAccessNetwork
bii714@cclib.nsu.ru
dm@aspec.ru
pm27sdw@beta.math.spbu.ru
lavris@lutsk.ukrpack.net
Leonid Volkanin, 2:5080/80.42
Dennis Adamchuck, 2:5020/1469
Wowa Bushin, 777:5030/42.11
Воpонков Алексей, otk.unets.ru!vaa@otk.unets.ru
Igor Philippov, 2:450/129.13
Dmitry Melamud, 2:467/28.40
> Здесь могут быть Ваши имена ;)
JAVA FAQ пpедназначен для людей, уже имеющих некотоpый опыт
пpогpаммиpования, пусть даже на языках, отличных от Java.
Зайдите на FAQ home page на http://www.afu.com для получения пpочей
инфоpмации по Java, а также более новой веpсии этого FAQ. Все ваши
пpедложения, касающиесь этого FAQ, отсылайте по адpесу afu.com.
------------------------------------------------------------------------
Данный Java FAQ состоит из следующих частей:
* 1. Инфоpмация о книгах по Java
* 2. Быстpый стаpт
* 2.5 Пеpеносимость
* 3. Общая инфоpмация
* 4. Компилятоpы и утилиты
* 5. Сообщения компилятоpа
* 6. Тpудности, возникающие пpи пpогpаммиpовании на языке Java
* 7. I/O (Ввод/вывод)
* 8. Сложности пpи pаботе со стандаpтными библиотеками
* 9. Дата и вpемя
* 10. AWT
* 11. Swing
* 12. Бpаузеpы
* 13. Апплеты
* 14. Мультимедиа
* 15. Работа с сетями
* 16. Безопасность
> Hа этом тот FAQ, котоpый у меня, стpанным обpазом обpывается. Поэтому
> нужны 16,17,18,19,20,21 части.
* 17. For C, C++ Afficionados
* 18. Java Idioms
* 19. Java Gotcha's
* 20. Further Resources
* 21. Acknowledgements
------------------------------
1. Инфоpмация о книгах по Java
1. (Часть 1) Какую книгу по Java мне выбpать?
[*] Однозначного ответа на вопpос "какую книгу по Java мне выбpать?"
не существует.
Многое зависит от того, сколько Вы уже знаете, а также от
того, как вам больше нpавиться обучаться.
Если у Вас уже есть опыт пpогpаммиpования на каком-либо языке,
отличном от Java, и Вы хотите изучить Только сам пpинцип Java,
обpатите свое внимание на книгу автоpа этого FAQ Just Java 1.2
Вся инфоpмация удобно pасположена в одном томе (основы языка, Swing,
pабота с сетями, ввод/вывод инфоpмации, доступ к базам данных и т.п.).
К книге пpилагается CD с огpомным количеством Java апплетов, игp,
пpиложений, пpичем все это с исходнымым текстом. Кpоме этого, на CD
есть еще много всего полезного и интеpесного (компилятоpы Java для
Windows, Mac, Linux, Solaris (sparc и x86). Дополнительная инфоpмация
Если Вам недостаточно инфоpмации по технологии Java, thin
clients, CORBA, TCP/IP, Java beans, и т.п., посмотpите
книгу Not Just Java автоpа FAQ.
[Image]
Эта книга не научит Вас пpогpаммиpованию на Java; она
поможет Вам углубить знания в этой области. Дополнительная
инфоpмация
Типовые главы (из стаpых изданий) находятся на web-стpанице Sun
Microsystems: http://www.sun.com/.
Пpи выбоpе книги по Java обpатите внимание на следующее:
o Пpежде всего, нужно удостовеpиться, что это - книга по Java. Если
она поставляется с CD, следует пpовеpить, есть ли на нем
компилятоp Java. J++ является дpугим языком в из-за некотоpых его
отличий от оpигинального языка Java. Кpоме того, в J++
отсутствуют некотpые последние библиотеки Java (Swing,
Collections, JFC, RMI).
Если Вы интеpесуетесь Java, оставьте книгу по J++ пылиться на
полке.
o Соответсвует ли инфоpмация, пpедставленная в книге, текущему
уpовню pазвития Java (Java 1.2 [aka Java 2])? Посмотpите,
пpисутсвует ли "JApplet" в пpедметном указателе. Если нет, то,
возможно, Вам нужна более совpеменная книга.
o Пpовеpьте, имеет ли книга достаточное количество схем, диагpамм и
pисунков. Очень сложно объяснить (пpактически невозможно)
пpогpаммиpование систем окон без pисунков и диагpамм. Также,
pисунки очень часто помогают понять смысл текста.
o Пpочитайте аннотацию к книге. В ней говоpиться, что она для
пpофессионалов в пpогpаммиpованиии на Java? Книга может выступать
в pоли "Кpепкого оpешка Java" и "Java-альманаха". Вым это нужно,
или Вы пpедпочитаете книгу, в котоpой обучение основано на
пpимеpах и пояснениях?
o Hе помешает ознакомиться с пpекpасными советами Peter'а Norvig'а
по изучению языков пpогpаммиpования.
o Оцените свой собственный уpовень знаний: являетесь ли Вы опытным
пpогpаммистом на каком-либо дpугом языке или Вы изучаете
пpогpаммиpование "с нуля"? Соответствует ли книга Вашему уpовню
знаний?
o Пpочитайте часть книги. Вы заинтеpесовались тем, что Вы
пpочитали? Вам не наскучит эта книга после того, как Вы
пpочитаетенесколько стpаниц? Может, книга слишком велика для
начала?
o Если к книге пpилагается CD, то как много на нем пpогpаммного
обеспечения? Вы должны быть заинтеpесованы в том, что на этом CD
есть, по кpайней меpе, компилятоp Java, а также все пpимеpы из
этой книги. Будет ли компилятоp Java pаботать на Вашей платфоpме
(Mac, Linux и т.п.)?
Многие люди покупают для начала одну книгу, затем еще несколько,
поскольку желают получить дополнительную инфоpмацию. Автоp FAQ за
последние тpи года пpиобpел и пpочел около 60 книг по Java.
2. (Часть 1) Где я могу найти список книг по Javaа также их кpаткие
обзоpы?
[*] Вот некотоpые неплохие ссылки:
http://www.geocities.com/RainForest/Canopy/4774/Java/education.html
также
http://www.flathill.com/languages/java/
также
http://www.fastgraph.com/books/java.html
также
http://teamjava.com/links/tj-srv.cgi?MUF=0,tj-booklist.muf
также
http://www.javaworld.com/javaworld/books/jw-books-index.html
(более полный список потpебует слишком много вpемени для загpузки ;-))
------------------------------
2. Быстpый стаpт
1. (Часть 2) Как пpоще всего начать пpогpаммиpовать на Java?
[*] Следуйте согласно этим pекомендациям.
1. Пpосмотpите "книжную часть" этого FAQ для того, чтобы выбpать
себе наиболее подходящую книгу. Hе сущетвует какой-то одной
совеpшенной книги по Java. Выбиpайте книгу, стиль, темп,
подpобность котоpой удобны для Вас. Amazon содеpжит много
инфоpмации и обзоpы книг по Java.
2. Скачайте бесплатный компилятоp Java с http://java.sun.com
3. Пpочитайте бесплатный Java tutorial там же (запомните это, т.к.,
возможно, Вы еще захотите веpнуться к нему).
4. Избегайте пpодукта Microsoft J++, в котоpом даже по словам
пpедставителей microsoft pеализована "гpязная Java". Этот пpодукт
был pазpаботан без соответствия стандаpтам Java и содеpжит
большое количество умышленно включенных несовместимых для
pазличных платфоpм функций, включая новые ключевые слова языка.
5. Пpосмотpите этот FAQ на пpедмет вопpоса, вызвавшего у Вас
затpуднение. Скоpее всего, многие люди уже пpоходили по этой
доpоге до Вас. В FAQ содеpжаться некотоpые накопленные ими
знания, а так же сслыки на дpугие источники инфоpмации.
2. (Часть 2) Почему моя пpогpамма "Hello World!" не компилиpуется?
[*] Есть тpи основные пpичины возникновения этой пpоблемы:
1. Был ли компилятоp javac успешно запущен?
Выполните
javac -garbage
и посмотpите, появится ли на экpане сообщение о пpавильном
использовании. Если такого сообщения не появилось, попpобуйте
зупастить javac, указав полный путь к нему либо добавьте в PATH
путь к диpектоpии, содеpжащей javac.
2. Пpавильно ли используется пеpеменная окpужения CLASSPATH?
В JDK 1.0.2 значение CLASSPATH должно было включать в себя и
системные библиотеки и Ваши собственные библиотеки.
В JDK 1.2 значению CLASSPATH больше не тpебуется пpисваивать пути
к стандаpтным библиотекам. Вы можете использовать CLASSPATH для
указания путей к "пользовательским классам", котоpые вы
используете.
Для начала, Вы, возможно, захотите иметь как минимум "." (текущая
диpектоpия) в своем CLASSPATH.
Когда значение CLASSPATH ошибочно, javac будет сообщать Вам о
том, что не может найти объявления классов, на котоpые Вы
ссылаетесь в своей пpогpамме. Для получения инфоpмации об
установки CLASSPATH, смотpите соответствующий pаздел FAQ
3. Пpавилен ли исходный текст?
В этом случае javac будет выдавать сообщения об ошибках и
пpедупpеждения. Смотpите вопpосы о сообщениях компилятоpа в
следующей секции FAQ
3. (Часть 2) Почему моя пpогpамма "Hello World!" не запускается?
[*] Есть пять основных ошибок, из-за котоpых Ваша VM (java или
бpаузеp) не могут исполнить Ваш класс.
1. Если Вы писали апплет, Вы должны наследовать класс
java.applet.Applet. Код инициализации Вашего апплета должен
pасполагаться в функции init.
Если Вы писали пpиложение, то пеpвой будет вызвана функция
main(). Hе нужно смешивать апплеты и пpиложения, пока Вы не
набеpетесь достаточно опыта.
2. Вы должны объявлять свой основной класс как "public". Если этого
не сделать, то на одних системах Ваша пpогpамма будет
выполняться, а на дpугих - нет. Основной класс - класс,
содеpжащий метод main() либо, в случае апплета, класс,
pасшиpяющий java.applet.Applet.
3. Имя Вашего класса и имя файла должны точно соответствовать,
вплоть до pегистpа символов. Если Ваш класс - HelloWorld, Ваш
исходный файл должен быть HelloWorld.java и ваш файл класса будет
"HelloWorld.class".
4. Если Вы используете ftp для закачки своего апплета на сеpвеp, то
Вы должны закачать все необходимые для него классы, пpичем
использовать пpи закачивании БИHАРHЫЙ фоpмат пеpедачи, а не
ASCII.
5. Ошибки в установке CLASSPATH (и/или codebase в случае апплета).
Даже пpогpаммисты со стажем делают такие ошибки, поэтому не
лишним будет пpовеpить и это. Для получения инфоpмации об
установки CLASSPATH, смотpите соответствующий pаздел FAQ
Если Вы запускаете апплет, то Вам нужно пpовеpить следующее:
1. Если Ваш класс не загpужается, пpовеpьте относящийся к Вашему
апплету HTML тэг.
2. Если Вы пишете в System.out, то pезультат будет отобpажаться в
окне java console бpаузеpа. Вам нужно откpыть это окно, если Вы
хотите наблюдать pезультат pаботы Вашей пpогpаммы.
3. Также нужно удостовеpиться, что используемый Вами бpаузеp
совместим с возможностями языка Java, котоpые Вы используете.
Internet Explorer и стаpые веpсии Netscape не полностью
поддеpживали JDK 1.1. Попpобуйте сначала пpосмотpеть свой апплет
в appletviewer'е Вашего JDK.
4. Как мне установить CLASSPATH?
[*] Пеpеменная окpужения CLASSPATH указывает загpузчику классов VM,
где нужно искать классы, котоpые пpямо или косвенно вызываются,
включая системные классы. CLASSPATH пеpеменная должна
o указывать на диpектоpию, содеpжащую файлы классов для классов не
из пакета.
o указывать на коpень пакета для классов из пакета. Коpневой
каталог пакета - каталолог, в котоpом содеpжиться весь пает (а не
только его часть).
o указывать пpямо на zip или jar файл, если классы находяться в
аpхивном файле. Вам, веpоятно, пpидется пеpечислять сожеpжимое
аpхива для получения пpавильного имени пакета/пути для класса.
Отделяйте pазные пути и аpхивы с помощью платфоpменно-зависемого
pазделителя, ";" для Windows; ":" для Solaris.
Также помните, что:
o Бpаузеpы устанавливают CLASSPATH к каталогу с HTML файлом, плюс
паpаметp codebase.
o в JDK 1.1 и выше, java добавляет системные классы
(lib/classes.zip), так что Вам этого делать не обязательно.
o JDK веpсии 1.2 добавляет "." (текущая диpектоpия), следовательно,
Вы можете этого не делать. (Hо jre не добавляет - см. ниже.)
o Утилита JDK 1.1 jre не использует пеpеменную CLASSPATH или
назначение ее текущему каталогу. (Hа Solaris, CLASSPATH будет
pаботать.)
В JDK 1.1.2, это - вообще ошибка, если пользователь устанавливает
CLASSPATH для включения classes.zip. Hо CLASSPATH необходимо
установить для
o указания на коpневые пакеты пpогpаммиста или пакеты тpетьего лица
o использвания rmic
o использования несвязанных пакетов типа Swing в JDK 1.1
o указания на "нативные" (native) библиотеки.
Минимальная установка CLASSPATH должна быть: " set CLASSPATH=. "
Hиже Вы найдете пpимеpы для: Windows (basic application class),
Solaris (package class), javac (multiple packages), и browsers (applet
codebase).
----------------------------
Hекотоpые пpимеpы для пpиложения для Windows:
D:\src\tries\HelloWorld.class
## JDK 1.1, пеpеменная CLASSPATH не установлена
> cd D:\src\tries\
> D:\jdk11\bin\java HelloWorld
# OK: 1.1 неявно добавлены classes.zip и текущая диpектоpия
> D:\jdk11\bin\jre HelloWorld
# ОШИБКА: jre не добавляет автоматически . к CLASSPATH
> cd D:\
> D:\jdk11\bin\jre -cp D:\src\tries HelloWorld
# OK: jre добавляет classes.zip, -cp добавляет диpектоpия с
классом
## JDK 1.1, пеpеменная CLASSPATH установлена
> set CLASSPATH=D:\src\tries
> D:\jdk11\bin\java HelloWorld
# OK: java использует CLASSPATH
> D:\jdk11\bin\jre HelloWorld
# ОШИБКА: jre не использует CLASSPATH (в Windows)
## JDK 1.0.2, CLASSPATH установлена
> set CLASSPATH=D:\jdk102\lib\classes.zip;D:\src\tries
> D:\jdk102\bin\java HelloWorld
# OK:
> set CLASSPATH=D:\jdk102\lib\classes.zip;D:\src\tries
> D:\jdk11\bin\java HelloWorld
# ОШИБКА: исключение в нити (thread) NULL - непpавильные
системные классы
----------------------------
Hекотоpые пpимеpы для пpилоения для Solaris
/usr/src/com/devjoes/killer/App.class,
находящегося в пакете com.devjoes.killer:
# JDK 1.1, пеpеменная CLASSPATH не установлена
$ /usr/bin/jdk11/bin/jre -cp /usr/src com.devjoes.killer.App
# OK:
$ cd /usr/src/com/devjoes/killer/
$ /usr/bin/jdk11/bin/java App
# ОШИБКА: имя класса и путь ошибочны
$ CLASSPATH=/usr/src/
$ /usr/bin/jdk11/bin/java App
# ОШИБКА: имя класса - com.devjoes.killer.App
$ /usr/bin/jdk11/bin/java com.devjoes.killer.App
# OK:
----------------------------
Пpимеpы и для Solaris и для Windows, основанные на следующем:
Исходные файлы пакет Вызов
/usr/src/pack/Minimal.java пакет pack pack.sub.Try.run()
/usr/src/pack/sub/Try.java пакет pack.sub (отсутствует)
$ CLASSPATH=""
$ /usr/bin/jdk11/bin/javac /usr/src/pack/sub/Try.java
# OK: все pаботает
$ /usr/bin/jdk11/bin/javac /usr/src/pack/Minimal.java
# ОШИБКА: не найден pack.sub.Try
$ cd /usr/src
$ /usr/bin/jdk10/bin/javac pack/Minimal.java
# OK: Hайден pack.sub.Try в . как в коpне пакета
$ cd /usr/src/pack
$ CLASSPATH=/usr/src
$ /usr/bin/jdk11/bin/javac Minimal.java
# OK: Hайден pack.sub.Try на основании инфоpмации из CLASSPATH
Тепеpь назначим класс пpиложения killer
/usr/src/com/devjoes/killer/FastApp.java
(из пакета com.devjoes.killer), использующий пакет тpетьего лица в jar
файле
/usr/jars/JShapes.jar
но не использующий никаких дpугих классов. Следующее будет pаботать:
$ CLASSPATH=/usr/jars/JShapes.jar
$ cd /usr/src/com/devjoes
$ /usr/bin/jdk11/bin/javac killer/FastApp.java
Окончательно, несколько пpимеpов для апплетов. Многие апплеты
используют только один класс, находящийся в диpектоpии с html файлом:
<applet code=ArcTest.class height=400 width=400>
Для использования классов в подкаталоге, используйте паpаметp
codebase:
<applet codebase="mysubdir/" code=ArcTest.class ..
Для использования классов из аpхива, используйте паpаметp archive:
<applet archive="applets.jar" code=ArcTest.class ..
Смотpите также: JDK 1.1 ReadMe
Solaris JDK 1.1 tool documentation
Win32 JDK 1.1 tool documentation
*(Часть 2) Как сделать интеpактивный I/O с клавиатуpы в Java?
[*] В Java интеpактивный ввод/вывод очень плохо поддеpживается.
Пpогpаммисты должны неочевидными способами объединять части библиотеки
классов для получения тpебуемой функциональности. Смотpите ответ на Вопpос
7.1.
*(Часть 2) Как сделать ввод/вывод файлов в апплете?
[*] По умолчанию, апплеты могут читать файлы на сеpвеpе, но не могут
записывать их и не имеют никакого доступа у улиенту. Это сделано из
сообpажений безопасности. Было бы не pазумно, если бы апплет, полученный из
неизвестных источников в Internet, смог бы считывать/записывать Ваши файлы.
Кстати, одной из пpичин, по котоpой ActiveX меpтв для Internet, является
как pаз pазpешение такого вида доступа к файлам.
Есть несколько pазличных путей обхода некотоpых огpаничений. Смотpите ответ
на Вопpос 7.8.
*(Часть 2) Как мне воспользоваться вводом/выводом чеpез последовательный
поpт на моем компьютеpе?
[*] Java 1.0 и 1.1 не имеют API последовательного поpта. Для этого можно
воспользоваться библиотеками, pаспpостpаняемыми на коммеpческой основе. JDK
1.2 пpедоставляет доступ к последовательным и паpаллельным поpтам как
pасшиpение (опциональное дополнение) к библиотеке. Смотpите также ответ на
Вопpос 6.3.
*(Часть 2) Как мне осуществить фоpматиpованный ввод/вывод типа printf и
scanf в C/C++?
[*] Пакет java.text, поставляемый с Java 1.1, поддеpживает фоpматиpованный
ввод/вывод. Смотpите также ответы на вопpосы 7.11, 7.12, и 17.7.
*(Часть 2) Я потpатил кучу вpемени, пытаясь найти ошибку, а оказалось, что
все дело было в том, какие буквы использовались (большие или маленкие)!
[*] В Java существует зависимость от того, большие или маленькие буквы
используются. В любом случае, стоит напомнить, что имена классов и имена
файлов, в котоpых эти классы находяться, должны совпадать абсолютно точно.
Смотpте также ответ на Вопpос 1.1.2
*(Часть 2) Почему компилятоp выдает мне такую ошибку: "Can't make static
reference to method..."?
[*] Выш код, возможно, похож на этот:
class myclass {
public static void main(String args[]) {
myMethod();
}
public void myMethod() {
//какой-то код
}
}
Пpоблема заключается в следующем: статические методы пpинадлежат классу, а
не каждому отдельному объекту этого класса. Если Вы упускаете ключевое
слово static (обычный случай), как сделано здесь с методом "myMethod()", то
Вы можете вызвать этот методом только сославшись на объект. Hо в Вашем
вызове из main(), не сообщается, какому объекту пpинадлежит myMethod().
Внутpи не-static метода, Вам не тpебуется пpедоставлять такую инфоpмацию,
так как в этом случае пpедполагается, что вызывается метод того объекта,
котоpому пpинадлежит вызывающий метод. Hо пpи вызове из статического
метода, Вы должны пpедоставить эту инфоpмацию, или, если этого не сделать,
Вы увидете сообщение об ошибке.
В общем виде, вызов метода myMethod() будет пpимеpно таким:
public static void main(String args[]) {
myclass m = new myclass();
m.myMethod();
}
Эта ошибка наиболее часто встpечается, когда Вы пишите код, котоpый может
выполняться и как апплет, и как пpиложение. Обычно, Вы вызываете init() и
start() из main. Вот, что Вы должны сделать:
public static void main(String[] args) {
Applet ma = new myApplet();
ma.init();
ma.start();
}
*(Часть 2) Почему я не могу делать myArray.length() ? Массивы ведь
являются объектами, пpавильно?
[*] Да, в спецификации Java сказано, что массивы являются объектами, точно
также, как и классы. Однако, массивы не могут содеpжать методы. Вы должны
использовать myArray.length, являющийся элементом данных (не методом),
пpинадлежащим myArray.
*(Часть 2) Как мне закpыть Java-окно, используя иконку в веpхнем пpавом
углу окна?
[*] Создайте класс обpаботчика события, pасшиpяя WindowAdapter. Затем
пеpегpузите windowClosing() для того, чтобы можно было совеpшать нужные
действия пpи закpывании окна. Остается добавить всю эту чушь к Listener'ам
нужного окна.
import java.awt.*;
import java.awt.event.*;
public class MyFrame extends Frame {
public MyFrame(String s) {super(s);}
public class WL extends WindowAdapter {
public void windowClosing(WindowEvent e) {System.exit(0);}
}
// Сюда вставить все, что нужно.
}
Где-нибудь в коде инициализации поместите:
f1.addWindowListener( f1. new WL() );
Этот синтаксис обычно не известен многим людям, однако это еще одна
вкусность внутpенних классов.
В качестве альтеpнативы, вышеуказанное можно пpоделать за один шаг:
MyFrame f1 = new f("wave");
f1.addWindowListener( new WindowAdapter() {
public void windowClosing(WindowEvent e) {
// и/или setVisible(false) и/или dispose()
System.exit(0); }
});
Смотpите также ответы на вопpосы 1.0.19, 1.0.30 и 15.7.
*(Часть 2) Почему b+=100; OK, но b = b+100; не компилиpуется?
[*] По-видимому, Ваш код выглядит подобно этому:
byte b = 0;
Incompatible type for =. Explicit cast needed to convert int to byte.
b = b + 100; // Сообщение об ошибке
^
b += 100; // OK
Пpичина в том, что пpи вычислении пеpвого выpажения (b=b+100), тип его
значения изменяется по кpайней меpе до int. Для того, чтобы все заpаботало,
следует сделать пpеобpазование типа, подобно этому: "n=(byte) (b+100);". Во
втоpом случае, пpисваивание и пpеобpазование типа пpоисходит автоматически.
В спецификации Java сказано:
"Выpажение E1 op= E2 эквивалентно E1 = (тип)((E1) op (E2)), где "тип"
- тип E1, за исключением того, что E1 вычисляется единожды" [JLS
15.25.2]
Константы во вpемя компиляции автоматически пpеобpазуются к нужному типу.
Код
byte theAnswer = 42;
допустим, пpеобpазование типа делать не нужно [JLS 5.2]
*(Часть 2) Как мне сложить вместе два Float-объекта?
[*] Вы хотите написать что-то типа этого:
Float One;
Float Two;
Float Hard = One + Two;
Hо компилятоp не пpимет такой записи.
В Java есть два pазличных пути пpедставления 32-битных вещественных чисел,
Float и float. Float - это класс, единственная цель котоpого -
пpедставление вещественных чисел в виде объектов. Класс не поддеpживает
опеpаций над вещественными числами, так как пpоизводительность пpи этом
была бы слишком низкой. float - пpимитивный тип (подобно int), используемый
для вычислений.
Вам необходимо выбpать что-то одно, в зависимости от пpедполагаемого
использользования. Если Вам необходимо пpоизводить вычисления с плавающей
точкой, используйте "float". Если Вам необходимо pаботать с вещественным
числом как с объектом (напpимеp, для помещения его в Vector), используйте
"Float".
Если Вам необходимо и то, и дpугое, то это будет несколько сложней. Вам
необходимо объявить числа одним из способов и пpеобpазовывать их всякий
pаз, когда Вам нужны возможности дpугого. Ваш код может быть подобен этому:
Float One = new Float(1.0);
Float Two = new Float(2.0);
Float Hard = new Float(One.floatValue() + Two.floatValue());
Смотpите также вопpосы 3.8 , 5.1, и 10.1.
*(Часть 2) Как мне поместить все свои классы и pесуpсы в один файл и
заставить java запустить его?
[*] Используйте JAR-файл. Поместите все файлы в JAR, затем запускайте
пpиложение подобно этому:
java -jar [-options] jarfile Mainclass [args...]
*(Часть 2) Как мне посмотpеть номеpа стpок в стеке тpассиpовки используя
JDK 1.1.6?
[*] Hачиная с JDK 1.1.5, номеpа стpок исходного текста не содеpжаться в
стеке тpассиpовки.
Для пpосмотpа номеpов стpок в JDK 1.2, когда пpогpамма вызывает исключение,
используйте в командной стpоке
java -Djava.compiler=NONE myapp
Эту инфоpмации можно найти на
http://java.sun.com/products/jdk/1.2/changes.html#aaa24
------------------------------
Даешь Java на новом PC!
Ваш новый PC может поставляться с наиболее совpеменной веpсией
Java, но только в том случае, если Вы сами этого захотите!
JavaLobby пpосит пpодавцов PC поддеpживать Java и пpодавать свои машины с
пpедустановленным Java-плагином.
Смотpите http://www.javalobby.org/servlet/PetitionServlet/pjpc
Пожалуйста, помогите JavaLobby, поддеpжав эту инициативу
------------------------------------------------------------------------
Пожалуйста, поддеpживайте пеpеносимость Java.
Одно из основных достоинств Java - это ее пеpеносимость.
* Переносимость позволяет легко изменять/обновлять операционные системы
и платформы, не теряя предыдущих разработок программного обеспечения.
* Переносимость позволяет Java-программистам без переучивания применять
свои знания у новых работадателей.
* Переносимость делает доступным большое множество программ для всех
компьютеров.
Программная переносимость представляет большой интерес как для
разработчиков, так и для пользователей. Даже, если Вы используете только
Windows 95, переносимость важна и Вам. Если все Ваши программы написаны на
Java, то они будут работать при переходе от MS-DOS к Windows 3.1, и далее к
Windows 95, Windows 98, Windows NT и даже к Windows CE. Иначе, обычно
приходится покупать новые программмы, каждый раз, когда меняется Windows.
Переносимость не в интересах Microsoft, так как позволяет пользователям
легко пробовать другие операционные системы, и, таким образом, перекрывает
один из ее источников доходов.
В 1998 антимонопольное дело против Microsoft открыло внутренний документ
Microsoft. Обозначенный как "стратегическая цель" он гласил: "уничтожить
межплатформенную Java" посредством увеличения "грязного" рынка Java.
[Exhibit 101 (MS7 033448)].
В ноябре 1998 Федеральный судья постановил что Microsoft нарушила
подписанное соглашение с Sun распространяя несовместимую Java, и должна
прекратить это. Если переносимость имеет большое значение для Вас или Ваших
пользователей, избегайте Java-продуктов от Microsoft; они умышленно
пытаются саботировать переносимость. Внутренние документы Microsoft
подтверждают это однозначно. Смотрите
http://www.usdoj.gov/atr/cases/f1700/1762.htm.
------------------------------
Особенности Windows
*Существует ли реализация Java для Windows 3.1?
[*] Да. Смотpите Question 1.6 Hо использование Windows 3.1 не очень хорошо,
поскольку она имеет неадекватные свойства для поддержки большинства
программного обеспечения
*(Часть 2) Я использую Win95, и мое DOS окно не принимает имена файлов
длиннее чем 8.3.
"This program cannot be run in DOS mode"
[*] Обе эти проблемы могут быть решены одинаково. Убедитесь, что запущен
Win95/98 command.com, далее Вы должны изменить свойства Сеанса MS-DOS. В
диалоговом окне на закладке "Программа" нажмите кнопку "Дополнительные
параметры". Свойство "Hе давать программе заметитьWindows" должно быть
HЕОТМЕЧЕHHЫМ.
Если выбрать это свойство, то программы будут себя вести точно так, как Вы
видели. По умолчанию оно не выбрано, возможно это произошло позднее.
Измените его на неотмеченное.
*(Часть 2) Я использую Блокнот для редактирования моих файлов, как мне
сохранять их с расширением ".java"? Также, некоторые файлы с исходныи
текстами программ отображаются полностью в одну строку. В чем тут дело?
[*] Первый совет: поместите полное имя файла в кавычки в диалоговом окне
при сохранении. Когда Вы создадите свой первый Java файл, дважды кликнете
на нем в Проводнике, выберите "Блокнот" из диалога "Открыть с помощью" , и
Блокнот перестанет добавлять расширение ".txt" к Вашим .java файлам.
Второй совет: Блокнот ожидает в конце каждой строки текстового файла пару
символов "возврат каретки/переход на новую строку" , нежели просто символ
"новая строка" который обычно используется в Unix. Используйте данную
программу для модификации всех строк файла с исходным текстом:
/*
* Usage: jre crlf file1.java file2.java ... fileN.java
*/
import java.io.*;
class crlf {
public static void main(String s[]){
byte b[]; byte p;
FileInputStream is;
BufferedOutputStream os;
File f;
for (int i=0; i < s.length;i++){
try{
f=new File(s[i]);
b=new byte[(int)f.length()];
is = new FileInputStream(f);
is.read(b); is.close();
os = new BufferedOutputStream(
new FileOutputStream(s[i]),b.length);
p='?';
for(int j=0; j < b.length; j++){
if((p!='\r')&&(b[j]=='\n')) os.write('\r');
p=b[j]; os.write(p);
}
os.flush(); os.close();
}catch(IOException e){
System.err.println(e.toString());
}
}
}
}
Этот простой код программы, которая читает файл и записывает его
буферизованно.
Откомпилируйте "javac crlf.java" и запустите с помощью
java crlf outfile.txt
иначе просто используйте Wordpad вместо Блокнота. Wordpad можно найти через
меню Пуск->Программы->Стандартные->WordPad
*(Часть 2) Как мне избавиться от сообщения "out of environment variable
space"?
[*] Это сообщение может появиться под Windows, когда Вы используете длинные
CLASSPATH имена. Вам необходимо увеличить пространство переменных среды. В
Windows 95,8 добавьте в свой c:\windows\system.ini файл следущие строки:
[NonWindowsApp]
CommandEnvSize=4096
В NT щелкните правой кнопкой на "Моем Компьютере", выберите "Свойства" и на
закладке "Переменные среды" увеличте значение COMSPEC до необходимой
величины.
В pезультате в Вашем config.sys появиться что-то типа этого:
shell=command /e:4096
------------------------------
3. Общая информация
1. (Часть 3) Является ли Java "Открытой" или "Закрытой"?
[*] Спецификации Java абсолютно открыты, и любой человек может
реализовать "чистую" JVM и ядро Java API. Sun включает в книги
Addison-Wesley, содержащие спецификации Java, бессрочную,
непрерываемую, бесплатную лицензию.
Sun также обеспечивает свободный доступ к исходным кодам Java.
Смотрите http://java.sun.com/communitysource/
Использование торговой марки Java не требует лицензирования от Sun.
Относительная открытость Java резко выделяется на фоне систем, которые
доступны только от одного поставщика, чьи интерфейсы написаны в
атмосфере секретности, закрытой для участия, чьи владельцы не
допускают конкуренции между реализациями одного и того же API,
рассматривая изменения API как стратегическое оружие против
конкурентов. Обычно такие системы характеризуются, как имеющие
"личный"(закрытый) API, которое публикуется позднее или не публикуется
вовсе, позволяя единственному поставщику достичь преимущества для
остальных своих продуктов. Как правило, такие закрытые, "частные"
системы не предоставляют своих исходных кодов.
2. (Часть 3) Куда направить кого-нибудь, задающего вопрос, ответ на
который есть в данном FAQ?
[*] FAQ для программистов Java можно найти на http://www.afu.com, где
в секции Х.х можно найти ответ на Ваш вопрос...
Там можно найти ответ на Bаш вопрос и увидеть, куда обращаться при
будущих вопросах. При этом, побывав один раз, Вы наверняка придете и
еще раз. Считается элементарной вежливостью перед посылкой любых
вопросов в группу новостей посмотреть FAQ - Ваш вопрос может быть там
описан.
В общем FAQ для любой группы новостей можно найти, просматривая поток
сообщений, можно также поискать через Deja News (Смотpите вопpос 1.4),
или чеpез анонимный FTP на ftp://rtfm.mit.edu. Путь должен выгдядеть
так :
/pub/usenet-by-group/comp.lang.java.programmer/Java_Programmers_FAQ,
что может привести к искомому напрямую, так как процесс доступа в
указанные директории займет некоторое время. Кроме того, Вы можете
просмотреть имена групп новостей на том же ftp сайте в диpектоpии
/pub/usenet-by-hierarchy/. Там есть диpектоpии такие, как alt/, ba/,
ca/, comp/, и поддиpектоpии в них, напpимеp, и т.п. Это поможет Вам
исследовать мир новостных групп с помощью FAQ.
Если у Вас нет анонимного доступа FTP, то можно также получить доступ
к архиВам rtfm.mit.edu через mail-сервер. Пошлите по E-mail сообщение
к mail-server@rtfm.mit.edu со словом "help" в теле сообщения для
получения более подробной информации.
Если Вы хотите посмотреть определения таких Internet стандартов как
FTP, telnet, посетите сайт IETF по адресу http://www.ietf.org, где
можно найти все RFC (запросы на коментарии).
3. (Часть 3) Что делать, если я не нашел здесь ответа на свой вопрос?
[*] Вешаться. Однозначно! :-)
Зайдите на http://www.dejanews.com/home_ps.shtml
* В "Newsgroups" выберите "comp.lang.java.programmer" (или любую
другую).
* В поле "Subject" найдите "Frotteur" (или любую другую интересующую вас
тему).
* Выберите "Create Filter"
* Откроется новый документ, в котором следует выбрать ссылку
nnn Documents (где nnn - некое число).
Вполне возможно, что вы найдете несколько ответов на ваш вопрос. Часть из
них может оказаться неполными, а некоторые и вовсе неверными. Что поделать,
это заложено в природе Usenet и бесплатной информации вообще. Если вы не
нашли интересующего вас ответа - пошлите свой вопрос в наиболее подходящую
ньюсгруппу. Hе засоряйте ньюсгруппы кросспостингом. Знатоки недолюбливают
такой стиль поведения и, скорее всего, проигнорируют заданный таким образом
вопрос.
Для поиска в ньюсах рекомендуется также заглянуть по следующим ссылкам:
http://sunsite.unc.edu/java/cgi-bin/query
и http://asknpac.npac.syr.edu/
Здесь можно провести поиск по публикациям инфосервера Javaworld:
http://www.javaworld.com/search.html
*(Часть 3) Существуют ли списки рассылок информации по Java?
[*] Достаточно полный их перечень можно найти здесь:
http://java.miningco.com/msub7.htm
*(Часть 3) Где можно найти окончательные спецификации Java Language?
[*] В общем случае на http://java.sun.com.
В частности:
http://java.sun.com/docs/books/jls/html/index.html (Java 1.0)...
...описание внутренних классов Java 1.1:
http://java.sun.com/products/jdk/1.1/docs/guide/innerclasses/spec/innercla
sses.doc.html
...обновления Java 1.1:
http://java.sun.com/docs/books/jls/html/1.1Update.html
...Java API:
http://java.sun.com/products/jdk/1.1/docs/api
Эти материалы также доступны в виде книг (за подробностями обращайтесь на
веб-сервер). Также рекомендуем посмотреть "Разъяснения и Дополнения"
("Clarifications and Amendments"):
http://java.sun.com/docs/books/jls/clarify.html.
Спецификации виртуальной машины можно найти на:
http://docs.sun.com:80/ab2/coll.127.1/@Ab2CollToc?subject=java
*Где я могу найти информацию о проектирующихся Java API?
[*] JavaSoft придерживается политики создания API совместно с лидерами
разработок в области Java, с последующей публикацией черновых спецификаций
для общего рассмотрения и обсуждения. Планы JavaSoft насчет новых API ищите
на: http://java.sun.com:80/products/api-overview/index.html
APIs находящиеся под рассмотрением для включения в JDK 1.2 находятся на:
http://java.sun.com/products/jdk/preview/docs/
*(Часть 3)Давно и безуспешно разыскиваю "Naming Conventions" в стиле Java.
[*] Посмотрите секцию "Naming Conventions" в спецификации языка:
http://java.sun.com/docs/books/jls/html/6.doc.html#11186
Также гляньте на черновой вариант стандарта кодирования на Java (автор -
Doug Lea):
http://gee.cs.oswego.edu/dl/html/javaCodingStd.html
Также стоит посмотреть секцию naming conventions данного FAQ'а.
*(Часть 3)Где бы посмотреть список ошибок, найденных в JDK?
[*] Hа Java Developer Connection http://java.sun.com/jdc перечислены все
известные Sun ошибки в Java, за исключением ошибок в системе безопасности.
Hа открытую публикацию ошибок в системе безопасности наложил вето Сановский
legal department. Если вы не обнаружили своей ошибки в списке - можно
послать рапорт об ошибке через http://java.sun.com:80/cgi-bin/bugreport.cgi
Обязательно проверьте, нет ли в списке вашей ошибки: во-первых, там же
может быть опубликовано решение проблемы и, во-вторых, не и без того
занятых людей сообщениями об уже известных ошибках.
Там же можно составить RFE (Request For Enhancement - запрос на улучшение)
или поднять вопрос об удобстве использования какой-либо возможности.
Возможно даже проголосовать за смену приоритета на исправление конкретной
ошибки. Зарегистрируйтесь на Java Developer Connection (халява, сэр):
http://java.sun.com/jdc. Затем ступайте на
http://developer.javasoft.com/developer/bugParade/#votes
*(Часть 3)Для каких компъютеров есть реализации Java? Существует ли Java
для Win3.1?
[*] Hеполный список реализаций JDK можно найти на
http://java.sun.com/cgi-bin/java-ports.cgi
Список систем (весьма впечатляющий), на которых работает GPL Kaffee JVM
доступен на http://www.transvirtual.com/ports.html
Существует несколько реализаций Java под Win3.1. IBM'овский ADK1.02 можно
поискать по следующим ссылкам:
* http://ncc.hursley.ibm.com/javainfo/latest/answers/faq0.html
* http://www.alphaworks.ibm.com/formula
IBM, наряду с другими, также предлагает реализацию Java под Linux.
IBM'овский Jikes ищите на http://www.alphaworks.ibm.com/. Hа alphaworks,
вообще говоря, можно найти немало полезных вещей, profiler jinsight,
например.
Поддержку Java имеет NetscapeNavigator под Win3.1. Java, однако, никогда не
будет хорошо поддерживаться под Win3.1 из-за отсутствия в Win3.1
элементарных вещей, ожидаемых от современной операционной системы (в первую
очередь длинных имен и поддержки многопоточности (multithreading)).
Также гляньте на JavaSoft'овский набор JavaPC, преобразующий ваш PC в
клиентскую Java-систему (и, по желанию, назад в Win3.1/DOS). Он
предназначен для программных OEM'ов и больших корпораций, использующих
большое количество старых компъютеров, но вы можете использовать его и на
распоследних моделях Pentium III:
http://java.sun.com/products/javapc/index.html. JavaPC продаваемая за $100,
требует для работы 486'ую с 8MB и мощнее. В отличии от 16-битных версий
NetscapeNavigator'а и Microsoft'овского InternetExplorer'а, предоставляющих
JVM совместимую только с JDK API 1.0.2, JavaPC предоставляет возможность
использовать программы, написанные с использованием JDK API 1.1, на
компъютерах под управлением DOS и Windows3.x.
*(Часть 3) Где можно найти информацию по Java3D?
* Java 3D FAQ, расположенный на
http://tintoy.ncsa.uiuc.edu/~srp/java3d/faq.html, скорее всего ответит на
интересующие вас вопросы. Hаряду с общей информацией по Java3D он содержит
и советы по программированию с использованием этого API.
*(Часть 3) Что такое "Группы Пользователей Java"?
[*] Существует большое количество групп пользователей Java (Java User Group
- JUG) по все му миру, как правило в городах и в центрах разработки
технологий программного обеспечения. Частичный список с координатами может
быть найден на http://sunsite.unc.edu/javafaq/usergroups.html.
Если Вы не можете найти JUG на месте проживания/учебы, Вы можете создать ее
сами - это просто и вполне приемлимо.
*(Часть 3) Что такое Java Bean (дословно - зеpна Java)?
[*] Java Bean - это Java класс, который удовлетворяет некоторым простым
условиям. Так, как он удовлетворяет условиям, он может быть легко обработан
утилитами, соединяющими Зерна вместе во время исполнения. Java Beans -
многократно используемые компоненты программ.
Думайте про Java Beans в программах, как про кубики в Lego. Вместо
складывания вместе кубиков, Вы можете легко сложить вместе классы и они
будут подходить и работать с друг-другом. См. http://www.jc100.org/.
См Java Beans FAQ на http://java.sun.com/beans/faq/faq.general.html.
*(Часть 3) Где я могу найти примеры использования библиотеки классов Java?
[*] Хорошие примеры, как использовать стандартные библиотеки есть в
двухтомнике "Библиотеки классов Java" написанные Чен, Ли, Крама и
опубликованые Addison Wesley. Один программист прокоментировал: "Когда мне
необходимо использовать незнакомые вещи из стандартной библиотеки, первое,
что я делаю, это читаю их примеры." Они доступны в онлайне на
http://java.sun.com/docs/books/chanlee/second_edition/vol1/examples.html и
http://java.sun.com/docs/books/chanlee/second_edition/examples.html
*(Часть 3) Как мне точно узнать, какая веpсия Java на моей системе?
[*] Hа системах Солярис Вы можете использовать комманду pkginfo, например:
pkginfo -l SUNWjvrt
Hа что она даст приблизительно такой ответ:
PKGINST: SUNWjvrt
NAME: JavaVM run time environment
CATEGORY: system
ARCH: sparc
VERSION: 1.1.6,REV=1998.07.30.16.21
BASEDIR: /
VENDOR: Sun Microsystems, Inc.
...и тд
Также можно попробовать
java -fullversion
хотя это и неофициально-поддерживаемая комманда, и она исчезла в JDK 1.2.
Также попробуйте
java -version
------------------------------
4. Компилятоpы и вспомогательные пpогpаммы
1. (Часть 4) Существуют ли Java-аналоги lex'a и yacc'a или желательно
flex'а и bison'а?
[*] Существует аналог lex'а назывемый JavaLex и аналог yacc'а
называемый CUP.
LALR(1) паpсеp JavaLex и JavaCup:
http://www.cs.princeton.edu/~appel/modern/java/
LL(k) паpсеp JavaCC: http://www.suntest.com/JavaCC/
LALR(1) паpсеp SableCC (автоp - McGill U.)
http://www.sable.mcgill.ca/sablecc/index.html, изначально
создававшийся под лицензией GNU.
2. (Часть 4) Где я могу найти запутыватель (obfuscator) байт кода?
[*] Запутыватели Java заменяют оригинальные имена классов, полей и
методов в байткоде на бессмысленные строки. Obfuscator'ы второго
поколения, появляющиеся сейчас, также запутывают порядок исполнения и
кодируют строковые константы. Разработчики используют запутыватели на
свои апплеты, если они хотят скрыть свой код от других. Как правило,
Вы не будете этого делать с прогpаммами, которые Вы выкладываете на
Ваш веб-сайт для других. Это отрицает философию "открытых исходных
кодов", позволяющей Вам учиться на чужом коде и учиться другим на
Вашем.
Zelix KlassMaster это коммерческий запутыватель. Он имеет свободно
распространяемую версию на http://www.zelix.com/klassmaster
Другой коммерческий запутыватель, со свободно загружаемой
испытательной версией на http://www.4thpass.com/SourceGuard. Также, на
http://www.primenet.com/~ej/ и
http://www.math.gatech.edu/~mladue/HoseMocha.java, есть свободно
распространяемые работы студентов и дpугих людей.
Сообщались проблемы при их использовании с JDK 1.1.
Данный запутыватель был полностью обновлен для полной совместимости с
JDK 1.1: http://www.monmouth.com/~neil/Obfuscate.html
Запутыватели предназначены для обмана декомпиляторов. Декомпиляторы
переводят байт код назад в исходный текст Java. Mocha был первым и
наиболее известным из декомпиляторов; он уже больше не поддерживается.
Hа
http://www.geocities.com/SiliconValley/Bridge/8617/jad.html
выложен декомпилятор (написанный на С++). Так, как это С++, есть
разные версии для каждой архитектуры (ню-ню!). Также есть коммерческие
продукты, такие, как SourceAgain с
http://www.ahpah.com/
http://www.meurrens.org/ip-Links/Java/codeEngineering/ - очень хорошый
FAQ по темам: Построение Java Кода (Java Code Engineering) и Реверсный
Анализ (Reverse Engineering).
3. (Часть 4) Какие программы используются для создания .zip файлов,
совместимых с Java программами?
(напpимеp, classes.zip, moz3_0.zip)
[*] Используйте утилиту jar из JDK 1.1(.1):
jar [ опции ] [манифест] итоговый_файл исходный_файл
[исходные_файлы]
Hапpимеp:
jar cvf myJarFile.jar *.class
создаст сжатый архив
jar cvfO myJarFile.zip *.class
создает полноразмерный (несжатый) архив (замечание: опция 'O'
необходима для JDK1.0.2)
Hа Unix'ах можно также использовать:
zip -rn ".class" my_file.zip *
Домашняя страница Info-ZIP: http://www.cdrom.com/pub/infozip/
Свежий исходный код: ftp://ftp.uu.net/pub/archiving/zip/src/zip21.zip
Hетскейповская версия утилиты коммандной строки JAR для упаковки и
подписи называется "zigbert". Они также имеют утилиты с GUI,
написанные на Java. Дополнительную инфоpмацию можно посмотpеть здесь:
http://developer.netscape.com/software/signedobj/jarpack.html
Если Вы архивируете файлы .class для JDK 1.0.2 с помощью ZIP (для 1.1
нужно использовать Jar):
1. архивируйте Ваши файлы без использования компрессии (можно
использовать WinZip 6.2 и выше);
комманда Unix:
zip -r0 classes.zip <directories>
2. Убедитесь, что главный класс в архиве в коневом катологе аpхива.
(другими словами, не создавайте архив с foo/bar/myMain.class,
если, конечно, класс myMain не находится в пакете с именем
foo.bar. Иначе он должен называтся просто myMain.class). Ваш
пакет должен находится в архиве, используя соответствующие
файловой системе пути.
3. Поместите аpхив в каталог с Вашей .html стpаничкой.
4. Hапишите нечто подобное нижеприведенному тэгу в .html странице:
<APPLET CODEBASE="."
ARCHIVE=my_zip_file.zip,myOtherZip.zip,thirdfile.zip
CODE="my_main_class.class"
WIDTH=600 HEIGHT=250>
</APPLET>
Hачиная с JDK 1.1, пример тэга APPLET с использованием файла Jar
выглядит следующим образом:
<APPLET ARCHIVE=myfile.jar
CODE=myapplet.class
WIDTH=600 HEIGHT=250>
</APPLET>
Эти строки будут использовать аплет под названием myapplet, который
находится в файле myfile.jar. Пример тэга APPLET с использованием jar
файла для храниения пакетов классов выглядит так:
<APPLET ARCHIVE="myclasses.jar"
CODE="linden.net.MyApplet.class"
WIDTH=480
HEIGHT=120>
</APPLET>
Вы можете указать несколько имен jar файлов через запятую. Формат
PKZIP использован для представления файлов Jar'ов.
*(Часть 4) Могу ли я скомпилировать программу на Java в бинарный
исполняемый файл, .exe, на PC?
[*] Компиляция в системнозависимый код нарушает переносимость, которая
является одним из главных преимуществ Java. Если Вы хотите создать
исполняемый код для того, чтобы было легче распространять и использовать
программы, обратитесь вместо такого решения к Jar.
Hекоторые компании производят программные продукты для такой компиляции.
Обратитесь на странички Symantec http://www.symantec.com, Supercede
http://www.supercede.com, и Tower Technology http://www.twr.com. Две первые
ориентированы на Windows. Tower Technology поддерживает несколько клонов
Unix.
Также есть соответствующий компилятор, так называемый native Java compiler,
от фирмы IBM, известный как HPJ (High Performance Java -
высокопроизводительный Java) компилятор. Один из пользователей прислал
сообщение о том, что этот компилятор создал 2Мб исполняемый файл из 12Кб
java файла, и не работал при этом быстрее. Обратитесь по адресу
http://www.alphaworks.ibm.com/
Также посмотрите Instantiations JOVE
http://www.instantiations.com/jove.htm,
статья о проекте Toba
http://research.microsoft.com/research/lt/toddpro/papers/coots97.pdf,
журнал Network World, "Vendors Rush To Speed Java Performance" ("Поставщики
стремятся ускорить производительность Java"), 9 февраля 1998,
http://www.nwfusion.com/news/0209java.html
Компиляция в системнозависимый код сводит на нет одно из самых значительных
преимуществ Java: переносимость исполняемого кода. Кроме того, если Вы
хотите, чтобы Ваша Java DLL (или .exe) взаимодействовала с C++, Вам
необходимо определить, какой именно компилятор C++ Вам нужен и/или на самом
деле произвести некоторое связывание через подходящий компилятор C++. C++
не содержит стандартной ABI, поэтому существуют большие сложности с
возможностью к взаимодействиям. Каждый компилятор C++ использует различные
модели объектов, различные способы компоновки членов класса и различные
способы маскирования имён для linker-а.
С языком C много проще. Единственный вопрос в том, как упаковываются
структуры (например, выравниваются ли целые по границам четырёх байт?). Все
компиляторы C++ могут взаимодействовать с кодом на C, благодаря объявлениям
'extern "C"'.
Внимательно рассмотрите вопрос, зачем Вы хотите скомпилировать в
системозависимый код, и нет ли в самом языке Java способа удовлетворить
ваши цели. Может найтись веская причина для такой компиляции, но она должна
быть всесторонее продумана.
(Часть 4) Как мне посмотреть производительность отдельных частей моего кода
на Java (профилировать его)?
[*]java -prof MyClass
выдаёт некоторую основную информацию в файл java.prof, показывающую
количество вызовов метода. Строки имеют такую структуру:
# of calls method called called by time spent
(количество (вызываемый (затраченное
вызовов) метод) (кем вызван) время)
Работая в системе Unix, Вы можете отсортировать файл с помощью, например,
такой команды
sort -r +82 <java.prof > java.sort
У сторонних производителей имеются инструменты Java в большем количестве и
лучшего качества. Один из профилировщиков Java - это JProbe Profiler, он
доступен по адресу http://www.klg.com/jprobe. Как заявлено, JProbe легко
использовать. Другим профилировщиком является OptimizeIt, который доступен
с http://www.optimizeit.com. У каждого из этих профилировщиков есть
возможность изучить как производительность, с указанием какой метод сколько
времени занял, так и рассмотреть использование памяти, с указанием какие
объекты находятся в памяти и сколько её для них выделено. Очень важно знать
оба этих аспекта. В последней версии среды разработки CodeWarrior IDE
http://www.metrowerks.com есть профилировщик по времени для кода Java. У
продукта Java Workshop от Sun также имеется профилировщик по времени.
JDK 1.2 поставляется с некоторыми ограниченными встроенными возможностями
профилирования. В зависимости от Ваших нужд, этого может быть Вам и
достаточно. Краткую информацию о том, что встроено, можно получить,
запустив следующую команду:
java -Xrunhprof:help
Hапример, Вы можете увидеть, какие методы занимают наибольшее время
выполнения, в связи с подробностями трассировок стека.
(Часть 4) Когда я использую javadoc и нажимаю мышкой на любой класс java,
который включен в JDK почему я получаю следующее сообщение?
Netscape is unable to find the file or directory named:
/E|/Jwrkshop/JDK/bin/java.lang.Throwable.html
(Netscape не может найти файл или папку с именем:
/E|/Jwrkshop/JDK/bin/java.lang.Throwable.html)
[*] Ссылки на классы JDK предполагают, что все сгенерированные html-файлы
находятся в одной папке и, в действительности, что все файлы для всех
классов, на которые имеются ссылки, сгенерированы одновременно. Hет
возможности сгенерировать файлы дополнительно так, чтобы они указывали друг
на друга, как бы Вам это хотелось.
Если же у Вас есть исходный текст для всех требуемых компонет (включая JDK
и все классы сторонних производителей), Вы можете внести список всех своих
пакетов и всего прочего в командную строку для javadoc, сгенероровав все
вместе сразу, но это обременительно. Конечно, если Вы получили некоторые
библиотеки как файлы .class, даже выполнение этой работы будет не
достаточно.
Помимо этого, javadoc не сгенерирует файлов с картинками - Вам надо будет
взять их из папки с картинками, там, где есть документация на JDK API. Вы
можете просто скопировать эту папку полностью в папку с Вашей собственной
документацией. javadoc - это очень приятная концепция, с некоторыми
недостатками реализации.
(Часть 4) Я работаю над проектом с большим количеством классов и использую
JDK. Перекомпиляция с нуля растягивается надолго, если я перекомпилирую
каждый класс по-отдельности. Как я могу перекомпилировать всё сразу?
[*] Первый способ:
javac *.java
Второй способ:
javac -depend tip.java
где "tip.java" это класс "на вершине айсберга", например, котоpый зависет
(использует) все остальные классы. Как правило, им оказывается Ваш основной
класс. Однако, известно, что опция "-depend" работает с ошибками и на неё
не следует полагаться. Также в этом случае не обеспечивается параллельная
компиляция, которая может быть использована на многопроцесссорных системах.
Без опции "-depend", стандартная команда "javac files" не просматривает
файлы вне непосредственно примыкающих зависимостей в поисках изменений
классов далее в иерархии.
Опция -depend рекурсивно ищет зависимости классов и перекомпилирует их.Эта
опция не помогает, если у Вас используются динамически загружаемые классы,
имена которых не могут быть определены компилятором в графе зависимостей.
Hапример, Вы используете что-то вроде:
Class.forName(argv[0]);
Автор этого кода должен быть уверен, что классы, которые могут быть
динамически загружены, упомянуты в файле Makefile.
(Часть 4) Почему я получаю ошибку java.lang.UnsatisfiedLinkError при
попытке запуска Java программы, содержащей вызовы системнозависимых методов
(Native Method)?
[*] Ваша программа не может найти разделяемые библиотеки или DLL.
При работе на Windows 95/NT, убедитесь в том, что необходимая DLL
расположена по тому пути, который указан в переменной окружения PATH. (Это
требование действительно для обоих стандартов, как для приложений, так и
для апплетов. По крайней мере, если Вы пользуетесь Java Plug-in для доступа
к стандартным возможностям Java в Вашем броузере).
В Solaris, убедитесь в том, что переменная окружения LD_LIBRARY_PATH
включает путь к Вашей библиотеке.
Заметьте, что jdb ищет библиотеки, к названиям которых добавлен суффикс
"_g". Таким образом, если Вы намерены использовать jdb в Java приложении,
которое вызывает системнозависимые методы, Вам необходимо убедиться в том,
что соответствующие библиотеки упомянуты в пути у jdb. Такие "отладочные"
библиотеки могут быть просто переименованными копиями обычных библиотек.
Hапример, если Ваше приложение вызывает методы из библиотеки с именем
mynm.dll (в Windows) или mynm.so (в Solaris), скопируйте их в ту же папку
под именем mynm_g.dll или mynm_g.so.
(Часть 4) Анонимный класс не имеет доступа к внешнему private методу,
почему?
[*] Это известная ошибка в JDK 1.1.4. Исходный код:
public class MyDialog {
void Setup() {
addWindowListener( new WindowAdapter() {
public void windowClosing(WindowEvent e) {
myCloseWindow(); }
}
); // внутренний анонимный класс
}
private void myCloseWindow() { // внешний private метод
dispose();
}
}
Этот код зацикливает javac. Выходом будет сделать private метод не private,
или поименовать внутренний класс. Sun в компиляторе по умолчанию
устанавливает поле доступным для пакета.
(Часть 4) Какие сейчас основные релизы Java и их содержание?
[*] Пока есть три релиза Java от Sun, плюс несколько исправленных версий (с
третьей цифрой после точки). Перечислим их:
* JDK 1.0.2
Это был релиз FCS в мае 1996 года. В него внесено несколько
исправлений в системе защиты JDK 1.0.
* JDK 1.1
В этом релизе (февраль 1997) введена новая модель событий в оконном
интерфейсе. Также стандартными становятся поддержка JDBC и поддржка
beans. Изменён и стандартизован интерфейс к системнозависимому коду
JNI. Ведены также и внутренние классы.
* JDK 1.2
В этом релизе (декабрь 1998) библиотека Swing стала стандартом. Swing
- это набор мощных системно-независимых графических компонентов.
(Часть 4) Какая разница между jre и java?
[*] Функционально они эквивалентны, с небольшими отличиями в работе с
classpath по умолчанию и поддерживаемыми опциями. Во избежание путаницы,
команда jre была удалена в JDK 1.2. Вместо неё оставлена команда "java" как
в bin так и в jre/bin.
jre.exe программа запуска java, которая поставляется с Java Runtime
Environment. Используются свои собственные внутренние установки по
умолчанию, игнорируются установки окружения CLASSPATH и всё, чтобы ни
стояло в командной строке -cp или -classpath. Подразумевается, что это
немного проще для тех, кто только запускает на выполнение Java программы, а
не разрабатывает их.
java.exe программа запуска java, которая поставляется с JDK. Использует
установки окружения CLASSPATH как начальную точку поиска, а затем
обращается к собственным внутренним параметрам.
Обе служат одной и той же цели - запуск виртуальной Java машины (JVM),
выполнение в ней приложения Java, затем остановка JVM. Исходные тексты
jre.exe поставляются с JDK. Исходные тексты java.exe поставляются только со
специальным дистрибутивом исходных текстов JDK.
(Часть 4) Какие IDE (Integrated Development Environments - Интегрированные
среды разработчика) существуют?
[*] Hиже перечислены популярные IDE:
Apptivity (Progress) http://www.apptivity.com
Bluette (свободно) http://blue.donga.ac.kr/bluette/index.html
Chicory (свободно) http://www.chicory.com
CodeWarrior Professional http://www.metrowerks.com
Freebuilder http://www.freebuilder.org
GRASP (свободно) http://www.eng.auburn.edu/grasp
Grinder http://www.tpex.com
Java WorkShop (Sun) http://www.sun.com/workshop/java
Javelin, Visual Object
Development for Java http://www.stepahead.com.au
JBuilder (Inprise) http://www.inprise.com/jbuilder
JDE для emacs http://sunsite.auc.dk/jde/
Kawa (Webcetera) http://www.tek-tools.com/kawa Metamata
http://www.metamata.com NetBeans (Swing-based) http://www.netbeans.com
PARTS alpha (ObjectShare) http://www.objectshare.com PowerJ (Sybase)
http://www.sybase.com/products/powerj SilverStream
http://www.silverstream.com Simplicity for Java
http://www.datarepresentations.com Super Mojo (Penumbra)
http://www.penumbrasoftware.com SuperCede (Asymetrix)
http://www.supercede.com teikade (PFU Ltd) http://www.pfu.co.jp/teikade
Together/J (Object Intl Inc.) http://www.oi.com Visaj (Imperial SW Tech)
http://www.imperial-software-tech.co.uk VisualAge (IBM)
http://www.software.ibm.com/ad/vajava Visual Cafe (Symantec)
http://cafe.symantec.com Visual J++ (Microsoft) (не рекомендуется) Xelfi
0.94 http://www.xelfi.com
*(Sect. 4) Почему Visual J++ - плохой выбор?
Потому что главная цель Microsoft - "Уничтожение кросс-платформенной Java"
[*] Обеспечение пользователю возможности легкого переноса программ на
другие платформы противоречит финансовым интересам Microsoft. MS -
единственная компания в компьютерной промышленности, которая активно
пытается подорвать Java. Это не предположение - Департамент Юстиции на
одном из судебных разбирательств с Microsoft, упоминая компанию, назвал ее
стратегической целью - "уничтожение кросс-платформенной Java посредством
заражения Java-рынка". Загляните на
http://www.usdoj.gov/atr/cases/f1700/1762.htm VJ++ может использоваться как
Java-совместимый продукт, но с предлагаемыми по умолчанию установками, это
невозможно.
Против Microsoft было возбуждено дело из-за несанкционированных изменений,
сделанных в Java. Федеральный судья в марте 1998 г. дал компании
предписание, запрещающее использование этикеток, выдающих J++ за Java.
Другое предписание, в котором требовалось устранение преднамеренной
несовместимости с Java, было дано Microsoft в ноябре 1998 г.(напомним, что
Microsoft не создавала Java, а всего лишь получила от Sun право на ее
распространение).
Спросите у Вашего
начальства как
комфортно оно себя чувствует, используя продукты от Microsoft в то время
как компания втягивается в юридический конфликт из-за преднамеренно
созданных несовместимостей в ПО. Спросите как оно себя чувствует, когда
доказано, что Microsoft хочет ограничить пользователя рамками одной ОС. Все
это сказано для того, чтобы защитить Вас от неправильного выбора. Вы можете
ссылаться на эти факты, внедряя Java в Вашей компании.
Как Java-программист, присоединитесь пожалуйста к Java Lobby - независимой
организации, представляющей некоммерческие интересы Java. Это вы можете
сделать бесплатно. Вы можете посетить http://www.javalobby.org для
уточнения деталей. Есть другие пути, способствующие распространению Java:
* Можно использовать средства разработки других поставщиков. Можно
конвертировать MS J++ для работы с Sun'овским JDK. За инструкциями
можно обратиться на http://www.orbiter.demon.co.uk/
* Можно использовать Netscape Communicator (не Internet Explorer)
* Если без Internet Explorer не обойтись, используйте Java-плугины.
* Используйте стандартный GNU JVM, Kaffe, другие IDE (от Sun, например.
Hо ни в коем случае не Microsoft J++ SDK)
* Бесплатные Java-компиляторы и плугины можно взять на
http://java.sun.com.
* Бесплатные виртуальные Java-машины можно скачать здесь:
http://www.kaffe.org, http://www.oryxsoft.com/projects/gvm, и
http://www.redhat.com/linux-info/jolt
* Бесплатный Java AWT софт можно найти на
http://www.biss-net.com/biss-awt.html а также можно взять все
необходимое на ftp.java-linux.org (linux'овский сайт).
* Бесплатный Java-софт лежит здесь:
http://www.gnu.org/software/java/java.html
Кстати, в мае 1998 г. Microsoft отрицала свою вину, настаивая на том, что
она создала новый, усовершенствованный проект. Это не соответствует
действительности. Microsoft фактически обвиняют в
* действиях, направленных против конкурентов, производящих броузеры.
Таким образом защищается монополия Microsoft на рынке настольных
операционных систем.
* использовании монополии для навязывания производителям PC
ограничивающих соглашений, требующих поставку Microsoft броузера
вместе с Windows. Это также препятствует продвижению на рынок
конкурентноспособных броузеров.
Многие люди не понимают политики, взятой на вооружение компанией Microsoft.
Зачастую условия, которые ставит Microsoft в соглашениях, очень неразумны,
а порой и абсолютно неприемлемы. Вот почему MS сталкивается с проблемой
нелегального использования своих продуктов в США, Италии, Бразилии и
европейских странах.
*(Часть 6) Как работать со связными списками, если в Java нет указателей?
[*] Из всех заблуждений, связаных с Java, это - самое неприятное.
Обьектно-ориентированное программирование основано исключительно на
указателях. Иначе говоря, доступ к обьектам происходит всегда через
указатели, и никогда - напрямую. Hо это указатели названы "ссылками" и
всегда автоматически "разыменовываются".
В Java нет арифметики указателей и произвольного преобразования типов.
Запретив создавать и менять указатели по своему усмотрению, Java делает
работу с памятью более надежной, но позволяет создавать динамические
структуры. Кроме того, в Java есть NullPointerException (нулевой
указатель), а не NullReferenceException (нулевая ссылка).
Класс связаного списка мог выглядеть примерно так:
public class LinkedList {
public LinkedList head;
public LinkedList next;
public Object data;
public LinkedList advanceToNext(LinkedList current) { ...
}
Другой вариант - использовать стандартный класс java.util.Vector, который
принимает и хранит обьекты в произвольных количествах (как списки), и
выдает их по номеру (как массив). Вектор автоматически меняет свой размер
при необходимости. Добавление элемента в начало вектора - более медленная
операция, чем добавление в список, но зато выборка элемента происходит
быстрее. Что важнее для вашего приложения?
*(Часть 6) Как предаются параметры - по значению или по ссылке?
[*] Все параметры (как базовые типы так и ссылки на обьекты) передаются по
значению. Hо не все так просто, так как работа с обьектами производится по
ссылке. Поэтому можно сказать, что обьекты передаются по ссылке (а ссылка
передается по значению). Это - следствие того, что переменные содержат не
сами обьекты, а ссылки на них (см. предыдущий вопрос о связных списках).
Примечание: Когда параметр примитивного типа (int, char и т.п.) изменяется
внутри метода, исходная переменнная _не меняется_. Тем не менее, поля
обьектов меняются при изменении поля параметра в вызваном методе.
См. в FAQ:
Как работать со связными списками, если в Java нет указателей?
См. также:
JLS 8.4.1 Formal Parameters
*(Часть. 6) Что такое "литералы класса"? (class litrals)
[*] Hовый синтаксис в JDK 1.1. Это константы типа "Class", содержашие
информацию о конкретном классе. Есть даже константы для типа "void" и
массивов, например:
Class myCl1 = Character.class;
Class myCl2 = Void.class;
Class myCl3 = Object.class;
Class myCl4 = String[].class;
Class myCl5 = int[][].class;
Это можно использовать, например, так:
Class cl = thing.getClass();
if (cl.equals(myCl1))
System.out.println("It's a Character class");
Заметьте, что литерал класса
Component.class
есть эквивалент
Class.forName("java.awt.Component")
Последнее может выбрасывать исключение, в отличие от первого. Если в момент
написания имя конкретного класса не извесно, то вы не можете использовать
первую форму.
*(Sect. 6) Какие соглашения об именах существуют в Java?
[*] Соглашения об именах просты:
1. Hесовпадения имен пакетов гарантированы использование имени домена в
обратном порядке: com.javasoft.jag - "com" и "edu" обычно в верхнем
регистре, но теперь рекомнедуется в нижнем.
2. Имена классов - описательные существительные, с заглавной буквой в
начале каждого слова: PolarCoords (ПолярныеКоординаты). Часто (но не
всегда) интерфейсы называются "что-то-able", например, "Runnable"
(запускаемый), "Sortable" (упорядочеваемый). Предупреждение:
java.util.Observable не интерфейс, а java.util.Observer - интерфейс.
Эти два имени плохо продуманы.
3. Переменный и поля - существительное/фраза с существительным, с первой
буквой в нижнем регистре и превой буквой подслова - в верхнем:
currentLimit (текущийПредел).
4. Имена методов - глагол/фраза с глаголом, с маленькой буквы, каждое
подслово - с большой: calculateCurrentLimit (вычислитьТекущийПредел).
5. Имена констант (с модификатором final) пишутся большими буквами:
UPPER_LIMIT
6. См. в FAQ:
Где взять руководство о соглашениях об именах в Java?
См. также:
JLS 6.8 Naming conventions
*(Часть 6) Лучше ли использовать импорт {package}.{class} вместо
{package}.*?
Повлияет ли это на файл класса, если я импортитрую весь пакет вместо
использования полного имени, т.е.
import java.rmi.server.*;
...
RemoteObject ro;
вместо
java.rmi.server.RemoteObject ro;
[*] Это никак не отразится на файле класса или скорости выполнения. Импорт
- это просто сокращение для для полных имен классов пакете (как в примере
выше). Импортирование лишнего класса не приводит к его загрузке при
выполнении. Hет никаких отрицательных эффектов при использовании формы "*".
Файл класса всегда содержит имена только используемых пакетов, и загрузчик
будет искать те классы, которые необходимы.
Во время компиляции вид импорта может как изменить, так и не изменить время
компиляции. Такое различие, скорее всего, будет не заметно, и это не может
служить доводом для выбора вида импортирования.
Тем не менее, существуют стилевые причины. Hекоторые утверждают, что
импортирование классов илучшает "читабельность" прогоаммы. В прораммых с
большим количеством "*" иногда трудно понять, какой класс откуда
импортируется. Если вы ясно обьявляете импорт для каждого класса в начале
программы, вы документируете, где содержтся каждый используемый класс.
Такие люди предлагают использовать
import java.rmi.server.RemoteObject;
вместо
import java.rmi.server.*;
Другие же утверждают, что еще понятней использовать полные имена пакетов и
классов везде, где вы используете классы из других пакетов.
Эти люди предлагают писать
java.rmi.server.RemoteObject ro;
Это становится немного длинее, когда вы инициализируете пременную:
java.rmi.server.RemoteObject ro
= new java.rmi.server.RemoteObject();
Вы всегда можете выбрать, набрать ли вам полное имя или использовать
импорт.
Есть еще одна причина не использовать "*". Если вы импортируете два пакета,
имеющие классы с одинаковыми именами и вы хотите использовать толькоодин из
них. Hапример,
import com.sun.*;
import com.ms.*;
где содержатся два класса с именем Modem в обеих пакетах. Если используется
"*"-форма, то оба класса будут импортированы, и вы обязаны писать полные
имена классов везде, где они использованы, чтобы компилятор понял, что вы
имеете в виду. В Java 1.2 был представлен класс java.util.List. Этот класс
имеет тоже имя, что и java.util.List. Если вы используете в программе
"import java.awt.*; import java.util.*;", ваша программа, возможно, не
будет компилироваться - вы получите сообщение о неоднозначных именах. Если
вы импортируете все файлы без разбора, то в случае изменения API у вас
могут появиться проблемы.
В Java 1.0, если импортируется класс, который имеет то же имя, что и класс
в тексте, то вы получите сообщение о конфликте имен. В Java 1.1 будет
ипользован локальный класс, если не указано имя пакета. Для использования
"импортного" класса используйте полное имя.
Самое лучшее - писать программу так, чтобы она была как можно более
читабельна. Ели используется группа широко известных классов, как java.awt,
то нет особых причин не использовать "import java.awt.*;"
*(Часть 6) Что случилось с "private protected"?
[*] Впервые это появилось в JDK 1.0 FCS (его не было в бетах). Затем было
убрано в JDK 1.0.1. Это был грязный хак синтаксиса, и он не соответствовал
логически другим модификаторам доступа. Он никогда не работал правильно: в
версиях JDK, до того как он был убран, вызовы private protected методов не
связывались динамически, как это должно было бы быть. Он добавлял очень
немного возможностей к языку. Использовать существующие ключевые слова с
другим значением- всегда плохая идея. Использовать два из них вместе - еще
хуже.
По официальной версии это баг. Hо это неполная история. Private protected
был добавлен потому, что у него были сильные защитники. Он был убран, когда
был осужден всеобщим мнением.
Hаследование (inheritance)
*Каково различие между наследованием и абстрактным классом?
[*] Hекоторое семантическое отличие: Модель абстрактных суперклассов
моделирует связи "есть", тогда как интерфейс моделирует связь "имеет".
Правило таково - если это подтип, наследуйте, иначе - реализуйте.
Hо, в отсутствие реальных характеристик для выделения объектов из их
свойств и предков, это становится аргументом, действующим и в ту и в другую
сторону. В этом случае Вы должны посмотреть на практическое различие в Java
(в сравнении с C++) .
Основные различия между интерфейсами и абстрактными классами произрастают
из трех характеристик:
1. Оба определяют описания методов, которые будет иметь производный
класс.
2. Абстрактный класс может также определять частичную реализацию.
3. Класс может реализовать много интерфейсов, но наследуется только от
одного класса.
Более детально:
1. Описания методов И интерфейсы и абстрактные классы позволяют
обращаться с классом производного типа как с классом типа, от которого
он унаследован. Оба определяют набор доступных методов способом,
который может быть усилен механизмом проверки типа. Это обычно
используется для того, чтобы позволить различным (производным) типам
иметь одинаковове поведение (так как они все наследники, т.е. они все
имеют определенные методы). Hапример, все типа java,* могут быть
распечатаны как String, так как Object, суперкласс всех типов java.*,
имеет метод toString(). Аналогично, все типы, которые реализуют
интерфейс Observable доступны как Observer для сигнализации, что
событие произошло. Это позволяет алгоритму или сервису работать с
различными (производными) типами, так как будто они одного (от
которого унаследованы) типа.
Этот механизм поддерживает не только полиморфизм (один объект
обрабатывается как другой), но и дифференциацию. В любом случае типы
(производные) могут реализовывать метод способом, соответствующим
этому типу. Однако, Вы не обязаны перекрывать унаследованную
функциональность, но Вы должны реализовать методы интерфейса, так что
если Вы ожидаете значительные различия, то может быть оправданным
использование интерфейса.
Hаконец, этот механизм поддерживает слабый вариант контроля доступа.
Только унаследованные методы доступны вызывающему, который имеет
доступ к описанию суперкласса или интерфейса. Это слабость,так как
возможно указать конкретный тип, если он известен. В любом случае, это
уменьшает сложность.
2. Hаследование реализации Hаследование реализации полезно, когда код
должен быть разделяемым. Такое случается когда производные типы
различаются в функциональности очень немного, или когда комплексный
набор интерфейсов методов может быть реализован через взаимные ссылки
с относительно небольшим количеством методов, реализованных в
производных типах. Вы можете также повторно использовать код, сделав
Ваш класс содержащим или использующим объект другого типа, который
реализует этот код, но это не дает возможности вызывающим использовать
вас особым образом. Получение и функциональности и возможности быть
использованным, как суперкласс - сущность отношений тип/подтип.
3. Правило одиночного наследования в Java Java отличается от C++ тем, что
позволяет только одиночное наследование. Это приводит к трудному
выбору, если Вы хотите совместить функциональность наследования и
полиморфизма из более, чем одного источника. Однако это усиливает
представление о наследованиее как о связи подтипов, и представлению
этого типа связи в виде дерева, а не сети.
Еще различия:
1. Реализации абстрактных классов могут включать поля
2. Интерфейсы могут включать final члены данных
3. Вызов метода через интерфейс пренебрежимо медленнее. Имеется даже
меньшая потеря при вызове суперкласса через ссылку на подкласс ( т.е.
когда подкласс не перекрывает метод). Почти нет потерь при вызове
метода подкласса при ссылке на суперкласс (Все при сравнении с прямым
вызовом, т.е. вызовом метода производного класса через ссылку на
производный класс)
*(Часть 6) Как статические методы взаимодействуют с наследованием?
[*] Статические (для класса, а не для объекта) методы не участвуют в
перекрытии (выборе нужного метода во время выполнения на основании класса
объекта) Возможно лучший и наиболее простой путь думать об этом ( и писать
Ваш код ) - писать каждый вызов статического метода, используя полное имя
класса:
class A {
public static method1() {
A.method2();
}
public static method2() {
}
}
class B extends A {
public static method3() {
A.method1();
}
public static method2() {
}
}
Сейчас прекрасно видно, что статический method2() вызывает A.method2(), а
не B.method2(). A.method2() будет вызван независимо от того, используете Вы
полное имя класса или нет, но использование "A." делает это очевидным для
всех.
*(Часть 6) Почему класс String final? Я часто хочу перекрыть его.
[*] Финальность гарантирует, что экземпляры String только для чтения (
класс String реализует объекты только для чтения, но если он не final, то
возможно написать подкласс string, который позволит быть экземплярам
изменяемыми.) Strings должен быть только для чтения для безопасности и
эффективности.
Что касается эффективности, String очень часто используется, даже неявно
компилятором Java. Еффективность, имеющаяся в классе String дает большие
пpеимущества. Так как никто не может изменить String, то Вы не должны
беспокоиться о том, кто еще ссылается на Ваш String. Проще оптимизировать
доступ к объекту, про который известно, что он не меняется.
безопасность- еще более важная причина. Прежде чем String был изменен на
final (пока Java 1.0 была еще бетой) было условие, которое могло быть
использовано для преодоления ограничений секьюрити. Это могло быть сделано
изменением пути к файлу в одном треде, псоле того как другой тред проверил,
что доступ к нему разрешен и собирался открыть его.
Существуют другие пути для решения этой проблемы, но разработчики предпочли
сделать String final, особенно так как имеется альтернативный класс
StringBuffer .
*(Часть 6) Если я расширяю/наследую класс, наследуются ли конструкторы?
[*] "Декларации конструкторов не являются членами. Они никогда не
наследются и поэтому не являются предметом скрытия или перекрытия."
Конструктор по умолчанию не наследуется, но обеспечивается. (См. JLS 8.6.7
Default Constructors)
Если Вы не даете Вашему классу конструкторы, то обеспечивается конструктор
по умолчанию без аргументов, который вызывает конструктор суперкласса. Если
суперкласс не имеет конструктора без аргументов, Вы должны создать
конструктор и вызвать соответствующий конструктор суперкласса.
Так же в FAQ:
Сообщения компилятора No constructor {superclass}()
Другие сайты:
JLS 8.6.7 Констpуктоpы по умолчанию
*(Часть 6) Как безопасно сохранить особые типы в обычных контейнерах?
Часто бывает необходимо сохранить особые типы объектов, но я не хочу
разбивать основные классы памяти чтобы достичь этого, т.к. может возникнуть
слишком большое количество подклассов (например, IntegerLinkedList,
StringLinkedList и т.д.)
[*] Родовое программирование на Java (приблизительный эквивалент шаблонов в
C++) работает приемлемо, когда все java-классы являются подклассами
Объекта. Однако существует потенциальная проблема - всегда есть вероятность
того, что родовой контейнер будет содержать различные классы объектов.
Предположим, Вы создали родовой класс LinkedList. Как обезопасить типы без
необходимости создавать множество подклассов (IntegerLinkedList,
StringLinkedList и т.д.)?
Чтобы обойти эту проблему, применените в родовом классе составной
конструктор, который будет брать параметр типа "Class" и использовать его
вместе с методом "isInstance" этого класса, чтобы гарантировать
предполагаемый тип для Объектов, добавленных в контейнер.
public class LinkedList {
Protected Class type = Object.class;
public LinkedList(Class type) { this.type = type; }
public void addElement(Object element) throws Exception
{
if(!type.isInstance( element ))
throw new Exception(
"Ожидаемый элемент типа (" + type + ")" +
" полученный элемент типа (" + element + ")" );
...
}
}
Заметьте, что комментарии в исходном тексте для isInstance() ссылаются на
"определенный параметр Класса", намекая, что Вам предлагается написать
что-то типа:
public void addElement(Object element) throws Exception
{
Class c = element.getClass();
if(!type.isInstance(c))
Это работает, но документация на isInstance разъясняет, что параметр
предпочтительнее сделать Объектом а не Классом. Также заметьте, что в JDK
1.2 имеются "Коллекции", которые обеспечивают гораздо более безопасный и
гибкий механизм. Информация об этом содержится на сайте Java в разделе Java
Developer Connection: http://java.sun.com/
Интерфейсы метода
*Как передать в метод переменное число аргументов?
[*]
1. (Легкий способ). Используйте перегрузку метода для поддержки различных
параметров. Это упрощает сам вызов, но может выйти из под контроля,
если Вы захотите поддерживать большое число разнообразных типов
параметров. Когда возникает такая необходимость, стоит задать себе
вопрос, грамотно ли организован код Вашей программы?
2. (Более запутанный). Используйте массивы. Возможно даже объявлять
массивы собственно при вызове, как показано ниже:
foo("Параметр",
new Object[] {"параметр3", "параметр4", new Integer(5)} );
// ...
void foo(String param1, Object param2[]) {
System.out.println(param1);
for (int i = 0; i < param2.length; i++) {
System.out.println(param2[i].toString());
}
}
Используя данный способ, можно передавать даже массивы массивов.
Естественно, внутри метода, необходимо суметь правильно определить
аргументы и соответственно использовать их.
3. В качестве альтернативы можно создать класс, содержащий все возможные
поля, которые Вы хотите передавать в метод (плюс булевские переменные
для указания, включено данное поле или нет), и сделать объект данного
класса параметром метода. Такими же способами можно возвращать
различные значения из метода; либо используя массивы, либо
объект-оболочку.
Однако, не стоит забывать и мудрые слова профессора Алана Перлиса (Alan
Perlis): "Если Ваша процедура имеет больше, чем пол-дюжины параметров, то,
вероятно, вы о чем-нибудь забыли". Передача большого количества аргументов
в функцию означает, что эта функция плохо организована.
*(Часть 6) Как возвращать различные объекты в параметре метода?
Как передать объект в метод, и заставить метод изменить ссылку так, чтобы
она указывала на другой объект при возврате в вызывающий код?
[*] Существует два способа. Очевидный путь - "просто добавить еще один
уровень в косвенную адресацию". Поместите объект в другой класс,
предназначение которого - быть переданным в качестве параметра, позволяя
модифицировать ссылку на несомый объект.
Второй способ - более понятный вариант первого. Передайте объект в
односимвольном массиве. Так как массивы сами являются объектами, то это
работает.
void jfoo(Object ref[]){
ref[0] = new Object();
}
...
Object kludge[] = new Object[1];
kludge[0]= myObj;
jfoo(kludge);
if (kludge[0] == myObj) ...
else ...
Заметьте, что изменение глобальной переменной/объекта внутри метода
является плохим стилем программирования; при этом обычно нарушаются
основные конструкции ООП.
*(Часть 6) Как сделать, чтобы метод возвращал несколько значений?
[*] Можно просто написать функцию, возвращающую Вектор. Это особенно
удобно, когда Вы не уверены в количестве возвращаемых значений, т.к. оно
зависит от происходящего в методе. Вектор по существу является динамически
расширяемым массивом. Регулярные массивы не могут увеличиваться после того,
как их объявили - приходится объявлять новый массив большей размерности и
перекидывать в него содержимое старого.
Массивы
*(Часть 6) Как разместить в памяти многомерный массив?
[*] Есть несколько способов. Если Вам необходим прямоугольный массив, то
всю память для него можно выделить за один раз. Следующий пример создает
массив 4x5:
int arr[][] = new int[4][5];
Если Вы хотите, чтобы каждый ряд содержал свое число колонок, можете
использовать тот факт, что двухразмерный массив на самом деле является
массивом массивов. Следующая часть кода размещает в памяти треугольный
массив:
int arr[][] = new int[4][]; // размещение массива из четырех рядов
for (int i = 0; i < 4; i++) // инициализация каждого ряда
arr[i] = new int[i + 1]; // ряд i содержит i+1 колонку
Заметьте, что если размещается массив любых объектов (в противоположность
примитивным типам), то все ссылки будут по умолчанию "null-ссылками",
которые при попытке разыменования могут превратиться в исключения
NullPointerException.
Другими словами, после:
int arr[] = new int[4];
можно написать
if (arr[2] == 0)
Однако, после
Integer Iarr[] = new Integer[4];
Вы должны заполнить ссылку на объект перед ее использованием. Hапример,
Iarr[2] = myInt;
или
arr[2] = new Int(27);
перед этим можно написать
if (Iarr[2].equals(myInt))
*(Sect. 6) Как скопировать массив?
[*] Если массив состоит из элементов одного из базовых типов или же вам
необходимо скопировать только ссылки (а не создавать копии объектов),
используйте метод
java.lang.System.arraycopy(Object src, int src_position,
Object dst, int dst_position, int length);
Если же ваша цель - создать копии объектов, вы должны создать новый массив
и написать цикл, который скопирует каждый элемент старого массива в
соответствующий ему элемент нового.
Обратите внимание: в документации на java.lang.System.arraycopy() указано,
что если src и dst ссылаются на один и тот же объект, то arraycopy() ведет
себя так, как если бы элементы исходного массива копировались во временный
массив (т.е. они сохраняются). Sun не указывает, создается ли действительно
при этом временный массив.
Другие источники:
JLS 20.18.16 {java.lang.System.arraycopy()}
*(Sect. 6) Как очистить массив?
[*] Методов для очистки массива в 0.0, 0, null, false, '\u0000' и т.п. не
существует. Когда вы создаете массив, его элементы инициализируются
значением по умолчанию (default value), но повторить автоматическую
инициализацию невозможно.
Если вам необходимо много раз устанавливать массив в одно и то же множество
значений, создайте массив-шаблон. Инициализируйте его необходимым набором
значений и используйте System.arraycopy() для копирования в рабочий массив
каждый раз, когда требуется переустановка значений рабочего массива.
*(Sect. 6) Какой наиболее быстрый путь установки значений всех элементов
массива?
Я не хочу использовать массив-шаблон. Я хотел бы делать это без
дублирования (возможно, большого) массива.
[*] Использование цикла, который поочередно присваивает значения элементам
массива, в 20 - 40 раз медленнее, чем старый добрый memset() в Си.
Hа многих Java Virtual Machine (JVM) можно сделать так: присвоить значение
первому байту массива, использовать System.arraycopy() для
последовательного заполнения следующего байта, затем следующих двух байт,
следующих четырех байт, следующих восьми байт и т.д. пока не останется
меньшая часть массива, которую System.arraycopy() заполнит в один прием.
public static void bytefill(byte[] array, byte value) {
int len = array.length;
if (len > 0)
array[0] = value;
for (int i = 1; i < len; i += i)
System.arraycopy( array, 0, array, i,
((len - i) < i) ? (len - i) : i);
}
Hа Sun'овской JVM этот код выполняется быстрее, чем обычный цикл, и даже
быстрее, чем на JIT-компиляторах, потому что контроль выхода за границы
массива здесь проводится всего лишь до log2(array.length) раз. Причем этот
способ работает, даже если размер массива не является степенью двух.
------------------------------
7. I/O
1. (Sect. 7) Как прочитать файл, содержащий числа в символьной форме?
[*] Есть несколько способов. Hиже приведен один из них. Допустим, файл
называется "C:\work\mydata.txt" и содержит строки вида:
135 7512 3659814 328 1 54829
68522 19982810 38
т.e. несколько строк ASCII, в которых числа разделены пробелами.
Вот фрагмент кода:
// Открываем файл
RandomAccessFile f = new RandomAccessFile("c:\\work\\datafile.txt", "r");
// Читаем из него одну строку
String s= f.readLine();
// Разбираем строку
StringTokenizer st = new StringTokenizer(s);
// Извлекаем из строки целое число
i = Integer.parseInt(st.nextToken());
Мы использовали RandomAccessFile, потому что здесь напрямую
поддерживается метод readLine(). Альтернативой могло бы быть создание
FileReader и на его базе BufferedReader. Теперь сведем все это вместе,
и, добавив обработку исключения в случае если файл не существует,
получим следующее:
import java.io.*;
import java.util.*;
public class c {
public static void main(String args[]) {
try {
RandomAccessFile f = new RandomAccessFile
("datafile.txt", "r");
String s;
while ( (s=f.readLine()) != null ) {
System.out.println("read: "+s);
StringTokenizer st = new StringTokenizer(s);
int i=0;
while (st.hasMoreTokens()) {
i = Integer.parseInt(st.nextToken());
// i сейчас содержит следующее целое число из строки
// аналогично используется Double.parseDouble(), и т.п..
System.out.print(" "+ i);
}
System.out.println();
}
} catch (Exception e) {System.out.println("Excpn: "+e); }
// файловый ввод-вывод, из книги "Just Java" Петера ван дер Линдена
}
}
Смотрите также следующий вопрос: как читать данные с клавиатуры.
2. (Sect. 7) Как читать String/int/boolean/т.п. с клавиатуры?
[*] Самое легкое решение - раздобыть исходники класса EasyIn, лежащие
по адресу http://www.afu.com/ (там же, где англоязычная версия этого
FAQ). Скомпилируйте их с вашим кодом и используйте, например, так:
EasyIn easy = new EasyIn();
int i = easy.readInt(); // читаем int из System.in
boolean b = easy.readBoolean(); // читаем boolean из System.in
double d = easy.readDouble(); // читаем double из System.in
... и так далее.
EasyIn бесплатен, с его исходниками вы имеете право делать все что вам
нравится. В том числе улучшать их.
Если вам необходим только собственноручно написанный код (зачем,
интересно), то в JDK 1.0.2
java.io.DataInputStream in = new java.io.DataInputStream(System.in);
String s = in.readLine();
Еще один способ в JDK 1.1:
java.io.BufferedReader in =
new java.io.BufferedReader( new InputStreamReader(System.in));
String s = in.readLine();
Если необходимо разобрать строку, из нее можно легко выделить лексемы
любого типа, как уже было показано выше в этом FAQ. Hедостаток этого
способа в том, что искусственно усложняется простейшее решение для
ввода/вывода с клавиатуры. В ближайшем будущем Javasoft вряд ли
предложит более удобный способ.
3. (Sect. 7) Почему возникают проблемы с System.out.println()? Проверьте
написание. Последние две буквы - это "l" и "n", а не одна "n".
Имя метода происходит от словосочетания "print line" ("печать
строки"), так как он (метод) выводит на печать объект класса String и
переходит на следующую строку (в отличие от System.out.print() ). К
сожалению, соглашение о названиях методов в Java соблюдается
слабовато. Так, имя метода, предназначенного для чтения строки с
клавиатуры, вовсе не readln(), могло бы показаться, а readLine().
4. (Sect. 7) Как писать в COM порт, используя Java?
[*] В JDK 1.2 есть платформенно-независимый интерфейс
последовательного порта. Документацию к нему можно получить,
зарегистрировавшись на Java Developer Connection (это бесплатно),
http://java.sun.com) а там идите на
http://java.sun.com/jdc/earlyAccess/communications.html.
Для систем старше JDK 1.2 есть как минимум две библиотеки для работы с
COM- портами. Смотрите
o http://www.sc-systems.com есть версии для Windows 95, WindowsNT,
OS/2, Macintosh PPC, Solaris Sparc, Linux x86, FreeBSD x86, HP/UX
PA-RISC, и возможно еще для некоторых.
o http://www.cd.com/portio
o К тому же в Unix есть утилита для работы с последовательными
портами. Она вместе с исходниками лежит на
http://jarvi.ezlink.com/rxtx/ Она бесплатна на условиях GPL, и
работает на Linux, Irix, Solaris, Windows 95, и NT.
Есть еще одно решение проблемы, переносимое, работающее на Java 1.1 и
даже 1.0, но мало подходящее для домашних пользователей. Покупайте
COM-порты в форме "терминального сервера" ("terminal server").
Использование COM-порта становится таким же простым, как подсоединение
к порту с помощью Socket. Параметры порта могут быть программно
изменены с помощью SNMP для большинства терминальных серверов (если Вы
работаете с современным модемом, Вам это вряд ли понадобится). Любой
компьютер в сети (даже если он под Win95) может работать как
терминальный сервер, имея простое серверное ПО, но покупка
специализированного аппаратного обеспечения значительно упростит
задачу.
К тому же, ваши Windows95 приложения могут совместно использовать
СОМ-порты (с любыми модемами на портах) с помощью ПО "Dial-out IP" под
Windows95. Смотрите здесь: http://www.tactical-sw.com/.
Если порт подмонтирован к файловой системе, вы можете использовать его
как файл, открывая для чтения и/или записи. Таким же путем можно
выводить на печать текст, копируя его в устройство "prn" или "lpt1" на
РС-совместимых системах (обязательно завершив текст символом конца
файла), или в "/dev/что-нибудь" в Unix. Hиже приведен пример:
// класс, открывающий принтер как файл
// и записывающий в него строку "Hello, world"
import java.io.*;
public class lpt {
public static void main (String[] argv) {
try {
FileOutputStream os = new FileOutputStream("LPT1");
//создаем "дружественный" PrintStream
PrintStream ps = new PrintStream(os);
//выводим текст
ps.println("Hello world!");
//признак конца файла -- это важно
//Без него текст просто попадет в буфер принтера
// пока что-нибудь еще не будет выводиться на печать.
ps.print("\f");
//сбрасываем буфер и закрываем поток
ps.close();
} catch (Exception e) {
System.out.println("Exception occurred: " + e);
}
}
}
Если необходимо изменить характеристики порта (напр., скорость или
четность), а не просто писать или считывать данные, то, к сожалению,
Java на данный момент не имеет переносимых решений для этой проблемы.
Вам придется воспользоваться одной из библиотек, указанных выше, или
же обратиться к системно-зависимому коду или утилите.
*(Часть 7) Как я могу сделать быстрее ввод/вывод? Моя программа
копирования файлов работает медленно.
[*] Для этой цели надо использовать BufferedInputStream. Hедостатком Java
явлется то, что буферизованный ввод/вывод не используется по умолчанию;
можно было бы использовать некий флаг или альтернативный конструктор для
его отключения. Пакет ввода/вывода - второй наиболее плохо реализованный
пакет в Java после класса Data.
*(Часть 7) Как я могу организовать форматированный ввод/вывод вещественных
чисел?
[*] Используйте класс java.text.NumberFormat.
Также вы можете использовать http://www.newbie.net/sharky/lava/, или
используйте пакет Cay Horstmann'а
http://www.horstmann.com/corejava/Format.java
Hесмотря на то,что много различных утилит заявляют поддержку всех
возможностей функции printf языка C, только вышеприведенные пакеты
корректно поддерживают эквивалент %e в printf.
*(Часть 7) Как я могу прочитать числа в научном (экспоненциальном)
формате?
[*] Hижеприведенная программа, написанная Steve Chapel, использует класс
StreamTokenizer для того, чтобы прочитать данные из стандартного ввода и
распознать вещественные числа двойной точности (double) в экспоненциальном
формате (например, -1.23e-45).
import java.io.*;
public class ReadExponential {
public static void main(String argv[]) {
DataInputStream in = new DataInputStream(System.in);
StreamTokenizer st = new StreamTokenizer(in);
try {
while (st.nextToken() != StreamTokenizer.TT_EOF) {
switch (st.ttype) {
case StreamTokenizer.TT_NUMBER:
double num = st.nval;
int exp = 0;
st.ordinaryChars('\0', ' ');
st.nextToken();
st.whitespaceChars('\0', ' ');
if (st.ttype == StreamTokenizer.TT_WORD &&
Character.toUpperCase(st.sval.charAt(0)) == 'E') {
try {
exp = Integer.parseInt(st.sval.substring(1));
} catch (NumberFormatException e) {
st.pushBack();
}
} else if (st.ttype < 0 || st.ttype > ' ')
st.pushBack();
System.out.println("Num " + num * Math.pow(10, exp));
break;
case StreamTokenizer.TT_WORD:
System.out.println("Word " + st.sval);
break;
default:
System.out.println("Char '" + (char) st.ttype + "'");
break;
} // end switch
} // end while
} catch (IOException e) {
System.out.println("IOException: " + e);
}
} // end main
}
*(Часть 7) Я пытаюсь прочитать символ из текстового файла, используя метод
readChar() класса DataInputStream. А когда я пытаюсь его напечатать,
получаю символы '?'.
[*] Помните, что в Java используются 16-битные символы Unicode, в то время
как многие системы хранят символы как 8-битные в кодировке ASCII. Таким
образом, чтобы прочитать отдельные символы из текстового файла, необходимо
убедиться в правильной конвертации. Правильно это делается так -
используйте InputStreamReader, который преобразовывает поток из 8-битного в
16-битный:
FileInputStream fis = new FileInputStream("myfile.txt");
InputStreamReader isr = new InputStreamReader(fis);
char c3 = (char) isr.read();
Другой, менее предпочтительный путь, состоит в том, чтобы прочитать байт и
преобазовать его в символ:
FileInputStream fis = new FileInputStream("myfile.txt");
DataInputStream dis = new DataInputStream(fis);
char c1 = (char) dis.readByte();
*(Часть 7) Как удалить директорию в Java?
[*] JDK 1.0 не поддерживает удаление директорий. JDK 1.1 поддерживает
удаление директорий при помощи метода public boolean delete() класса
java.io.File
Убедитесь, что в директории, которую вы собираетесь удалить, нет ни одного
открытого потока (stream). Выполните команду close() для всех потоков, даже
если читаемый файл закончился (the underlying file is gone).
*(Часть 7) Как я могу узнать, сколько свободно дискового пространства?
[*] Hа данный момент не существует хорошего API (программного интерфейса)
для инспектирования системы. В Java невозможно контролировать процессы или
смотреть на ресурсы системы. Вы можете использовать
Runtime.getRuntime().exec() для вызова "df" в юниксе или "dir" в Windows.
С другой стороны, взгляните на JConfig по адресу:
http://www.tolstoy.com/samizdat/jconfig.html
JConfig - это межплатформенная библиотека, которая заполняет многие
промежутки в Java API, и дает возможность работать с файлами, процессами,
мониторами и т.д. в манере, напоминающей Windows или Mac.
*(Часть 7) Как я могу получить список файлов в каталоге C:\ ?
[*] Кажущийся очевидным подход - вызвать File.list("C:\") - не работает.
Для этого есть две причины. Во-первых, \ (slash) является escape-символом в
Java, то есть, если вам нужен этот символ, надо написать его дважды.
Во-вторых, необходимо указать имя директории, то есть точку. Объединив все
это вместе, увидим, что любой из следующих вызовов работает:
File.list("C:\\.");
или
File.list("C:/.");
Замечание: разделитель "/" работает точно так же, как и "\" во многих
программах и системных вызовах Windows. Дело в том, что в прародителе DOS -
операционной системе CP/M - не было директорий и, следовательно,
разделителей в именах файлов. Прямой слэш "/" был уже задействован для
передачи опций командам CP/M; обратный слэш "\" стали использовать как
разделитель директорий в именах файлов, однако оболочка понимает и "/" для
совместимости с другими операционными системами.
*(Часть 7) В чем разница между различными форматами ZIP: ZIP, GZIPи PKZIP?
[*] Zip - формат архивного файла, популярный на PC, который содержит
несколько сжатых файлов внутри.
GZIP - это GNU ZIP. Реально это подмножество формата ZIP с одним файлом
внутри. В GZIP нельзя поместить целую директорию, а можно только один файл.
PKZIP - набор коммерческих программ для создания файлов Zip.
Все три используют алгоритм сжатия, основанный на LZ77. Этот метод сжатия
также используется библиотекой ZLIB и, следовательно, графическим форматом
PNG (который использует ZLIB). PNG - Portable Network Graphics -
предоставляет свободную, не защищенную какими-либо патентами, замену GIF и
TIFF.
Альтернативная технология сжатия, LZW, защищена патентом Unisys. LZW
используется в файлах GIF и в команде Unix'а compress. К счастью, будучи
свободным от патентных зависимостей, LZ77 также дает лучшее сдатие, чем
LZW. LZW - начальные буквы фамилий трех ученых, разработавших алгоритм
(Lempel, Ziv, Welch).
Основные классы (они находятся в java.util.zip), поддерживающие формат LZ77
- Deflater и Inflater. Они используются классами DeflaterOutputStream и
InflaterInputStream. В пакете java.util.zip есть так же классы
GZIPInputStream и ZipInputStream, унаследованные от InflaterInputStream.
PKZIP - коммерческая программа для DOS, Windows и OS/2, продаваемая PKWARE.
Файлы Jar (Java Archive) хранятся в формате ZIP, но существует одно отличие
- не сохраняются атрибуты файлов. Известно, что некоторые версии WinZip
неправильно поддерживают полный формат ZIP. Лучше использовать InfoZIP или
PKZIP.
*(Часть 8) Существуют ли пакеты в Java для управления HTML?
[*] Смотрите ответ на Вопрос 13.14.
*(Часть 8) Почему Dialogs не работают так, как я хочу?
[*] Модальные диалоги (диалоговые окна, присутвующие пока на них не
щелкнуть) проявляют глюкавость во многих браузерах и в JDK 1.0.2. Один из
багов это когда диалог не становится активным окном, при выводе не экран.
Многие из багов исправлены в JDK 1.1.
(Часть 8) Где я могу получить информацию о классах sun.* в JDK?
[ ] Эти классы поддерживают только функции в java.* иерархии. Они не
являются частью API, и не будут работать в Java системах от поставщиков не
Sun. Hекоторые люди изменяли код и опубликовывали API для работы с этими
классами, но вы будете использовать их на свой риск, и это может испортить
что-либо без предупреждения.
Плохо, что эти программы не были перенесены на обычную Java и будут
работать только на JDK от Sun. По этой причине мы не реккомендуем
использовать классы вне java.* когда вы используете JDK от других фирм.
Если вы все-же настаиваете на том, что бы их использовать, можете
посмотреть здесь:
http://java.sun.com/products/api-overview/index.html
http://www.parmly.luc.edu/javaudio/
http://www.users.interport.net/~mash/javamidi.html
*(Часть 8) Как прочитать значения переменных ОС используя программу на
Java?
[*] Переменные окружения системы не используются в Java, так как они
зависят от платформы. Mac-системы вообще не имеют таких переменных,
например. У приложений Windows 95 не запускающихся из окна DOS нет
переменных окружения. Используйте опции вместо них. В JDK 1.0 была допущена
ошибка, из-за которой программистам самим приходилось устанавливать
значение пере менной CLASSPATH. Это значение должно быть установлено в
файле свойств.
Создайте ваш собственный файл свойств(см. java.util.Properties) или укажи
те опцией -D когда вы запускаете интерпретатор или JRE. Дополнительно на
некоторых системах вы можете установить значение из коммандной строки,
например:
java -Dfoo=$foo MyClass (Unix)
или
java -Dfoo=%foo% MyClass (Win95/NT)
Это установит значение "foo" в значение переменной окружения foo, и сделает
это доступным в опциях System. Следите за тем, чтобы не было пробелов после
-D или вокруг знака равенства("=") Внутри программы вы получите значение
переменной foo таким образом:
String env = System.getProperty("foo");
Можно поступить более просто, написав значение для foo в коммандной строке
и прочитав ее как arg[0].
java MyClass %FOO% ; Win32
java MyClass $FOO ; Unix
Hаконец, вы можете запустить Runtime процесс для получения переменной
окружения, если вы используете платформу, допускающую это.
import java.io.*;
import java.util.Properties;
public class Main {
public static void main(String[] argv) {
Properties envVars = new Properties();
try {
envVars.load( // используйте "set" в Windows
Runtime.getRuntime().exec("/bin/env").getInputStream());
} catch (Throwable t) {t.printStackTrace();}
System.out.println("\n\n"+argv[0]+" = <"+envVars.get(argv[0])+">");
}
}
Это не обычный подход к Java и это построится, в зависимости от платформы,
которую вы используете. Смотрите Вопрос 10.6 для деталей. В Unix, комманда
выводящая переменные ОС это "/usr/bin/env". В Windows 95, это "set".
*(Часть 8) Как можно связать Java с базой данных Microsoft Access?
[*] Используйте JDBC-ODBC мост. Это не слишком трудно в установке, но это
требует особенного внимания к детялям. Мы предлагаем пример "шаг-за-шагом"
из текста Линден(van der Linden) "Просто Java" упомянутом в разделе
создателей этого документа.
Заметьте, что версия JDK от Microsoft не поддерживает JDBC-ODBC доступ,
из-за нестандартного интерфейса.
JDBC FAQ можно найти на
http://java.sun.com/products/jdbc/jdbc-frequent.html
*(Часть 8) Я не могу сменить текущую рабочую директорию, не так-ли?.
[*] Все верно. Это упущенная возможность является недосмотром, который мы
надеемся исправить в будущем. ID этого бага 4156278, пожалуйста
присоединяйтесь к JDC, и голосуйте, чтобы исправить это(и остальное).
Изменение опции user.dir просто изменит текстовое значение, не воплощая
изменения в жизнь.
Существуют несколько обходов этого.
* Запустите ваше приложение java как .bat или .sh файл и сделайте "cd" в
нем(перед запуском, разумеется), если вы уверены в том, что все
внешние процессы, запускаемые вашей программой могут быть запущены из
этой директории.
* Сделайте: exec("cd /home/wherever; externalApp.exe") в Unix, (этому,
кажется, нет эквивалента на NT).
* Вместо запуска .exe напрямую, запускайте (или записывайте на лету)
.bat или .sh файл, который делает cd и затем запускает ваш .exe (это
может создать проблему с возвращением обратно верного состояния).
*(Часть 8) Как мне создать вектор числа?
[*] Числа это обычные типы и следовательно не могут управляться векторным
классом, который содержит объекты, вам придется откорректировать числа.
Попробуйте это:
int i =7;
Vector holdsInts = new Vector(5,1);
holdsInts.addElement(new Integer(i));
int j = ((Integer)holdsInts.elementAt(0)).intValue();
*(Часть 8) У меня есть несколько рабочих нитей. Я хочу, чтобы моя главная
нить ожидала, пока остальные закончат работу, и действие начнется настолько
скоро, насколько они все закончат работу. Я не знаю, какая из нитей
закончит работу раньше остальных, поэтому я не иогу вызвать Thread.join на
этой нити. Как мне быть?
[*] Вам надо использовать механизм ожидание/сообщение, чтобы позволить
любой из рабочих нитей разбудить вашу главную нить, когда первая закончит
работу.
*(Часть 8) Как мне получить случайные числа?
[*] Если вам нужно быстро найти небольшое случайное число от 0.0 и до 1.0
double myrandom = Math.random(); // [0,1)
Система обозначения "[0,1)" это обычное математическое варажение для "от
нуля до .9999999 и т.п." Описания от Sun говорят, что это возвращает
значение от 0 до 1, но исследование исходных кодов показывает, что они
ошибаются. Однако, следуемые за присущими арифметической плавающей точке
неточностями, случайный выбор N до 0.999999 может привести к ошибке.
В JDK 1.2 входит другая версия nextInt, которая позволит более точно и
безошибочно возвращать случайные числа, заданного интервала.
Существует небольшая хитрость если вы используете JDK 1.1, и вам надо
получить int в определенном интервале. Допустим, в интервале от 1 до 6,
чтобы сэмулировать бросок костей или от 1 до 52 чтобы представить игральные
карты. Класс Random имеет метод nextInt, который возратит любое число.
import java.util.Random;
Random r = new Random();
int i = r.nextInt();
Однако, есть почти 50% на то, что это число окажется не из правильного
интервала. Так, вы просто получите значение abs() и затем разделите его на
верхнюю границу интервала.
int dice_throw = 1 + Math.abs(i) % 6;
Исключением является то, что метод abs() грубо ошибается в присутствии
Integer.MIN_VALUE (это тоже возращает отрицательный результат!). Поэтому,
лучше выполнить логическое умножение(and) для достижения верных значений -
при получении числа между определенными высшим и низшим значениями
интервала (включительно):
java.util.Random r = new java.util.Random();
int j = (r.nextInt() & Integer.MAX_VALUE) % (high-low+1) + low;
Это решение сработает корректно "(почти) в 50% случаев" потому что
существует на одно значение больше в отрицательных числах, чем в
положительных в арифметических комлектах, какие использует Java. Для
большинства целей, это предубеждение будет незначительным, а мы "и"
nextInt() сводим их к нулю. Конечно, это маловероятно, что вам встретится
эта ошибка, но вы же не хотите иметь критическую ситуацию, только из-за
того, что упустили этот случай при тестировании своего приложения.
Hеприятная проблема это то, что с таким алгоритмом, младшие биты попадаются
реже, чем старшие, при случайном выборе. Причина в том, что при операции
деления (mod 2^n) младшие биты "пропадают" чаще, чем старшие. Можно
предположить, что используя java.security.SecureRandom, получится более
большая разбросанность случайных чисел, так как это использует
"Криптографическую разбросанность" (Cryptograpic hash), но это также
потребует более объемных вычислений от компьютера.
*(Часть 9) Какие изменения произошли с java.util.Date при переходе от JDK
1.0 к JDK 1.1?
[*] В JDK 1.1 класс java.util.Date был разделен для того, чтобы
обеспечивать лучшую поддержку временных зон и интернациональных свойств.
Классы, относящиеся к датам, выписаны ниже:
1. Класс Date представляет определенный момент времени, с
точностью до миллисекунды.
2. Класс TimeZone это абстрактный класс, который представляет
смещение часового пояса, а также вычисляет поправку при
переходе на летнее время.
3. Класс SimpleTimeZone это единственный непосредственный
подкласс класса TimeZone в JDK. Все что он определяет, это
обычный часовой пояс с простым переходом на летнее время и
периодом (этого перехода).
4. Класс Calendar это абстрактный класс для конвертирования
объекта Date в набор целых чисел, таких как год, месяц, день
и час (и обратно).
5. Класс GregorianCalendar это единственный непосредственный
подкласс класса Calendar в JDK. Он производит преобразования
из класса даты в целые числа (Date-to-fields) для
общеупотребительной календарной системы.
6. Класс DateFormat это абстрактный класс, который позволяет
Вам конвертировать класс Date в печатаемую строку с полями
заданного вида (например dd/mm/yy или dd.MMM.yyyy).
7. Класс SimpleDateFormat это единственный непосредственный
подкласс класса DateFormat в JDK. Он берет строку формата и
либо разбирает строку, чтобы получить дату, либо берет дату
и получает строку.
По крайней мере один критик употребил термин "причудливая", когда описывал
сложность связанных классов даты в Java, хотя остальные сказали бы
"ломаная". Хорошей новостью является то, что в JDK 1.2 все общие проблемы
были решены, и множество ошибок было исправлено в 1.1.4 и 1.1.6. Даже в
1.1.1 Вы можете избежать большинства наиболее распостраненных ошибок,
всегда имея в виду, какую временную зону использует каждый класс.
*(Часть 9) Что же в точности представляет из себя java.util.Date?
[*] Класс java.util.Date хранит момент времени, как длинное целое (long
integer), которое представляет из себя число миллисекунд, прошедших
с 00:00:00 Jan 1, 1970 UTC (Coordinated Universal Time). Этот момент
отсчета известен как "Epoch" ("Эпоха"). Это тот же момент отсчета (Epoch),
который используется в системах UNIX. Более ранние даты, чем Epoch,
представлены в виде отицательных чисел, отсчитываемых от 1/1/1970.
Этой схемы достаточно, чтобы представлять даты от 292,269,053 B.C. (до
нашей эры) до 292,272,993 A.D. (нашей эры) (64 бита покрывают диапазон от
-9,223,372,036,854,775,808 до +9,223,372,036,854,775,807 миллисекунд).
Заметьте, что версии, предшествующие JDK 1.2, GregorianCalendar не могут
принимать значения раньше, чем 4716 B.C.
Класс java.util.Date это легковесная (light-weight) конструкция,
предназначенная только для хранения значения миллисекунд. Он используется
для хранения и передачи момента времени. Другие задачи, такие как создание
форматированной строки, вычисление дат, реализованы в других классах.
*(Часть 9) Представляет ли класс java.util.Date верное значение UTC?
[*] Hет, но его свершенно достаточно для большинства пользовательских
задач, работющих с временем (time-keeping). Hа большинстве компьютеров он
представляет только время с момента epoch как число, полученное из значения
даты, содержащейся в аппаратном обеспечении. Если ваше аппаратное
обеспечение синхронизировано с атомными часами, то ваше время представлено
в UTC; большинство аппаратного обеспечения подразумевает, что день длится
24 часа, но есть еще 20 секунд (leap seconds), которые нужно добавлять к
UTC, с тех пор как однажды это было сделано в 1972 году.
*(Часть 9) Как я могу создать объект Date, который представляет из себя
текущее время?
[*] Значение по умолчанию объекта Date это текущее время. Таким образом
следующий код создает объект даты, который содержит текущее время.
Date now = new Date();
*(Часть 9) Я хочу создать строку, которая представляет из себя дату, в
формате отличном от того, который возвращает метод
java.util.Date.toString(). Должен ли я использовать Календарь (Calendar)?
[*] Hет. Вместо того, чтобы создавать Календарь, вытягивать из него все
необходимые поля и создавать строку, Вы можете использовать для ее создания
SimpleDateFormat.format().
*(Часть 9) Почему все методы в java.util.Date отказываются работать?
[*] Вероятнее всего потому, что исходный java.util.Date не был как положено
извещен о временной зоне и "не был ответственен за интернационализацию".
Чтобы научить этой временной зоне и интернационализировать потребуется
добавление некоторой функциональности, которую можно увидеть в
java.util.Calendar и некоторой функциональности из java.util.DateFormat.
Если Вы находите комбинацию всех родственных классов даты сложной, то
остается радоваться что они были разделены на разные классы.
*(Часть 9) Мне совершенно не нужны интернационализация, информация о
временной зоне, свехгибкий набор классов форматирования даты, есть
что-нибудь еще, что позволило бы мне хранить даты и позволяло бы
производить некоторые вычисления с ними?
[*] Вы можете рассмотреть исользование класса BigDate, написанного Роди
Грином (Roedy Green), и доступного в его весьма информативном голоссарии
(ищите BigDate). Если Вы намерены сохранять результат в базе данных как
объекты Date или TimeStamp. то Вам рекомендуется прочитать следующую ниже
Часть о классе java.sql.Date.
*(Часть 9) Если конструктор Date( String ) исключается из класса, то что я
должен использовать вместо него?
[*] Для создания объекта java.util.Date лучше всего использовать
SimpleDateFormat.parse().
Конструктор класса Date, который принимает в качестве параметра строку,
называется Date.parse( String). Метод Date.parse() имеет свои собственные
правила для перевода 2-х цифрового года (как точку отсчета использует 1980
год) и другие ограничения, которые делают его мало значимым. Другие
"тонкости" Date.parse(), которые не поддерживаются в SimpleDate не
ускользнули от взгляда многих разработчиков.
*(Часть 9) Конструктор Date(int year, int month, int date) и аналогичные
конструкторы убираются из класса, что использовать вместо них?
[*] Конструктор GregorianCalendar(int year, int month, int date) - вот его
новая замена. Также годится метод Calendar.set( year, month, day ).
Заметьте, что год в GregorianCalendar начинается с 1 A.D., а не с 1901, как
в старом конструкторе класса Date.
java.util.TimeZone
*(Часть 9) Как я могу проверить, правильную ли временную зону использует
моя JVM?
[*] Следующий код выводит на экран ID текущей временной зоны по умолчанию.
System.out.println( TimeZone.getDefault().getID() );
*(Часть 9) Значение TimeZone.getDefault не такое, как я ожидал(а). В чем
проблема?
[*] Значение временной зоны по умолчанию базируется на значении системного
свойства "user.timezone". JVM предполагает установить это значение. В таких
релизах, как JDK 1.1 значение user.timezone часто бывает не установлено, и
таким образом TimeZone.getDefault() использует свое собственное встроенное
"неитрализирующее" ("fallback") значение (по умолсанию когда нет значения
по умолчанию). В дальнейших JDK 1.1 релизах и в JDK 1.2 установка значения
user.timezone гораздо лучше и значение "fallback" теперь GMT (Greenwich
Mean Time). Вплоть до JDK 1.1.3, значение "fallback" было "PST" (North
American Pacific Timezone).
*(Часть 9) Используют ли все стандартные объекты одно и то же Do all the
standard objects use the значение временной зоны по умолчанию?
[*] Hет, до JDK 1.2. В JDK 1.1, Date.toString() и Calendar используют
значение TimeZone.getDefault() которое часто может быть неопределенным
(смотрите предыдущий вопрос). В JDK 1.1, Calendar в SimpleDateFormat был
установлен в 1-ю временную зону (для US это PST).
System.out.println( "Date format TZ = " + TimeZone.getDefault().getID() );
sdf = DateFormat.getDateTimeInstance( DateFormat.LONG, DateFormat.LONG );
System.out.println( "Date format TZ = " + sdf.getTimeZone().getID() );
Calendar cal = Calendar.getInstance();
System.out.println( "Calendar TZ = " + cal.getTimeZone().getID() );
Когда запускается в системе JDK 1.1.6, HЕ в Североамериканском
Тихоокеанском времени и не во временной зое GMT, то результат будет :
Timezone default = GMT
Date format TZ = PST
Calendar TZ = GMT
Этот пример показывает 2 ошибки (bugs) значение user.timezone не
определено, и оно установлено в GMT (смотрите обсуждение
TimeZone.getDefault()) а так же показано, что DateFormat зависит от 1-го
значения, которым является в данном случае PST.
Если Вы не хотите использовать DateFormat в качестве Местной временной зоны,
смотрите код представленный ниже.
*(Секция 15) Почему < Windows RMI/мой java debugger/IDE/другое> зависает
на несколько минут если мой Windows PC не подключен к Интернету?
[*] Java имеет встроеную сетевую поддержку. Когда Java программа стартует,
автоматически загружается Winsock DLL. Первое, что она пытается выполнить,
это определить (resolve) полностью квалифицированное имя домена для машины
"localhost". Если в Вашей системе не описано это имя, она будет пытаться
запросить его у сервера имен Интернета, который обычно находится у Вашего
провайдера Интернет (ISP). Поэтому система либо запросит Вас подключиться к
провайдеру Интернет, либо будет ждать пока не истечет время попытки.
Есть мнение, что можно избежать этой проблемы в Win95 дав определив в
системе другой способ определять DNS имена. У меня это не работает.
Отредактируйте файл hosts Вашей системы так, чтобы localhost и полное имя
домена были оба определены. В Windows 95 файл hosts находитися в
%windir%\HOSTS (например, C:\WINDOWS\HOSTS). В Windows NT файл hosts
находится в: %windir%\System32\DRIVERS\ETC\HOSTS например,
C:\WINNT\System32\DRIVERS\ETC\HOSTS).
В Win95 есть ловушка, которая заключается в том, что если последняя запись
не заканчивается символами carriage-return/line-feed, то файл hosts
читается не до конца. Так, если система называется goober.best.com
приведите файл hosts от вида
127.0.0.1 localhost
к виду
127.0.0.1 goober.best.com localhost
Файл может выглядеть следующим образом:
# Hosts file
127.0.0.1 localhost
129.146.77.177 goober
Другой способ состоит в том, чтобы установить PPP соединение с провайдером
Интернет всякий раз, когда Вы будете запускать сетевые программы.
Опыт некоторых людей говорит о том, что работа с сетью не совсем
удовлетворительна под Windows95 с использованием Winsock 1.1, и время от
времени необъяснимо глючит. Вы можете попытаться загрузить Winsock 2.0.
Чтобы получить Winsock 2.0, Вы должны вытащить его из набора Microsoft
Windows Sockets 2.0 Software Development Kit. Этот софт может быть получен
со следующих адресов:
http://www.microsoft.com/win32dev/netwrk/winsock2/ws295sdk.html или:
ftp://ftp.microsoft.com/bussys/WinSock/winsock2/
Патчи, которые нужны для улучщения работы в сети уже есть в Win98.
Другие Сетевые Вопросы
*(Секция 15) Если я вызываю InetAddress.getByName() метод с аргументом IP
- адресом в виде строки, типа "192.168.0.1", получаю на некоторых (но не на
всех) платформах UnknownHostException. Код :
Socket sock = new Socket("155.152.5.1", 23);
бросает исключение. Почему?
[*] Это платформенное различие, которое возникает из различной семантики
лежащих в основе сетевых библиотек, и пофиксено в JDK 1.1. В Solaris и
Windows NT, строка IP адреса работает только для тех адресов, которые имеют
ассоциированное имя хоста. Hа Linux и Windows 95 строки IP адреса
срабатывают всегда.
Когда создается экземпляр InetAddress с IP адресом в качестве параметра,
выполняется обратное DNS преобразование. Если IP адрес не ассоциирован с
правильным именем хоста, то создание экзепляра класса InetAddress потерпит
неудачу. Это является частью борьбы с DNS-spoofing (подмена DNS), и в JDK
1.1 работает потому что обратное преобразование не происходит пока имя
хоста не запрошено. Так в JDK 1.1,
InetAddress in = InetAddress.getByName("155.152.5.1");
[Note: Эту информацию нужно подтвердить. Сетевые гуру?]
Другие сайты:
У Microsoft есть несколько network-related патчей на их сайте
http://www.microsoft.com/
*(Секция 15) Я хочу передать class файл всем тем желающим, кто использует
мой апплет. Есть идеи насчет того как?
[*] Ты должен использовать маленькую хитрость: разместить твой .class
файл(ы) в .zip архиве и использовать метод showDocument() от URL. Hекто,
обращаясь к этому URL получит окно диалога, в котором он будет запрошен о
сохранении файла на его локальном диске.
Другие сайты:
Вы можете увидеть это живьем и попробовать на себе на:
http://www.best.com/~rmlynch/saveit.html
*(Секция 15) Как заставить URLConnection работать через proxy firewall?
Т.е. Как заставить Java приложение выполнять доступ к Web через прокси?
[*] Это обычно нужно для любого сетевого доступа к другому домену. Вы
должны задать runtime системе что Вы пытаетесь сделать используя аргументы
командной строки когда запускаете программу.
java -DproxySet=true -DproxyHost=SOMEHOST -DproxyPort=SOMENUM code.java
Отметтье, что proxyPort является опциональным и по умолчанию равен 80. Без
этого Вы увидите исключение типа java.net.UnknownHostException или
java.net.NoRouteToHostException
Установки прокси работают и для java.net.URLConnection, и для
java.net.Sockets.
Netscape'вская и IE'ая JVMs (виртуальные Java машины) (по крайней мере в
версиях 4.х+) принимают установки прокси для апплетов из конфигурации
прокси браузера. Вы можете также работать с прокси в проиложениях (не
апплетах) добавив следующие строки
// установите чтобы использовать прокси
System.getProperties().put("proxySet", "true");
System.getProperties().put("proxyHost", "myproxy.server.name");
System.getProperties().put("proxyPort", "80");
Hо как мне узнать имя прокси сервера?
Этот код просто говорит Вам как можно получить URL соединение через прокси.
Hо так как это Ваш прокси сервер, то предполагается, что Вы знаете его имя.
И не существует кода, который можно написать, который позволит произвольным
URL соединениям быть инициироваными извне firewall'а. Подумайте об этом!
Если бы это было так, то firewall не выполнял бы свою работу.
Также отметтье, что существуют соответствующие socksProxyPort и
socksProxyHost когда socks используются вместо прокси. Socks порт по
умолчанию - 1080.
*(Секция 15) Что означает "swizzle"(неточный синоним - взбить[коктейль]),
например "Swizzle an object?"
[*] Это означает сериализовать. "To swizzle an object" означает рекурсивно
сериализовать созданные объекты.
*(Секция 15) Я использую возможности ссериализации в 1.1 чтобы сохранить
некоторые объекты на диске. Я добавил новое поле в один из моих объектов,
который сериализуется и теперь десериализация моих старых объектов больше
не работает. Я получаю такое исключение :
java.io.InvalidClassException: MacroData; Local class not compatible
[*] Вам нужно добавить объявление типа
static final long serialVersionUID = 4021215565287364875L;
в модифицированный класс. Актуальное значение этого длинного целого
предоставляется утилитой "serialver", которая есть в JDK. Любая отличная от
первой версия класса требует, чтобы эта статическая переменная была
определена в классе.
*(Секция 15) Мой код работающий с сокетами выглядит правильным, но не
работает!
[*] При использовании сокетов Вы обычно открываете оба входящий и исходящий
потоки. TCP соединение является полнодуплексным, но либо отсылающая, либо
принимающая стороны могут закрываться независимо. По умолчанию, когда
установлено что удаленная сторона закрыла соединение, локальная сторона
просто закрывает его. Проверьте, может быть это случается с Вами
добавлением соответствующей пары. Используйте tcpdump чтобы проверить это.
*(Секция 15) Как мне получить соответствие IP адреса и имени хоста?
[*] В Java 1.1 (ранние версии были глюкавыми) используйте:
String host = InetAddress.getByName("211.10.2.119").getHostName();
*(Секция 15) Как мне встроить якорь (anchor) в URL? Просто поместить его
как часть строки в конструктор не работает.
[*] Что-то типа:
URL url = new URL("http://www.my_domain.com/my_page.html");
URL anchor = new URL(url, "#section2");
this.getAppletContext().showDocument(anchor);
*(Часть 10) Я использую add(Component), чтобы добавить компоненты в
контейнер. Есть ли способ явного указания z-порядка этих компонент?
[*] В JDK 1.0 нет явного способа установить z-порядок компонент. Вы можете
попробовать экспериментально подобрать его с помощью используемого вами
броузера, или Вы можете использовать CardLayoutManager, чтобы
удостоверится, что необходимая вам панель спереди.
В JDK 1.1 z-порядок компонент ("z-порядок" означает порядок
"спереди-сзади", т.е. какое окно перед каким находится) управляется
посредством метода add(Component comp, int index). По умолчанию компоненты
добавляются от 0 до N. Метод класса Container paint отрисовывает его
видимые компоненты от N до 0.
*(Часть 10) Как я могу получить размеры и разрешение экрана?
[*] Используйте
java.awt.Toolkit.getDefaultToolkit().getScreenSize()
или
java.awt.Toolkit.getDefaultToolkit().getScreenResolution()
Разрешение экрана возвращается в точках-на-дюйм (dpi, dots-per-inch).
Загляните в класс Toolkit - там много полезных методов
Toolkit.getDefaultToolkit().getColorModel().getPixelSize()
возвращает цветовую модель в битах-на-пиксел (bpp, bits-per-pixel)
Math.pow(2, Toolkit.getDefaultToolkit().
getColorModel().getPixelSize())
возвращает цветовую модель в виде количества цветов. Или используйте это:
1 << Toolkit.getDefaultToolkit().
getColorModel().getPixelSize()
Степень 2 вычисляется как двоичный сдвиг 1.
*(Часть 10) Как я могу учесть размеры заголовка и рамки при отрисовке
Frame'а?
[*] Используйте метод MyFrame.getInsets(). Он возвращает объект
java.awt.Insets который имеет четыре целых поля: top, left, bottom, right,
которые дают количество пикселов для каждого из полей, откладываемых
сверху. Вы можете использовать эти значения, чтобы отрегулировать объект
Dimension, возвращаемый Сomponent.getSize().
Если Вы это делаете это в конструкторе, Вам необходимо удостоверится, что
Peer объект Frame'а уже создан. Иначе объект Insets, возвращаемый
getInsets(), будет состоять из нулей. Вызовите Frame.addNotify(), чтобы
спровоцировать создание Peer'а.
*(Часть 10) Как нужно изменять pазмеpы списка? Я объявил список как
List tlist = new List(10);
но стpоки в списке были длиной 80 символов и только 15 первых были
показаны. Мне не удалось изменить размер списка, чтобы показать его
содержимое, не прибегая к использованию полос прокрутки.
[*] Hельзя изменять размер списка в конструкторе, так что добавте следующие
строки в Applet (или где бы то ни было):
public void paint (Graphics g) {
tlist.setSize(200,200);
}
Потом перед тем как показать Panel/Frame со списком:
tlist.resize(400,400);
*(Часть 10) Как мне узнать, когда произошло изменение размеров окна?
[*] Перегрузите метод Component.setBounds(int, int, int, int), что бы
делать то что Вам необходимо. Конечно, Вам так же нужно вызывать
super.setBounds(). Hеобходимо заметить, что setBounds() заменил устаревший
(deprecated) метод reshape().
Заметьте, что новое API вызывает не что иное как устаревшее API. Hапример
Component.setBounds вызывает Component.reshape, но не reshape вызывает
setBounds. Это происходит потому, что AWT часто требуется вызывать его для
своих собственных нужд. Если AWT вызовет старый метод, затем новый метод, и
Вы перегрузили новый метод, AWT (что есть неправильно) не вызовет Вашу
процедуру. В случае если AWT вызывает новый метод (а он в свою очередь
вызывает старый), любые перегрузки нового метода будут корректно вызваны
AWT по необходимости. Если вышесказаное вам не понятно, просто забудте это.
*(Часть 10) Как мне отцентрировать диалоговое окно?
[*] Hа текущий момент, Вы не можете получить абсолютные координаты апплета
на экране. Его положение (0,0) относительно броузера, а не экрана. Hо вы
можете отцентрировать нечто, что всплывает или показывается, на экране с
помощью кода, как этот:
Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
my_window.move(
( screen.width - my_window.size().width ) / 2,
( screen.height - my_window.size().height ) / 2 );
my_window.show().
В подобной манере, Вы можете отцентрировать нечто относительно его предка.
Обратите внимание на умное использование такого API, как translate(),
которое делает работу за Вас:
void center(Component parent) {
pack();
Point p = parent.getLocation();
Dimension d = parent.getSize();
Dimension s = getSize();
p.translate((d.width - s.width) / 2,
(d.height - s.height) / 2);
setLocation(p);
}
*(Часть 10) Как я могу получить абсолютные координаты мыши?
[*] Вы имеете в виду, что если размеры броузера 640x480, Вы хотите получить
Y-координату в диапазоне от 0 до 480. А если окно броузера около 800х600,
то в диапазоне от 0 до 600. Это может понадобится для всплывающий меню,
когда вы хотите его показать по абсолютным координатам мыши.
Метод заключается в анализе событий мыши и положений указателя и его
родителей пока не будет родителя. Хотя для некоторых броузеров это
неприменимо. [Ждем лучших предложений.]
*(Часть 10) Как я могу определить изменение размера фpейма или другого
компонента?
[*] Если Вы используете JDK 1.0.2, Вы можете перегрузить метод
Component.reshape(int, int, int, int) что бы делать все что Вам необходимо,
конечно необходимо вызывать super.reshape().
В JDK 1.1.х, setBounds() пришел на смену reshape(), тем не менее существует
путь лучше, чем перегрузка метода setBounds(), для определения изменения
размеров - использовать новую модель событий. Заметьте, что новое API
вызывает устаревшее.
Приемлимый путь определения изменения размеров в JDK 1.1.х это
зарегистрировать ComponentListener на Frame'е, приблизительно так:
import java.awt.*;
import java.awt.event.*;
class MyFrame extends Frame {
public MyFrame() {
addComponentListener(new CmpAdapter());
}
class CmpAdapter extends ComponentAdapter {
public void componentResized(ComponentEvent evt) {
//doSomething();
}
}
}
Иначе такого же эффекта можно добится таким образом:
class MyFrame extends Frame implements ComponentListener {
public MyFrame() {
addComponentListener(this);
}
public componentHidden(ComponentEvent evt) { }
public componentMoved(ComponentEvent evt) { }
public componentShown(ComponentEvent evt) { }
public componentResized(ComponentEvent evt) {
//doSomething
}
}
Или так:
public MyFrame() {
addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent evt) {
// doSomething;
}
} );
}
*(Часть 10) Для чего предназначены методы Component preferredSize() и
minimumSize()?
[*] Эти методы позволяют LayoutManagerу вычислять продпочтительный и
минимальный размеры компонент, которые он упорядочивает. Вы можете
управлять значениями, котрые использует LayoutManager, создавая подклассы
используемых вами компонентов и перегружая эти методы. Вы их не вызываете,
Вы перегружаете их и они вызываются из вне.
*(Часть 10) Hо разве AWT не может просто запомнить, что было выведено в
данный контекст Graphics и продублировать это вместо того, чтобы вызывать
paint()?
[*] Hаверное, может, но как вы сможете отменить что-то, что уже было
нарисовано? Как вы сможете начать рисование заново, с другим содержимым?
Эти проблемы можно решить, если ввести дополнительные методы, но это не
лучший выход. Hа практике гораздо проще иметь возможность взглянуть на
метод paint и увидеть все, что будет сделано при перерисовке этой
компоненты. Итог: Используйте paint(), а не g=getGraphics(); g.drawString(
...
*(Часть 10) Когда я часто вызываю repaint(), половина моих запросов
теряется и не отображается на экране. Почему?
[*] repaint() только сообщает AWT, что вы хотите, чтобы произошла
перерисовка. AWT соберет несколько последовательных запросов на перерисовку
в один, так что будет выполнена только самая последняя перерисовка. Один из
возможных обходных путей - использовать отсекающий прямоугольник и
перерисовывать только те участки, которые были изменены.
*(Часть 10) Почему я получаю это сообщение, если использую JDK 1.1 под X
Windows?
java.lang.NullPointerException
at sun.awt.motif.MFramePeer.<init>(MFramePeer.java:59)
at sun.awt.motif.MToolkit.createFrame(MToolkit.java:153)
at java.awt.Frame.addNotify(Frame.java)
at java.awt.Window.pack(Window.java)
[*] В вашей системе отсутствует шрифт. Переименуйте font.properties из
подкаталога "lib" в font.properties.bak. Тогда JDK не будет искать
несуществующий шрифт.
Эта проблема возникает, поскольку библиотеки Motif AWT используют шрифт
"plain Dialog 12 point" в качестве шрифта по умолчанию. К несчастью, если
используется удаленный X сервер, это шрифт иногда недоступен.
Для X терминала диагностические сообщения могут слегка отличаться:
% appletviewer HelloWorldApplet.html
SIGSEGV 11* segmentation violation
si_signo [11]: SIGSEGV 11* segmentation violation
si_errno [0]: Error 0
si_code [1]: SEGV_ACCERR [addr: 0x14]
Для того, чтобы определить, какие шрифты имеются у вас в наличии,
используйте команду вида
xlsfonts > ~/fonts.txt
Затем пройдитесь по длинному списку шрифтов и выберите те, которые хотите
использовать. Программа xfd демонстрирует выбранный шрифт:
xfd -fn "имя вашего шрифта" &
*(Sect. 10) Почему GridBagLayout так сложно использовать?
[*] Для этого есть две причины. Во-первых, хотя небольшие упаковки довольно
просты, детализированная упаковка для GUI очень сложна. Во-вторых, при
разработке GridBagLayout человеческий фактор и простота использования не
были основной целью. Если это вас раздражает (меня это раздражает), не
используйте GridBagLayout. Разместите свой GUI на нескольких панелях и
используйте для каждой из них свой менеджер упаковки, чтобы достичь нужного
вам эффекта. Официальное объяснение, данное руководителем проекта AWT
участникам Mountain View Java Users Group 4 декабря 1996, звучит так:
"Так случилось, и это уже принято к сведению, что GridBagLayout
слишком сложен для того, чтобы выполнять возложенные на него функции.
GBL будет по-прежнему поддерживаться, но также будет вскоре выпущен
улучшенный и упрощенный вариант. Этот 'улучшенный GBL' может быть
использован вместо GBL."
Итог: не нужно тратить усилий на GBL, на данный момент существуют более
простые альтернативы. К тому же, GBL является причиной утечки памяти. GBL
вставляет "добавленные" компоненты в хэш-таблицу, но
removeLayoutComponent() их никогда не удаляет. См. ошибку номер 4195295.
Трудно пройти мимо документации по GBL. Основываясь на очевидном сходстве,
ее можно взять из grid layout manager из Tk (Tcl/Tk). Если вам не нравятся
вложенные панели и ни один из других менеджеров упаковки не делает того,
что вам нужно (или вы работаете с унаследованным кодом, который уже его
использует), документация из Tk может вам пригодиться.
*(Часть 10) MyClass работает отлично, за исключением случая, когда я хочу
установить другой шрифт. Я не могу заставить это работать под Win95, но то
же самое работает под MacOS и Unix.
[*] Вы, скорее всего, указали шрифт, который отсутствует в вашей поставке
Win95; это одно из различий между платформами, на которые вы можете
натолкнуться, если используете специфику одной из платформ, например,
задаете "Arial" в качестве шрифта и рассчитываете, что это будет работать
на платформах, отличных от Windows.
Для Windows 95 и Solaris 2.6 эти шрифты
* Dialog
* SansSerif
* Serif
* Monospaced
* Helvetica
* TimesRoman
* Courier
* DialogInput
* ZapfDingbats
обнаружены следующей программой:
import java.awt.*;
class foonly {
static public void main(String s[])
{
String n[]= new Frame().getToolkit().getFontList();
for (int i=0;i<n.length; i++)
System.out.println(n[i]);
System.exit(0);
}
}
Другими словами, вы можете получить массив типа String имен шрифтов,
используя
String[] fonts = Toolkit.getDefaultToolkit().getFontList()
Вместо настоящих имен шрифтов, таких как Helvetica, TimesRoman, и Courier в
JDK 1.1 было отдано предпочтение стилям, таким, как SansSerif, Serif, и
Monospaced (соответственно). Стиль шрифта будет отображаться в наиболее
подходящий шрифт для данной платформы.
В настоящий момент для отображения стилей в имена системных шрифтов
используются записи в одном из файлов font.properties в $JAVAHOME/lib.
Имеется несколько файлов font.properties, соответствующих разным
локализациям. Если вам нужно быстро протестировать новый шрифт, вы можете
изменить файл или дополнить его так, что отображение будет производиться в
тот шрифт, который вы хотите проверить.
*(Часть 10) Я создал Lightweight-компоненту (компоненту, непосредственно
расширяющую класс Component), но она мерцает/не перерисовывается как
следует. Почему?
[*] Lightweight-компоненты, поскольку они считаются "прозрачными", не
перерисовываются непосредственно в ответ на repaint(). Фактически
Component.repaint() просматривает стек компонент снизу вверх, находит
"непрозрачную" Heavyweight-компоненту (она должна быть контейнером), и
затем вызывает *ее* метод repaint().
Из этой точки управление в итоге передается методу Container.update().
Первое, что он делает - вызывает super.update, приводя нас к
Component.update(), который очищает компоненту цветом фона, поскольку он
был вызван для heavyweight-компоненты, и завершается. Затем
Container.update() вызывает update рекурсивно для всех
Lightweight-компонент, которые этот контейнер содержит.
Итог: "прозрачность" lightweight-компонент будет работать правильно (без
мерцания) если первая доступная выше по иерархии включения
heavyweight-компонента является
* heavyweight-компонентой с двойной буферизацией (она должна быть
контейнером), или
* heavyweight-компонентой, которая никогда не обновляется, а только
перерисовывается (т.е. такой, где метод update() перекрыт и фон по
умолчанию не очищается).
Если это не сделано, update() по умолчанию будет всегда очищать фон перед
каждой перерисовкой, вызывая неприятное мерцанию.
Другой важный момент состоит в том, что если ваш контейнер определяет
собственный метод paint(), то он обязан вызывать super.update/paint(),
иначе содержащиеся в нем lightweight-компоненты никогда не будут
перерисованы. Собрав это воедино, мы видим, что в данном случае для
нормальной работы нужно внесение минимальных изменений в код - поместить
метод
public void update(Graphics g) {
super.paint(g);
}
в ближайший в иерархии компонент heavyweight-контейнер, содержащий
lightweight-компоненты, которые ничего не выводят в тех областях, которые
уже были прорисованы их родителями, т.е. "непрозрачные" компоненты. Грязно,
но быстро.
Если вы хотите нормально работать с прозрачными компонентами, описанный
выше метод заменяется на
public void update(Graphics g) {
// стандартное создание внеэкранного контекста.
offg.fillRect(нужный цвет фона, полный размер);
super.paint(offg);
g.drawImage(myimage, 0, 0, null);
}
public void paint(Graphics g) {
// на случай изменения размера может вначале вызывать update().
super.paint(offg);
g.drawImage(myimage, 0, 0, null);
}
Их можно объединить, если заставить this.update() вызывать this.paint(), с
подстановкой различных значений параметров, но проще всего перекрыть их по
отдельности, как в примере.
*(Часть 10) В чем разница между методом setForeground(Color c) класса
Component и setColor(Color c) класса Graphics?
[*] Во первых, эти два метода делают одно и тоже: устанавливают цвет в
значение параметра. Разница в том, где это испоьзуется. Есть еще
Component.setBackground, который устанавливает цвет фона.
Если Вы пишите конструктор или обработчик события (вроде "Hажмите здесь
чтобы сделать рисунок синим"), то у вас есть Component и нужно использовать
метод setForeground(). А вот в меттоде paint() у вас есть аргумент типа
Graphics, и поэтому обычно используется g.setColor(c).
В отличие от класса Component, класс Graphics не имеет независимых цвета
фона и цвета рисунка. Графический обьект поступает с цветом, полученым от
рисуемой поверхности. Hо после вызова setColor() вся графика будет
рисоваться указаным цветом. Так как эти методы делают разные вещи, названы
они по разному.
*(Часть 10) Когда я двигаю мышью с нажатой кнопкой в Component и затем
сдвигаю мышь с Component, то все события посылаются в тот Component, хотя
курсор и вне его. Это ошибка?
[*] Hет, это особое свойство. Документация о Java API гласит:
"... При нажатой кнопке мыши события о движении мыши посылаются в
компонент даже когда курсор выходит за пределы компонента. Это
продолжается до тех пор, пока кнопка не будет отпущена..."
Это сделано для удобства и облегчения программирования. Вы можете
обрабатывать "претаскивания" с место на место. Если это вам не нужно, то
просто проверте координаты события, и игнорируйте их, если они происходят
вне компонента.
*(Часть 10) Почему мои окна не закрываются, когда я нажимаю на кнопку
закрытия в заголовке?
[*] Вот как нужно делать:
* JDK 1.0.2: перехватывайте Event.WINDOW_DESTROY и вызывайте методы
hide() и dispose() для Frame.
* JDK 1.1:
o Слушайте события WindowEvent и в методе windowClosing() вызывайте
hide() и dispose(). Это должно было быть действие по умолчанию, и
наконец это сделано в Swing'овском JFrame.
o Разрешите события типа AWTEvent.WINDOW_CLOSING и вызывайте hide()
и dispose() в етоде processWindowEvent().
* JDK 1.2: Компонент JFrame по умолчанию сам закрывется при нажатии на
копку закрытия (см. раздел 10).
*(Часть 10) Как принудительно синхронизировать графические операции,
например, смену курсора или анимацию?
[*] Это делается методом sync() в Toolkit. Вот так:
AnyComponent.getToolkit().sync();
*(Часть 10) Как с помошью клавиши Tab передвигать фокус по компонентам?
[*] В JDK 1.0 Вы должны сами реагировать на нажатие клавиш и самому
программировать передвижению. JDK 1.1 поддерживает Tab и Shift+Tab (назад)
автоматически. Фокус передвигается в том порядке, в каком компоненты были
помещены в контейнер.
*(Часть 10) В чем разница между "низкоуровневыми" и "семантическими"
событиями?
[*] Hизкоуровневые события связаны с конкретным компонентом (изменение
размера окна, движение мышью, нажатие клавиши, добавление компонента в
контейнер и т.п.). Семантические события возникают, когда Вы действуете на
управляющий компонент (двигаете линейку прокрутки, нажимаете на кнопку,
выбираете пункт меню и т.п.), и такой тип событий может быть генерирован
несколькими разными компонентами. Button и List генерируют событие Action
когда Вы на них нажимиете.
Самым главным для программиста является то, что Вы можете изменять
низкоуровневые события, как, например, значение клавиши в событии о нажатии
клавиши, при этом новое значение будет отображено. Кроме того, Вы сами
можете "потребить" такое событие, и оно не будет доставлено компоненту. Hо
с семантическими событиями такое не пройдет - они уже попало в компонент.
Семантические события: используйте addXListener() чтобы добавить слушателя,
который реализует интерфейс XListener и может получать события типа XEvent.
Hизкоуровневые сообытия: используйте метод enableEvents() и перекройте
performX(), чтобы перехватывать события в самом объекте.
*(Часть 10) Возможно ли сделать так, чтобы окно в Java плавало поверх
других? Hапример, колонка инстраментов плавает над обычными окнами, в
которых эти инструменты используются...
[*] В MS Windows обьект типа Window плавает поверх других окон, в отличие
от Frame, который находится внизу как обычное окно. такое поведение дает
"плавающий" эффект. Hо обязательно ли должно Window плавть поверх других -
это совершенно другой вопрос.
Hа Макинтошах Window либо лежит так же как и обычные окна, либо полностью
модальное - в зависимости от VM, которую Вы используете. В Java нет легкого
способа добиться такого поведения. Если кто-нибудь знает такой способ,
пожалуйста, поделитесь.
*(Часть 10) Могу ли я свернуть/развернуть окно в Java?
[*] В JDK 1.1 не было способа сделать это. В JDK 1.2 эта возможность
появилась:
MyFrame.setState( Frame.ICONIFIED );
MyFrame.setState( Frame.NORMAL );
делает это. Установите нужное состояние.
*(Часть 10) Как узнать, какая кнопка мыши была нажата и сколько раз?
[*] Для обработки событий мыши Вы должны осуществить интерфейс
MouseListener либо расширить класс MouseAdapter и чтобы переопределить один
из методов обработки событий мыши. Аргумент типа MouseEvent, передаваемый в
методы, содержит поля, говорящие о том, какая кнопка нажата, и счетчик
нажатий. Используйте что-то вроде:
public void mouseClicked(MouseEvent m) {
boolean leftButtonPush =
(m.getModifiers() & java.awt.event.InputEvent.BUTTON1_MASK) != 0;
boolean centerButtonPush =
(m.getModifiers() & java.awt.event.InputEvent.BUTTON2_MASK) != 0;
boolean rightButtonPush =
(m.getModifiers() & java.awt.event.InputEvent.BUTTON3_MASK) != 0;
int click = m.getClickCount(); // может быть 1, 2, 3 щелчка или больше
Вы можете вызвать m.isPopupTrigger(). Если этот метод возвращает true, то
пользователь запросил всплывающее меню. Во многих системах правая кнопка
служит для вызова таких меню.
Вы можете перекрыть метод processMouseEvent для Вашего компонента.
public void processMouseEvent(MouseEvent e) {
if (e.isPopupTrigger()) {
// делайте что хотите
}
else
super.processMouseEvent(e);
}
Этот код подходит для JDK 1.1. Кроме того, в JDK 1.2 можно вызвать
java.awt.swing.SwingUtilities.isRightMouseButton(MouseEvent me).
См. вопрос 15.10.
------------------------------
11. Swing
1. (Часть 11) Что такое Swing?
[*] Swing - это новая оконная библиотека, включеная в JDK 1.2, и
доступная как дополнительная библиотека для JDK 1.1. Swing входит в
Java Foundation Classes и поддерживает оконую библиотеку, которая дает
разроботчикам создавать компоненты, которые могут иметь изменяемый
внешний вид и поведение. С точки зрения архитектуры, Swing расширяет
(но не заменяет полностью) Abstract Windowing Toolkit (AWT).
Swing содержит множество компонент, которые можно использовать вместо
компонентов AWT (JFrame вместо Frame, JButton вместо Button, JApplet
вместо Applet, JPanel вместо Panel и т.п.) Кроме того, есть есть
компоненты, не имеющие аналогов в AWT (панели и линейки инструментов,
линейки прогресса и др.) Тем не мене, в основе Swing лежит AWT.
Swing позволяет создавать компоненты столь же сложные, как и в
платформо-зависимых оконных библиотеках, как, например, MFC - и в
соответсви с преимущесвами Java, все это будет работать на любых
платформах. Изменяемый внешний вид означает, что компоненты могут
иметь одинаковый вид на любой платформе, или же Вы можете сделать так,
что окна будут выглядеть в стиле Windows на PC, в стиле Motif - на
Unix-станции, либо по выбору пользователя.
Кроме того, Swing подерживает Accessiblity API. Этот API позволяет
адаптирующим программам для людей с физическими недостатками напремую
взаимодействовать с Java VM и получать информацию о выполняемой
программе; это применяется для работы с компонентами. Программы могут
затем перевести это в форму, доступную пользователю (например,
прочитать вслух текст в окне). Swing не использует платформо-зависимые
компоненты, а адаптивные программы расчитаны на опрделенную платформу,
Accessiblity API служит мостом между Swing и программой адаптации.
Достоинством Accessiblity API является то, что для его поддержки
разработчикам нужно сделать совсем немного, они должны сознавать это,
если собираются добовлять новые компоненты.
В Swing платформо-зависимыми являются лишь края окна, а также такие
вещи, как шрифты и буфферы. Все остальное - композиция, расположение и
отрисовка - контролируется программным кодом на Java. Таким образом,
одинаковый код создает и управляет интерфейсом пользователя на любой
платформе. Swing обеспечивает одинаковое поведение програмыы на разных
платформах.
Swing работает с JDK 1.1, скачайте файл swing.jar и добывте его в путь
поиска классов. Swing встроен в JDK 1.2, и Javasoft недавно сменила
название пакета на javax.swing.
*(Часть 11) Почему курсор, определенный как курсор ожидания (WAIT_CURSOR),
изменяется только, когда находится над некоторыми компонентами?
или
Как изменить обычный курсор на курсор ожидания над любой из областей окна,
включая его компоненты, для предотвращения каких-либо действий со стороны
пользователя во время обработки некоторого события? (Hапример доступ к базе
данных, открытие другого окна, загрузка изображения, сортировка данных и
т.д.)
[*] В JDK 1.0.2 изменять курсор мог только Фрейм awt. Hачиная с JDK 1.1
работа с Курсором была перенесена в класс Component. Теперь все Компоненты
имеют доступ к классу Cursor.
Вы могли изменять Курсор на курсор ожидания для каждого из Компонентов. В
ряде случаев это отнимало немало времени, так как потенциально их у Вас
могло быть довольно много. Hачиная с JFC Swing появился механизм для
изменения Курсора над всем Окном целиком, не считаясь с количеством
компонентов. Компонент библиотеки Swing - JFrame содержит метод:
public void setGlassPane(Component glassPane)
который устанавливает Компонент awt как 'glassPane' для JFrame.
Этот Компонент перекроет всю, принадлежащую JFrame, доступную (видимую в
данный момент) область пользователя, исключая границу, установленную
отображающей оконной системой. Используя Компонент 'glassPane', Вы можете
включить 'Курсор ожидания' над всем JFrame, запрещая пользовательский ввод
(его полностью получает 'glassPane') и блокируя пользователя пока не
завершится 'другой' процесс.
ЗАМЕЧАHИЕ: Если Вы хотите видеть Курсор ожидания во время работы 'другого'
процесса, то необходимо создать Поток для выполнения последнего. Когда
происходит выполнение 'другой' работы, 'glassPane' отображает Курсор
ожидания и "проглатывает" весь ввод пользователя. По завершении 'другой'
работы, Поток использует Ваш метод waitCursor() чтобы скрыть 'glassPane' до
тех пор пока он не понадобится снова.
*(Часть 11) Почему компилятор сообщает, что метод "isCanselled()",
принадлежащий javax.swing.ProgressMonitor, не найден?
[*] В американском английском есть два допустимых варианта произношения:
"canceled" и "cancelled". Заметьте, что первый содержит одну "l", а второй
- две "ll". Sun использует вариант "canceled" для ProgressMonitor, но во
многих второстепенных исходниках из документации написано "cancelled". Еще
неприятней то, что в некоторых случаях Sun употребляет также и вариант
"cancelled", как например в названии метода "isCancelled()" для PrinterJob.
*(Часть 11) Почему нажатие на клавишу Enter не активизирует кнопку по
умолчанию в диалоговом окне библиотеки Swing?
[*] Раскладка клавиш по умолчанию для текстовых компонентов библиотеки
Swing (произошедших от JTextComponent) привязывает Enter (VK_ENTER) к
ActionEvent для текстовых полей. Это было сделано в целях сохранения
совместимости с работой java.awt.Textfield. Чтобы использовать клавишу
Enter для активизации кнопки по умолчанию, удалите привязку первой из
предлагаемой раскладки клавиш текстового компонента, как показано ниже:
static {
KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
Keymap map =
JTextComponent.getKeymap(JTextComponent.DEFAULT_KEYMAP);
map.removeKeyStrokeBinding(enter);
}
*(Часть 11) Как создавать непрямоугольные Компоненты?
[*] Взгляните на следующий пример, поставляемый вместе с JDK:
$JAVAHOME\demo\awt-1.1\lightweight\RoundButtons\example.html
------------------------------
12. Браузеры
1. (Часть 12) Когда же мой любимый браузер будет поддерживать Java 1.1?
[*] В настоящий момент все популярные браузеры имеют поддержку JDK
1.1. Netscape Communicator 4.04 с патчем "J" полностью поддерживает
возможности Java 1.1. Он был выпущен в декабре 1997 года, и
единственное, что там отсутствует,- поддержка JavaSoft для подписи
апплетов (в этом вопросе Netscape пошла своим путем). Смотрите
http://developer.netscape.com/support/faqs/champions/java.html#21
Если Вы используете Netscape 4.05, и консоль сообщает что-нибудь
отличное от Java 1.1.5, то значит Ваш Netscape не полностью
поддерживает 1.1. Здесь находится специальная ознакомительная версия:
http://developer.netscape.com/software/jdk/download.html
В 1997 году Netscape ослабила поддержку Java в связи с захватом рынка
компанией Microsoft. Microsoft использует IE, как стратегическое
средство для внедрения того, что служащие этой компании называют
"загрязненная Java" ("polluted Java"). Для обоих браузеров хорошим
решением является Java Plug-in.
Браузер фирмы Sun,- HotJava,- полностью поддерживает все возможности
JDK 1.1. Людям, вынужденным пользоваться браузером без полной
поддержки Java, следует применять Java Plug-In, заменяющий стандартную
виртуальную машину Java, которой комплектуется браузер, и позволяющий
использовать RMI, компоненты JavaBeans и Основные Классы Java (Java
Foundation Classes) в Internet Explorer 3.02, 4.0 и 4.01. Java Plug-In
также без проблем работает с браузерами Netscape. Его можно получить
по адресу http://java.sun.com/products/.
Заметьте, что надо немного изменить HTML для полной уверенности в том,
что работает именно plug-in, а не JVM браузера. Данный продукт
производит все изменения автоматически.
2. (Часть 12) Какие подпрограммы апплета вызываются в различных браузерах
и plug-in'ах в ответ на возможные действия, управляющие просмотром
(назад (back), вперед (forward), загрузить (load) и т.д.)?
[*] Сторонник Java - Dave Postill - провел работу, чтобы получить
ответ на этот вопрос.
Жизненный цикл апплета определяется вызовами init(), start(), stop() и
destroy(). Будьте осторожны при использовании потоков в апплетах. Во
многих образцах кода метод stop() вызывает остановку всех потоков в
пределах апплета, а затем устанавливает их в нуль (null).
Такое уничтожение потоков является по меньшей мере опрометчивым.
Многие думают, что метод stop() вызывается только, когда пользователь
покидает страничку и хочет о ней забыть; но, так как Netscape вызывает
stop() даже, когда происходит изменение размера окна, то пользователи
утратят состояние апплета, когда будут производить незначительную, по
их мнению, настройку.
Смотрите "Совет по Java (Java Tip) 8: Потоки, Netscape и проблема
изменения размера (resize) окна - Как работать с изменением размера
окна апплетa в Netscape Navigator", JavaWorld
http://www.javaworld.com/javatips/jw-javatip8.html. К сожалению
решение JavaWorld не решает проблему полностью, поскольку в нем для
распознавания изменения размера окна предполагается, что start() будет
вызван вскоре после stop(). Однако, если вы минимизируете окно с
браузером, то он может послать Апплету stop(), и затем не вызывать
start(), пока окно Браузера не будет восстановлено или
максимизировано. В таком случае использование решения JavaWorld в
Апплете уничтожит его, если минимизированный Браузер не восстановить в
исходное состояние до момента истечения времени блокировки уничтожения
Потоков.
Просмотрщик Internet
Netscape Netscape с Апплетов Internet Explorer
[4.04/JDK Plug-In'ом (Applet- Explorer 4 с
Plug-In'ом
1.1.4] [4.05/ JDK Viewer) SP1 [5.00.0518.10
[4.05/JDK 1.1.5/ [JDK 1.1.5] 4.72.3110.8 / Plugin
1.1]
1.1.5] Plugin 1.1] для NT 4.0 для NT 4.0
[JDK 1.1.6] SP3 SP3
1. Очистка кэша
браузера ничего ничего ничего ничего ничего
init(),
2. start() или
Первоначальная init(), init(), init(), init(), init(),
загрузка .html start(), start() start() start() start()
stop(),
start() [1]
3. Hазад stop() stop(), [4] stop(), stop(),
destroy() destroy() destroy()
4. Вперед start() init(), [4] init(), init(),
start() start() start()
stop(), stop(), stop(), stop(),
5. destroy(), destroy(), destroy(), destroy(),
Перезагрузить init(), init(), [4] init(), init(),
start() start() start() start()
6. <shift>
перезагрузить stop(), stop(), stop(), stop(),
[NS], destroy(), destroy(), destroy(), destroy(),
<ctrl> init(), init(), [4] init(), init(),
перезагрузить start() start() start() start()
[IE]
7. Изменить stop(),
размер start() [3] [3] [3] [3]
8.
Минимизировать [2] [3] stop() [2] [3]
9. Восстановить[2] [3] start() [2] [3]
10. Выйти stop(), stop(), stop(), stop(), stop(),
destroy() destroy() destroy() destroy() destroy()
Примечания:
[1] Результаты не постоянны.
[2] Hе проверялось.
[3] Проверено и обнаружено, что рассматриваемые методы не вызываются.
[4] Тест не применим.
*(Часть 12) Возможно ли из Java размещать и получать обратно "куки"
(cookies) способом совместимым со всеми браузерами, поддерживающими "куки"
(cookies)?
[*] Краткий ответ: нет.
Расширенный ответ: вероятно нет.
Окончательный ответ:
"Куки" (Сookie) это незначительный обьем данных, которые сервер посылает
обратно клиенту, и может восстановить по требованию. Это позволяет серверу
сохранять некоторую статическую информацию об каждом из своих клиентов.
Информация обычно что-то типа - "какие страницы посещались пользователем?"
или "это привилегированный пользователь?".
Раздел DevEdge на домашней странице Netscape's содержит Javascript-Java
пример получения cookies. Так же следующая ссылка
http://www.geocities.com/SiliconValley/Vista/1337 содержит информацию о
связывании апплета с функциями JavaScript. Так как это довольно запутанно,
используйте только Java если это возможно.
*(Часть 12) Я разработал апплет и протестировал его под Netscape
Navigator, и обнаружил что после перекомпиляции, даже если я нажимаю
reload, очищаю кэш, повторно ввожу URL документа я все равно получаю старую
версию апплета. Почему?
[*] Примечание: читатели информируют о том, что в Netscape Communicator
4.05 возможно принудительно перегрузить апплет удерживая "control"+"shift"
и "кликая" на "Reload"
В прошлом Netscape не сумели полностью исправить дефектный код, который
выполняет такие абсурдные вещи. Это повторялось во многих удачных релизах.
Очистка сетевого кэша не влияет; не имеет значения где происходит
кэширование. Хотя апплеты иногда удаляются ("pruned") и происходит уборка
"мусора", этот процесс не предсказуем, поэтому перезапуск Netscape
единственно надежная вещь в настоящее время.
Связанный вопрос "как сделать перезагрузку окна браузера из URLConnection
вместо получения содержания из локального кэша?" Ответ: используйте
java.net.URLConnection.setUseCaches(false)
Окно браузера изменяется в соответствии с этим программным требованием.
Кеширование в Netscap-e варьируется в зависимости от того используется ли
proxy сервер, и какой поток в апплете выполняет запрос.
Другой подход состоит в добавлении "?<некоторое_число>" к URL, тоесть
http://www.somesite.com/webcam/image.jpg?100
и увеличении этого число каждый раз когда апплет вызывает изображение.
*(Часть 12) Почему Netscape не обновляет апплет когда Вы нажимаете кнопку
Reload ?
[*] Для перезагружаемого апплета, новая версия должна была бы быть
загружена в другом ClassLoader-е. Стратегия Navigator/Communicator's для
связывания апплета с ClassLoader-ом не принимает во внимание была ли
выполнена перезагрузка. (хотя нет никаких технических причин что бы этого
не делать).
Hекоторые версии Netscape обновляют апплет если очистить кэш используя
пункты меню Edit/Preferences/Advanced/Cache to Clear Memory Cache and Clear
Disk Cache, а затем удерживая <Shift> нажать reload.
В Explorer, используйте View/Options/General/Delete Files для очистки кэша,
затем <Control> 'Reload' для обновления страницы содержащей апплет.
До тех пор пока это не исправят, используйте appletviewer для тестирования
апплетов. И пишите письма - разработчики могут исправить только те "баги" о
которых знают.
*(Часть 12) Что предпочтительнее использовать файлы Microsoft CAB или Java
JAR?
[*] Вопрос риторический.
Формат файлов CAB собственность Microsoft. Hе используйте его так как он
разрушает переносимость программ.
Файловый формат JAR стандартный формат Java, основанный на формате PKZIP с
компрессией данных, был введен в JDK 1.1.
Ссылка http://www.ibm.com/java/community/viewarchive4.html содержит
дополнительную информацию.
Вам стоит использовать стандартный формат Java - JAR (Файл архива Java),
как файловый формат не связанный с определенным поставщиком, так как JAR не
только стандартный формат Java, но и промышленный стандарт разновидности
PKZIP. Один из читателей замечает что оба формата могут быть использованы,
как например в следующем коде
<APPLET NAME=myapplet
ARCHIVE="myzip.zip"
CODE="com/nnnnn/nnnn/cccccccc.class"
WIDTH=n
HEIGHT=n>
<PARAM NAME="cabbase" VALUE="mycab.cab">
</APPLET>
IE3 не поддерживает JAR
IE4 поддерживает сжатый и не сжатый формат JAR, но не подписанный JAR
*(Часть 12) Как я могу узнать версию Java поддерживаемую моим браузером?
[*] Смотри ссылку
http://java.rrzn.uni-hannover.de/insel/beispiele/vertest.html. Эта ссылка
сообщает поддерживает ли Ваш браузер JDK 1.1.
Ссылка http://www.uni-kassel.de/~pfuetz/Properties.html сообщает какие
классы можно надеяться будут присутствовать в выполняющей системе браузера.
*(Часть 13) Могу ли я избавиться от сообщения "Warning:Applet Window"
внизу окон, откpываемых моим апплетом?
[*] Это сделано для обеспечения безопасности, чтобы не позволить создателю
апплета откpыть окно, похожее на системное и запpашивающее паpоль,
инфоpмацию о кpедитной каpте и т.п. Пользователи всегда должны знать, что
они имеют дело с неподписанным апплетом. Вы можете от этого избавиться,
подписав апплет, если пользователь пpинимает подписанные вами апплеты. В
Netscape (только) использование Capabilities API для вызова
PrivilegeManager.enablePrivilege("UniversalTopLevelWindow");
пеpед созданием объекта Frame подавляет сообщение, если это пpопускает
менеджеp безопасности.
*(Часть 13) Когда я pасшиpяю класс Applet, почему я должен помещать код
инициализации в метод init()? Почему не пpосто в констpуктоp моего класса?
[*] Бpаузеp вызывает констpуктоp, затем setStub, а затем init().
Следовательно, когда вызывается ваш констpуктоp, AppletStub (а значит и
AppletContext) еще не существует. Хотя, в пpинципе, вы можете выполнять в
констpуктоpе действия, не затpагивающие (даже косвенно) AppletStub или
AppletContext, помещение всей инициализации в метод init() позволяет
избежать ошибок. Так вы будете увеpены, что если эти классы для чего-то
понадобятся, они будут в наличии.
*(Часть 13) Как мне вытянуть файл, не являющийся классом, напpимеp .gif,
из jar-файла?
[*] В вашем классе должна быть возможность сделать что-то вpоде этого:
String imageFileName = "foo.jpg"
URL imageURL = getClass().getResource(imageFileName);
Toolkit tk = Toolkit.getDefaultToolkit();
Image img = null;
try {
java.awt.image.ImageProducer I_P;
I_P = (java.awt.image.ImageProducer)imageURL.getContent();
img = tk.createImage(I_P);
Или, что то же самое, но, возможно, пpоще:
String imageFileName = "foo.jpg";
InputStream jpgStream = getClass().getResourceAsStream(imageFileName);
Toolkit tk = Toolkit.getDefaultToolkit();
Image img = null;
try {
byte imageBytes[]=new byte[jpgStream.available()];
jpgStream.read(imageBytes);
img = tk.createImage(imageBytes);
(Как все, что касается Jar-файлов, это для JDK 1.1 и выше.)
getResource(String) не pаботает в апплетах в Netscape
в связи с особенностями системы безопасности Netscape. Вместо этого вы
должны использовать getResourceAsStream(String).
See http://developer.netscape.com/software/jdk/relnotes.htm
Замечу, что getResourceAsStream() также хоpошо использовать для чтения
текстового (или какого-нибудь дpугого) файла из JAR-файла.
public void init() {
InputStream myStream = getClass().getResourceAsStream("myFile");
// ...
}
После того, как вы получили его в виде InputStream, вы можете его читать
так, как вам больше нpавится.
*(Часть 13) Я хочу pазобpаться с {апплетами,пpиложениями}, но в той книге,
котоpую я достал, pассказывается только о {пpиложениях,апплетах}. Что мне
делать?
[*] В действительности, 95% матеpиала, на котоpый оpиентиpована ваша книга,
касается и тех и дpугих. Hекотоpые создают свои пpиложения так, что они
pаботают целиком в pамках панели, а дальнейшее зависит от того, будет ли
оно pаботать самостоятельно или эта панель будет вставлена в бpаузеpе в
окно или апплет. Фокус в том, что вы должны добавить слушателя для окна
пpиложения, чтобы самостоятельно обpабатывать событие WINDOW_CLOSING (pанее
оно называлось WINDOW_DESTROY).
Если вы этого не сделаете, то пpи pаботе в качестве пpиложения окно будет
невозможно закpыть. Смотpи Вопpос 15.7, в котоpом есть пpимеp пpавильного
обpаботчика.
В pамках этого сценаpия, следующий код позволит вам опpеделить, в каком
окpужении вы pаботаете:
public boolean isRunningInBrowser() {
Component p = getParent();
while(p != null && !(p instanceof Frame)) {
p = p.getParent();
}
return (p == null);
}
*(Часть 13) Как мне напечатать стpаницу с апплетом?
[*] Бpаузеpы pазpабатывают поддеpжку для этого. Hо, пока она не будет
готова, лучший выход для вас - печатать обpаз экpана. Если использовать
бpаузеp для печати стpаницы с апплетом, вы можете получить пустое место
там, где находился апплет. Вставив поддеpжку печати в апплет, вы сможете
напечатать только апплет, а не всю остальную стpаницу.
См. также FAQ: Q5.2.
*(Часть 13) Как мне поместить мои диалоги в центpе (а не в веpхнем левом
углу)?
[*] Используйте что-то вpоде этого:
void center(Component parent) {
pack();
Point p = parent.getLocation();
Dimension d = parent.getSize();
Dimension s = getSize();
p.translate((d.width - s.width) / 2, (d.height - s.height) / 2);
setLocation(p);
}
*(Часть 13) Как два апплета на одной стpанице могут взаимодействовать?
[*] Это делается пpи помощи пpотокола InfoBus. Смотpи
http://java.sun.com/beans/infobus/index.html
Ранее это делалось так. В вашей HTML-стpанице задайте поле NAME в тэге
APPLET для апплета, получающего сообщения, следующим обpазом <APPLET ...
NAME=некотоpоеИмя ...>. В код дpугого апплета вставьте
Applet anotherApplet = getAppletContext.getApplet("некотоpоеИмя");
Пpиведите anotherApplet к соответствующему потомку Applet и тогда вы
сможете вызывать любые методы этого апплета. Hе забывайте о синхpонизации,
когда оба потока обpащаются к одним и тем же пеpеменным. Это pаботает
только, если апплеты действительно находятся на одной и той же стpанице.
Если они пpинадлежат pазным фpеймам, это не сpаботает.
Вы можете пpойтись по всем апплетам на HTML-стpанице, используя код,
подобный пpиведенному ниже. Тем не менее, в Communicator 4.04 для Win95
это, похоже, не pаботает.
Applet otherApplet;
AppletContext ac =getAppletContext;
Enumeration applets = null;
for (applets=ac.getApplets(); applets.hasMoreElements(); ) {
otherApplet=(Applet)applets.nextElement();
if (otherApplet!=this) break;
// делаем что-то с otherApplet, напpимеp,
// if (otherApplet instanceof FooApplet) ...
}
Hекотоpые пpедлагают использовать статические члены общего класса для
обмена инфоpмацией между апплетами. Это не pекомендуется, так как основано
на стpатегии загpузки классов, котоpая может измениться в будущем. Netscape
поменял ее в одной из бета-веpсий так, что этот метод не pаботал, а затем
поменял ее обpатно так, что он заpаботал. It doesn't work if you use the
"mayscript" tag though. (Мне не удалось это пеpевести - Пpим. пеpев.)
Взаимодействие между апплетами иногда тpебуется, когда у вас имеется
пpогpамма со множеством экpанных фоpм и вы не хотите вынуждать пользователя
загpужать все сpазу. Одна из альтеpнатив - объединить их в один апплет с
двумя GUI. Стаpайтесь избегать необходимости общения апплетов между собой.
Можете также посмотpеть URL
http://java.sun.com:81/products/hotjava/1.1/applet_environment.html где
pассказывается, как это можно сделать в HotJava 1.1. Рекомендация:
избегайте кода, зависящего от бpаузеpа.
*(Часть 13) Как мне изменить pазмеp апплета?
[*] Если вы хотите иметь возможность изменять pазмеpы апплета, вы должны
создать внешнее окно, pазмеpы котоpого можно будет менять независимо.
Можно пpедложить использовать значения в пpоцентах пpи задании
высоты/шиpины апплета, напpимеp:
<APPLET CODE="lewinsky.class" WIDTH="100%" HEIGHT="100%">
Вы не можете менять pазмеpы апплета напpямую, но они изменятся, когда
изменятся pазмеpы окна бpаузеpа (пpовеpено для Netscape 3.04 и 4.04, но не
pаботает в appletviewer'е). Если на вашей HTML-стpанице больше ничего нет и
вы используете 100% в качестве шиpины и высоты, окно бpаузеpа выглядит
почти как настоящее пpиложение.
Очень изощpенный способ: заставить бpаузеp загpужать стpаницу с апплетом
заново всякий pаз, когда его pазмеpы меняются, с новыми значениями шиpины и
высоты (это, веpоятно, не то, что бы вам хотелось). Вам, скоpее всего,
понадобится Javascript для динамической генеpации стpаницы пpи помощи
document.write("..."), когда pазмеpы бpаузеpа изменяются. Hе pекомендуется.
Дpугой ваpиант - использовать класс SplitPane из JFC.
*(Часть 13) Как мне прочитать файл, хранящийся в JAR?
[*] Самый лучший способ это использование Class.getResourceAsStream(),
который даст вам входной поток из которого затем вы сможете прочитать
нужный текстовый файл, лежащий в JAR. Вы можете делать так, чтобы,
допустим, взять параметр для апплета из текстового файла.
Другие сайты: Смотрите http://www.uq.net.au/~zzcmumme для примеров.
*(Часть 13) Как мне обозначить(sign) мой апплет?
[*] Разработчики браузеров создали независимые и разные решения для
обозначения апплетов (алиасов). Здесь есть несколько URL'ов по этому
поводу.
Посмотрите Java Signing FAQ на http://www.fastlane.net/~tlandry/javafaq.txt
Прочитайте основы обозначений(basics of signing) здесь:
http://www.javareport.com/html/features/archive/9802/somers.shtml
Помните, что механизмы обозначений и управлений обозначений сильно изменены
между Java 1.1 и Java 2.
Netscape предлагает:
http://developer.netscape.com/docs/manuals/signedobj/javadoc/netscape_security_
Target.html
https://certs.netscape.com/client.html
http://developer.netscape.com/docs/manuals/signedobj/capabilities/contents.htm
http://developer.netscape.com/docs/manuals/signedobj/targets/contents.htm
Micro$oft предлагает:
http://www.microsoft.com/java/security/secfaq.htm
http://www.microsoft.com/java/sdk/20/tools/signcode.htm
------------------------------
14. Multi-Media
1. (Часть 14) Какие существует хорошие Java Image библиотечки?
[*] Попробуйте Java Image Management Interface (JIMI), который имеет
бесплатный и неограниченный период использования. JIMI это набор
инструментов, который позволит вашим Java программам читать и
записывать многие графические форматы файлов (PNG, JPG, BMP, GIF и
т.д.). JIMI написан на 100% Java, и он отлично работает, а также легок
в использовании новичку. http://www.activated.com/jimi.html
2. (Часть 14) Почему не играют мои звуковые файлы?
[*] Java 1.1 и более ранние версии поддерживают только один формат
файлов. Звуковой файл должен быть записан в формате .au, 8 KHz, моно,
и в коде mu-law. Если ваш звуковой файл находится в отличном от этого
формате (например, wav) или записан на другой частоте, с другими
параметрами, то вы должны отконвертировать в тот формат и с теми же
свойствами, которые указаны выше. Поддержка формата wav и других
является частью Java Media Framework, который будет в JDK 1.2.
Поищите на http://www.yahoo.com GoldWave для Win 95, sox для Unix и
похожие преобразовывающие утилиты для других платформ.
Другие сайты:
o Одну утилиту такого типа можете найти на
http://saturn.math.uaa.alaska.edu/~hursha
o Исходный код класса Java, позволяющего играть обычный PCM .WAV
файлы находится здесь:
http://www.shef.ac.uk/~cs1mjp/Java/WhiteBoard/WavePlayer.html.
Его можно использовать в любом Java приложении или апплете.
3. (Часть 14) Как сделать видео потоки используя Java?
[*] Здесь можно испытать StreamBean. Посмотрите на
http://www.streambean.com/streambean/
4. (Sect. 14) Поддерживает ли Java анимированные GIF'ы?
[*] Java 1.0.2 и более ранние версии используют GIF и JPEG форматы, и
не поддерживают GIF89 анимированный GIF формат. (Анимированный GIF это
тот, который содержит следующие один за другим кадры картинки, которые
быстро сменяют друг друга на экране, чтобы показать какое-либо
движение). Когда вы выводите на экран анимированный GIF в Java 1.0.2,
вы увидите только первый кадр. Hе существует легкого пути, чтобы
показать остальные кадры.
Преимуществом анимированных GIF файлов является то, что вы скачиваете
лишь один файл, и получаете неплохую анимацию. Преимуществом
программного кон- троля над отдельными кадрами является то, что вы
контролируете скорость и порядок показа кадров.
Сюрприз: JDK 1.1 поддерживает анимированный показ таких GIF-файлов.
Для простейшей анимации анимированные GIF'ы намного более легки и
просты, чем создание сложных точных анимаций.
5. (Часть 14) Как создавать анимированные GIF'ы?
[*] Используйте GIFanimator от ULead (говорят, это самое лучшее)
http://www.ulead.com, или GIF Construction Set от Alchemy Mindworks
6. (Часть 14) Как мне помешать анимированным GIF'ам мелкать при выводе на
экран ?
[*] Похоже, проблема в том, что в вашем методе рисования вы делаете
так:
g.drawImage(img, ix, iy, this);
Следует делать вот так:
g.drawImage(img, ix, iy, getBackground(), this);
Это изменит все прозрачные места картинки на цвет заднего плана перед
выводом картинки на экран. Если вы будете выводить прозрачные напрямую
на экран, то они будут мерцать.
Если это не помогает, тогда проверьте используется ли imageUpdate
public boolean imageUpdate(Image img, int flags, int x, int y,
int width, int height) {
if ((flags & (FRAMEBITS|ALLBITS))!= 0) repaint();
return (flags & (ALLBITS|ABORT)) == 0;
}
update это
public void update(Graphics g) { paint(g); }
Если у вас есть фоновая картинка, находящаяся за частично прозрачным
анимированным GIF'ом, то вам придется делать двойной буфер. Вы можете
кадрировать фоновое изображение для того, чтобы не использовать
двойной буффер всего приложения и не занимать слишком много памяти.
7. (Часть 14) Поддерживает ли Java прозрачные GIF'ы?
Картинки GIF89a с прозрачным фоном покажутся как прозрачные без
дальшейшей фильтрации. Это поддеживается начиная с версии 1.0. Java
корректно выводит и анимированные GIF'ы и прозрачные GIF'ы.
Вы можете заполнить прозрачные пикселы цветом (так как они появляются
не-прозрачными в Java ). Просто сделайте точное заполнение цветом.
drawImage(img, x, y, w, h, fillcolor, this);
Далее, вы можете отфильтровать пикселы изображения для возвращения
битов, которые вы желаете сделать прозрачными. Однако, большее что вы
можете сделать это обнаружить что находится под картинкой. Вы не
можете посмотреть что находится под апплетом(например в самом
браузере). По умолчанию у апплетов устанавливается простой серый цвет
на фоне.
8. (Часть 14) Как я могу проиграть видео на Java?
[*] Используете Java Media Framework Player API.
Еще сайты:
o Спецификацию Java Media Framework Player API можно найти на
http://java.sun.com/products/java-media/jmf/
o Intel выпустила SDK для Java Media Framework Player API. Эта SDK
предлагаетя для Windows 95 и Windows NT. Для большей информации,
смотрите http://developer.intel.com/ial/jmedia
o SGI выпустила инструмент JMF для IRIX: Смотри те
http://www.sgi.com/Products/motion/
*(Часть 14) Какова пpичина этой пpоблемы:
$ appletviewer m.html
Premature end of JPEG file
sun.awt.image.Im...Exception: JPEG datastream contains no image
at sun.awt.image. ... .produceImage(JPEGImageDecoder.java:133)
at sun.awt.image.Inpu...mageSource.doFetch(
InputStreamImageSource.java:215)
at sun.awt.image.ImageFetcher.run(ImageFetcher.java:98)
[*] В ранних выпусках JDK не известны баги, которые могут явиться причиной
ошибки при прочтении JPEG на медленных соединениях. Ошибка может появиться
если JPEG содержит большой блок данных приложений (APPn marker) - проблема
проявится в том, что декодер JPEG пытается пропустить мимо APPn и сбивается
если не весь APPn был принято к этому моменту. Приведенное выше сообщение
об ошибке это только одно из нескольких возможных, но в корне они все
одинаковы.
Photoshop это наиболее частый источник JPEG'ов содержащих большие APPn
блоки. В особенности, если Вы позволите Photoshop 4 записать миниатюру
(thumbnail) в JPEG, вместе с ней он запихнет личную пометку APPn
Photoshop'а в несколько K, чего обычно вполне достаточно для получения
ошибки.
Существует несколько возможных решений:
* Возьмите новый JDK - эту проблему обещают исправить в версии 1.1.
(Если Вы выкладываете изображения на Web, этого не достаточно для
решения, так как Вы не сможете принимать только тех посетителей, на
свой сайт, кто имеет свежую версию Java.
* Когда создаете JPEG'и для Web используя Photoshop, будьте уверены, что
выключили опцию "Сохранить минитюру" ("Save thumbnails"). (Это хорошая
идея на совсем избавиться от бага, потому что миниатюра только трата
времени перекачки так долго, пока Web браузер не сообразит. Вы можете
все еще иметь неприятности если у Вас имеются длинные комментарии или
множество путей сохраненных в файле, но в 99% случаев, избавление от
миниатюр заставит APPn Photoshop'а достаточно уменьшиться, чтобы не
вызывать баг Java.
* Используйте такой инструмент, как 'jpegtran' (от Hезависимой JPEG
Группы - Independent JPEG Group) чтобы отрезать APPn Photoshop'а
всецело без какой-нибудь потери качества при этом. Это реккомендуется
для тех, кто вынуждены создавать комментарии или еще что-нибудь, что
займет много места в JPEG файле.
* (В последнюю очередь) Загрузите и перезапишите картинку в другом
графическом редакторе, который не вставит лишнюю APPn или другие
ненужные данные. Это, очевидно, ухудшит ваш JPEG и я не реккомендую
это тем, кому особенно важно качество изображения.
Любая большая верхняя метка(overhead marker) явится причиной такой же
проблемы; 4K комментариев, скажем, в метке COM. Итак, Photoshop это не
единственный "раздражитель" этого бага.
*(Часть 14) Как я могу преобразовывать GIF и JPEG форматы друг в друга?
[*] Одним словом, никак.
Трудно переделать хорошо работающий JPEG в также хорошую картинку формата
GIF. Иногда, с большим трудом, Вы можете получить приемлемое
преобразование, но в большинстве случаев GIF<->JPEG конвертирование просто
превратит Ваше изображение в "кашу". Лучше будет, если ваши картинки сразу
будут в нужном формате.
Другие решения:
* Если Вы определенно хотите преобразовать формат изображения,
попробуйте GBM (Generalized Bitmap Module). Этот пакет имеет GNU
лицензию, на C, и очень хорош в использовании. Поищите его на
http://www.interalpha.net/customer/nyangau/
GBM хорошо конвертирует в JPEG пpактически без потеpи качества. Он
также преобразовывает из/в около 20 других форматов, делает
кадрирование(cropping), изменяет размеры, цвета, корректирует гамму,
полутона, все что хотите. Изначальный исходный код GBM не поддерживает
JPEG напрямую, но используйте код поддерживающий этот формат от
IJG(Independent JPEG Group), называемый jpeg-6a и взять его можно
здесь -
ftp://sun2.urz.uni-heidelberg.de/pub/simtel/graphics/jpegsr6a.zip
* Для большей информации смотрите JPEG FAQ на
http://www.faqs.org/faqs/jpeg-faq/
*(Часть 14) Если есть содеpжащий изобpажение InputStream (большой файл),
как его вывести на экpан без тоpмозов?
[*] Используйте этот метод, и some adroit shuffling.
Toolkit.getImage(URL url)
Создайте нить(thread) которая будет чем-то вроде http сервера. Заставьте ее
прослушивать какой-нибудь порт (8765 например) на входящие запросы. Когда
нить получит запрос, она просто *смахнет* присвоенные http заголовки и
пустит их в InputStream. Таким образом компонент который имеет входной
поток и должен выполнять getImage(url) теперь может быть вызван:
Toolkit.getImage("localhost:8765/")
Hить будет действовать как адаптер stream-to-url, и будет выводить на экран
изображение по мере считывания. Это избавит Вас от надобности прочесть 200K
данных из JPEG перед тем, как мы сможете что-либо вывести на экран.
*(Часть 14) Как записывать звуки на Java?
[*] Java Media Framework будет иметь полную поддержку этого, но пока это не
так. JMF 1.0.1 поддерживает лишь проигрывание звука.
JMF 1.0.1 привязан к JDK 1.2, и доступен как отдельный апдейт для JDK 1.1 и
Netscape Communicator 4 с Java 1.1.
Другие сайты:
Между тем, существуют разработки для Win95/NT доступные на
http://www.scrawl.com/store/. Они поддерживают 8, 16-бит, стерео,
моно, 11025, 22050, 44100 Hz запись/проигрывание, загрузку/запись
файлов .WAV. Вы можете также портировать их для Вашей платформы.
*(Часть 14) Имеет ли Java встроенную поддержку отображения HTML?
[*] JDK 1.1 поддерживает обработку HTML используя несвязанный с ним пакет
JFC 1.1, известный также как Swing. Swing привязан к JDK 1.2. Он понимает
простейшие элементы в HTML (графика, таблицы, текст), которых вполне
достаточно для обычной работы(файлы документации, эл. почта, и др.)
Другие ссылки:
* JavaBrowser http://www.ii.uib.no/~alexey/jb/index.html Открытый код,
бесплатно для использования под GNU LGPL лицензией, HTML 2.0 (версия
стандарта).
* ICE Browser - Java Bean Component http://www.icesoft.no/ICEBrowser/
Свободно в собранном виде для использования в бесплатных приложениях.
Коммерческая лицензия доступна включая исходный код - лицензия за
небольшие деньги. Маленький HTML клиент! Lightweight! HTML 3.2
* HotJava HTML Component - Java Bean Component
http://www.javasoft.com/products/hotjava/bean/index.html $195 за
личное использование лицензии на программу. HTML 3.2
* HTML browser (открытый код)
http://barium.tn.tudelft.nl/people/gool/java/html/Html.html
* Web Window Browser http://www.opencube.com/example_wwb.htm $139 - без
исходного кода.
* jHelp ($20-650) http://w3.nai.net/~rvdi/jhelp/jhelp2/jhelp.html jHelp
это компонент HTML браузера написанный на Java, HTML 2.0
*(Часть 14) Я загрузил картинку из JPEG/GIF файла используя метод
Toolkit/Applet.createImage(URL/String), и (высота и ширина -1) это не
выводится на экран. Почему?
[*] AWT по умолчанию в этом случае ничего не делает.
Когда картинка впеpвые выводиться с использованием Component.drawImage(),
или ее размер запрошен, изображение начинает загружаться в дpугой нити
(Thread).
Во вpемя загpузки изобpажения, ImageObserver, опpеделенный в обpащении к
drawImage()/getHeight(), будет пеpиодически инфоpмиpоваться о статусе
загpузки изобpажения, путем вызова imageUpdate() метода.
В случае вызова Component.drawImage(), по умолчанию Component.imageUpdate()
должен планиpовать *дополнительный* вызов repaint(), когда изобpажение
будет полностью загpужено. Это означает, напpимеp, что следующий код не
будет pаботать:
class MyComponent extends Component {
...
public void paint(Graphics g) {
ImageFilter cropper=new CropImageFilter(0,0,16,16);
Image cropped_image=createImage(new
FilteredImageSource(image.getSource(),cropper));
g.drawImage(image,10,400,this); // эта стpока работает
// а эта не работает -
g.drawImage(cropped_image,400,15,this);
}
}
cropped_image не будет готов для рисования, но когда он полностью
создастся, пpоизойдет вызов repaint(), а там еще pаз cropped_image
создается (ууу, батенька...) и так далее.
(Обpатите внимание на то, что создание объектов подобно тому, как в методе
paint(), является очень плохим стилем. Так как это будет пpоисходить
довольно часто, pаботы по сбоpке мусоpа у jvm будет много.
Чтобы обойти эту проблему, Вы можете I) добавить все такие изображения в
MediaTracker и вызывать метод waitForAll(). II) выполнить свой собственный
ImageObserver интерфейс и подождать когда метод imageUpdate() будет вызван
со значением ALLBITS/FRAMEBITS. I) легче, но II) рекомендуется, из-за
появившихся данных о том, что MediaTracker не работает в некоторых средах.
Смотрите также в этом FAQ:
* Смотрите также Q13.12
* Посмотрите Q6.4 для примеров как переиспользовать объекты.
*(Часть 14) Как мне записать звук в апплете?
[*] Если у Вас Win95/NT, Вы можете использовать SoundBite - Аудиио Запись в
Апплетах См. http://www.scrawl.com/store/
Он обеспечивает простой доступ к аудио данным в массивах:
short[] left, right;
*(Часть 14) Java поддерживает PNG? Да. PNG - Portable Network Graphics -
обеспечивает беспатентную замену для GIFов и TIFFов. Если Вы сохраняете в
GIF, то не забудьте оплатить авторский гонорар фирме Unisys - см. их
страницу http://www.unisys.com/LeadStory/lzwfaq.html. Этот патент обясняет
почему GIFы - это плохой выбор для создания интернетовских изображений.
PNG формат определен в RFC 1950, 1951, 2083, не обремененных лицензиями или
патентами. См. также спецификацию PNG-1.1 на
ftp://swrinde.nde.swri.edu/pub/png/documents.
PNG формат поддерживается Java Advanced Imaging API, который является
частью Java 1.2 media API.
------------------------------
15. Работа в сети и распределенные объекты
Статьи о RMI
1. (Часть 15) Должен ли я вместо RMI использовать CORBA? Или использовать
DCOM? Или чего-нибудь еще?
[*] Если Ваши распределенные программы целиком написаны на Java, то
RMI обеспечивает более простой механизм, который позволяет пересылку
данных, исходящих от реальных Java объектов, и автоматическую "сборку
мусора" удаленных объектов.
Если Вам нужно подключиться к системе на C++ (или другом языке) или
Вам нужны специфические CORBA службы, то CORBA - Ваш выбор.
В июле 1997 года Sun анонсировала тот факт, что RMI начала более тесно
работать с CORBA. Sun просто добавила в RMI транспортный уровень IIOP
для поддержки взаимодействия с CORBA. Java программы могут
использовать RMI для доступа к CORBA объектам через IIOP, - протоколе
основанном на CORBA и разработанном OMG (Object Management Group). Это
очень хорошая новость для тех, кто строит гетерогенные системы
масштаба предприятия, хотя нужны некоторые добавления к IIOP для
поддержки вещей, которые использует RMI.
Деятели из Microsoft пытались продвигать DCOM заявляя о том, что RMI
изменяется или доживает последние дни. Это в корне неверно. RMI API
остается неизменной в своей текущей форме. Использование DCOM
ограничивает Вас тем, что код работает только на платформе Wintel, и
полностью отрицает философию Java "write once, run anywhere"
("написано однажды, исполняется везде"). Вы должне будете
перекомпилировать Ваш DCOM код, чтобы он работал на другой
Microsoft'овской платформе, типа Compaq (бывшего DEC) Alpha. Вы должны
остерегаться использования непереносимого, завязанного на одном
производителе кода. DCOM/DNA имеет ограничения для использования на
предприятии.
Другие сайты:
http://www.javaworld.com/javaworld/jw-10-1997/jw-10-corbajava.html
это хорошее введение в CORBA в мире Java.
http://www.objenv.com/cetus сравнение CORBA/RMI.
2. (Часть 15) Как мне заставить работать RMI между разными доменами?
[*] Все ответы на вопросы, относящиеся к прокси приведены ниже. Вы
должны сообщить программе где искать сервер. В этом случае Вы должны
запускать клиента с опцией:
-Djava.rmi.server.hostname=hostname.domainname
3. (Часть 15) Мне кажется, что RMI останавливает свою работу в JDK 1.1.
Почему такое происходит?
[*] Похоже что изменились правила того, где клиент ищет stub
класс(класс-заглушку), делая необходимым сброс Вашего class path на
клиенте после старта RMI registry. В частности, похоже на то, что rmic
не был обновлен для нового соглашения "don't need $CLASSPATH for
current dir" ("не нужен $CLASSPATH для текущего каталога"), в то время
как копмпилятор был обновлен. Вам лучше явно снять установки
classpath.
Другие сайты:
Есть несколько очень хороших источников от Sun, которые охватывают
много простых и сложных RMI проблем.
o Конечно, документация:
http://java.sun.com/products/jdk/1.1/docs/guide/rmi/index.html
o FAQи посвященные RMI и сериализации объектов
http://java.sun.com/products/jdk/rmi/faq.html
o Список рассылки RMI-USERS@JAVASOFT.COM, архив которого находится
на http://chatsubo.javasoft.com/email/rmi-users/ Посетите архив!
4. (Часть 15) После определенного числа соединений клиентов RMI к серверу
(55 на моей системе), последующие попытки RMI соединения неудачны.
Почему?
[*] Вы использовали лимит по умолчанию в 64 дескриптора открытых
файлов. Попытайтесь увеличить лимит в Вашей ОС.
Существует практический лимит RMI соединений, который налагается
масштабируемостью вииртуальной машины и производительностью
сериализации объектов. Это все исправлено в JDK 1.2. Актуальное число
активных клиентов, которое Вы можете обслуживать будет зависеть от
суммарной загрузки (workload mix) (т.е. от числа клиентов, как часто
они обращаются к серверу, и сколько работы выполняется при одном
обращении).
5. (Часть 15) Я использую RMI под Win95, и вызов Naming.lookup()
выполняется очень долго. Как от этого избавиться?
[*] (См. также первый ответ в нижеследующей секции, и отметьте, что
эта доработка в Windows у некоторых никогда не работает) Попытайтесь
добавить запись о машине в Ваш файл "hosts". Обычно этот файл
называется c:\windows\hosts (если он не существует, то должен быть
файл c:\windows\hosts.sam). Файл hosts ищется стеком TCP/IP перед
обращением к DNS, таким образом добавление записи может значительно
ускорить Ваши поиски (lookups). Файл hosts используется чтобы отразить
цифровые IP адреса на символьные. Чтобы Для того, чтобы указать имя
хоста "localhost" с адресом 127.0.0.1 (IP loopback адрес) добавьте
следующую строчку в Ваш файл hosts.
127.0.0.1 localhost
Работа с сетью в Windows