A package is a namespace. It groups related classes, prevents name collisions between unrelated
libraries, and — through access modifiers — decides who can see what. Two classes called
Order can coexist happily as long as they live in different packages.
Declaring a package
package com.lovemesomecoding.orders;
public class Order {
private final String id;
public Order(String id) {
this.id = id;
}
public String getId() { return id; }
}
The package line must be the first statement in the file, before any import. The
package name maps directly onto directories, and the compiler enforces it:
src/main/java/
com/
lovemesomecoding/
orders/
Order.java package com.lovemesomecoding.orders;
OrderService.java
payments/
PaymentGateway.java package com.lovemesomecoding.payments;
The convention is your domain name reversed — com.lovemesomecoding.orders — which is
what guarantees global uniqueness. Package names are always lowercase, and by convention singular
where it reads naturally.
A class with no package line lands in the default package. This works for a
one-file experiment and nothing else: classes in the default package cannot be imported from a named
package, so anything real will be unable to use them.
Imports
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import static java.util.Map.entry; // static import: the member itself
class Demo {
void run() {
List<String> names = new ArrayList<>();
Map<String, Integer> ages = Map.ofEntries(entry("Folau", 30));
System.out.println(names.size() + ages.size());
}
}
An import is purely a convenience — it lets you write List instead of
java.util.List. It does not make the code bigger or slower; nothing is "loaded" by an
import.
Two things you never need to import: classes in java.lang (String,
Integer, System, Math) and classes in the same package as
yours.
The wildcard form import java.util.*; works but is discouraged in shared code, for one
concrete reason — it hides where a type came from, and when two wildcard imports both offer a
List the compiler rejects the file and you have to work out why. Let your IDE manage
explicit imports.
When two classes with the same simple name are genuinely needed in one file, one of them must be fully qualified:
import java.util.Date;
class Demo {
void run() {
Date modern = new Date();
java.sql.Date legacy = new java.sql.Date(0); // no second import possible
System.out.println(modern + " " + legacy);
}
}
The four access modifiers
Access control is where packages stop being organisational and start being structural. There are four levels, and one of them has no keyword:
| Modifier | Same class | Same package | Subclass, other package | Anywhere |
|---|---|---|---|---|
private | yes | — | — | — |
| (none) — package-private | yes | yes | — | — |
protected | yes | yes | yes | — |
public | yes | yes | yes | yes |
public class Order {
private String secret; // this class only
String internal; // package-private — anything in this package
protected String forHeirs; // package, plus subclasses anywhere
public String id; // everyone
}
The default is package-private, not public. Most people assume a field with no modifier is public; it is not, and that default is a useful one. A class or method with no modifier is visible to its own package and invisible outside it — which is exactly right for a helper that exists to serve its neighbours.
Use it deliberately. If a class is only used by others in the same package, leaving it package-private means you can change or delete it without breaking anyone.
protected is narrower than it looks and worth using sparingly: it exposes a member to
every subclass anywhere, forever, which makes it part of your public API in practice.
Laying out a real project
Two common approaches. By layer groups classes by what kind of thing they are:
com.lovemesomecoding.shop
controller/ OrderController, ProductController
service/ OrderService, ProductService
repository/ OrderRepository, ProductRepository
model/ Order, Product
By feature groups them by what part of the business they serve:
com.lovemesomecoding.shop
order/ OrderController, OrderService, OrderRepository, Order
product/ ProductController, ProductService, ProductRepository, Product
shared/ config, error handling
By-layer is what most tutorials show and it is fine for a small application. By-feature scales better: everything about orders is in one place, a change to orders touches one directory, and — the real payoff — most of those classes can be package-private, because their only callers are in the same package. With by-layer, every service has to be public just so a controller in another package can reach it.
Pick one and apply it consistently. Mixing the two produces a tree where nobody can guess where anything lives.
Compiling with packages
# from the source root
javac -d out com/lovemesomecoding/orders/Order.java
# run using the FULLY QUALIFIED class name
java -cp out com.lovemesomecoding.orders.Order
Note the run command: once a class is in a package, its real name includes that package. In practice a build tool such as Maven or Gradle does all of this, but it is worth seeing once so the "could not find or load main class" error makes sense when you meet it.
Modules, and why you can mostly ignore them
Java 9 added a second, larger unit of organisation: the module, declared in a
module-info.java at the source root.
module com.lovemesomecoding.shop {
requires java.sql; // what this module depends on
exports com.lovemesomecoding.shop.order; // what other modules may use
}
A module states its dependencies and — the important half — which of its packages are visible
outside it. A public class in a package the module does not export is unreachable from
other modules, which finally makes "public" mean something less than "public to the entire world".
It matters enormously for the JDK itself, which is how java.base,
java.sql and the rest are separated. For ordinary application code most teams never
write a module-info.java, and a build tool's dependency management covers the same
ground. Recognise it when you see it; do not go looking for it on day one.
Next
That completes the structural half of the language. Collections is next — the containers you will use in essentially every program you write.