Как правило, в Java классы объединяются в пакеты. Пакеты позволяют организовать классы логически в наборы. Организация классов в виде пакетов позволяет избежать конфликта имен между классами. Ведь нередки ситуации, когда разработчики называют свои классы одинаковыми именами. Принадлежность к пакету позволяет гарантировать однозначность имен.
По умолчанию в языке Java уже имеется ряд встроенных
пакетов, например, java.lang, java.util, java.io и другие, которые мы можем использовать. Кроме того, пакеты могут иметь вложенные пакеты.
В частности, для работы с консолью применяется класс System, который расположен в пакете java.lang.
Если надо использовать классы из других пакетов, то нам надо подключить эти пакеты и классы. Исключение составляют классы из пакета
java.lang (например, System), которые подключаются в программу автоматически.
Например, для работы с датами предназначен класс Date находится в пакете java.util, поэтому мы можем к нему обратиться с помощью выражения:
java.util.Date
Например:
class Program {
public static void main(String[] args) {
java.util.Date curDate = new java.util.Date();
System.out.println(curDate);
}
}
То есть мы указываем полный путь к файлу в пакете при создании его объекта. Однако такое нагромождение имен пакетов не всегда удобно, и в качестве альтернативы мы можем импортировать пакеты и классы в проект с помощью директивы import:
import пакет.класс;
Например, импортируем класс Date и используем его для вывода на консоль текущих даты и времени:
import java.util.Date;
class Program {
public static void main(String[] args) {
Date curDate = new Date();
System.out.println(curDate);
}
}
Директива import указывается в самом начале кода, после чего идет имя подключаемого класса (в данном случае класса Date).
Повторюсь, что классы из пакета java.lang подключаются автоматически, поэтому на не надо отдельно импортировать класс System для вывода на консоль.
В примере выше мы подключили только один класс, однако пакет 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:
import static java.lang.System.*;
import static java.lang.Math.*;
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, поэтому мы можем его использовать без указания класса.
Классы необязательно определять в пакеты. Если для класса пакет не определен, то считается, что данный класс находится в пакете по умолчанию, который не имеет имени. Например, пусть у нас исходный файл имеет следующий код:
class Program {
public static void main(String[] args) {
Person tom = new Person("Tom");
tom.print();
}
}
class Person
{
String name;
Person(String name){
this.name = name;
}
void print() {
System.out.printf("Person %s\n", name);
}
}
В данном случае в файле определено два класса - Program и Person, и оба они определены в безымянном пакете. Собственно все классы, которые определялись в предыдущих статьях, определялись именно в безымянных пакетах.
Чтобы указать, что класс принадлежит определенному пакету, надо использовать директиву package, после которой указывается имя пакета:
package название_пакета;
Но посмотрим как нам определить пакеты. Как правило, названия пакетов соответствуют физической структуре проекта, то есть организации каталогов, в которых находятся файлы с исходным кодом. А путь к файлам внутри проекта соответствует названию пакета этих файлов. Например, если классы принадлежат пакету mypack, то эти классы помещаются в проекте в папку mypack. А если классы принадлежат пакету "com.mypack", то они помещаются в папку "com/mypack".
Например, определим следующую структуру проекта:
Файл Program.java
Папка types
Файл Person.java
Таким образом, в корневой папке у нас есть файл Program.java и папка types, в которой лежит файл Person.java
Пусть файл Person.java будет выглядеть следующим образом:
package types;
public class Person
{
String name;
public Person(String name){
this.name = name;
}
public String getName() {
return this.name;
}
}
Первая строка (package types) указывает, что класс Person принадлежит пакету "types". Принадлежность класса пакету указывается в самом начале кода файла.
Другой важный момент - ключевое слово public. Если мы хотим, чтобы какие-то компоненты класса - поля, методы, конструкторы были доступны в других пакетах, то эти компоненты должны предваряться
ключевым словом public, как в данном случае конструктор класса и метод getName(). Более того, чтобы класс в целом был доступен в других пакетах, он также должен быть
определен с помощью слова public
Далее в файле Program.java используем класс Person из пакета types:
import types.Person; // испортируем класс Person из пакета types
class Program {
public static void main(String[] args) {
Person tom = new Person("Tom");
System.out.println(tom.getName()); // Tom
}
}
Вначале кода подключаем класс Person из пакета types (import types.Person) и затем мы можем обращаться к функционалу класса, который определен с ключевым словом public
Пойдем дальше и добавим в папку types каталог test, а в него - новый файл PersonTest. То есть вся структура проекта будет выглядеть следующим образом:
Файл Program.java
Папка types
Файл Person.java
Папка test
Файл PersonTest.java
В файле PersonTest.java определим следующее содержимое:
package types.test; // класс PersonTest находится в пакете types.test
import types.Person; // импортируем класс Person из пакета types
public class PersonTest
{
public static void personNameTest(){
String name = "Bob";
Person bob = new Person(name);
if(name == bob.getName()){
System.out.println("personNameTest passed!");
}
else{
System.out.println("personNameTest failed...");
}
}
}
Первой строкой указываем, что класс PersonTest находится в пакете "types.test". Опять же обратите внимание на соответствие названия пакета структуре каталогов: класс PersonTest находится в папке "types/test" и в пакете "types.test".
Второй строкой импортируем класс Person из пакета types.
Далее определяем класс с единственным статическим методом personNameTest(), который проверяет имя человека, используя функционал класса Person. Опять же, чтобы метод
personNameTest() был доступен в других пакетах, он определен с модификатором public.
Далее изменим файл Program.java:
import types.test.PersonTest; // испортируем класс PersonTest из пакета types.test
class Program {
public static void main(String[] args) {
PersonTest.personNameTest(); // personNameTest passed!
}
}
Первой строкой испортируем класс PersonTest из пакета types.test и в методе main вызываем метод PersonTest.personNameTest(), тем самым проверяя, что функционал класса Person работает как надо.
До сих пор и примерах выше главный файл программы с методом main располагался в безымянном пакете по умолчанию. Но естественно он также можем располагаться в каком-то именнованном пакете.
Например, добавим в текущую папку проектов новую папку с названием metanit, а в нее поместим файл Program.java. Чтобы в итоге у нас получилась следующая структура проектов:
Папка metanit
Файл Program.java
Папка types
Файл Person.java
Папка test
Файл PersonTest.java
И в файле Program.java определим следующий код:
package metanit;
import types.test.PersonTest; // испортируем класс PersonTest из пакета types.test
class Program {
public static void main(String[] args) {
PersonTest.personNameTest(); // personNameTest passed!
}
}
Первой строкой определяем, что наш пакет располагается в пакете "metanit".
Теперь нам надо все это скомпилировать и запустить. В консоли перейдем к корневому каталогу, где располагаются папки "metanit" и "types". И для компиляции программы выполним команду:
javac metanit/Program.java
Далее для запуска программы выполним команду
java metanit.Program
Полный пример работы:
eugene@Eugene:/workspace/java$ javac metanit/Program.java eugene@Eugene:/workspace/java$ java metanit.Program personNameTest passed! eugene@Eugene:/workspace/java$
При работае с пакетами следует учитывать, что компилятор не проверяет структуру каталогов при компиляции исходных файлов. Например, предположим, что у вас есть исходный файл, начинающийся с директивы
package com.mycompany;
Компилятор javac может скомпилировать файл, даже если он не находится в подкаталоге "com/mycompany". Исходный файл скомпилируется без ошибок, если он не использует другие невстроенные пакеты. Однако скомпилированная программа не запустится, пока вы предварительно не переместите все файлы классов в нужное место. Виртуальная машина не обнаружит классы, если пакеты не соответствуют каталогам.