How Do Packages Work In Java

Packages are used to store related classes in one folder for better code maintenance and reusability. Here are some details that you need to understand to create and use packages in Java:
  1. Why we need packages
  2. What is package hierarchy
  3. How to place class in a package and compile it
  4. How to use classes placed in other packages
  5. How to compile to place .class files separate from source files
  6. What is NoClassDefFoundError
  7. How to use classpath to link classes placed at other locations and libraries
Lets see each section one by one:

Why we need packages

When we start learning Java, we usually place different classses in same folder but real Java applications has tens or even thousands of classes, as the project size increase, so the number of classes. Most software consists of multiple modules and each module may consists of multiple classes. If we place all classes in one folder, it become difficult to locate a particular piece of code or a class that provide a specific functionality. You have to comprehend or remember a lot of things to jump to right class. Developers who join the project team later, finds it difficult to understand the code or search different modules of the application. So identifying classes of one module and placing them together has advantages. For example, for an academic project, all accounts related classes should be stored in accounts folder and classes that belong to "admissions" should be placed in admission folder or package. It makes the code maintainance easy.

Here is another advantage of using package, in different modules we may need to create classes of same name. When all classes are placed in one folder, the file system do not allow to create classes of same name but if we place the classes in different packages, where each package stores classes of specific module, we can make classes of same name in different module because each module code is stored in different folder. Storage of packages follows hierarchical structure, as explained below.

What is package hierarchy

Rather than placing classes in different folders, we make folder hierarchies i.e. one main folder that contain other folders and each sub-folder contains other folders in it and so on. This approach helps to make packages unique. To make the package hierarchy unique, we start the package hierarchy following the domain name in reverse order. For exampple, assume an institute name is: DevTrainings whose website is http://devtrainings.com, want to develop an application for Academics management. Lets call the software: Academics Management Information System (AMIS for short). The unique package name would start with website URL in reverse order and then the software name: e.g. com.devtrainings.amis. It indicates, there is a com folder that contains devtrainings folder, that contains amis folder. We would place our application code inside 'amis' folder. If we need to divide our application in different modules, we can create more folders in amis, for example, all classes that belong to accounts management would be placed in acounts folder.

How to declare package for a class and compile it

We make package declaration in java code to indicate the class belong to some package. To place Student class in com.devtrainings.amis package, we would code as follows:

package com.devtrainings.amis;

public class Student{

private int id;

public Student(int id){
this.id = id;
}

public int getId(){
return id;
}

public void setId(int id){
this.id = id;
}
}

I have created a hierarchy of three folders: com/devtrainings/amis and placed the Student class inside amis folder. On my system, the package is placed at D:\codesamples. So the Student class is located at "D:\codesamples\com\devtrainings\amis\Student.java". Notice, I have not written the absolute path i.e.  starting from D:\codesamples in source code of Student class but used relative path i.e. starting from com folder. Using relative path, helps to make it portable. All you need is to transfer the com folder, all paths or imports inside classes are relative to it.

Present Working Directory

When you create classes without using package declaration and compile them using javac command, you must have noticed, javac command compiles the classes that are placed in PWD (Present Working Directory), assume you only write the class name and extension. PWD is directory where your command prompt is pointing to. For example when I open my Command Prompt, its output looks like:

Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

C:\Users\Asif>

In above output, PWD is C:\Users\Asif.

Compiling a class placed in package

To compile classes that are placed in a package, your PWD should be the folder where your package is located. In above example, it should be the folder where the 'com' folder is placed. For example it should be:

D:\codesamples>

Because the root folder of our package i.e. 'com' is placed in codesamples, from here you would write javac command passing class name with full path because Student.java file is not placed in codesamples folder, but inside com/devtrainings/amis folder.

D:\codesamples>javac com/devtrainings/amis/Student.java

It would compile the Student class and place the Student.class file inside same package i.e. com/devtrainings/amis . As far above code is concern, your can also compile Student class with PWD pointing to D:\codesamples\com\devtrainings\amis

D:\codesamples\com\devtrainings\amis>javac Student.java

but it is not good practice and does not work always. Why? Its explained in next section. Current take away is, always compile and run your code from the folder where you have created the package, D:\codesamples> in above case.

How to use classes from other packages

We use import statement to include classes that are placed in different packages. If you have used Scanner class to get input from user, you must have made import declaration that indicates in which package of JDK the Scanner class reside i.e. java.util.Scanner. Note that, when you compile or run a class, all import statements inside the class are resolved from JDK or from PWD. Lets create a class StudentTest in com.devtrainings.amis.application package that uses Student class. The Student class belong to different package, so we have to import it in StudentTest code. If Student class package is same as StudentTest package, then there is no need to import the Student class into StudentTest because classes placed in same package can access each other directly i.e. without import statement.

package com.devtrainings.amis.application;

import com.devtrainings.amis.Student;

public class StudentTest{

public static void main(String args[]){

Student s = new Student(1);
System.out.println( "Student ID is : " + s.getId() );
}
}

To compile StudentTest class, the PWD must be the folder where the package is placed i.e. D:\codesamples and we must enter the complete path of Student class to compile and run it. See below code listing.

D:\codesamples>javac com/devtrainings/amis/application/StudentTest.java

D:\codesamples>java com/devtrainings/amis/application/StudentTest

Student ID is : 1

Always Compile and Run from Package Location

In previous section, I explained the PWD must be the folder where package is placed, lets see what happens when PWD points to the folder where your class-to-be-compiled is placed.

D:\codesamples\com\devtrainings\amis\application>javac StudentTest.java
StudentTest.java:3: error: cannot find symbol
import com.devtrainings.amis.Student;
^
symbol:   class Student
location: package com.devtrainings.amis
StudentTest.java:9: error: cannot find symbol
Student s = new Student(1);

Did you noticed the compiler could not find the Student class. Why? because it concatenate the path declared in Student class import statement with PWD and try to load the file. In above given output, PWD was D:\codesamples\com\devtrainings\amis\application> and import statement path was com.devtrainings.amis.Student, the compiler tried to load the class from D:\codesamples\com\devtrainings\amis\application\com\devtrainings\amis\Student.java which is incorrect. But when we compile the class with PWD: D:\codesamples> (as done in previous section), it gets compiled successfully. Because if we concatenate the Student class import statement location with PWD, the Student class path becomes correct i.e. D:\codesamples\com\devtrainings\amis\Student.java

How to compile to place class files separate from source files

When we compile a .java file, the compiler generates bytecode file with .class extension and its placed in same folder where the Java source file resides. It makes sense when are developing the application, but when you need to deliver the project to a client, we do not give source file but only .class files. So if there are many packages in your project, each having multiple classes, how you can compile the a class such that the output file is placed in desired location instead of with source code. You can pass -d switch to give a directory path with javac command where you want to place the .class files. Below I have compiled the StudentTest class.

D:\codesamples>javac -d D:\MyJavaApp com/devtrainings/amis/application/StudentTest.java

MyJavaApp folder already exist on my system in 'D' drive. If you browse the D:\MyJavaApp location, you would see the StudentTest.class and all classes that it import or use e.g. Student class, are compiled and placed in given directory, with proper package hierarchy automatically created. Please note, if there are some classes that are not linked with Student or StudentTest etc., they would not be compiled. For that you have to use javac command passing multiple files or using wild-cards. For example if there is a class Account in package com.devtrainings.amis.accounts package which is not lined with Student (i.e. Student class don't use Account nor Account class use Student), we can select multiple classes to compile like this:

D:\codesamples>javac -d D:\MyJavaApp com/devtrainings/amis/application/*.java com/devtrainings/amis/accounts/*.java

What is NoClassDefFoundError

NoClassDefFoundError is very common. As discussed earlier, when you run a Java application, the classes application use, must be visible to JVM so that JVM can load them. By default, the JVM searches classes in two locations i.e. JVM library and in your PWD. If the program you want to run use a class that do not exist in both locations, the compiler would generate NoClassDefFoundError. See below code sample (I have removed the Student class from com/devtrainings/amis/ folder).

D:\codesamples>java com/devtrainings/amis/application/StudentTest
Exception in thread "main" java.lang.NoClassDefFoundError: com/devtrainings/amis/Student
at com.devtrainings.amis.application.StudentTest.main(StudentTest.java:9)
Caused by: java.lang.ClassNotFoundException: com.devtrainings.amis.Student
at java.net.URLClassLoader$1.run(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
... 1 more

Above code is only to demonstrate the concept, the classes that belong to our project are definitely found as they are located in same root package (i.e. starting from com). I removed Student.class file just to show what happens when a class is not found when you run a program that use it. NoClassDefFoundError mostly occurs when you use a class from other class-libraries in your program but forget to tell JVM about that library location in your system. In next section, I have explained how you can link Java libraries provided by third parties with you program.


How to use classpath parameter to link classes placed at other locations

If our Java application uses a class that do not reside in JVM and PWD, we must provide its location using -classpath switch when compiling and running the application from command line. I would give two examples to demonstrate the classpath usage:

Example 1 - Using classpath to link classes residing outside the PWD

Assume there is a class Teacher that reside in c:\data folder and its package is com.example, lets further assume we want to create the Teacher object inside StudentTest class defined above (i.e. in package com.devtrainings.amis.application), here is the code listings:

package com.example;

public class Teacher {
public String name;

public Teacher(String name){
this.name = name;  
}
}
And StudentTest class that uses Teacher looks like this:
package com.devtrainings.amis.application;
import com.example.Teacher;

public class StudentTest{

public static void main(String args[]){  
Teacher teacher = new Teacher("Alice");
System.out.println(teacher.name);
}
}

In above StudentTest class, although Teacher class is imported into StudentTest, but as its package com.example do not exist in PWD below, so the JVM would be unable to find the Teacher class. Lets see the error:

D:\codesamples>javac com/devtrainings/amis/application/StudentTest.java
com\devtrainings\amis\application\StudentTest.java:2: error: package com.example does not exist
import com.example.Teacher;
^
com\devtrainings\amis\application\StudentTest.java:7: error: cannot find symbol
Teacher teacher = new Teacher("Alice");
Here we need to specify the location of Teacher package using -classpath switch (or just -cp). Now it would compile successfully:

D:\codesamples>javac -classpath C:\data\;. com/devtrainings/amis/application/StudentTest.java

D:\codesamples>java -classpath C:\data\;. com/devtrainings/amis/application/StudentTest
Alice

The semicolon in classpath C:\data\;. is used to split multiple locations. The dot (.) indicates the current folder. So we are asking JVM to load classes from JVM (the default), current folder, and C:\data\ folder. You also need to specify the same classpath switch to run the java application.


Example 2 - Using classpath to link classes from 3rd party java libraries or jar files

Java library is a collection of Java components. These components include Java classes, interfaces, enums, annotations, configuration or property files, etc. When we write real Java applications, we do not write all code ourselves. Different companies provide Java libraries that contains components to perform specific tasks. These libraries are available as JAR files (aka. Java Archive). To use classes from these JAR files, we use import statement in our classes to include them and add JAR files into our classpath when compiling or running the Java application so that JVM could locate and load them. Now I explain how use classes from these JAR File? First we must understand from where we get these JAR files. There are many companies and developer that publish these libraries on internet. For example, a company named Apache backs different open source projects. These projects are not limited to Java but also include other languages projects. A project named Apache Commons provide set of Java libraries designed for different needs. You can download library that most suits your requirements. For example, a library "Apache Commons Lang" provide many features related to Java Language Basics, "Compress" provide components related to processing compressed files, "DBUtils" provide helper classes that facilitate to interact with database more easily, "Email" helps sending emails in Java, and "FileUpload" helps to process file uploaded at server from a browser. Download the Apache Commons Lang in zip file and decompress it, its contains a file commons-lang3-3.3.2.jar (I have placed in in D:\libs folder), this is the file that we need to add in our classpath. There is a class StringUtils in this JAR file (see complete API docs here) which contains a method isEmpty. isEmpty takes a string as argument and tell whether it string is empty or not. I have used in StudentTest class, given below and then I would compile and run the class adding JAR into my classpath.
package com.devtrainings.amis.application;
import org.apache.commons.lang3.StringUtils;

public class StudentTest{

public static void main(String args[]){
String name = "alice";
String email = "";

System.out.println("name is empty: " + StringUtils.isEmpty(name));
System.out.println("email is empty: " + StringUtils.isEmpty(email));
}
}
Notice we have imported the StringUtils class in StudentTest. A class must be imported if it do not belong to java.lang package and nor it exist in StudentTest package. The complete package name can be seen from API Docs (link given above). Here I share how to use classpath to add external jar files when compiling and running the Java application.
D:\codesamples>javac -classpath D:\libs\commons-lang3-3.3.2.jar;. com/devtrainings/amis/application/StudentTest.java

D:\codesamples>java -classpath D:\libs\commons-lang3-3.3.2.jar;. com/devtrainings/amis/application/StudentTest
name is empty: false
email is empty: true
If you followed me at each step, hope you have learned basics of packages. Write your questions in comments, I would try to answer !

Comments

  1. The best Explanation i have ever found . keep doing such a nice work .

    ReplyDelete

Post a Comment