Java packages¶
The section describing the Java code file structure mentioned that a code file can begin with
a package declaration of form package package_name;
. A package declaration states that
all classes defined in the code belong to the package named in the package declaration.
Consider the simple example code:
package fi.tuni.prog3.helloworld;
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
Here the class HelloWorld
was declared to belong into the package fi.tuni.prog3.helloworld
.
This in effect means that the class HelloWorld
has a so-called “fully qualified” name
fi.tuni.prog3.helloworld.HelloWorld
. A package declaration defines the class into a namespace.
This is on a high level similar to, e.g. C++ namespaces. References to classes or interfaces of a
package use a similar dot notation as class member references. For example
fi.tuni.prog3.helloworld.HelloWorld
refers to the class HelloWorld
in the package
fi.tuni.prog3.helloworld
, and on the other hand fi.tuni.prog3.helloworld.HelloWorld.main
refers
to the main
member function of that class.
The package name fi.tuni.prog3.helloworld
used above was an example of the widely adopted
convention that package names are based on the internet domain name of the code owner. A package
name lists the domain name parts in reverse order. E.g. here the Tampere university domain name
tuni.fi
formed the basis of the package name, and the suffixes prog3
and helloworld
would further
specify that the package is related to this course and its specific task. A real life example could be, e.g. the fact
that Java libraries published by Google and Microsoft are defined in packages whose names start
with com.google
and com.microsoft
, respectively.
Packages help avoid class name clashes. For example, the Java class library contains two different
classes named Date
: one in the package java.sql
and another in the package java.util
.
These classes could not be used in the same program without packages, because in that case the
Java compiler would not know which class the name Date
refers to. We can use fully qualified
names, that is, names prefixed with package names, to refer to classes, and this allows to
distinguish the two Date
classes by referring to them as java.sql.Date
and
java.util.Date
. It should be obvious by now that a package should not contain two classes with
the same name, as then even the fully qualified names would be unable to distinguish between them.
Packages vs import
We stated before that import
statements can be used to make external classes accessible in
the current code file. To be more precise, the import
statements allow us to use a simple
(not fully-qualified) class name when referring to a class defined in some other package. An
import
is not needed if we use a fully qualified class name. For example the following two
code snippets that use the sort
function of the class Arrays
from the package
java.util
are in effect identical:
import java.util.Arrays;
public class SortedParameters {
public static void main(String[] args) {
Arrays.sort(args);
for(String arg : args) {
System.out.println(arg);
}
}
}
public class SortedParameters {
public static void main(String[] args) {
java.util.Arrays.sort(args);
for(String arg : args) {
System.out.println(arg);
}
}
}
As mentioned above, import
statements concern classes defined in different packages.
A code file can refer to all classes of the same package with simple class names without using
import
statements.
Java’s core package java.lang
is imported automatically without the need to use a import
statement.
A second motive for using packages is that they facilitate grouping related classes together. This is useful especially in large software projects that may contain hundreds or even thousands of classes. Using packages in Java actually more or less imposes a hierarchical directory structure on the code base: Java interprets a package name as a directory path that specifies where the code files are stored. Dot characters of a package are interpreted as separate directory levels.
For example the class HelloWorld
defined in the package fi.tuni.prog3.helloworld
should be
stored in the subdirectory fi/tuni/prog3/helloworld
(Mac or Linux) or in fi\tuni\prog3\helloworld
(Windows).
A class defined in a package should be executed using the fully qualified class name, for example
as java fi.tuni.prog3.helloworld.HelloWorld
. Here the Java virtual machine would begin the program
execution from the class file fi/tuni/prog3/helloworld/HelloWorld.class
(Mac or Linux) or from
fi\tuni\prog3\helloworld\HelloWorld.class
(Windows). The virtual machine assumes that the run command
is given in a directory which has the package directory as its immediate subdirectory. For example,
if the above-mentioned package is a part of a Maven project in a Mac or Linux system, i.e.,
in the src/main/java/fi/tuni/prog3/helloworld/
directory, the
java fi.tuni.prog3.helloworld.HelloWorld
must be entered in the java
directory in order
to run the HelloWorld
program.
If we follow the principle that package names begin with reversed internet domain names, the code base will be organized into a directory structure where, e.g. all code from the same organization will be located under the same subdirectory.
Packages and Maven¶
It is recommended to use packages in Maven projects. Just like in the example class,
the code files begin with a package statement, which was package fi.tuni.prog3.helloworld;
in our
HelloWorld
example. The source files are also arranged in a directory structure corresponding
to their packages. Therefore the HelloWorld
class would be placed in the path
src/main/java/fi/tuni/prog3/helloworld/HelloWorld.java
.
Packages require some changes to the project file pom.xml
too. Here’s an example of a project
file that could be used for a Maven project made for the HelloWorld
example class.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>fi.tuni.prog3</groupId>
<artifactId>helloworld</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<build>
<plugins>
<plugin>
<groupId>com.jolira</groupId>
<artifactId>onejar-maven-plugin</artifactId>
<version>1.4.4</version>
<executions>
<execution>
<configuration>
<mainClass>fi.tuni.prog3.helloworld.HelloWorld</mainClass>
<onejarVersion>0.97</onejarVersion>
<attachToBuild>true</attachToBuild>
</configuration>
<goals>
<goal>one-jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
In Maven projects it is customary that the groupId
and the artifactId
together form
the Java package of the project. For example, if the goal is to place the source files of the
project in the package fi.tuni.prog3.helloworld
, the groupId
would be fi.tuni.prog3
and the
artifactId
would be helloworld
. This is not enforced by Maven; however, experienced developers
using Maven expect this rule to be followed, so you should follow it too, even if the values
technically could be different.
Note also that the value of the mainClass
element has to include the fully qualified package
of the main class. In this example, since the main class HelloWorld
is in the package
fi.tuni.prog3.helloworld
, the value of the mainClass
element needs to be
fi.tuni.prog3.helloworld.HelloWorld
.
The OneJar files of the packaged Maven projects are created with the same naming principles and into
the same location as the unpackaged projects created in the previous rounds: The JAR file is
named by using the artifactId
and version
of the Maven project and the file is placed
in the target
directory of the Maven project. For example, the JAR file for the packaged
HelloWorld example would be target/helloworld-1.0.one-jar.jar
in Linux and Mac systems and
target\helloworld-1.0.one-jar.jar
in Windows systems. The JAR file can be executed, as earlier,
from the project root directory without any additional package related arguments, for example
as java -jar target/helloworld-1.0.one-jar.jar
.
In this week’s exercises and in those of subsequent weeks your source code must be attached to the stipulated package in order for the automatic grading to successfully grade your work.