How to manage Java project structure

JavaJavaBeginner
Practice Now

Introduction

Effective project structure is crucial for successful Java development. This comprehensive guide explores the fundamental principles of organizing Java projects, providing developers with practical insights into creating scalable, maintainable, and well-structured software solutions.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL java(("Java")) -.-> java/FileandIOManagementGroup(["File and I/O Management"]) java(("Java")) -.-> java/ConcurrentandNetworkProgrammingGroup(["Concurrent and Network Programming"]) java(("Java")) -.-> java/ObjectOrientedandAdvancedConceptsGroup(["Object-Oriented and Advanced Concepts"]) java/ObjectOrientedandAdvancedConceptsGroup -.-> java/classes_objects("Classes/Objects") java/ObjectOrientedandAdvancedConceptsGroup -.-> java/class_methods("Class Methods") java/ObjectOrientedandAdvancedConceptsGroup -.-> java/packages_api("Packages / API") java/ObjectOrientedandAdvancedConceptsGroup -.-> java/oop("OOP") java/FileandIOManagementGroup -.-> java/files("Files") java/ConcurrentandNetworkProgrammingGroup -.-> java/working("Working") subgraph Lab Skills java/classes_objects -.-> lab-419476{{"How to manage Java project structure"}} java/class_methods -.-> lab-419476{{"How to manage Java project structure"}} java/packages_api -.-> lab-419476{{"How to manage Java project structure"}} java/oop -.-> lab-419476{{"How to manage Java project structure"}} java/files -.-> lab-419476{{"How to manage Java project structure"}} java/working -.-> lab-419476{{"How to manage Java project structure"}} end

Creating a Basic Java Project Structure

Java projects follow certain organizational conventions that help developers manage code effectively. In this step, you will create a simple Java project structure manually to understand the fundamental components.

Understanding Java Project Components

A Java project typically contains:

  • Source code files (.java)
  • Compiled bytecode files (.class)
  • Resource files (configuration, images, etc.)
  • Documentation

Creating a Simple Project Structure

Let's start by creating a basic project structure in your workspace. We'll build a simple "HelloWorld" application to demonstrate the concepts.

  1. First, open a terminal in your LabEx environment. Your terminal should already be in the /home/labex/project directory.

  2. Create a project directory for our Java application:

mkdir -p hello-java-app/src
cd hello-java-app
  1. Inside the src directory, create a simple Java class file:
mkdir -p src/com/example/app
  1. Now, let's create our first Java class. Open the code editor and create a new file called HelloWorld.java in the path hello-java-app/src/com/example/app/:
package com.example.app;

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, Java Project World!");
    }
}
  1. Let's compile this Java code. In the terminal, navigate back to the project root and run:
cd /home/labex/project/hello-java-app
mkdir -p bin
javac -d bin src/com/example/app/HelloWorld.java

The output should show no errors if compilation was successful.

  1. Now, run the compiled Java application:
java -cp bin com.example.app.HelloWorld

You should see the output:

Hello, Java Project World!

Understanding the Project Structure

Let's review what we've created:

hello-java-app/
├── bin/              ## Compiled bytecode (.class files)
│   └── com/
│       └── example/
│           └── app/
│               └── HelloWorld.class
└── src/              ## Source code (.java files)
    └── com/
        └── example/
            └── app/
                └── HelloWorld.java

This structure follows these principles:

  • Source code separation: All Java source files are in the src directory
  • Package structure: The com.example.app package corresponds to directories com/example/app/
  • Compiled code separation: Bytecode files are in a separate bin directory

Key Concepts

  • Packages: Java uses packages to organize classes and avoid naming conflicts
  • Directory structure: Package names map directly to directory structures
  • Classpath: The -cp flag tells Java where to find the compiled classes

You have now created a basic Java project structure manually. This foundation will help you understand more complex project structures used in real-world applications.

Organizing Java Code with Packages

In this step, you will learn how to organize your Java code using packages and create a more structured application with multiple classes. Proper package organization is essential for maintainable Java projects.

Understanding Package Conventions

Java packages follow a hierarchical naming convention:

  • Start with a domain name in reverse (e.g., com.example)
  • Add project or organization name (e.g., com.example.project)
  • Add functional areas (e.g., com.example.project.model)

Let's implement this structure in our project.

Creating a Multi-Package Project

We'll build a simple library management system with distinct packages for different concerns:

  1. Navigate to your project directory:
cd /home/labex/project
mkdir -p library-app/src
cd library-app
  1. Create a structured package layout:
mkdir -p src/com/example/library/model
mkdir -p src/com/example/library/service
mkdir -p src/com/example/library/util
  1. First, create a model class. Open the code editor and create a new file called Book.java in the path library-app/src/com/example/library/model/:
package com.example.library.model;

public class Book {
    private String title;
    private String author;
    private int year;

    public Book(String title, String author, int year) {
        this.title = title;
        this.author = author;
        this.year = year;
    }

    // Getters
    public String getTitle() {
        return title;
    }

    public String getAuthor() {
        return author;
    }

    public int getYear() {
        return year;
    }

    @Override
    public String toString() {
        return "Book{title='" + title + "', author='" + author + "', year=" + year + "}";
    }
}
  1. Next, create a service class in library-app/src/com/example/library/service/BookService.java:
package com.example.library.service;

import com.example.library.model.Book;
import java.util.ArrayList;
import java.util.List;

public class BookService {
    private List<Book> books = new ArrayList<>();

    public void addBook(Book book) {
        books.add(book);
    }

    public List<Book> getAllBooks() {
        return new ArrayList<>(books);
    }

    public Book findBookByTitle(String title) {
        for (Book book : books) {
            if (book.getTitle().equalsIgnoreCase(title)) {
                return book;
            }
        }
        return null;
    }
}
  1. Create a utility class in library-app/src/com/example/library/util/BookFormatter.java:
package com.example.library.util;

import com.example.library.model.Book;

public class BookFormatter {
    public static String formatBookInfo(Book book) {
        return String.format("'%s' by %s (%d)",
            book.getTitle(), book.getAuthor(), book.getYear());
    }
}
  1. Finally, create the main application class in library-app/src/com/example/library/LibraryApp.java:
package com.example.library;

import com.example.library.model.Book;
import com.example.library.service.BookService;
import com.example.library.util.BookFormatter;

public class LibraryApp {
    public static void main(String[] args) {
        // Create service
        BookService bookService = new BookService();

        // Add some books
        bookService.addBook(new Book("The Great Gatsby", "F. Scott Fitzgerald", 1925));
        bookService.addBook(new Book("To Kill a Mockingbird", "Harper Lee", 1960));
        bookService.addBook(new Book("1984", "George Orwell", 1949));

        // Display all books
        System.out.println("Library Catalog:");
        for (Book book : bookService.getAllBooks()) {
            System.out.println(BookFormatter.formatBookInfo(book));
        }

        // Find a specific book
        Book foundBook = bookService.findBookByTitle("1984");
        if (foundBook != null) {
            System.out.println("\nFound book: " + foundBook);
        }
    }
}
  1. Compile all the Java files:
mkdir -p bin
javac -d bin src/com/example/library/model/Book.java src/com/example/library/service/BookService.java src/com/example/library/util/BookFormatter.java src/com/example/library/LibraryApp.java
  1. Run the application:
java -cp bin com.example.library.LibraryApp

You should see the output:

Library Catalog:
'The Great Gatsby' by F. Scott Fitzgerald (1925)
'To Kill a Mockingbird' by Harper Lee (1960)
'1984' by George Orwell (1949)

Found book: Book{title='1984', author='George Orwell', year=1949}

Understanding the Package Structure

Let's examine the structure we've created:

library-app/
├── bin/                              ## Compiled bytecode
└── src/                              ## Source code
    └── com/
        └── example/
            └── library/
                ├── model/            ## Data structures
                │   └── Book.java
                ├── service/          ## Business logic
                │   └── BookService.java
                ├── util/             ## Utility functions
                │   └── BookFormatter.java
                └── LibraryApp.java   ## Main application

Package Organization Principles

This structure follows important design principles:

  1. Separation of Concerns:

    • model package: Contains data structures
    • service package: Contains business logic
    • util package: Contains utility functions
  2. Logical Grouping: Related classes are grouped together in the same package

  3. Intuitive Navigation: The package structure makes it easy to locate specific functionality

  4. Import Management: Classes reference each other through imports, making dependencies clear

By organizing code in this way, you create more maintainable, scalable applications that are easier to understand and extend.

Using Maven for Project Management

In this step, you will learn how to use Apache Maven to manage your Java project. Maven is a powerful build automation and dependency management tool that simplifies project setup and maintenance.

Understanding Maven

Maven provides:

  • Standard project structure
  • Dependency management
  • Build automation
  • Project information management
  • Consistent build process across projects

Setting Up a Maven Project

Let's create a new project using Maven:

  1. First, check if Maven is installed:
mvn --version

You should see output similar to this:

Apache Maven 3.6.3
Maven home: /usr/share/maven
Java version: 11.0.18, vendor: Ubuntu, runtime: /usr/lib/jvm/java-11-openjdk-amd64
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "5.15.0-1036-azure", arch: "amd64", family: "unix"
  1. Navigate to your project directory:
cd /home/labex/project
  1. Create a new Maven project using an archetype (a project template):
mvn archetype:generate \
  -DgroupId=com.example.calculator \
  -DartifactId=simple-calculator \
  -DarchetypeArtifactId=maven-archetype-quickstart \
  -DarchetypeVersion=1.4 \
  -DinteractiveMode=false

This command will create a new project with a standard directory structure.

  1. Examine the project structure:
cd simple-calculator
ls -la

You should see output similar to:

total 24
drwxr-xr-x 4 labex labex 4096 ... .
drwxr-xr-x 6 labex labex 4096 ... ..
-rw-r--r-- 1 labex labex  174 ... .gitignore
-rw-r--r-- 1 labex labex  720 ... pom.xml
drwxr-xr-x 4 labex labex 4096 ... src

The key file here is pom.xml (Project Object Model), which defines the project configuration.

  1. Examine the Maven standard directory layout:
find src -type d

You should see:

src
src/main
src/main/java
src/main/java/com
src/main/java/com/example
src/main/java/com/example/calculator
src/test
src/test/java
src/test/java/com
src/test/java/com/example
src/test/java/com/example/calculator

This is the Maven standard directory layout:

  • src/main/java: Source code
  • src/main/resources: Resource files
  • src/test/java: Test code
  • src/test/resources: Test resources
  1. Let's look at the generated App.java file:
cat src/main/java/com/example/calculator/App.java

You should see a simple "Hello World" class:

package com.example.calculator;

/**
 * Hello world!
 *
 */
public class App
{
    public static void main( String[] args )
    {
        System.out.println( "Hello World!" );
    }
}

Enhancing the Maven Project

Let's enhance our calculator project by adding more classes:

  1. Create a new file called Calculator.java in src/main/java/com/example/calculator/:
package com.example.calculator;

public class Calculator {

    public int add(int a, int b) {
        return a + b;
    }

    public int subtract(int a, int b) {
        return a - b;
    }

    public int multiply(int a, int b) {
        return a * b;
    }

    public double divide(int a, int b) {
        if (b == 0) {
            throw new ArithmeticException("Cannot divide by zero");
        }
        return (double) a / b;
    }
}
  1. Now, modify the existing App.java file to use our Calculator class:
package com.example.calculator;

/**
 * Simple Calculator Application
 */
public class App
{
    public static void main( String[] args )
    {
        Calculator calculator = new Calculator();

        // Perform some calculations
        System.out.println("Addition: 5 + 3 = " + calculator.add(5, 3));
        System.out.println("Subtraction: 10 - 4 = " + calculator.subtract(10, 4));
        System.out.println("Multiplication: 6 * 7 = " + calculator.multiply(6, 7));
        System.out.println("Division: 20 / 4 = " + calculator.divide(20, 4));

        System.out.println("Calculator application completed successfully!");
    }
}
  1. Build the project using Maven:
mvn compile

You should see output ending with:

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
  1. Package the application into a JAR file:
mvn package

This command compiles your code, runs tests, and packages the application.

  1. Run the packaged application:
java -cp target/simple-calculator-1.0-SNAPSHOT.jar com.example.calculator.App

You should see the output:

Addition: 5 + 3 = 8
Subtraction: 10 - 4 = 6
Multiplication: 6 * 7 = 42
Division: 20 / 4 = 5.0
Calculator application completed successfully!

Understanding the Maven POM File

The Project Object Model (POM) file contains the project configuration. Open the pom.xml file in the editor and examine its structure:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.example.calculator</groupId>
  <artifactId>simple-calculator</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>simple-calculator</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <!-- Build configuration... -->
</project>

Key elements in the POM file:

  • groupId: Organization or project identifier
  • artifactId: Project name
  • version: Project version
  • dependencies: External libraries used by the project
  • build: Configuration for building the project

Maven Key Commands

Here are some essential Maven commands:

  • mvn compile: Compiles source code
  • mvn test: Runs tests
  • mvn package: Creates distributable package (JAR, WAR)
  • mvn install: Installs package in local repository
  • mvn clean: Removes build artifacts (target directory)
  • mvn clean install: Combination of clean and install

Maven has dramatically simplified Java project management by providing conventions, dependency management, and build automation. This standardized approach helps developers focus on writing code rather than managing project structure.

Summary

In this lab, you have learned essential strategies for managing Java project structure through hands-on practice:

  1. You started by creating a basic Java project structure manually, understanding the fundamental components of source directories, packages, and compiled code organization.

  2. You then advanced to creating a more complex project with proper package organization, implementing separation of concerns by dividing functionality into model, service, and utility packages.

  3. Finally, you learned how to use Maven, a powerful build automation tool, to create and manage standardized Java projects with dependency management and automated build processes.

These foundational skills will serve as a solid base for your future Java development work, allowing you to create well-organized, maintainable, and scalable applications. By following established conventions and best practices for project structure, you will collaborate more effectively with other developers and produce higher-quality code.