Чем файл java отличается от class
При компиляции класса Java создается файл класса с тем же именем. Однако, в случае вложенных классов или вложенных интерфейсов, он создает файл класса с именем, сочетающий внутренние и внешние имена классов, включая знак доллара.
В этой статье мы увидим все эти сценарии.
2. Подробная информация
В Java мы можем написать класс в классе. Класс, написанный внутри, называется вложенным классом, а класс, в который находится вложенный класс, называется внешним классом. Область вложенного класса ограничена областью его прилагаемого класса.
Аналогичным образом, мы можем объявить интерфейс в другом интерфейсе или классе. Такой интерфейс называется вложенным интерфейсом.
Мы можем использовать вложенные классы и интерфейсы для логически групповых сущностей, которые используются только в одном месте. Это не только делает наш код более читаемым и управляемым, но и увеличивает инкапсуляцию.
В следующих разделах мы подробно обсудим каждый из них. Мы также посмотрим на enums.
3. Вложенные классы
Вложенный класс является классом, который объявляется внутри другого класса или интерфейса. Каждый раз, когда нам нужен отдельный класс, но все же мы хотим, чтобы класс вел себя как часть другого класса, вложенный класс является лучшим способом его достижения.
Когда мы компилировать файл Java, он создает .class файл для класса ограждения и отдельные файлы классов для всех вложенных классов. Сгенерированный файл класса для класса ограждения будет иметь то же имя, что и класс Java.
Прежде всего, давайте создадим простой класс Java:
3.1. Статические вложенные классы
Как следует из названия, вложенные классы, которые объявлены статические называются статическими вложенными классами. В Java разрешается использовать только вложенные статический.
Статические вложенные классы могут иметь как статические, так и нестатическое поля и методы. Они привязаны к внешнему классу, а не к определенному экземпляру. Следовательно, нам не нужен экземпляр внешнего класса, чтобы получить к ним доступ.
Давайте объявим статический вложенный класс в нашем Внешние класс:
Когда мы компилировать наши Внешние класс , компилятор создает два файла класса, один для Внешние и еще один для СтатическаяНеупойс :
3.2. Нестатическое гнездование классов
Внешний класс может иметь только общедоступный или по умолчанию доступ, в то время как внутренний класс может быть частным, общедоступный, защищенный или с доступом по умолчанию. Тем не менее, они не могут содержать статических элементов. Кроме того, нам нужно создать экземпляр внешнего класса, чтобы получить доступ к внутреннему классу.
Давайте добавим еще один вложенный класс в наш Внешние класс:
Он генерирует еще один файл класса:
3.3. Местные классы
Локальные классы, также называемые внутренними классами, определяются в блоке – группе утверждений между сбалансированными скобками. Например, они могут быть в методе тела, для петля, или если статья. Область действия локального класса ограничена внутри блока так же, как и локальные переменные. Локальные классы при составлении отображаются как знак доллара с автоматически генерируемым номером.
Файл класса, генерируемый для локального класса, использует конвенцию именования — OuterClassName$1LocalClassName.class
Давайте объявим локальный класс в методе:
Компилятор создает отдельный класс файла для нашей Местные класс:
Аналогичным образом, мы можем объявить местный класс в если статья:
Несмотря на то, что мы создаем другой локальный класс с тем же названием, компилятор не жалуется. Он создает еще один класс файла и называет его с числом увеличилось:
3.4. Анонимные внутренние классы
Как следует из названия, анонимные классы являются внутренними классами без имени. Компилятор использует автоматически генерируемый номер после знака доллара, чтобы назвать файл класса.
Мы должны объявить и мгновенно анонимных классов в одном выражении в то же время. Они обычно расширяют существующий класс или реализуют интерфейс.
Рассмотрим быстрый пример:
Здесь мы создали анонимный класс, расширяя Внешние класс, и компилятор добавил еще один файл класса:
Аналогичным образом, мы можем реализовать интерфейс с анонимным классом.
Здесь мы создаем интерфейс:
Теперь давайте создадим анонимный класс:
Рассмотрим пересмотренный список файлов классов:
Как мы видим, файл класса генерируется для интерфейса HelloWorld и еще один для анонимного класса с именем Внешни$ $2 .
3.5. Внутренний класс в интерфейсе
Мы видели класс внутри другого класса, далее, мы можем объявить класс в интерфейсе. Если функциональность класса тесно связана с функциональностью интерфейса, мы можем объявить ее внутри интерфейса. Мы можем пойти на этот внутренний класс, когда мы хотим написать реализацию по умолчанию для методов интерфейса.
Давайте объявим внутренний класс внутри нашего HelloWorld интерфейс:
И компилятор генерирует еще один файл класса:
4. Вложенные интерфейсы
Вложенные интерфейсы, также известные как внутренние интерфейсы, объявлены внутри класса или другого интерфейса. Основная цель использования вложенных интерфейсов заключается в разрешении пространства имен путем группировки связанных интерфейсов.
Давайте посмотрим, как создать вложенные интерфейсы.
4.1. Интерфейс внутри интерфейса
Интерфейс, заявленный внутри интерфейса, неявно общедоступен.
Давайте объявим наш интерфейс внутри HelloWorld интерфейс:
Это позволит создать новый класс файла под названием HelloWorld$HelloSomeone для вложенного интерфейса.
4.2. Интерфейс внутри класса
Интерфейсы, заявленные внутри класса, могут принимать любой модификатор доступа.
Давайте объявим интерфейс внутри нашей Внешние класс:
Он будет генерировать новый файл класса с именем: Внешнийкласс$СтатическийКласс
5. Энумы
enum был введен в Java 5. Это тип данных, который содержит фиксированный набор констант, и эти константы являются примерами этого enum .
enum декларация определяет класс называется e num типа (также известного как перечисленный тип данных). Мы можем добавить много вещей к enum как конструктор, методы, переменные, и то, что называется постоянным конкретным телом класса.
Когда мы создаем enum , мы создаем новый класс, и мы неявно расширения Энум класса. Энум не может унаследовать любой другой класс или не может быть продлен. Тем не менее, он может реализовать интерфейс.
Мы можем объявить enum как отдельный класс, в его собственном файле источника или другой член класса. Давайте посмотрим все способы создания enum .
5.1. Энум как класс
Во-первых, давайте создадим простую enum :
Когда он будет составлен, компилятор создаст файл класса с именем Уровень для нашего enum.
5.2. Энум в классе
Теперь давайте объявим вложенный enum в нашем Внешние класс:
Компилятор создаст отдельный классный файл под названием Внешни$ цвет для нашего вложенного enum.
5.3. Enum в интерфейсе
Аналогичным образом, мы можем объявить enum в интерфейсе:
Когда HelloWorld интерфейс компилятора добавит еще один классный файл под названием HelloWorld$Прямой.
5.4. Энум в пределах enum
Мы можем объявить enum внутри другого enum :
Наконец, давайте посмотрим на генерируемые файлы класса:
Компилятор создает отдельный файл класса для каждого из enum Типы.
6. Заключение
В этой статье мы увидели различные конвенции именования, используемые для файлов класса Java. Мы добавили классы, интерфейсы и enums внутри одного файла Java и наблюдали, как компилятор создает отдельный файл класса для каждого из них.
Все используемые мною среды java, включая Javasoft JDKs, предполагают,
что исходный код класса с модификатором public хранится в файле с точно
таким же именем, как и имя класса, и расширением .java. Несоблюдение этого
соглашения может стать причиной многих проблем, которые выявятся во время
компиляции.
Начинающие студенты (программисты) часто забывают об этом соглашении,
и, например, задают имя файла в соответствии с заданием: Lab6.java.
Ошибочный пример: Имя файла Lab6.java -
Исправленный пример: Имя файла Airplane.java -
Заметьте: предполагается, что имя класса начинается с заглавной буквы. В
операционных системах, которые учитывают регистр символов в именах файлов,
могут появиться дополнительные проблемы, особенно у студентов, изучающих
Java под Unix, и привыкших к системе именования файлов в DOS. Класс
MotorVehicle должен храниться в файле MotorVehicle.java, но не в файле
motorvehicle.java.
2. Сравнение с помощью ==
В Java строки - это объекты класса java.lang.String. Оператор ==, применяемый
к объектам, проверяет на равенство ссылки на объекты! Иногда студенты не
понимают семантики оператора == и пытаются применить его для сравнения
строк.
Ошибочный пример:
Правильный способ сравнения 2х строк на равенство - это использование метода
equals() класса java.lang.String. Он возвращает true, если строки одинаковой
длины и состоят из одних и тех же символов. (Прим. перев.: вообще-то это не
гарантирует равенство. На самом деле, equals проверяет, равны ли посимвольно
2 строки)
Исправленный пример:
Эта ошибка - дурацкая, потому что на самом деле Java код получается
синтаксически правильным, а в итоге работает не так как нужно. Некоторые
студенты также пытаются применять операторы сравнения > и <=, вместо метода compareTo() класса java.lang.String. Эту ошибку обнаружить
проще, потому что она вызывает ошибки на этапе компиляции.
3. Забыл проинициализировать объекты, являющиеся элементами массива.
В Java массив объектов - это на самом деле массив ссылок на объекты. Создание
массива - это просто создание набора ссылок, ни на что не указывающих (то
есть равных null). Чтобы на самом деле создать "полноценный" массив
объектов, необходимо проинициализировать каждый элемент массива. Многие
студенты не понимают этого; они считают, что, создавая массив объектов, они
автоматически создают сами объекты. (В большинстве случаев, студенты
приносят эту концепцию из C++, где создание массива объектов приводит к
созданию самих объектов путем вызова их конструктора по умолчанию).
В примере ниже, студент хочет создать 3 объекта класса StringBuffer. Код
будет откомпилирован без ошибок, но в последней строке произойдет
исключение NullPointerException, где происходит обращение к несуществующему
объекту.
Ошибочный пример:
Чтобы не допускать эту ошибку, необходимо не забывать проинициализировать
элементы массива.
4. Помещение в один файл сразу нескольких классов с модификатором public
Файлы с исходным java-кодом определенным образом связаны с классами,
содержащимися в этих файлах. Связь можно охарактеризовать так:
5. Подмена поля класса локальной переменной.
Java позволяет объявлять внутри метода переменные, чье имя совпадает с
полями класса. В таком случае преимущество будет отдано локальным
переменным, и они будут использоваться вместо полей.
Компилятор выдаст ошибку, если переменные с одинаковыми именами будут
разных типов. Если же они одинаковых типов, ошибки компиляции не будет, и
будут непонятны причины неправильной работы программы.
Исправленный пример:
Еще одно возможное место появления этой ошибки - задание имени параметра
метода таким же, как и имя поля класса. Это хорошо выглядит в конструкторах,
но для обычных методов не подходит.
6. Забыл вызвать конструктор родителя (суперкласса).
Когда класс расширяет другой класс, каждый конструктор подкласса должен
вызвать какой-либо конструктор суперкласса. Обычно это достигается вызовом
конструктора суперкласса методом super(x), помещенным в первой строке
конструктора. Если в первой строке конвтруктора нет вызова super(x),
компилятор самостоятельно вставляет этот вызов, но без параметров: super().
(прим. перев.: х. се, а я и не знал )
Иногда студенты забывают об этом требовании. Обычно это не является
проблемой: вызов конструктора суперкласса вставляется компилятором и все
работает отлично. Однако если у суперкласса нет конструктора по умолчанию
(прим. перев.: то есть конструктора без параметров), то компилятор выдаст
ошибку. В примере ниже все конструкторы суперкласса java.io.File имеют
1 или 2 параметра:
Ошибочный пример:
Решением проблемы является вставка явного вызова правильного
конструктора суперкласса:
Исправленный пример:
Более неприятная ситуация возникает, когда у суперкласса есть конструктор
по умолчанию, но он не полностью инициализирует объект. В таком случае
код скомпилируется, но результат работы программы может быть
неправильным или может возникнуть исключение.
7. Неправильный перехват исключений
Система обработки исключений в Java достаточно мощная, но трудная для
понимания новичками. Студенты, хорошо владеющие C++ или Ada, обычно не
испытывают сложностей, в отличие от программистов C и Fortran. Примеры ниже
показывают некоторые распространенные ошибки.
Ошибочный пример:
Исправленный пример:
Порядок расположения блоков catch определяет очередность перехвата
исключений. Нужно учитывать, что каждый такой блок перехватит все
исключения указанного класса или любого его подкласса. Если не учесть
это, то можно получить недостижимый блок catch, на что укажет компилятор.
В примере ниже SocketException является подклассом IOException.
Ошибочный пример:
Исправленный пример:
Ошибочный пример:
Исправленный пример:
8. Метод доступа имеет тип void
9. Вызов нестатичных методов класса из метода main()
Ошибочный пример:
Есть 2 способа исправления ошибки: сделать нужный метод статичным
или создать экземпляр класса. Чтобы правильно выбрать нужный способ,
задайте себе вопрос: использует ли метод поля или другие методы класса.
Если да, то следует создать экземпляр класса и вызвать у него метод,
иначе следует сделать метод статичным.
Исправленный пример 1:
Исправленный пример 2:
10. Использование объектов класса String как параметров метода.
В Java класс java.lang.String хранит строковые данные. Однако, строки в Java
(1) обладают постоянством (то есть их нельзя изменять),
(2) являются объектами.
Поэтому с ними нельзя обращаться как просто с буфером символов, это
неизменяемые объекты. Иногда студенты передают строки, ошибочно
расчитывая на то, что строка-объект будет передана как массив символов
по ссылке (как в C или C++). Компилятор обычно не считает это ошибкой.
Ошибочный пример.
В примере выше студент хочет изменить значение локальной переменной test1,
присваивая новое значение параметру line в методе appendTodaysDate.
Естественно это не сработает. Значение line изменится, но значение test1
останется прежним.
Эта ошибка возникает из-за непонимания того, что (1) java объекты всегда
передаются по ссылке и (2) строки в Java неизменяемы. Нужно осмыслить, что
объекты-строки никогда не изменяют своего значения, а все операции над
строками создают новый объект.
11. Объявление конструктора как метода
Конструкторы объектов в Java внешне похожы на обычные методы. Единственные
отличия - у конструктора не указывается тип возвращаемого значения и
название совпадает с именем класса. К несчастью, Java допускает задание
имени метода, совпадающего с названием класса.
В примере ниже, студент хочет проинициализировать поле класса Vector list
при создании класса. Этого не произойдет, так как метод 'IntList' - это не
конструктор.
12. Забыл привести объект к нужному типу.
Как и во всех других объектно-ориентированных языках, в Java можно
обращаться к объекту как к его суперклассу. Это называется 'upcasting',
он выполняется в Java автоматически. Однако, если переменная, поле
класса или возвращаемое значение метода объявлено как суперкласс, поля
и методы подкласса будут невидимы. Обращение к суперклассу как к подклассу
называется 'downcasting', его нужно прописывать самостоятельно (то есть
привести объект к нужному подклассу).
Студенты часто забывают о приведении оъекта к подклассу. Чаще всего это
случается при использовании массивов объектов Object и коллекций из пакета
java.util (имеется ввиду Collection Framework). В примере ниже объект String
заносится в массив, а затем извлекается из массива для сравнения с другой
строкой. Компилятор обнаружит ошибку и не станет компилировать код, пока не
будет явно указано приведение типов.
Ошибочный пример.
Смысл приведения типов для некоторых оказывается затруднительным.
Особенно часто затруднения вызывают динамические методы. В примере выше,
если бы использовался метод equals вместо compareTo, компилятор бы не
выдал ошибку, и код бы правильно отработал, так как вызвался бы метод
equals именно класса String. Нужно понять, что динамическое связывание отличается от downcasting.
13. Использование интерфейсов.
Ошибочный пример.
Исправленный пример:
Связанная с этим ошибка: неправильный порядок блоков extends и implements.
Согласно спецификации Java, объявление о расширении класса должно идти
перед объявлениями о реализации интерфейсов. Также, для интерфейсов
ключевое слово implements нужно писать всего 1 раз, несколько интерфейсов
разделяются запятыми.
Еще ряд ошибочных примеров:
Исправленные примеры:
14. Забыл использовать значение, возвращаемое методом суперкласса
Java позволяет вызывать из подкласса аналогичный метод суперкласса с
помощью ключевого слова keyword. Иногда студентам приходится вызывать
методы суперкласса, но при этом часто они забывают использовать
возвращаемое значение. Особенно часто это случается у тех студентов,
которые ещ не осмыслили методы и их возвращаемые значения.
В примере ниже студент хочет вставить результат метода toString()
суперкласса в результат метода toString() подкласса. При этом он не
использует возвращаемое значение метода суперкласса.
Ошибочный пример.
Для исправления ошибки обычно достаточно присвоить возвращаемое
значение локальной переменной, и затем использовать эту переменную
при вычислении результата метода подкласса.
15. Забыл добавить AWT компоненты
В AWT используется простая модель построения графического интерфейса:
каждый компонент интерфейса должен быть сначала создан с помощью своего
конструктора, а затем помещен в окно приложения с помощью метода add()
родительского компонента. Таким образом, интерфейс на AWT получает
иерархическую структуру.
Студенты иногда забывают об этих 2х шагах. Они создают компонент, но
забывают разместить его в окне приожения. Это не вызовет ошибок на этапе
компиляции, компонент просто не отобразится в окне приложения.
17. Забыл запустить поток
В примере ниже, студент хочет создать анимацию картинки используя интерфейс
Runnable, но он забыл запустить поток.
Ошибочный пример.
Исправленный пример:
Жизненный цикл потока и связь потоков и классов, реализующих
интерфейс Runnable - это очень важная часть программирования на Java,
и не будет лишним заострить свое внимание на этом.
18. Использование запрещенного метода readLine() класса java.io.DataInputStream
В Java версии 1.0 для считывания строки текста необходимо было
использовать метод readLine() класса java.io.DataInputStream. В Java
версии 1.1 был добавлен целый набор классов для ввода-вывода,
обеспечивающий операции ввода-вывода для текста: классы Reader и
Writer. Таким образом с версии 1.1 для чтения строки текста надо
использовать метод readLine() класса java.io.BufferedReader. Студенты
могут не знат об этом изменении, особенно если они обучались по
старым книгам. (прим. перев. вообще-то уже не актуально. вряд ли кто-то
станет сейчас учиться по книгам 10летней давности.)
Старый метод readLine() оставлен в JDK, но объявлен как запрещенный, что
часто смущает студентов. Необходимо понять, что использование метода
readLine() класса java.io.DataInputStream не является неправильным, оно
просто устарело. Необходимо использовать класс BufferedReader.
Ошибочный пример.
Исправленный пример:
Есть и другие запрещенные методы в версиях, более поздних чем 1.0, но
этот встречается чаще всего.
19. Использование типа double как float
Как и в большинстве других языков, в Java поддерживаются операции
над числами с плавающей точкой (дробными числами). В Java есть 2
типа-примитива для чисел с плавающей точкой: double для чисел с
64-битной точностью по стандарту IEEE, и float, для чисел с 32-битной
точностью по стандарту IEEE. Трудность заключается в использовании
десятичных чисел, таких как 1.75, 12.9e17 или -0.00003 - компилятор
присваивает им тип double.
Java не производит приведение типов в операциях, в которых может произойти
потеря точности. Такое приведение типов должен осуществлять программист.
Например, Java не позволит присвоить значение типа int переменной типа byte
без приведения типов, как показано в примере ниже.
Так как дробные числа представлены типом double, и присваивание double
переменной типа float может привести к потере точности, компилятор
пожалуется на любую попытку использовать дробные числа как float. Так что
использование присваиваний, приведенных ниже, не даст классу
откомпилироваться.
Это присваивание сработало бы в C или C++, для Java все гораздо строже.
Есть 3 способа избавиться от этой ошибки.
Можно использовать тип double вместо типа float. Это наиболее простое
решение. На самом деле нет особого смысла использовать 32-битную
арифметику вместо 64-битной, разницу в скорости все равно скушает
JVM (к тому же в современных процессорах все дробные числа приводятся
к формату 80-битного регистра процессора перед любой операцией).
Единственный плюс использования float - это то, что они занимают
меньше памяти, что бывает полезно при работе с большим числом дробных
переменых.
Можно использовать модификатор для обозначения типа числа, чтобы
сообщить компилятору как хранить число. Модификатор для типа
float - 'f'. Таким образом, компилятор присвоит числу 1.75 тип double,
а 1.75f - float. Например:
Можно использовать явное приведение типов. Это наименее элегантный способ,
но он полезен при конвертации переменной типа double в тип float. Пример:
Подробнее о числах с плавающей точкой можно почитать здесь и здесь.
-- комментарий переводчика --
все.
в примере 10 на самом деле допущена ошибка 9. я ее сразу заметил, но
забыл написать примечание. а исправлять не стал чтобы не было
расхождений с первоисточником
Продолжаем разговор о том, как Java Virtual Machine работает внутри. В предыдущей статье (оригинал на анг.) мы рассмотрели подсистему загрузки классов. В этой статье мы поговорим о структуре class-файлов.
Как мы уже знаем, весь исходный код, написанный на языке программирования Java, сначала компилируется в байт-код с помощью компилятора javac , входящего в состав Java Development Kit. Байт-код сохраняется в бинарный файл в специальный class-файл. Затем эти class-файлы динамически (при необходимости) загружаются в память загрузчиком классов (ClassLoader).
Рисунок — компиляция исходного кода Java
Каждый файл с расширением .java компилируется как минимум в один файл .class . Для каждого класса, интерфейса и модуля, определенных в исходном коде, создается по одному .class файлу. Это также относится к интерфейсам и вложенным классам.
Примечание — для простоты файлы с расширением .class будем называть “class-файлами”.
Давайте напишем простую программу.
Запуск javac для этого файла приведет к появлению следующих файлов.
Как видите, для каждого класса и интерфейса создается отдельный class-файл.
Что внутри class-файла?
Class-файл содержит следующую информацию.
Магическое число, сигнатура. Первые четыре байта каждого class-файла всегда 0xCAFEBABE . Эти четыре байта идентифицируют class-файл Java.
Версия файла. Следующие четыре байта содержат мажорную и минорную версию файла. Вместе эти номера определяют версию формата class-файла. Если class-файл имеет основной мажорную версию M и минорную m, то мы обозначаем эту версию как M.m.
У каждой JVM есть ограничения по поддерживаемым версиям class-файлов. Например, Java 11 поддерживает major версию с 45 до 55, Java 12 — с 45 по 56.
Пул констант. Таблица структур, представляющих строковые константы, имена классов, интерфейсов, полей, методов и другие константы, которые есть в структуре ClassFile и ее подструктурах. Каждый элемент пула констант начинается с однобайтового тега, определяющего тип константы. В зависимости от типа константы следующие байты могут быть непосредственным значением константы или ссылкой на другой элемент в пуле.
Флаги доступа. Список флагов, которые указывают класс это или интерфейс, public или private, финальный класс или нет. Различные флаги, такие как ACC_PUBLIC , ACC_FINAL , ACC_INTERFACE , ACC_ENUM и т. д. описаны спецификации Java Virtual Machine Specification.
This class. Ссылка на запись в пуле констант.
Super class. Ссылка на запись в пуле констант.
Интерфейсы. Количество интерфейсов, реализованных классом.
Количество полей. Количество полей в классе или интерфейсе.
Поля. После количества полей следует таблица структур переменной длины. По одной для каждого поля с описанием типа поля и названия (со ссылкой на пул констант).
Количество методов. Количество методов в классе или интерфейсе. Это число включает только методы, которые явно определены в классе, без методов, унаследованных от суперклассов.
Методы. Далее находятся сами методы. Для каждого метода содержится следующая информация: дескриптор метода (тип возвращаемого значения и список аргументов), количество слов, необходимых для локальных переменных метода, максимальное количество слов стека, необходимых для стека операндов метода, таблицу исключений, перехватываемых методом, байт-коды метода и таблица номеров строк.
Количество атрибутов. Количество атрибутов в этом классе, интерфейсе или модуле.
Атрибуты. После количества атрибутов следуют таблицы или структуры переменной длины, описывающие каждый атрибут. Например, всегда есть атрибут “SourceFile”. Он содержит имя исходного файла, из которого был скомпилирован class-файл.
Хотя class-файл напрямую не человекочитаемый, в JDK есть инструмент под названием javap, который выводит его содержимое в удобном формате.
Давайте напишем простую программу на Java, указанную ниже.
Давайте скомпилируем эту программу с помощью javac , которая создаст файл HelloWorld.class , и используем javap для просмотра файла HelloWorld.class . Запустив javap с параметром -v (verbose) для HelloWorld.class получим следующий результат:
Здесь вы можете увидеть, что класс публичный ( public ) и у него в пуле констант 37 записей. Есть один атрибут (SourceFile внизу), класс реализует два интерфейса (Serializable, Cloneable), у него нет полей и есть два метода.
Возможно, вы заметили, что в исходном коде есть только один статический метод main, но class-файл говорит, что есть два метода. Вспомните конструктор по умолчанию — это конструктор без аргументов, добавленный компилятором javac , байт-код которого также виден в выводе. Конструкторы рассматриваются как методы.
// Компиляция этой Java-программы
// результат в нескольких файлах классов.
public static void main(String[] args)
System.out.println( "Class File Structure" );
Для компиляции:
После компиляции в соответствующей папке будет 3 файла классов с именем:
- Sample.class
- Student.class
- Test.class
Структура файла одного класса содержит атрибуты, которые описывают файл класса.
Представление структуры файла класса
Элементы файла класса следующие:
-
magic_number: первые 4 байта файла класса называются magic_number. Это предопределенное значение, которое JVM использует для определения того, создан ли файл .class допустимым компилятором или нет. Предопределенное значение будет в шестнадцатеричной форме, то есть 0xCAFEBABE .
Теперь давайте посмотрим, что произойдет, когда JVM не найдет действительное магическое число. Предположим, у нас есть файл .java с именем Sample.java, как показано ниже, и следуйте пошаговому процессу в вашей системе.
Шаг 1. Компиляция с использованием javac Sample.java.
Шаг 2: Теперь откройте файл Sample.class. Это будет выглядеть следующим образом.
Шаг 3: Теперь удалите хотя бы один символ из этого файла Sample.class в начале файла и сохраните его.
Шаг 4: Теперь попробуйте запустить эту команду с помощью Java Sample и увидеть волшебство т.е. вы получите во время выполнения исключения (см выделенного текста ниже изображения):
Примечание: это может варьироваться в зависимости от того, насколько вы удаляете данные файла .class .
Примечание . Файл .class, созданный компилятором более низкой версии, может быть выполнен JVM высокой версии, но файл .class, созданный компилятором более высокой версии, не может быть выполнен JVM более низкой версии. Если мы попытаемся выполнить, мы получим исключение во время выполнения.
Эта демонстрация для ОС Windows выглядит следующим образом:
Шаг 2: Теперь проверьте другую версию, которая может быть выше или ниже, чем уже установленная. эта ссылка для скачивания.
И установите это на свой ПК или ноутбук и запишите адрес установки.
Шаг 3: Откройте второе окно командной строки и задайте путь к папке bin установленного jdk, установленного во время второго шага. И проверьте версию компилятора Java и версию JVM.
Шаг 4: Теперь в первой командной строке скомпилируйте любой действительный файл .java . Например: см. Выше файл Sample.java . Скомпилируйте это как:
Шаг 5: Теперь на втором окне командной строки попробуйте запустить выше скомпилированный файл класса кода и посмотреть , что произойдет. Есть исключение во время выполнения, которое я выделил на рисунке ниже.
Примечание: Внутренняя версия jdk 1.5 означает 49.0, а 1.6 означает 50.0, а 1.7 означает 51.0 и т. Д. Версия файла класса, где цифры перед десятичной точкой представляют major_version, а цифры после десятичной точки представляют minor_version.
Пакеты. Использование пакетов в Java. Директивы import и package . Компилированные модули ( *.java ). Промежуточные .class файлы. Структура проекта. Использование стандартных библиотек Java
Содержание
- 1. Пакеты в Java
- 2. Как подключить пакет к существующему проекту в Java Eclipse? Структура проекта, который содержит пакеты
- 3. Какая общая форма вызова класса из пакета? Пример
- 4. Пример, который демонстрирует использование одного имени класса в разных пакетах и в разных каталогах (папках)
- 5. Какое назначение директивы import ? Общая форма. Пример
- 6. Как в директиве import указать, что могут быть использованы все классы из данного пакета?
- 7. Что собой представляет ключевое слово package ?
- 8. Что такое компилированный модуль в Java?
- 9. Какие требования к реализации компилированного модуля ( .java )?
- 10. Сколько классов могут быть реализованы в компилированном модуле?
- 11. Какое назначение файлов с расширением .class ?
- 12. Какие преимущества дает использование ключевых слов package и import ?
- 13. Пример, который демонстрирует структуру проекта и размещение компилированных модулей
- 14. Пример подключения класса из стандартной библиотеки Java
Поиск на других ресурсах:
1. Пакеты в Java
Каждый проект на языке Java в своей работе использует так называемые пакеты. Пакет – это отдельный модуль (пространство имен), которому соответствует одноименный каталог (папка). Пакет содержит библиотеки (группы) классов. Эти классы объединены между собой в одном пространстве имен или пакете. Количество реализованных классов в пакете неограничено. Классы, которые объявляются в пакете, реализуются в файлах с расширением *.java .
Пакеты могут иметь разнообразные уровни вложения (подкаталоги, подпапки). На разных уровнях вложения пакетов имена могут повторяться. Сохранение пакетов и файлов *.java в проекте осуществляется в виде древовидной структуры файловой системы.
2. Как подключить пакет к существующему проекту в Java Eclipse? Структура проекта, который содержит пакеты
В системе Java Eclipse подключение пакета есть стандартной командой. Предварительно может быть создан проект. В проекте может размещаться произвольное количество пакетов. В каждом пакете может быть произвольное количество классов.
Для создания пакета в некотором проекте используется команда
В результате откроется окно, изображенное на рисунке 1. В окне указывается имя проекта и пакета, который будет размещаться в этом проекте.
После выбора кнопки Finish происходит следующее. Проекту с именем MyProject03 соответствует специальная папка (каталог) с таким же именем. В этом каталоге создается несколько подкаталогов (подпапок). Один из них подкаталог с именем src. В этом подкаталоге создается каталог MyPackage , соответствующий создаваемому пакету.
3. Какая общая форма вызова класса из пакета? Пример
Чтобы обратиться к имени класса из пакета используется следующая общая форма:
- PackageName – имя пакета, в котором реализован класс с именем ClassName ;
- ClassName – имя класса.
Если пакет PackageName1 содержит подпапку (подпакет) с именем PackageName2 , то доступ к классу с именем ClassName имеет вид:
Подобным образом формируется доступ к классу в случае более более сложных уровней вложения.
Например. Пусть задан пакет с именем Figures . В пакете реализованы два класса с именами Circle и Rectangle . Тогда, фрагмент кода, который создает объекты этих классов будет следующий:
4. Пример, который демонстрирует использование одного имени класса в разных пакетах и в разных каталогах (папках)
На рисунке 2 изображена иерархия, которую могут образовывать пакеты.
Рис. 2. В проекте MyProject03 три пакета с именами MyPackage01 , MyPackage02 , MyPackage02.MyFolder02
Как видно из рисунка, в проекте MyProject03 созданы 3 пакета с именами MyPackage01 , MyPackage02 , MyPackage03 . Каждый из пакетов имеет класс с одинаковым именем MyClass01 . Таким образом происходит разделение одинаковых имен.
Ниже приведен демонстрационный код, создающий объекты классов MyClass01 , которые размещены в разных пакетах (папках), согласно схеме из рисунка 2.
5. Какое назначение директивы import ? Общая форма. Пример
Директива import позволяет сократить весьма длинные строки обращений к классам (интерфейсам, перечислениям), которые реализованы в пакетах.
В простейшем случае общая форма директивы import следующая:
- PackageName – имя пакета, который подключается;
- ClassFileName – имя файла с расширением *.java , в котором реализован класс или группа классов, к которому нужно обращаться по сокращенному имени.
Чтобы из любого другого класса, который размещается в другом пакете данного проекта, иметь доступ к методам файла Circle.java из пакета Figures нужно в начале указать:
Ниже приводится пример подключения и использования класса Circle из другого пакета и другого класса.
Если в пакете OtherPackage убрать строку
то объект класса Circle нужно было бы создавать следующим образом
6. Как в директиве import указать, что могут быть использованы все классы из данного пакета?
Бывают случаи, когда в пакете реализованы несколько классов (файлов с расширениями *.java ) и возникает необходимость подключения всего множества классов. В этом случае общая форма директивы import может быть следующая:
- Package – имя пакета с библиотекой подключаемых классов.
7. Что собой представляет ключевое слово package ?
Ключевое слово package задает имя библиотеки, в которую могут входить разное количество компилированных модулей. Компилированные модули – это файлы с расширением .java .
Если в модуле (файле с расширением .java ) указывается строка наподобие
то это означает, что данный модуль есть частью библиотеки с именем PackageName .
8. Что такое компилированный модуль в Java?
Каждый компилированный модуль может содержать не большее одного открытого класса. Компилированный модуль входит в состав пакета. В пакете может быть любое количество компилированных модулей.
9. Какие требования к реализации компилированного модуля ( .java )?
К реализации компилированного модуля предъявляются следующие требования:
- имя компилированного модуля должно иметь расширение .java ;
- имя компилированного модуля без учета расширения .java должно совпадать с именем открытого ( public ) класса, который реализован в этом модуле;
- имя компилированного модуля и имя реализованного в нем открытого класса должны начинаться из большой буквы;
- в компилированном модуле может быть не более одного открытого ( public ) класса. Если в компилированном модуле есть еще другие (вспомогательные) классы, то они должны быть скрытыми (без модификатора public ).
10. Сколько классов могут быть реализованы в компилированном модуле?
Компилированный модуль может иметь любое количество классов. Однако, открытым ( public ) должен быть только один класс. Имя этого класса должно совпадать с именем компилированного модуля.
11. Какое назначение файлов с расширением .class ?
Файлы с расширением .class получаются в результате компиляции файлов с расширением .java (компилированных модулей). Файлы с расширением .class есть промежуточными файлами, которые затем объединяются компоновщиком в исполнительный модуль.
Например, в языке программирования Delphi временные скомпилированные файлы имели расширение *.dcu . В языке программирования C++ временные скомпилированные файлы имели расширение .obj . После компоновки из этих файлов получался *.exe файл (исполняемый модуль).
После этого, файлы с расширением .class объединяются в пакет и сжимаются утилитой JAR . Это все осуществляет интерпретатор Java.
12. Какие преимущества дает использование ключевых слов package и import ?
Использование ключевых слов package и import позволяет удобно поделить пространство имен таким образом, чтобы предотвратить возникновению конфликтов между разными разработчиками классов на Java.
13. Пример, который демонстрирует структуру проекта и размещение компилированных модулей
Пусть в Java Eclipse создан проект с именем Project03 . Проект размещается в папке по умолчанию, которая используется системой Java Eclipse. В проекте созданы 2 пакета с именами Figures , OtherPackage . В пакетах реализованы по одному компилированному модулю соответственно с именами Circle.java и MyClass.java . Рисунок 3 отображает структуру проекта Project03 системы Java Eclipse.
Рис. 3. Отображение структуры проекта Project03 в окне Package Explorer
В файле (модуле) Circle.java разработан класс Circle . В файле MyClass.java разработан класс MyClass . В классе MyClass из пакета OtherPackage (см. рис. 3) подключается модуль Circle.java с помощью директивы
Если компилятор встречает директиву import , он формирует полное имя с учетом пути к каталогу, который записан по умолчанию в системе.
таким образом, получается полное имя модуля Circle.java :
По желанию путь к каталогу (папке) по умолчанию можно изменить.
14. Пример подключения класса из стандартной библиотеки Java
Чтобы обратиться к имени класса из стандартной библиотеки Java используется один из двух способов:
- Использование полной формы или полного имени класса в библиотеке.
- Использование сокращенной формы имени класса. В этом случае, полное имя пакета с нужным классом, может быть задано явным образом с помощью директивы import .
Полная форма обращения к имени нужного класса в простейшем случае выглядит так
Например. Фрагмент кода, который использует класс Vector из стандартной библиотеки Java. В данном случае стандартная библиотека Java носит имя java.util .
В вышеприведенном коде происходит обращение к классу Vector по полному имени:
Читайте также: