- Learn package organization and naming best practices
- Understand when to use static members
- Master common patterns and avoid anti-patterns
Packages and Static Members Best Practices in Java
You know how packages and static members work. Now let's talk about using them well—organizing code that scales and avoiding common mistakes.
Package Organization Best Practices
1. Follow Reverse Domain Name Convention
Use reverse domain names to ensure uniqueness:
// Good - unique and professional
com.company.project.module
org.organization.library.component
edu.university.department.course
// Avoid - generic and potentially conflicting
utils
helpers
common
tools
2. Use Hierarchical Package Structure
Organize packages in a logical hierarchy that reflects your application's architecture:
com.example.ecommerce
├── controller/
├── service/
├── repository/
├── model/
│ ├── user/
│ ├── product/
│ └── order/
├── util/
└── config/
3. Keep Package Names Lowercase
Package names should always be lowercase with no underscores:
// Good
com.example.useraccount
com.example.orderprocessing
// Avoid
com.example.UserAccount // Uppercase
com.example.user_account // Underscore
4. Avoid Deep Package Hierarchies
Don't create packages that are too deeply nested (more than 5-6 levels):
// Good - reasonable depth
com.example.app.service.user
// Avoid - too deep
com.example.application.businesslogic.service.user.account.management
Static Members Best Practices
1. Use Static for Constants
Static final variables are perfect for constants:
public class HttpStatus {
public static final int OK = 200;
public static final int NOT_FOUND = 404;
public static final int INTERNAL_SERVER_ERROR = 500;
private HttpStatus() {} // Prevent instantiation
}
2. Prefer Static Methods for Utility Functions
Use static methods for pure functions that don't need instance state:
public class MathUtils {
public static double calculateDistance(double x1, double y1, double x2, double y2) {
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
}
public static boolean isPrime(int number) {
if (number <= 1) return false;
for (int i = 2; i <= Math.sqrt(number); i++) {
if (number % i == 0) return false;
}
return true;
}
}
3. Use Static Factory Methods
Provide static factory methods instead of public constructors:
public class User {
private String username;
private String email;
private UserType type;
private User(String username, String email, UserType type) {
this.username = username;
this.email = email;
this.type = type;
}
public static User createRegularUser(String username, String email) {
return new User(username, email, UserType.REGULAR);
}
public static User createAdminUser(String username, String email) {
return new User(username, email, UserType.ADMIN);
}
}
4. Avoid Static for Mutable State
Don't use static variables for mutable shared state:
// Avoid - mutable static state
public class GlobalCounter {
public static int counter = 0; // Dangerous in multi-threaded environments
}
// Better - use instance variables or thread-safe alternatives
public class Counter {
private int value = 0;
public synchronized void increment() {
value++;
}
public synchronized int getValue() {
return value;
}
}
Common Anti-Patterns to Avoid
1. Overusing Static Imports
// Avoid - pollutes namespace
import static java.util.Arrays.*;
import static java.util.Collections.*;
import static java.lang.Math.*;
// Better - explicit imports
import java.util.Arrays;
import java.util.Collections;
2. Static Methods That Should Be Instance Methods
public class Calculator {
// Bad - should be instance method
public static double add(double a, double b) {
return a + b;
}
}
// Better
public class Calculator {
public double add(double a, double b) {
return a + b;
}
}
3. God Classes with Too Many Static Methods
// Avoid - single class doing too much
public class Utils {
public static void sendEmail(String to, String subject, String body) { }
public static void saveToDatabase(Object obj) { }
public static String formatDate(Date date) { }
public static void logMessage(String message) { }
}
// Better - separate concerns
public class EmailService {
public void sendEmail(String to, String subject, String body) { }
}
public class DatabaseService {
public void save(Object obj) { }
}
Package Visibility and Encapsulation
Use Package-Private Effectively
// In com.example.service
public class UserService {
// Public API
public User createUser(String username, String email) {
validateInput(username, email);
User user = new User(username, email);
saveUser(user);
return user;
}
// Package-private helper methods
void validateInput(String username, String email) {
// Validation logic
}
void saveUser(User user) {
// Persistence logic
}
}
Testing Static Members
Testing Static Methods
public class StringUtils {
public static String capitalize(String str) {
if (str == null || str.isEmpty()) return str;
return str.substring(0, 1).toUpperCase() + str.substring(1).toLowerCase();
}
}
// Test class
public class StringUtilsTest {
@Test
public void testCapitalize() {
assertEquals("Hello", StringUtils.capitalize("hello"));
assertEquals("World", StringUtils.capitalize("WORLD"));
assertNull(StringUtils.capitalize(null));
}
}
Testing Classes with Static Dependencies
public class UserService {
private static DatabaseConnection db = DatabaseConnection.getInstance();
public User findUser(String id) {
// Use db connection
return null;
}
}
// Test with dependency injection
public class UserServiceTest {
@Test
public void testFindUser() {
// Mock or use test database
UserService service = new UserService();
// Test logic
}
}
Documentation and Maintenance
Document Package Purpose
/**
* This package contains all data access objects and repository classes
* for the user management system.
*
* Classes in this package handle database operations for user entities.
*/
package com.example.user.repository;
Document Static Members
public class Configuration {
/**
* Maximum number of concurrent connections allowed.
* Default value: 10
*/
public static final int MAX_CONNECTIONS = 10;
/**
* Calculates the timeout based on the number of retries.
* @param retries number of retry attempts
* @return timeout in seconds
*/
public static int calculateTimeout(int retries) {
return Math.min(retries * 5, 60);
}
}
Good package organization and smart static usage come from experience. Follow reverse domain naming, keep packages focused, and treat static members with caution. Overuse of static leads to hard-to-test code. Use them for utilities and constants, not for managing state. Your future self will thank you.
