faqs.org.ru

 Главная > Программирование > Web-программирование >

FAQ по языку Java

Секция 3 из 7 - Предыдущая - Следующая
Все секции - 1 - 2 - 3 - 4 - 5 - 6 - 7

   * Бесплатные виртуальные 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, это - самое неприятное.
Обьектно-ориентированное программирование основано исключительно на
указателях. Иначе говоря, доступ к обьектам происходит всегда через
указатели, и никогда - напрямую. Но это указатели названы "ссылками" и
всегда автоматически "разыменовываются".

В 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) Как предаются параметры - по значению или по ссылке?

[*] Все параметры (как базовые типы так и ссылки на обьекты) передаются по
значению. Но не все так просто, так как работа с обьектами производится по
ссылке. Поэтому можно сказать, что обьекты передаются по ссылке (а ссылка
передается по значению). Это - следствие того, что переменные содержат не
сами обьекты, а ссылки на них (см. предыдущий вопрос о связных списках).

Примечание: Когда параметр примитивного типа (int, char и т.п.) изменяется
внутри метода, исходная переменнная _не меняется_. Тем не менее, поля
обьектов меняются при изменении поля параметра в вызваном методе.

См. в FAQ:
Как работать со связными списками, если в Java нет указателей?
См. также:
JLS 8.4.1 Formal Parameters

*(Часть. 6) Что такое "литералы класса"? (class litrals)

[*] Новый синтаксис в 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. Несовпадения имен пакетов гарантированы использование имени домена в
     обратном порядке: 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;

[*] Это никак не отразится на файле класса или скорости выполнения. Импорт
- это просто сокращение для для полных имен классов пакете (как в примере
выше). Импортирование лишнего класса не приводит к его загрузке при
выполнении. Нет никаких отрицательных эффектов при использовании формы "*".
Файл класса всегда содержит имена только используемых пакетов, и загрузчик
будет искать те классы, которые необходимы.

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

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

import java.rmi.server.RemoteObject;



вместо

import java.rmi.server.*;



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

java.rmi.server.RemoteObject ro;



Это становится немного длинее, когда вы инициализируете пременную:

        java.rmi.server.RemoteObject ro

                  = new java.rmi.server.RemoteObject();



Вы всегда можете выбрать, набрать ли вам полное имя или использовать
импорт.

Есть еще одна причина не использовать "*". Если вы импортируете два пакета,
имеющие классы с одинаковыми именами и вы хотите использовать толькоодин из
них. Например,

    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 методов не
связывались динамически, как это должно было бы быть. Он добавлял очень
немного возможностей к языку. Использовать существующие ключевые слова с
другим значением- всегда плохая идея. Использовать два из них вместе - еще
хуже.

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

Наследование (inheritance)

*Каково различие между наследованием и абстрактным классом?

[*] Некоторое семантическое отличие: Модель абстрактных суперклассов
моделирует связи "есть", тогда как интерфейс моделирует связь "имеет".
Правило таково - если это подтип, наследуйте, иначе - реализуйте.

Но, в отсутствие реальных характеристик для выделения объектов из их
свойств и предков, это становится аргументом, действующим и в ту и в другую
сторону. В этом случае Вы должны посмотреть на практическое различие в Java
(в сравнении с C++) .

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

  1. Оба определяют описания методов, которые будет иметь производный
     класс.
  2. Абстрактный класс может также определять частичную реализацию.
  3. Класс может реализовать много интерфейсов, но наследуется только от
     одного класса.

Более детально:

  1. Описания методов И интерфейсы и абстрактные классы позволяют
     обращаться с классом производного типа как с классом типа, от которого
     он унаследован. Оба определяют набор доступных методов способом,
     который может быть усилен механизмом проверки типа. Это обычно
     используется для того, чтобы позволить различным (производным) типам
     иметь одинаковове поведение (так как они все наследники, т.е. они все
     имеют определенные методы). Например, все типа java,* могут быть
     распечатаны как String, так как Object, суперкласс всех типов java.*,
     имеет метод toString(). Аналогично, все типы, которые реализуют
     интерфейс Observable доступны как Observer для сигнализации, что
     событие произошло. Это позволяет алгоритму или сервису работать с
     различными (производными) типами, так как будто они одного (от
     которого унаследованы) типа.
     Этот механизм поддерживает не только полиморфизм (один объект
     обрабатывается как другой), но и дифференциацию. В любом случае типы
     (производные) могут реализовывать метод способом, соответствующим
     этому типу. Однако, Вы не обязаны перекрывать унаследованную
     функциональность, но Вы должны реализовать методы интерфейса, так что
     если Вы ожидаете значительные различия, то может быть оправданным
     использование интерфейса.
     Наконец, этот механизм поддерживает слабый вариант контроля доступа.
     Только унаследованные методы доступны вызывающему, который имеет
     доступ к описанию суперкласса или интерфейса. Это слабость,так как
     возможно указать конкретный тип, если он известен. В любом случае, это
     уменьшает сложность.
  2. Наследование реализации Наследование реализации полезно, когда код
     должен быть разделяемым. Такое случается когда производные типы
     различаются в функциональности очень немного, или когда комплексный
     набор интерфейсов методов может быть реализован через взаимные ссылки
     с относительно небольшим количеством методов, реализованных в
     производных типах. Вы можете также повторно использовать код, сделав
     Ваш класс содержащим или использующим объект другого типа, который
     реализует этот код, но это не дает возможности вызывающим использовать
     вас особым образом. Получение и функциональности и возможности быть
     использованным, как суперкласс - сущность отношений тип/подтип.
  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];


Вы должны заполнить ссылку на объект перед ее использованием. Например,

        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() в Си.

На многих 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);
    }


На Sun'овской JVM этот код выполняется быстрее, чем обычный цикл, и даже
быстрее, чем на JIT-компиляторах, потому что контроль выхода за границы
массива здесь проводится всего лишь до log2(array.length) раз. Причем этот
способ работает, даже если размер массива не является степенью двух.

                       ------------------------------

7. I/O

  1. (Sect. 7) Как прочитать файл, содержащий числа в символьной форме?

     [*] Есть несколько способов. Ниже приведен один из них. Допустим, файл
     называется "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. Недостаток этого
     способа в том, что искусственно усложняется простейшее решение для
     ввода/вывода с клавиатуры. В ближайшем будущем 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) может работать как
     терминальный сервер, имея простое серверное ПО, но покупка
     специализированного аппаратного обеспечения значительно упростит
     задачу.

Секция 3 из 7 - Предыдущая - Следующая

Вернуться в раздел "Web-программирование" - Обсудить эту статью на Форуме
Главная - Поиск по сайту - О проекте - Форум - Обратная связь

© faqs.org.ru