Что такое import в java
Перейти к содержимому

Что такое import в java

Как импортировать пакет в java

Чтобы импортировать пакет в Java , вам нужно использовать ключевое слово import в начале файла, перед объявлением класса.

import java.util.ArrayList; import java.util.List; public class MyClass  // код класса > 

Здесь мы импортируем пакеты java.util.ArrayList и java.util.List , которые позволяют нам использовать классы ArrayList и List внутри класса MyClass .

Если вам нужно импортировать все классы из пакета, вы можете использовать символ звездочки * .

import java.util.*; public class MyClass  // код класса > 

Здесь мы импортируем все классы из пакета java.util , что дает нам возможность использовать любой класс в этом пакете внутри класса MyClass .

29. Java – Пакеты

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

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

Некоторые из существующих пакетов в Java:

  • java.lang – связывает фундаментальные классы.
  • java.io – классы для ввода и выводные функции связаны в этом пакете.

Программисты могут определять их пакеты для связывания групп классов/интерфейсов и т.д. Группировка связанных классов, реализованных вами, является хорошим практическим решением, т.к. программист сможет легко определить, что классы, интерфейсы, перечисления и аннотации связаны.

Так как пакет создаёт новое пространство имён, в нём не будет никаких конфликтов с именами в других пактах. Используя пакеты, легче предоставить управление доступом и легче найти связанные классы.

Создание пакета

Как создать пакет в Java? Во время создания пакета вы должны выбрать ему имя и включить оператор package вместе с этим именем поверх каждого исходного файла, который содержит классы, интерфейсы, перечисления и типы аннотаций, которые вы хотите включить в пакет.

Оператор package должен быть первой строкой в исходном файле. Может быть только один запрос package в каждом исходном файле, и он применяется ко всем типам в этом файле.

Если оператор package не использован, тогда классы, интерфейсы, перечисления и типы аннотаций будут помещены в текущий пакет по умолчанию.

Чтобы скомпилировать программы на Java с операторами package, то вам нужно использовать опцию –d, как показано ниже.

javac -d Destination_folder file_name.java 

Затем в указанном месте назначения создается папка с указанным именем пакета, а файлы скомпилированных классов будут помещены в эту папку.

Пример 1

Давайте взглянем на пример, которые создаёт пакет под названием animals. Полезно использовать имена пакетов с маленькой буквы, чтобы избежать конфликтов с именами классов и интерфейсов.

В следующем примере пакета содержится интерфейс с названием animals.

/* File name : Animal.java */ package animals; interface Animal

Теперь давайте реализуем вышеприведённый интерфейс в этом же пакете animals:

package animals; /* File name : MammalInt.java */ public class MammalInt implements Animal < public void eat() < System.out.println("Млекопитающее кушает"); >public void travel() < System.out.println("Млекопитающее путешествует"); >public int noOfLegs() < return 0; >public static void main(String args[]) < MammalInt m = new MammalInt(); m.eat(); m.travel(); >> 

А сейчас скомпилируем java-файлы, как показано ниже:

$ javac -d . Animal.java $ javac -d . MammalInt.java 

Теперь пакет/папка с именем animals будет создана в текущей директории, и файлы классов будут помещены в неё.

Вы можете запустить файл класса внутри пакета и получить результат, указанный ниже.

Mammal eats Mammal travels 

Ключевое слово import

Если класс хочет использовать другой класс в том же пакете, то не нужно использовать имя пакета. Классы в одном пакете могут найти друг друга без особого синтаксиса.

Пример 2

Итак, класс под названием Boss добавлен в пакет payroll, который уже содержит Employee. Boss может обратиться к классу Employee без использования префикса payroll, как показано в следующем классе Boss.

package payroll; public class Boss < public void payEmployee(Employee e) < e.mailCheck(); >> 

Что произойдёт, если класс Employee не будет включен в пакет payroll? Тогда класс Boss должен будет использовать одну из следующих техник для обращения к классу в другом пакете:

  • Можно использовать полноценное имя класса. К примеру:
payroll.Employee 
  • Импорт пакета. В Java пакет может быть импортирован с помощью ключевого слова import и wild card. (*). Например:
import payroll.*; 
  • Сам класс можно импортировать с помощью ключевого слова import. Взглянем:
import payroll.Employee; 

Примечание: Файл класса может содержать любое количество операторов импорта (import). Операторы импорта (import) должны появляться после оператора пакета (package) и перед объявлением класса.

Структура директории пакетов

Происходят два явления, когда класс помещён в пакет:

  • Имя пакета в Java становится частью имени класса, как мы только что выяснили в прошлой секции.
  • Имя пакета должно соответствовать со структурой директории, где находится соответствующий байт-код.

Есть лёгкий способ работы с вашими файлами в Java:

Поместите исходный код для класса, интерфейса, перечисления или типа аннотации в текстовый файл, имя которого является простым именем типа с расширением .java.

// File Name : Car.java package vehicle; public class Car < // Class implementation. >

Теперь поместите исходный файл в директорию, имя которой отражает имя пакета, к которому принадлежит класс:

. \vehicle\Car.java 

Полноценное имя класса и пути будет выглядеть так:

  • Имя класса – vehicle.Car
  • Путь к файлу – vehicle\Car.java (в Windows)

В общем, компания использует своё обратное доменное имя в Интернете для своих именований пакетов в Java.

Например: доменное имя компании называется apple.com, тогда все имена их пакетов будут начинаться с com.apple. Каждый компонент имени пакета соответствует поддиректории.

Например: у компании есть пакет com.apple.computers, в котором содержится исходный файл Dell.java, тогда он содержится в серии поддиректорий, как указано здесь:

. \com\apple\computers\Dell.java 

Компилятор создаёт во время компиляции другой файл вывода для каждого класса, интерфейса и перечисления, определённых в нём. Обычное имя файла вывода состоит из имени типа и расширения .class.

// File Name: Dell.java package com.apple.computers; public class Dell < >class Ups

Теперь скомпилируйте это, используя опцию –d:

$javac -d . Dell.java 

Файлы скомпилируются следующим образом:

.\com\apple\computers\Dell.class .\com\apple\computers\Ups.class 

Вы можете импортировать все классы и интерфейсы, определённые в \com\apple\computers\ вот так:

import com.apple.computers.*; 

Как и исходные файлы .java, скомпилированные файлы .class должны быть в серии директорий, которые отражают имя пакета. Однако путь к файлам .class не должен быть таким же, как путь к исходным файлам .java. Вы можете разделить директории для классов и источников вот так:

\sources\com\apple\computers\Dell.java \classes\com\apple\computers\Dell.class 

Делая так, становится возможным предоставить доступ к директории классов другим программистам, не раскрывая ваши источники. Вам также нужно распределить файлы классов и источников таким образом, чтобы компилятор и Java Virtual Machine (JVM) могли найти все типы, которые использует ваша программа.

Полный путь к директории классов

\classes называется путь класса и устанавливается с системной переменной CLASSPATH. И компилятор, и JVM создают путь к вашим файлам .class, добавляя имя пакета к пути класса.

\classes – это путь класса, а имя пакета com.apple.computers, тогда компилятор и JVM будут искать файлы .class в

Путь класса может включать несколько путей. Множество путей должны быть отделены точкой с запятой (Windows) или двоеточием (Unix). По умолчанию компилятор и JVM ищут текущую директорию и JAR-файл, содержащий классы платформы Java, чтобы эти директории были автоматически включены в путь класса.

Установить системную переменную CLASSPATH

Чтобы отобразить текущую CLASSPATH переменную, используйте следующие команды в Windows и UNIX (Bourne shell):

  • Для Windows – C:\> set CLASSPATH
  • Для UNIX – % echo $CLASSPATH

Чтобы удалить содержимое переменной CLASSPATH, используйте:

  • Для Windows – C:\> set CLASSPATH =
  • Для UNIX – % unset CLASSPATH; export CLASSPATH

Чтобы установить системную переменную CLASSPATH:

Оглавление

  • 1. Java – Самоучитель для начинающих
  • 2. Java – Обзор языка
  • 3. Java – Установка и настройка
  • 4. Java – Синтаксис
  • 5. Java – Классы и объекты
  • 6. Java – Конструкторы
  • 7. Java – Типы данных и литералы
  • 8. Java – Типы переменных
  • 9. Java – Модификаторы
  • 10. Java – Операторы
  • 11. Java – Циклы и операторы цикла
  • 11.1. Java – Цикл while
  • 11.2. Java – Цикл for
  • 11.3. Java – Улучшенный цикл for
  • 11.4. Java – Цикл do..while
  • 11.5. Java – Оператор break
  • 11.6. Java – Оператор continue
  • 12. Java – Операторы принятия решений
  • 12.1. Java – Оператор if
  • 12.2. Java – Оператор if..else
  • 12.3. Java – Вложенный оператор if
  • 12.4. Java – Оператор switch..case
  • 12.5. Java – Условный оператор (? 🙂
  • 13. Java – Числа
  • 13.1. Java – Методы byteValue(), shortValue(), intValue(), longValue(), floatValue(), doubleValue()
  • 13.2. Java – Метод compareTo()
  • 13.3. Java – Метод equals()
  • 13.4. Java – Метод valueOf()
  • 13.5. Java – Метод toString()
  • 13.6. Java – Метод parseInt()
  • 13.7. Java – Метод Math.abs()
  • 13.8. Java – Метод Math.ceil()
  • 13.9. Java – Метод Math.floor()
  • 13.10. Java – Метод Math.rint()
  • 13.11. Java – Метод Math.round()
  • 13.12. Java – Метод Math.min()
  • 13.13. Java – Метод Math.max()
  • 13.14. Java – Метод Math.exp()
  • 13.15. Java – Метод Math.log()
  • 13.16. Java – Метод Math.pow()
  • 13.17. Java – Метод Math.sqrt()
  • 13.18. Java – Метод Math.sin()
  • 13.19. Java – Метод Math.cos()
  • 13.20. Java – Метод Math.tan()
  • 13.21. Java – Метод Math.asin()
  • 13.22. Java – Метод Math.acos()
  • 13.23. Java – Метод Math.atan()
  • 13.24. Java – Метод Math.atan2()
  • 13.25. Java – Метод Math.toDegrees()
  • 13.26. Java – Метод Math.toRadians()
  • 13.27. Java – Метод Math.random()
  • 14. Java – Символы
  • 14.1. Java – Метод Character.isLetter()
  • 14.2. Java – Метод Character.isDigit()
  • 14.3. Java – Метод Character.isWhitespace()
  • 14.4. Java – Метод Character.isUpperCase()
  • 14.5. Java – Метод Character.isLowerCase()
  • 14.6. Java – Метод Character.toUpperCase()
  • 14.7. Java – Метод Character.toLowerCase()
  • 14.8. Java – Метод Character.toString()
  • 15. Java – Строки
  • 15.1. Java – Метод charAt()
  • 15.2. Java – Метод compareTo()
  • 15.3. Java – Метод compareToIgnoreCase()
  • 15.4. Java – Метод concat()
  • 15.5. Java – Метод contentEquals()
  • 15.6. Java – Метод copyValueOf()
  • 15.7. Java – Метод endsWith()
  • 15.8. Java – Метод equals()
  • 15.9. Java – Метод equalsIgnoreCase()
  • 15.10. Java – Метод getBytes()
  • 15.11. Java – Метод getChars()
  • 15.12. Java – Метод hashCode()
  • 15.13. Java – Метод indexOf()
  • 15.14. Java – Метод intern()
  • 15.15. Java – Метод lastIndexOf()
  • 15.16. Java – Метод length()
  • 15.17. Java – Метод matches()
  • 15.18. Java – Метод regionMatches()
  • 15.19. Java – Метод replace()
  • 15.20. Java – Метод replaceAll()
  • 15.21. Java – Метод replaceFirst()
  • 15.22. Java – Метод split()
  • 15.23. Java – Метод startsWith()
  • 15.24. Java – Метод subSequence()
  • 15.25. Java – Метод substring()
  • 15.26. Java – Метод toCharArray()
  • 15.27. Java – Метод toLowerCase()
  • 15.28. Java – Метод toString()
  • 15.29. Java – Метод toUpperCase()
  • 15.30. Java – Метод trim()
  • 15.31. Java – Метод valueOf()
  • 15.32. Java – Классы StringBuilder и StringBuffer
  • 15.32.1. Java – Метод append()
  • 15.32.2. Java – Метод reverse()
  • 15.32.3. Java – Метод delete()
  • 15.32.4. Java – Метод insert()
  • 15.32.5. Java – Метод replace()
  • 16. Java – Массивы
  • 17. Java – Дата и время
  • 18. Java – Регулярные выражения
  • 19. Java – Методы
  • 20. Java – Потоки ввода/вывода, файлы и каталоги
  • 20.1. Java – Класс ByteArrayInputStream
  • 20.2. Java – Класс DataInputStream
  • 20.3. Java – Класс ByteArrayOutputStream
  • 20.4. Java – Класс DataOutputStream
  • 20.5. Java – Класс File
  • 20.6. Java – Класс FileReader
  • 20.7. Java – Класс FileWriter
  • 21. Java – Исключения
  • 21.1. Java – Встроенные исключения
  • 22. Java – Вложенные и внутренние классы
  • 23. Java – Наследование
  • 24. Java – Переопределение
  • 25. Java – Полиморфизм
  • 26. Java – Абстракция
  • 27. Java – Инкапсуляция
  • 28. Java – Интерфейсы
  • 29. Java – Пакеты
  • 30. Java – Структуры данных
  • 30.1. Java – Интерфейс Enumeration
  • 30.2. Java – Класс BitSet
  • 30.3. Java – Класс Vector
  • 30.4. Java – Класс Stack
  • 30.5. Java – Класс Dictionary
  • 30.6. Java – Класс Hashtable
  • 30.7. Java – Класс Properties
  • 31. Java – Коллекции
  • 31.1. Java – Интерфейс Collection
  • 31.2. Java – Интерфейс List
  • 31.3. Java – Интерфейс Set
  • 31.4. Java – Интерфейс SortedSet
  • 31.5. Java – Интерфейс Map
  • 31.6. Java – Интерфейс Map.Entry
  • 31.7. Java – Интерфейс SortedMap
  • 31.8. Java – Класс LinkedList
  • 31.9. Java – Класс ArrayList
  • 31.10. Java – Класс HashSet
  • 31.11. Java – Класс LinkedHashSet
  • 31.12. Java – Класс TreeSet
  • 31.13. Java – Класс HashMap
  • 31.14. Java – Класс TreeMap
  • 31.15. Java – Класс WeakHashMap
  • 31.16. Java – Класс LinkedHashMap
  • 31.17. Java – Класс IdentityHashMap
  • 31.18. Java – Алгоритмы Collection
  • 31.19. Java – Iterator и ListIterator
  • 31.20. Java – Comparator
  • 32. Java – Дженерики
  • 33. Java – Сериализация
  • 34. Java – Сеть
  • 34.1. Java – Обработка URL
  • 35. Java – Отправка Email
  • 36. Java – Многопоточность
  • 36.1. Java – Синхронизация потоков
  • 36.2. Java – Межпоточная связь
  • 36.3. Java – Взаимная блокировка потоков
  • 36.4. Java – Управление потоками
  • 37. Java – Основы работы с апплетами
  • 38. Java – Javadoc

Что такое import в java

Как правило, в Java классы объединяются в пакеты. Пакеты позволяют организовать классы логически в наборы. По умолчанию java уже имеет ряд встроенных пакетов, например, java.lang , java.util , java.io и т.д. Кроме того, пакеты могут иметь вложенные пакеты.

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

Чтобы указать, что класс принадлежит определенному пакету, надо использовать директиву package , после которой указывается имя пакета:

package название_пакета;

Как правило, названия пакетов соответствуют физической структуре проекта, то есть организации каталогов, в которых находятся файлы с исходным кодом. А путь к файлам внутри проекта соответствует названию пакета этих файлов. Например, если классы принадлежат пакету mypack, то эти классы помещаются в проекте в папку mypack.

Классы необязательно определять в пакеты. Если для класса пакет не определен, то считается, что данный класс находится в пакете по умолчанию, который не имеет имени.

Например, создадим в папке для исходных файлов каталог study . В нем создадим файл Program.java со следующим кодом:

package study; public class Program < public static void main(String[] args) < Person kate = new Person("Kate", 32); kate.displayInfo(); >> class Person < String name; int age; Person(String name, int age)< this.name = name; this.age = age; >void displayInfo() < System.out.printf("Name: %s \t Age: %d \n", name, age); >>

Директива package study в начале файла указывает, что классы Program и Person, которые здесь определены, принадлежат пакету study.

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

Для компиляции программы вначале в командной строке/терминале с помощью команды cd перейдем к папке, где находится каталог study.

cd C:\java

Например, в моем случае это каталог C:\java (то есть файл с исходным кодом расположен по пути C:\java\study\Program.java).

Для компиляции выполним команду

javac study\Program.java

После этого в папке study появятся скомпилированные файлы Program.class и Person.class. Для запуска программы выполним команду:

java study.Program

Компиляция пакетов в Java

Импорт пакетов и классов

Если нам надо использовать классы из других пакетов, то нам надо подключить эти пакеты и классы. Исключение составляют классы из пакета java.lang (например, String ), которые подключаются в программу автоматически.

Например, знакомый по прошлым темам класс Scanner находится в пакете java.util , поэтому мы можем получить к нему доступ следующим способом:

java.util.Scanner in = new java.util.Scanner(System.in);

То есть мы указываем полный путь к файлу в пакете при создании его объекта. Однако такое нагромождение имен пакетов не всегда удобно, и в качестве альтернативы мы можем импортировать пакеты и классы в проект с помощью директивы import , которая указывается после директивы package:

package study; import java.util.Scanner; // импорт класса Scanner public class Program < public static void main(String[] args) < Scanner in = new Scanner(System.in); >>

Директива import указывается в самом начале кода, после чего идет имя подключаемого класса (в данном случае класса Scanner).

В примере выше мы подключили только один класс, однако пакет java.util содержит еще множество классов. И чтобы не подключать по отдельности каждый класс, мы можем сразу подключить весь пакет:

import java.util.*; // импорт всех классов из пакета java.util

Теперь мы можем использовать любой класс из пакета java.util.

Возможна ситуация, когда мы используем два класса с одним и тем же названием из двух разных пакетов, например, класс Date имеется и в пакете java.util , и в пакете java.sql . И если нам надо одновременно использовать два этих класса, то необходимо указывать полный путь к этим классам в пакете:

java.util.Date utilDate = new java.util.Date(); java.sql.Date sqlDate = new java.sql.Date();

Статический импорт

В java есть также особая форма импорта — статический импорт. Для этого вместе с директивой import используется модификатор static :

package study; import static java.lang.System.*; import static java.lang.Math.*; public class Program < public static void main(String[] args) < double result = sqrt(20); out.println(result); >>

Здесь происходит статический импорт классов System и Math. Эти классы имеют статические методы. Благодаря операции статического импорта мы можем использовать эти методы без названия класса. Например, писать не Math.sqrt(20) , а sqrt(20) , так как функция sqrt() , которая возвращает квадратный корень числа, является статической. (Позже мы рассмотрим статические члены класса).

То же самое в отношении класса System: в нем определен статический объект out , поэтому мы можем его использовать без указания класса.

Фундаментальное руководство по пакетам в Java

  1. Что такое пакет
  2. Примеры пакетов
  3. Назначение пакетов
  4. Правила создания пакетов
  5. Импорт классов
  6. Компиляция и запуск
  7. Возможные ошибки и их решение

Введение

Пакеты в Java появились с самого начала, во времена, когда язык еще назывался Oak. Их описание уже присутствует в его ранних спецификациях.

Java-пакеты реализуют так называемое пространство имен (namespace), позволяющее использовать в проекте файлы с одинаковыми именами. Такой подход существует с давних времен во многих языках.

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

И наконец, с помощью пакетов можно ограничивать доступ (из других пакетов) как целиком к классам, так и к их отдельным элементам.

Об этом и многом другом и пойдет речь в данной статье.

1. Что такое пакет

Пакетом (пространством имен) в Java называется структура вложенных по какому-то признаку папок с размещенными в них классами (интерфейсами, перечислениями, аннотациями), необходимыми проекту.

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

Например, если какие-то классы размещаются в папке calculator по адресу:

ru/topjava/startjava/calculator

, то с точки зрения Java они будут храниться в следующем пакете (в качестве разделителя уже используется точка вместо слеша):

ru.topjava.startjava.calculator

При этом в начале каждого из классов необходимо разместить строку, указывающую на его принадлежность к конкретному пакету, используя ключевое слово package и имя пакета (точка с запятой в конце — обязательна):

package ru.topjava.startjava.calculator;

Если подобная строка не будет присутствовать в классах, то они автоматически добавятся в безымянный пакет (unnamed package).

Имя пакета ru.topjava.startjava.calculator означает следующее (подробно правила именования пакетов рассматриваются в последующих главах):

  • ru.topjava — доменное имя разработчика кода, записанное в реверсивном виде
  • startjava — код пишется для проекта под названием StartJava
  • calculator — подпроект, реализуемый в рамках проекта StartJava

Создадим структуру из вложенных папок и файл CalculatorMain.java, отобразив ее в консоли с помощью консольной команды tree:

> tree /F src └──ru └──topjava └───startjava └──calculator CalculatorMain.java

Напишем простую реализацию класса CalculatorMain, указав имя его пакета:

package ru.topjava.startjava.calculator; public class CalculatorMain < public static void main(String[] args) < System.out.println("Калькулятор запустился."); >>

2. Примеры пакетов

На примере Java API вы можете ознакомиться со структурой стандартных пакетов.

Например, берем java.io — это пакет. Именно так, с использованием точки в качестве разделителя, он используется в Java. Но с точки зрения файловой системы, эта запись трактуется как: в папку под названием java вложена папка с именем io. Вы можете самостоятельно в этом убедиться.

Для этого откройте архив src.zip, найдя его расположение у себя на компьютере, например, с помощью консольной команды where. В нем хранится множество вложенных папок с исходниками.

> where /R C:\ src.zip C:\Program Files\BellSoft\LibericaJDK-20\lib\src.zip C:\Program Files\JetBrains\IntelliJ IDEA 2022.3\lib\ant\src.zip

Таких архивов может найтись несколько: все зависит от количества установленных JDK от разных вендоров — выбирайте любой.

Открыв данный архив и перейдя к месту хранения, например, класса String, вы увидите, что пакеты — это обычные вложенные папки с классами (путь к классу с определенного момента совпадает с именем пакета):

Класс String как раз хранится в пакете java.lang.

И таких классов в Java — тысячи (1, 2). Если бы они все размещались бессистемно, то разобраться в них было бы крайне тяжело.

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

  • java.io содержит классы, отвечающие за ввод/вывод
  • java.math содержит классы, обеспечивающие математические операции
  • java.time содержит классы для работы с датой и временем и т. д.

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

Кроме того, в отдельных папках рекомендуется хранить class-файлы, сторонние библиотеки, изображения, документацию и т. д. Все эти меры упрощают понимание и сопровождение приложения.

Рассмотрим еще примеры.

На картинке ниже изображены два пакета: java.lang и java.util. Важно отметить, что папка src (сокр. от source) не считается за пакет, т. к. является общей (по соглашению) для всех исходников и размещается в корне проекта. По этой причине src не указывается в именах пакетов. В своих проектах вам тоже не нужно это делать.

Обратимся теперь за примером к Spring Framework. Данный фреймворк содержит тысячи классов, разложенных по пакетам.

Например, класс BeanFactory.java размещается в пакете:

org.springframework.beans.factory
org.springframework.context

Из структуры репозитория и имен пакетов видно, что у этих классов корневой папкой для исходников является уже не src, a группа вложенных папок. При этом часть пути до org не будет входить в имя пакетов.

src/main/java/org/springframework/ src/main/kotlin/org/springframework src/test/java/org/springframework/ src/test/kotlin/org/springframework/

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

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

3. Назначение пакетов

3.1. Структурирование проекта

На картинке ниже java-файлы беспорядочно свалены в одну папку.

Вместо этого исходники могли бы быть разложены по смыслу в разные папки, например, со следующими названиями:

При этом папки могут содержать подпапки (подпакеты), еще более структурирующие файлы.

В итоге классы можно было бы разложить, например, в следующие пакеты, группируя исходя из их назначения:

ru.topjava.basejava.exception ru.topjava.basejava.sql ru.topjava.basejava.storage.serializer

Подобное разделение, безусловно, упрощает поддержку и понимание проекта в целом, а также назначение каждого его класса.

3.2. Ограничение доступа

В Java существует четыре модификатора доступа, одним из которых является package private (его еще называют default или no modifier). У него нет своего ключевого слова. Данный вид доступа применяется по умолчанию, когда никакой из модификаторов не указан явно.

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

Например, этот класс виден только в пакете, в котором находится сам:

package ru.topjava.example; class SomeClass < void method() < . >>

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

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

3.3. Поддержка классов с одинаковыми именами

Повторяющиеся имена классов в Java в рамках одного проекта — обычное явление, которое не приводит к конфликтам имен только потому, что они хранятся в разных пакетах.

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

Например, полное имя класса Random из стандартного пакета будет java.util.Random. А полное имя вашего класса может быть ru.topjava.startjava.Random. При этом возможности данных типов могут использоваться даже в рамках одного класса без каких-либо проблем.

В Java есть два стандартных класса под названием Date: один хранится в пакете java.sql, а второй — в java.util. Из-за того, что пакеты у них разные, и к каждому классу можно обратиться через его полное имя, ошибок при компиляции не возникнет.

Возможность сослаться через имя пакета на конкретный тип данных и позволяет иметь в одном проекте классы с одинаковыми именами.

Пример использования классов с одинаковыми именами рассматривается в другой главе.

4. Правила создания пакетов

Когда Java набирала свою популярность во времена развития веба, то появилось универсальное правило, смысл которого заключался в следующем: если какая-то компания разрабатывает библиотеки или целое приложение, то у нее наверняка есть свой сайт с доменным именем, например, topjava.ru. А как мы знаем, доменные имена повторяться не могут.

В итоге решили, что для обеспечения уникальности пакетов их имена следует начинать с префикса в виде доменных имен второго уровня их разработчиков, записанных в обратном порядке. Если сайт называется topjava.ru, то пакет будет начинаться с имени ru.topjava.

Такой подход можно объяснить тем, что более информативные и значимые составляющие пакета должны размещаться ближе к классу, который в нем находится. Значимость папок, хранящих класс тем выше, чем они к нему ближе. А в доменных именах — все наоборот, вот и пришлось пойти на такой реверс в именовании пакетов.

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

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

hyphenated-name.example.org → org.example.hyphenated_name example.int → int_.example 123name.example.com → com.example._123name

Общие правила:

  1. имя пакета должно быть простым и отражать общий смысл объединенных в нем классов
  2. все слова, входящие в имя пакета, должны писаться строчными буквами без использования camelCase или подчеркиваний, а также в единственном числе:
java.io jakarta.enterprise.lang.model org.hibernate.action.internal com.apple.quicktime.v3

Имена пакетов пишутся строчными буквами, чтобы избежать конфликта с именами классов или интерфейсов.

  1. Если какая-либо из частей имени пакета является ключевым словом, начинается с цифры или любого другого символа, который не разрешен в качестве начального символа идентификатора, добавьте к нему префикс в виде символа подчеркивания
  2. В Java все стандартные пакеты начинаются с зарезервированных слов java или javax. Это означает, что имена ваших пакетов не могут начинаться с этих слов
  3. Для именования пакета желательно использовать короткие слова или сокращения, например, util вместо utilities, lang вместо language или аббревиатуры — awt, http и проч. Только не придумываете свои собственные сокращения, используйте общепринятые
  4. Следует избегать более одного слова подряд в элементах имени пакета. Если все же этого не удастся сделать, то они записываются слитно:
org.springframework.kafka.aot org.hibernate.beanvalidation gov.whitehouse.socks.mousefinder
  1. Имя пакета необходимо отделить от последующего кода пустой строкой
  2. Точка с запятой в конце имени пакета — обязательна
  3. Пакет объявляется без переноса строки. Ограничение на длину строки на объявление пакета (как и импортов) не распространяется
  4. В одном файле может располагаться только один package
  5. Нельзя переименовывать пакет, не переименовав папку, в котором хранятся его классы (и наоборот)
  6. Если в классе явно не указывается принадлежность к какому-либо пакету, то такой класс попадает в безымянный пакет. При этом безымянная папка не создается. Такие пакеты предназначены только для небольших приложений или когда вы только начинаете процесс разработки. В реальных проектах классы вне пакетов не создаются
  7. Классы из безымянного пакета не могут быть импортированы
  8. Для маленьких приложений вы можете помещать свои исходные файлы в папку src без создания пакетов

5. Импорт классов

Указывать все время полное имя класса — достаточно утомительное занятие, захламляющее код (хотя без этого, бывает, не обойтись). Для облегчения этого процесса в Java существует возможность подключения нужного функционала с помощью оператора import.

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

Например, чтобы воспользоваться методами класса Scanner, следует перед объявлением класса прописать следующее:

import java.util.Scanner;

Данная запись состоит из ключевого слова import, имени пакета и класса, возможности которого вам требуются (точка с запятой в конце — обязательна). Если бы данный способ импорта не был внедрен в Java, то его отсутствие сильно бы захламило код, т. к. пришлось бы каждый раз указывать относительный путь в виде пакета до нужного класса.

В этом случае создание объекта выглядело бы следующим образом:

java.util.Scanner console = new java.util.Scanner(java.lang.System.in);

Это очень громоздко. А с учетом всего сказанного, эту строку можно переписать так:

Scanner console = new Scanner(System.in);

Хочу акцентировать ваше внимание на том, что import нужен для того, чтобы Java-программистам было проще использовать классы из других пакетов, не вводя все время их полные имена, например, String вместо java.lang.String. Данный оператор просто делает код короче и читабельней. Это синтаксический сахар, уменьшающий количество печатаемых вами символов.

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

5.1. Способы импорта

Импортировать нужный функционал можно следующими способами:

  • импорт конкретного класса
  • импорт конкретного метода/поля класса
  • импорт всего пакета целиком

Импорт конкретного класса представляет собой наиболее распространенный способ подключения:

import java.util.Arrays; import org.springframework.beans.BeansException; import ru.topjava.basejava.storage.ArrayStorage;

Импорт конкретного метода/поля (появился в Java 5) используется для импорта статических членов класса (статический импорт):

import static java.lang.Math.max; import static org.junit.jupiter.api.Assertions.assertEquals; import static ru.topjava.basejava.storage.DataStreamSerializer;

Последний способ — подключение всего пакета со всем содержимым. Для этого используется * (символ подстановки):

import java.util.*; import jakarta.annotation.*; import org.springframework.beans.factory.config.*

Если взглянуть на примеры из реальных проектов (1, 2), то видно, что какое бы количество классов не импортировалось, в коде не используется импорт всего пакета, а статический импорт применяется преимущественно в классах с тестами. Учитывайте это!

Рассмотрим причины, по которым многие программисты используют импорт конкретного класса, а не всего пакета. Это связано с тем, что явный импорт:

  • показывает, какие внешние классы используются в коде
  • снижает вероятность возникновения коллизий имен, возникающих при импорте пакетов, содержащих классы с одинаковыми именами
  • позволяет избежать проблем, когда в какой-либо пакет разработчик добавляет новый класс (например, в последнюю версию сторонней библиотеки был добавлен новый функционал), а вы при этом использовали *. Это внезапно для вас может привести к ошибкам компиляции из-за конфликта имён, которых раньше не было
5.2. Две неочевидных особенностей импорта

Если классы находятся в одном пакете и используют возможности друг друга, то выполнять import не нужно, т. к. они смогут найти друг друга без каких-либо сложностей.

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

Кроме того, возможно, вы заметили, что класс String никогда не приходится импортировать. Вы его просто используете, как есть. Все дело в том, что он, да и все остальные классы из пакета java.lang, являются фундаментальными классами и также импортируются компилятором автоматически.

Получается, что в любой класс Java сама импортирует два пакета: текущий и java.lang.

С учетом всего сказанного, импорты класса CalculatorMain, описанного ранее, будут выглядеть так (хоть мы этого и не видим):

package ru.topjava.startjava.calculator; import java.lang.*; import ru.topjava.startjava.calculator.*; public class CalculatorMain < public static void main(String[] args) < System.out.println("Калькулятор запустился."); >>

Обратите внимание на строку 9. Вам не приходится делать импорт, чтобы использовать возможности вывода текста на консоль, т. к. класс System входит в пакет java.lang. Это очень полезная магия!

5.3. Подпакеты

Пакеты, находящиеся внутри другого пакета, называются подпакетами. Например, java.util.concurrent и java.util.function — это подпакеты пакета java.util. Они не импортируются рекурсивно, например, при помощи import java.util.*. Их нужно импортировать явно.

Несмотря на то, что пакеты хоть и выглядят вложенными, на самом деле вы должны рассматривать java.util, java.util.concurrent, java.util.function и т. п., как три разных, не связанных между собой пакета, имена которых имеют общую часть.

5.4. Частые вопросы про import

Очень часто начинающие программисты задаются рядом вопросов, связанных с импортом. На некоторые из них я подготовил ответы:

  • import работает так же, как директива #include в языках C/C++?
    Нет, у них разный механизм работы: import не помещает код импортируемого класса в ваш класс, как это делает #include. В C/C++ же данная директива заменяется перед компиляцией на содержимое файла, на который указывает — происходит обычный копипаст. Затем класс передается компилятору со всеми включенными в него другими файлами, как если бы это был один файл. Например, на место #include копируется содержимое файла stdio.h, в то время, как import java.util.Arrays означает, что если в вашем файле нет данного класса, то его необходимо искать по полному имени, указанном в импорте.
  • влияет ли работа import на производительность (скорость выполнения) программы?
    Работа import (сколько бы их не было) никак не влияет на производительность, т. к. он используется только на этапе компиляции. В class-файлах никаких импортов просто физически нет.
  • увеличивает ли import размер class-файлов?
    Импорт не увеличивает размер байт-кода, т. к. в процессе компиляции компилятор заменяет каждое имя класса на полное имя, а затем удаляет оператор импорта. Таким образом, оператор не присутствует в байт-коде и не включат в место своего объявления код импортируемых классов. Он существует только в исходниках для поиска классов.
  • есть ли какая-либо разница между class-файлами при использовании import или при написании полного имени классов?
    Class-файлы с использованием импорта или без будут идентичными как по размеру, так и по содержанию. Вы можете посмотреть байт-код class-файлов, используй javap -c — никакой разницы между ними не будет. В обоих случаях байт-код будет иметь полные имена, помещенные туда компилятором.
  • влияет ли import на время компиляции?
    Если в классе много импортов, то при компиляции могут возникнуть небольшие задержки, которые будут настолько малы, что о них не стоит беспокоиться.

6. Компиляция и запуск

Из данного раздела вы узнаете, как компилировать и запускать классы, находящиеся в именованных пакетах, из консоли.

6.1. Создание папки для хранения class-файлов

Взглянем еще раз на картинку, которую мы уже видели ранее, где свалены в одну папку множество файлов:

Обратите внимание, что на ней class-файлы размещаются совместно с java-файлам, чего быть не должно. Обычно class-файлы хранятся в отдельной папке под названием out (bin или classes).

В корне вашего проекта всегда должна находиться отдельная папка для хранения сгенерированных компилятором файлов. Ее создание может выглядеть так:

> md out & ls -a ./ ../ .git/ .gitignore out/ src/

На самом деле ее можно не создавать вручную, т. к. при первой же компиляции она (со всеми подпапками) создастся автоматически (либо ее сгенерирует ваша среда разработки).

6.2. Стандартный способ компиляции и запуска
6.2.1. Компиляция

Компиляция выполняется по следующей схеме:

javac [options] [source-files]

, где [options] обозначает параметры javac, а [source-files] указывает на исходные файлы, которые требуется скомпилировать.

Подробнее это выглядит так:

  • консоль необходимо открыть в корне вашего проекта (где находится папка src)
  • в ней напишите через пробел:
    • команду javac
    • опцию -d (сокр. от destination), указывающую на папку для размещения сгенерированных class-файлов
    • имя этой папки. В нашем случае — out
    • опцию -verbose (необязательно), если вы хотите видеть, что происходит в процессе компиляции: время компиляции, какие файлы компилируются и т. д.
    • относительный путь до папки с классами и маску вида *.java, чтобы не перечислять каждый класс

    Пример компиляции всех классов в пакете:

    > javac -d out/ src/ru/topjava/startjava/calculator/*.java

    Пример компиляции конкретного класса

    > javac -d out/ src/ru/topjava/calculator/CalculatorMain.java

    После выполнения команды, благодаря ключу -d, в папке out (как и сама папка, если на момент компиляции ее еще не было) будет создана иерархия папок, повторяющая иерархию для компилируемых классов. При этом class-файлы появятся не в корне out, а по адресу /out/ru/topjava/startjava/calculator/.

    Если программа содержит несколько классов, которые при этом размещаются в разных пакетах, то процесс компиляции несколько изменится.

    Рассмотрим пример из трех простых классов, описывающих собаку, ее хозяина и класс запуска. Все эти классы лежат в разных пакетах.

    package ru.topjava.clinic.animal; public class Dog < private String name; public Dog(String name) < this.name = name; >public String getName() < return name; >public void bark() < System.out.println("Bark! Bark! Bark!"); >>
    package ru.topjava.clinic.person; import ru.topjava.clinic.animal.Dog; public class Owner < private String name; private Dog dog; public Owner(String name, Dog dog) < this.name = name; this.dog = dog; >public String toString() < return name + " и " + dog.getName(); >>
    package ru.topjava.clinic; import ru.topjava.clinic.animal.Dog; import ru.topjava.clinic.person.Owner; public class Main < public static void main(String[] args) < Dog dog = new Dog("Spike"); Owner owner = new Owner("John", dog); System.out.println("Меня и мою собаку зовут " + owner); System.out.println("Голос, " + dog.getName()); dog.bark(); >>

    Взглянем на получившуюся иерархию:

    > tree /F src src └───ru └───topjava └───clinic │ Main.java │ ├───animal │ Dog.java │ └───person Owner.java
    javac -d out/ src/ru/topjava/clinic/animal/Dog.java src/ru/topjava/clinic/person/Owner.java src/ru/topjava/clinic/Main.java

    Как видно, нам пришлось вручную указать все используемые классы и пути до них, что не всегда бывает удобно. Можно воспользоваться вариантом короче, указав параметр ‑sourcepath, говорящий компилятору, что все необходимые исходники нужно искать в папке src. При этом обязательно нужно указать путь до класса с методом main(). Компилятор заглянет в класс Main, чтобы узнать, какие зависимости (другие классы) в нем используются, и будет искать их, начиная с папки src и во всех ее подпапках:

    javac -d out/ -sourcepath src/ src/ru/topjava/clinic/Main.java

    Если не указать -sourcepath, то возникнет ошибка компиляции.

    6.2.2. Запуск

    Запуск классов выполняется по следующей схеме:

    java [options] [mainclass]

    , где [options] обозначает параметры команды java, а [mainclass] указывает на полное имя запускаемого класса (не путать с именем файла), содержащего метод main().

    Подробнее это выглядит так:

    • консоль по прежнему должна быть открыта в корне вашего проекта
    • в ней напишите через пробел:
      • команду java
      • опцию -verbose (необязательно)
      • опцию -cp (или -classpath), указывающую на папку с сгенерированными class-файлами и внешними библиотеками (обычно в виде отдельных файлов с расширением .jar), к которым программа должна получить доступ во время выполнения
      • имя этой папки. В нашем случае — out
      • полное имя класса (пакет + имя), содержащего метод main (без указания расширения файла .class). Имя пакета необходимо записывать, используя точки
      > java -cp out/ ru.topjava.startjava.calculator.CalculatorMain
      Калькулятор запустился.

      Запуск программы, состоящей из нескольких классов, ничем не будет отличаться, т. к. нужно указывать только класс, содержащий метод main(). Остальные зависимости будут найдены в папке out автоматически.

      java -cp out/ ru.topjava.clinic.Main
      Меня и мою собаку зовут John и Spike Голос, Spike Bark! Bark! Bark!
      6.3. Дополнительные способы компиляции и запуска
      6.3.1. Options-файлы (файлы аргументов)

      Если вы работаете над какой-то программой, и вам надоело все время прописывать пути до классов, а также параметры компиляции и запуска, то есть решение, облегчающее эту работу. Кроме того, данный способ подойдет для компиляции множества классов, когда использование * невозможно, т. к. компилировать требуется не все файлы в папке, а выборочно.

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

      Компиляция

      Компиляция выполняется по следующей схеме:

      > javac @argfile

      , где @argfile — имя файла аргументов, которое обязательно должно начинаться с @.

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

      Абсолютный путь — это путь до ваших классов, начинающийся от корня файловой системы. Например, D:\Java\StartJava\src\ru\topjava\clinic\animal\Dog.java.

      А относительный путь — это путь до классов, начинающийся от корня проекта. В данном случае src\ru\topjava\clinic\animal\Dog.java.

      Если вы размещаете options-файлы в папке с java-файлами (а не в корне), то для указания адреса, например, для папки out, при использовании относительного пути, необходимо использовать следующий синтаксис ..\..\, который позволит подняться до корня проекта.

      Например, ..\..\..\..\out, где каждый ..\ переносит на одну папку выше в иерархии папок.

      Какой способ выбрать — зависит от ситуации: если предполагается, что options-файлами будут пользоваться и другие люди, то лучше использовать относительный путь. А если только вы — абсолютный.

      Создать options можно в любом текстовом редакторе, а размещать в папке с компилируемыми классами или в корне проекта — все зависит от необходимости и удобства.

      Разберем пример использования options-файла при компиляции.

      Например, пусть классы Dog, Person и Main, разбираемые ранее, хранятся в одной папке src/ru/topjava/animal/

      Необходимо их скомпилировать, указав при этом папку для class-файлов.

      С использованием относительного пути:

      -d ..\..\..\..\out Dog.java Person.java Main.java

      С использованием абсолютного пути:

      -d D:\Java\StartJava\out Dog.java Person.java Main.java

      Компиляция (консоль должна быть открыта в папке с компилируемыми классами, а не в корне проекта):

      javac @options

      Можно и так (тогда в options уже не нужно перечислять файлы):

      > javac @options *.java

      Если вы работаете с терминалом, для которого символ @ является ключевым словом, например, с Windows PowerShell, то его необходимо экранировать (включая название файла), используя одинарные кавычки:

      > javac '@options'

      Можно использовать другой вариант, поместив компилируемые классы и параметры в разные файлы (sources и options). Это позволит не писать руками компилируемые файлы, например, если их много, или они хранятся в разных пакетах, а генерировать их содержимое с помощью командной строки. При этом удобнее sources и options размещать уже в корне проекта.

      Для разных платформ генерация может быть следующей:

      Для macOS/Linux (вы можете использовать этот способ и в Windows, например, в Git Bash):

      find ./src/ -type f -name "*.java" > sources

      Данную запись можно расшифровать так: найди (find) в папке ./src/ (и в подпапках) среди файлов (-type f) только те, которые имеют расширение .java (-name «.java»). Все, что будет найдено, помести (>) в файл sources.

      Посмотрим содержимое файла sources:

      > cat sources ./src/ru/topjava/clinic/Dog.java ./src/ru/topjava/clinic/Main.java ./src/ru/topjava/clinic/Person.java

      Если вам не нужны все классы, которые хранятся в src, то следует указать путь до конкретной папки.

      dir src /S /B *.java > sources

      Получи (dir) из папки src и из всех ее подпапок (/S) имена файлов без дополнительной информации (/B), которые имеют расширение *.java. Все что было найдено, помести (>) в файл sources.

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

      В итоге компиляция будет следующей:

      javac @options @sources
      6.3.2. Использование Globstar

      В Bash существует опция под названием globstar, позволяющая использовать двойной подстановочный знак ** в качестве команды для рекурсивной обработки. В Windows вы можете попробовать globstar в Git Bash.

      Для того чтобы посмотреть, включен ли globstar (по умолчанию он выключен), выполните следующую команду:

      > shopt globstar globstar off

      , где shopt — это сокращение от shell options (параметры оболочки).

      Для его включения используйте команду:

      > shopt -s globstar

      Для отключения используйте команду:

      > shopt -u globstar

      После включения этой опции компиляция будет выглядеть так:

      > javac -d out/ src/**/*.java

      Эта запись означает: пройдись по всем подпапкам в src и скомпилируй там все java-файлы.

      Если классы в папке src зависят друг от друга (код одной программы размещается в разных пакетах), то компилировать нужно так:

      > javac -d out/ -sourcepath src/ src/**/*.java
      6.3.3. Использование Pipe

      Pipe (канал) — это механизм объединения нескольких команд для получения конечного результата.

      Для компиляции можно объединить команды find и javac:

      > find ./src/ -type f -name "*.java" -exec javac -d ./out/ '<>' +
      Запуск

      Запуск с помощью файлов-аргументов выполняется по следующей схеме:

      > java @argfile

      , где @argfile — имя файла аргументов, которое должно начинаться с символа @. Обычно данный файл, предназначенный для запуска, называют optionsj.

      Пример содержимое такого файла с использованием относительного пути:

      -cp ..\..\..\..\out ru.topjava.clinic.Main

      С использованием абсолютного пути:

      -cp D:\Java\StartJava\out ru.topjava.clinic.Main

      Пример запуска (консоль должна быть открыта в папке с компилируемыми классами, а не в корне проекта):

      > java @optionsj

      7. Возможные ошибки и их решение

      При использовании пакетов на первых порах могут возникать ошибки, на поиск решения которых может уходить значительное время. Для его экономии и облегчения освоения данной темы была написана эта глава в формате “вопрос-ответ”.

      Общее правило: если при компиляции или запуске у вас возникают ошибки, которые не связаны с кодом, то попробуйте закомментировать строку с оператором package и повторить свои действия. Если после этого ошибка уйдет, то поиск проблемы необходимо сосредоточить на пакетах и всём, что с ними связано.

      А теперь перейдем к обсуждению конкретных проблем и их решению. Для разнообразия буду использовать globstar, описанный ранее.

      • При компиляции исходников сгенерированные классы появляются в корне папки out
        Это связано с тем, что вы забыли указать принадлежность классов к пакету. Напишите в начале классов package и имя пакета.
      • Генерируемые классы появляются не в папке out, а в папке с исходниками
        Причина в том, что при компиляции не была указана папка для генерируемых компилятором файлов. Необходимо использовать параметр -d и путь до папки out.
      • При запуске выдается ошибка, что не был найден класс с main-методом (Could not find or load main class)
        Причиной может быть все что угодно:
        • не указанно (или указано с опечатками) полное имя класса при запуске
        • при запуске указывается имя class-файла (с расширением class), а не имя класса (без расширения). Компилятор будет думать, что class является частью имени класса, что не верно. Удалите class у имени файла в аргументе
        • неверно указан путь до папки out
        • попробуйте вместо относительного пути до классов (если используете options-файлы) использовать абсолютный. Если это сработает, то ищите проблему в относительном пути

        Рассмотрим в качестве примера три простых класса, находящихся в разных пакетах:
        ru.topjava.abc
        ru.topjava.def
        ru.topjava.ghi

        При этом два класса имеют одинаковое имя Message и метод print().

        package ru.topjava.abc; import ru.topjava.def.Message; import ru.topjava.ghi.Message; public class Main < public static void main(String[] args) < Message msg1 = new Message(); msg1.print(); Message msg2 = new Message(); msg2.print(); >>
        package ru.topjava.def; public class Message < public void print() < System.out.println("def.Message"); >>
        package ru.topjava.ghi; public class Message < public void print() < System.out.println("ghi.Message"); >>

        Скомпилируем классы из корня проекта, используя globstar:

        > javac -d out/ src/ru/topjava/**/*.java

        Вы можете сделать тоже самое, используя -sourcepath, если не работает globstar:

        > javac -d out/ -sourcepath src/ru/topjava/ src/ru/topjava/abc/Main.java

        Компилятор выдаст ошибку о том, что в классе импортируются два класса с одинаковыми именами, но из разных пакетов; а также что ссылка на Message неоднозначна

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

        В итоге измененный класс будет выглядеть так:

        package ru.topjava.abc; public class Main < public static void main(String[] args) < ru.topjava.def.Message msg1 = new ru.topjava.def.Message(); msg1.print(); ru.topjava.ghi.Message msg2 = new ru.topjava.ghi.Message(); msg2.print(); >>

        Компиляция и запуск программы на этот раз пройдет без ошибок:

        > javac -d out/ src/ru/topjava/**/*.java > java -cp out/ ru.topjava.abc.Main def.Message ghi.Message

        Существуют и другие рекомендации, позволяющие избежать этих ошибок:

        • если повторяющиеся имена классов являются вашими, а не частью внешних библиотек, к которым у вас нет доступа, то просто переименуйте один из своих классов
        • можно создавать промежуточные классы-обертки, которые не будут ничего делать, кроме как содержать поле экземпляра класса, имя которого дублируется

        Заключение

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *