Java Programming

Java Packages

Need, Creation, Use and Organisation

Chahat Kalsi
9 min readJan 14, 2023

A package is the quintessential unit of organisation in a Java project. As its most basic definition, we can define a package as a group of logically related classes.

To keep the code base clean and readable as a project increases in its size and scope, the necessity of organizing one’s code into a stipulated and well understood package structure becomes more and more apparent.

In this article, I discuss everything related to Java packages, from their need, to their creation, use, naming and organisation. I discuss the best practices as employed by experienced Java developers, and as used in the industry.

Let’s get started!

What is a Java Package?

Any software project that extends past implementing trivial functionality contains several components to it. Let’s consider a simple API as an example. Some components it would contain would be:

  • The Controller
  • The Service Layer (Containing business logic)
  • The Data Access Layer (To communicate with the database)

Following a clean and modular design pattern, all these functionalities should each be given their own Java dedicated class files. Further, these class files should be clubbed together into a single directory, since they serve distinct functionalities, but ultimately have the same common purpose. This directory containing logically related Java classes would be a package.

A screenshot of a package structure. IntelliJ (my recommended IDE for Java) marks the packages with a “dot” in the directory icon. Here, com.example.demo, professor and student are all packages.

Let’s take the student package in the above screenshot as an example. It contains the classes Student, StudentConfig, StudentController, and StudentService, as well as the interface StudentRepository. It is abundantly clear how these related classes belong to the same package. Similarly, the classes within the package professor would also be logically related to each other.

The Package Structure

Now that we understand what a Java package is, it’s time to look more deeply into the package structure itself.

Despite appearing like a high level concept to beginners in Java, packages are essentially directories only. In fact, on the disk they exist as directories.

If we have a package called com.example.demo, it would consist of the directory demo, nested within the directory example, which in turn is nested within the directory com.

The above screenshot shows the package structure as seen from the disk. Look at how the packages are simply nested directories.

We can see how packages can contain sub packages, analogous to just how directory trees exist on the disk. We can also understand how two classes inside the same package cannot have the same name, just how any two files in a directory should be distinctly named.

Creating a package structure

Creating a package structure is trivial. We need to have an src directory as a root to hold our project. This directory itself is not a package.

Within this directory, we can start creating our packages following the standard naming conventions, which I will discuss later in this article.

However, directly placing the packages within src is not recommended. Take a look at the example screenshots attached above. The package com.example.demo sits inside src/main/java. The way I have structured this application is in accordance with the recommended way as suggested by Maven. I’m planning on writing another article discussing this Java design pattern in near future. I will make sure to link it here for reference.

Classes in a package

We saw how to create a package structure. However, all classes that belong inside a package should declare themselves as belonging to it to be considered members of that package.

This can be done simply via a package statement, usually the very first line of a Java program.

package com.example.demo;

public class DemoApplication{
...

DemoApplication.java from the previous example screenshot would have the above as the first line.

Similarly, any class inside the com.example.demo.student package would have the following declaration.

package com.example.demo.student;

public class Student{
...

So, in order to create a package the following are the only two steps we need to perform:

  • Create a nested directory with your required package name
  • Create classes within that directory which have the package declaration

Importing classes from across packages

Importing a class refers to making all of the imported class’ public methods and variables accessible to the class that it is being imported into.

If two classes belong to the same package, we don’t need to import one to use it in another. As an example, the StudentController class above can reference StudentService without needing to import it.

So then, how do we import classes that belong to different packages? We use the import statement to do so.

To import the class com.example.demo.professor.Professor inside, say, com.example.demo.student.StudentController,

import com.example.demo.professor.Professor;

To import all classes from a package,

import com.example.demo.professor.*;

What if two classes in different packages have the same name?

Let’s consider the case where we have two classes with the same name in two different packages. As an example, assume we have a ClassA which belongs to com.example.package1, and a ClassA which belongs to com.example.package2.

To import both, we CANNOT do:

import com.example.package1.ClassA;
import com.example.package2.ClassA;

This is because when we import a class, the compiler internally refers to it using its name (ClassA, in this case). Similar to how we can’t have two variables having the same name inside the same program, we can’t have two classes with the same name inside a program either.

So, then how to go about using these two classes in the same program? The answer lies in the fact that we don’t have to import classes to use them (yes, I know I said earlier that we have to ;P). In reality what the import statement does is allow us to refer to a class using just its name instead of its Fully Qualified Name (FQN). The fully qualified class name is composed of the full package path, with the class name itself included at the end. But that doesn’t stop us from referring to it using its FQN.

For instance, by importing com.example.package1.ClassA we can refer to its type as simply ClassA, instead of refering to it by its FQN, which is, com.example.package1.ClassA.

Therefore, to use two classes having the same name but from different packages, we can simply use their FQNs to refer to them. This is how that would look like:

com.example.package1.ClassA classA1 = new com.example.package1.ClassA();
com.example.package2.ClassA classA2 = new com.example.package2.ClassA();

In fact, one doesn’t have to use import statements in their programs at all. Using the FQNs, we can refer to any class we want and start using it.

Package Naming Conventions

Here are some conventions to follow while naming your packages.

  • Package names are completely in lowercase. Even if the package name is a combination of multiple words, such as mypackage, all the words would still begin with a lowercase letter. This naming convention is followed to avoid package names looking the same as class or interface names.
  • Packages are structured using reverse domain names. As an example, if I’m working for a company whose website is “example.com”, I would create a package structure like com.example. In other words, I will first have a directory called “com”, and will nest the directory “example” within it. This reverse domain name convention is followed to avoid packages that we create ourselves from having the same names as the built-in Java packages.
Take a look at how the I have created the packages com.example.package1 and com.example.package2 using the lowercase name and reverse domain name conventions.

Importance and need of packages

At this point in the article, the importance of using packages as a way of organizing one’s program should be apparent. Packages are integral to creating clean, understandable and modular Java applications.

However, packages are fundamental to Java in more ways than just being a means of organizing your application. Let’s go over the how.

Namespace Isolation

Packages are Java’s way of providing namespaces. A namespace is a region to hold logically related members, where each member can be identified with a unique name. With is definition in hand, we can see how packages are analogous to namespaces.

Access Protection

The official Oracle documentation says,

You can allow types within the package to have unrestricted access to one another yet still restrict access for types outside the package.

This means that it is possible to stop members inside a package from being referenced outside it. This is achieved via the package-protected (default) access modifier.

Members of a class can have declared as package protected. Such members can’t be accessed outside the package they belong to. Let’s see how to declare something as package protected.
public String str1;

public String str1;
private String str2;
protected String str3;
String str4;

In the above code block,

  • str1 is public, and will be visible across classes and packages
  • str2 is private, and will be visible only to other members of its class
  • str3 is protected, and will be visible only to members within its package, and classes that inherit this class outside its package
  • str4 has the default package-protected modifier. This means that its visiblity is restricted to members of its own package only, or in other words, this string cannot be accessed across packages.
A succinct summary of access modifiers taken from the Oracle Java documentation.

Organizing your code into packages

Now that we know all about what packages are, and why they are needed, the next thing to consider is how exactly to go about organizing our code into packages.

There is no hard and fast rule about what classes should belong to the same package. However, generally there should be some logical reason behind why the classes which are grouped together in a package belong together.

Some approaches you can follow while grouping classes in packages are now discussed.

Grouping by functionality

Let’s say I’m creating a website. I can create a blog package then which will contain all the files related to it. There will be a means to access and modify the database, a means to interact with the frontend, and a means to connect it all together. All these different components belong in the package blog because they all ultimately serve the common purpose of making the blog inside my website work.

Grouping by layer

Continuing the same website example from above. Let’s say I have to add a portfolio to showcase my projects to my website. So now if I were to divide via functionality as discussed above, I would have created a new package portfolio, and put all the files related to it in it.

Another approach is to group by layer. In this method, I will create packages according to what layer the purpose it serves belongs to. So I can have a package called database and put all code controlling access to the database inside it, another package called service to hold business logic inside it and so on.

I recommend using the first approach for your applications. It feels more readable and is more manageable as the application size grows.

In this article I discussed everything related to packages. We started with discussing what packages are and how they are structured, moved on to discuss how to use members of a package inside a class, further talked about the standard package naming conventions, dived deep into the need and importance of packages, and finally discussed how to organize our code into packages.

On this note, I end this article. I wrote this with the objective of helping beginners in Java understand one of the most fundamental concepts of software design using the language. Hope this helps someone out there! Happy coding!

The screenshots I used in this article are from projects available on my GitHub at:

Feel free to reach out to me in case of any doubts or questions, or maybe just to chat!

--

--

Chahat Kalsi

Computer science student with a passion for learning intricacies of programming languages, and discovering computational solutions to interesting problems!