Skip to content
On this page

Package

In the previous code, we named the classes and interfaces with simple names such as Person , Student , Hello , etc.

In reality, if Bob writes a Person class, and Jack also writes a Person class, and now John wants to use both Bob's Person and Jack's Person , what should I do?

If Bob writes an Arrays class, and JDK also comes with an Arrays class, how to resolve the class name conflict?

In Java, we use package to resolve name conflicts.

Java defines a namespace called a package: package . A class always belongs to a certain package. The class name (such as Person ) is just an abbreviation. The real complete class name is [package].[class].

For example:

Bob's Person class is stored under the package bob , so the complete class name is bob.Person ;

Jack's Arrays class is stored under the package mr.jack , so the complete class name is mr.jack.Arrays ;

The Arrays class of JDK is stored under the package java.util , therefore, the complete class name is java.util.Arrays .

When defining class , we need to declare which package this class belongs to on the first line.

Bob’s Person.java file:

java
package bob; // Declare the package name bob

public class Person {
}

Jack’s Arrays.java file:

java
package mr.jack; // Declare the package name mr.jack

public class Arrays {
}

When the Java virtual machine is executed, the JVM only looks at the complete class name. Therefore, as long as the package name is different, the class is different.

Packages can be multi-layered, separated by . For example: java.util .

special attention

There is no father-son relationship between packages. java.util and java.util.zip are different packages, and there is no inheritance relationship between them.

class that does not define a package name uses the default package, which can easily cause name conflicts. Therefore, it is not recommended to not write a package name.

We also need to organize the above Java files according to the package structure. Assuming that package_sample is used as the root directory and src is used as the source code directory, then the entire file structure is:

package_sample
└─ src
    ├─ bob
    │  └─ Person.java
    └─ mr
       └─ jack
          └─ Arrays.java

That is, the directory level corresponding to all Java files must be consistent with the package level.

The compiled .class files also need to be stored according to the package structure. If you use an IDE and put the compiled .class file in the bin directory, then the compiled file structure is:

package_sample
└─ bin
   ├─ bob
   │  └─ Person.class
   └─ mr
      └─ jack
         └─ Arrays.class

Package Scope

Classes located in the same package can access the fields and methods of the package scope. Fields and methods that are not modified with public , protected , or private are package scopes. For example, the Person class is defined under the hello package:

java
package hello;

public class Person {
    // package scope:
    void hello() {
        System.out.println("Hello!");
    }
}

The Main class is also defined under the hello package:

java
package hello;

public class Main {
    public static void main(String[] args) {
        Person p = new Person();
        p.hello(); // It can be called because Main and Person are in the same package
    }
}

import

In a class , we always refer to other class . For example, if Bob's bob.Person class wants to reference Jack's mr.jack.Arrays class, there are three ways to write it:

The first is to write the complete class name directly, for example:

java
package bob;

public class Person {
    public void run() {
        // Write the complete class name: mr.jack.Arrays
        mr.jack.Arrays arrays = new mr.jack.Arrays();
    }
}

Obviously, it is more painful to write the complete class name every time.

Therefore, the second way to write is to use the import statement to import Jack's Arrays , and then write a simple class name:

java
package bob;

// Import full class name:
import mr.jack.Arrays;

public class Person {
    public void run() {
        // Write a simple class name: Arrays
        Arrays arrays = new Arrays();
    }
}

When writing import , you can use * to import all class under this package (but not including class in sub-packages):

java
package bob;

// Import all classes of the mr.jack package:
import mr.jack.*;

public class Person {
    public void run() {
        Arrays arrays = new Arrays();
    }
}

We generally do not recommend this way of writing, because after importing multiple packages, it is difficult to see which package Arrays class belongs to.

There is also an import static syntax, which can import static fields and static methods of a class:

java
package main;

// Import all static fields and static methods of the System class:
import static java.lang.System.*;

public class Main {
    public static void main(String[] args) {
        // Equivalent to calling System.out.println(…)
        out.println("Hello, world!");
    }
}

import static is rarely used.

The .class file finally compiled by the Java compiler only uses the full class name . Therefore, in the code, when the compiler encounters a class name:

  • If it is a complete class name, search the class directly based on the complete class name;
  • If it is a simple class name, search in the following order:
    • Find whether this class exists in the current package ;
    • Find whether the import package contains this class ;
    • Find whether the java.lang package contains this class .

If the class name cannot be determined according to the above rules, a compilation error will be reported.

Let's look at an example:

java
// Main.java
package test;

import java.text.Format;

public class Main {
    public static void main(String[] args) {
        java.util.List list; // ok,Use full class name -> java.util.List
        Format format = null; // ok,Use imported classes -> java.text.Format
        String s = "hi"; // ok,String using java.lang package -> java.lang.String
        System.out.println(s); // ok,System using the java.lang package -> java.lang.System
        MessageFormat mf = null; // Compilation error: MessageFormat cannot be found: MessageFormat cannot be resolved to a type
    }
}

Therefore, when writing a class, the compiler will automatically do two import actions for us:

  • By default, other class of the current package are automatically import ;
  • By default, import java.lang.* .

Notice

The java.lang package is automatically imported, but packages like java.lang.reflect still need to be imported manually.

If there are two class with the same name, for example, mr.jack.Arrays and java.util.Arrays , then only one of them can import , and the other must have the complete class name.

Best Practices

To avoid name conflicts, we need to determine unique package names. The recommended practice is to use an inverted domain name to ensure uniqueness. For example:

  • org.apache
  • org.apache.commons.log
  • com.bob.sample

Subpackages can be named according to their functions.

Be careful not to have the same name as the classes in java.lang package, that is, do not use these names for your own classes:

  • String
  • System
  • Runtime
  • ...

Be careful not to have the same name as a common JDK class:

  • java.util.List
  • java.text.Format
  • java.math.BigInteger
  • ...

Compile and run

Suppose we create the following directory structure:

work
├── bin
└── src
    └── com
        └── itranswarp
            ├── sample
            │   └── Main.java
            └── world
                └── Person.java

Among them, the bin directory is used to store compiled class files, and src directory stores Java source codes according to the package structure. How do we compile these Java source codes at once?

First, make sure the current directory is the work directory, which is the parent directory where src and bin are stored:

sh
$ ls
bin src

Then, compile all Java files in the src directory:

sh
$ javac -d ./bin src/**/*.java

The command line -d specifies that the output class files are stored in the bin directory. The following parameters src/**/*.java represent all .java files in the src directory, including subdirectories of any depth.

Note: Windows does not support ** this method of searching all subdirectories, so compiling under Windows must list all .java files in sequence:

sh
C:\work> javac -d bin src\com\itranswarp\sample\Main.java src\com\itranswarp\world\Persion.java

If compiled without errors, the javac command will have no output. You can see the following class files in bin directory:

bin
└── com
    └── itranswarp
        ├── sample
        │   └── Main.class
        └── world
            └── Person.class

Now, we can run the class file directly. Determine the classpath based on the location of the current directory. For example, if the current directory is still work , the classpath is bin or ./bin :

$ java -cp bin com.itranswarp.sample.Main 
Hello, world!

Practise

Please create a project according to the following package structure:

oop-package
└── src
    └── com
        └── itranswarp
            ├── sample
            │   └── Main.java
            └── world
                └── Person.java

Summary

Java's built-in package mechanism is to avoid class naming conflicts;

The core classes of JDK use the java.lang package, which will be automatically imported by the compiler;

Other commonly used classes in JDK are defined in java.util.* , java.math.* , java.text.* ,...;

It is recommended to use an inverted domain name for package names, such as org.apache .

Package has loaded