In Java, a package is a collection of related classes, interfaces, and sub-packages. This section delves into the functionality of Java packages and their role in structuring, overseeing, and utilizing classes within Java applications.
What is Package in Java?
In Java, a package is a container that consolidates interconnected classes, interfaces, and sub-packages into a cohesive entity. Packages play a crucial role in structuring extensive software projects, preventing naming clashes, managing accessibility, and promoting code reusability and modularity.
Java Packages are categorized into two main types:
- Built-in Packages
- User-defined Packages
Java Built-in Packages
Built-in packages are predefined packages provided by Java that contain ready-to-use classes and interfaces. Some of the built-in packages that are commonly used are:
- lang : It contains those classes that are required for language support, such as classes for primitive data types and math operations. This package is automatically imported into Java.
- io : It contains all those classes that are required for the input/output operations.
- util: It contains utility classes that are needed for the data structures, such as Array , List , Map , etc.
- applet : All those classes that are required for finding applets are found in this package.
- awt : It contains all those classes that are required for creating components of the GUI (Graphical User Interface).
- net : It includes classes that are used for doing tasks in the field of networking.
Java User-defined Packages
Programmer-created packages, known as user-defined packages, are utilized to organize personal classes and interfaces. These packages serve to prevent naming clashes, enforce access control, and enable the organization of extensive projects in a structured and modular manner.
Example
In this example, we are going to generate a pair of Java files in separate packages or directories. Additionally, we will produce a total of three files: one for each of the directories, and the final file will be designated for the main class.
Take a look at the following scenario. Two directories, named mypackage1 and mypackage2, have been established.
Inside mypackage1, create a file Class1.java.
The provided code pertains to the Class1.java file.
// Note that Class1 is the part of the package mypackage1. Therefore, package
// mypackage1; statement is written.
package mypackage1;
public class Class1 {
public void get1() {
System.out.println("Inside the package 1.");
}
}
In the same manner, generate a file named Class2.java within the mypackage2 directory.
The provided code pertains to the Class2.java file.
// Note that Class2 is the part of the package mypackage2. Therefore, package
// mypackage2; statement is written.
package mypackage2;
public class Class2 {
public void get2() {
System.out.println("Inside the package 2.");
}
}
Next, create a new file named Main.java outside of the two packages.
The provided code is designed for the Main.java module.
Example
// importing both the classes
import mypackage1.Class1;
import mypackage2.Class2;
public class Main {
// main method
public static void main(String args[])
{
// instantiating both the classes Class1 and Class2
Class1 obj1 = new Class1();
Class2 obj2 = new Class2();
obj1.get1();
obj2.get2();
}
}
Output:
Inside the package 1.
Inside the package 2.
Note: If we change the access specifier of the classes Class1 and Class2 from public to protected, default, or private, then the compiler will give an error. Observe the following.
Save this file as Class1.java
package mypackage1;
// public class Class1 {
protected class Class1 { // changed the access specifier to protected
public void get1() {
System.out.println("Inside the package 1.");
}
}
Save this file as Class2.java
package mypackage2;
// public class Class2 {
class Class2 { // changed the access specifier to default
public void get2() {
System.out.println("Inside the package 2.");
}
}
Upon executing the Main.java file, the subsequent error becomes apparent.
.\mypackage1\Class1.java:3: error: modifier protected not allowed here
protected class Class1 { // changed the access specifier to protected
^
Main.java:2: error: Class1 is not public in mypackage1; cannot be accessed from outside package
import mypackage1.Class1;
^
Main.java:4: error: Class2 is not public in mypackage2; cannot be accessed from outside package
import myPackage2.Class2;
^
Main.java:11: error: Class1 is not public in mypackage1; cannot be accessed from outside package
Class1 obj1 = new Class1();
^
Main.java:11: error: Class1 is not public in mypackage1; cannot be accessed from outside package
Class1 obj1 = new Class1();
^
Main.java:13: error: Class2 is not public in mypackage2; cannot be accessed from outside package
Class2 obj2 = new Class2();
^
Main.java:13: error: Class2 is not public in mypackage2; cannot be accessed from outside package
Class2 obj2 = new Class2();
Explanation
The issue arises due to the limitations imposed by the protected and default access modifiers on the visibility of classes outside the package. As a result, the primary class is unable to reach Class1 and Class2.
Note: In this example, we have created the folders mypackage1 and mypackage2 explicitly. If we want to build it using the command prompt, then do the following.
Begin by consolidating the three files, namely Class1.java, Class2.java, and Main.java, into a single directory. In this instance, the designated location is the test directory.
Make sure that the access specifier for both Class1 and Class2 is set to public in this scenario. Next, proceed with compiling it by utilizing the provided command.
javac -d . *.java
In this case, the flag -d is used for specifying the destination. Following -d, a period (.) indicates the current directory, while *.java encompasses all Java files.
Upon completion of the compilation process, the following results are observed.
After creating both packages, proceed to execute the Main.class file by using the following command.
java Main
And we get the output as:
Inside the package 1.
Inside the package 2.
Static Import of the Packages
The feature of static import made its debut in Java 5 and has persisted in subsequent Java versions. It enables the utilization of public static members (fields or methods) from a class in the code without explicitly mentioning the class where they are defined.
The illustration below showcases the usage of static package imports in Java.
Example
import static java.lang.System.*; // notice the word static in the import statement
public class Main {
public static void main(String args[]) {
// no need to write System.out.println
// only out.println will do the work
out.println("In the main method");
}
}
Output:
In the main method
Explanation:
The "System" class is part of the java.lang package, containing a static variable called "out" within it. Through static import, it becomes possible to directly access the "out" variable without explicitly mentioning the class name "System".
Dealing With Name Conflicts Using Packages
As previously mentioned, conflicts in names can be managed through the use of packages. To further clarify this concept, let's explore an example for better comprehension.
Example
import java.sql.*;
import java.util.*;
public class Main {
public static void main(String args[]) {
// creating an object of the class Date
Date d = new Date(44l);
System.out.println("Inside main method.");
}
}
Output:
Main.java:7: error: reference to Date is ambiguous
Date d = new Date(44l);
^
both class java.util.Date in java.util and class java.sql.Date in java.sql match
Main.java:7: error: reference to Date is ambiguous
Date d = new Date(44l);
^
both class java.util.Date in java.util and class java.sql.Date in java.sql match
2 errors
Explanation
The code includes imports for both the java.sql and java.util packages, each containing its own Date class. This results in ambiguity when instantiating the Date class, as the compiler cannot determine whether to use the Date class from java.sql or java.util. Consequently, an error occurs due to this ambiguity.
In order to address this issue, it is necessary to utilize the class name in conjunction with the package name. By providing the fully qualified name, the compiler is explicitly informed about which Date class should be referenced.
Example
import java.sql.*;
import java.util.*;
public class Main {
public static void main(String args[]) {
// Date class comes with the java.sql package name. It indicates that
// Date class used here belongs to the java.sql package. Hence, there is no any
// ambiguity. Therefore, no error has occurred.
// creating an object of the class Date
java.sql.Date d = new java.sql.Date(44l);
System.out.println("Inside main method.");
}
}
Output:
Inside the main method.
Java Subpackage
A package enclosed within another package is commonly referred to as a subpackage. This type of package is established to provide additional categorization for the main package.
For instance, Sun Microsystems introduced a package called java, which encompasses various classes such as System, String, Reader, Writer, Socket, and more. These classes each serve a distinct purpose within the package. For instance, the Reader and Writer classes handle Input/Output tasks, while the Socket and ServerSocket classes are utilized for networking functionalities, among others.
Sun has organized the Java package into various subpackages like lang, net, io, etc. The io package contains classes related to Input/Output, the net package includes Server and ServerSocket classes, and so forth.
A common convention for defining a package is using the format domain.company.package, such as com.example.bean or org.sssit.dao.
The illustration below showcases how Subpackages function in Java.
Example
package com.example.core;
class Main {
public static void main(String args[]){
System.out.println("Hello subpackage");
}
}
To Compile: javac -d . Main.java
To Run: java com.example.core.Main
Output:
Hello subpackage
Advantages of Using Packages
Various benefits of packages in Java include:
1. Avoid Naming Conflicts
Utilizing packages helps prevent clashes in names between classes.
Consider a scenario where a class named Employee is utilized by both the HR and IT departments within a company. In such a situation, to determine which specific department the Employee class belongs to, we can leverage the concept of packages. By utilizing the fully qualified names company.hr.Employee and company.it.Employee, we can distinctly identify the Employee class within each department, thereby mitigating any naming conflicts that may arise.
2. Organize Classes
Packages are utilized for structuring classes, interfaces, and other elements within a program.
Consider a library with separate sections for different types of books, such as Math books in one section and Science books in another. Similarly, in programming, we can organize related classes together using packages. In Java, built-in classes are structured using packages as well. Refer to the diagram below for a visual representation.
- Access Control: This package is responsible for managing the access levels of protected and default members. Consider the significance of default and protected access specifiers in the absence of packages.
- Enhanced Reusability: Java improves the management of code reusability and facilitates data encapsulation through the utilization of packages.
Package Structure and Naming Rules
In this section, we will explore the organization of packages and guidelines for naming conventions in Java programming.
1. Naming Conventions
It is recommended to use lowercase letters for Java packages, with components separated by dots. When a component represents a class, the class name should start with a capital letter.
To read more Java Naming Conventions
2. Structure of Directory
The names of the folders and the package names are intricately linked.
Consider a scenario where the package name is structured as company.hr.Employee. In this context, "company" serves as the main directory, within which there exists a subdirectory named "hr". Inside the "hr" subdirectory, a class named Employee is located. It is important to observe that the capitalization of the letter E signifies that Employee is a class name.
Example:
import java.lang.*;
In this case, lang represents a subpackage contained within the java package.
Accessing Classes from Package
In this section, we will explore the process of accessing a class from packages in Java programming.
Importing a Specific Class
To utilize classes from a package, the import keyword is employed. In the case of desiring access to a particular class within the java.util package, the subsequent statement is utilized:
import java.util.ArrayList;
In this example, we are referring to a particular class named ArrayList from the java.util package. Likewise, when incorporating a different class from another package, we can write:
import java.math.BigInteger;
In this instance, we've brought in the BigInteger class from the java.math package.
Importing All Classes
To utilize a class without employing the import statement, one must include the complete package name along with the class name when referring to it in the code.
Example
public class Main {
static void foo() {
// we are writing the class name along with the package name
java.util.ArrayList<Integer> al = new java.util.ArrayList<Integer>();
}
public static void main(String args[]) {
foo();
System.out.println("Inside the main method");
}
}
Output:
Inside the main method
Note: By applying the import package.*; statement, we know that it imports all the classes and interfaces present in that package. However, all the classes, interfaces, etc., will never be imported from their sub-packages.
In situations where there are two packages containing classes with identical names, such as java.sql.Date and java.util.Date, it is recommended to utilize the fully qualified names to prevent any potential conflicts. This concept will be further elaborated on in the upcoming sections of this guide.