Multiple Source Files

The Course
•
6 min read

A program can be organized into several source files without becoming several independent programs. Main.java supplies the entry point, while Session.java defines a model Main uses. This is a useful place for a boundary: main chooses objects and actions; the model stores state and implements its own behavior.

In this editor, click a filename tab to read or change it. The marked entry is Main.java. Run starts that file and resolves the helper class from Session.java. Changing tabs does not change which file runs. Both tabs form one program, and each Run starts with fresh object and static state.

Match names and keep files flat

Name each helper file after its class, including capitalization. A public class Session belongs in Session.java for this course. Use flat filenames: packages and folders are outside this playground’s supported layout. A mistyped entry or a nested Java path produces an authoring/layout error rather than silently running a different source file.

Imports belong to the file that uses the imported name. If Session needs an ArrayList later, put the import in Session.java; importing it in Main does not make it visible everywhere. Also note that a valid flat helper never referenced by the program may remain unchecked, just as in the Java source launcher. The presence of a tab does not prove it was used or tested.

Follow the boundary

Read Main first, then open Session and follow the constructor and methods it calls. Predict both summary lines before running. These fixed values are trusted; validation of arbitrary input comes next day.

Two files, one program Java
public class Main { public static void main(String[] args) { Session java = new Session("Java", 25); Session reading = new Session("Reading", 10); java.complete(); System.out.println(java.summary()); System.out.println(reading.summary()); } } public class Session { private final String topic; private final int minutes; private boolean done; // These examples supply trusted values. Day 6 adds input validation. public Session(String topic, int minutes) { this.topic = topic; this.minutes = minutes; } public void complete() { done = true; } public String summary() { if (done) { return topic + ": " + minutes + " min, done"; } return topic + ": " + minutes + " min, planned"; } }
Check your prediction
Java: 25 min, done
Reading: 10 min, planned

Main constructs two independent objects, then completes only the Java session. Session owns the summary wording and completion field. Open the Session.java tab, change min to minutes in summary, and rerun to see both lines change.

Put your objects in a list

Day 4 used a list of Strings. You can use your own Session type in the same place: ArrayList<Session> holds Session objects. The list manages positions; each Session still manages its own completion state. This example builds on the single-file Session from Private State & Methods, so you can trace the list behavior without switching tabs.

A list holds references to your objects Java
import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        Session first = new Session(25);
        ArrayList<Session> sessions = new ArrayList<>();
        sessions.add(first);
        sessions.add(new Session(10));

        System.out.println(sessions.size());
        System.out.println(first.remaining());

        // get returns the object at index 0, not a separate copy.
        Session selected = sessions.get(0);
        selected.complete();

        for (Session session : sessions) {
            System.out.println(session.remaining());
        }
    }
}

class Session {
    private final int minutes;
    private boolean done;

    Session(int minutes) {
        this.minutes = minutes;
    }

    public void complete() {
        done = true;
    }

    public int remaining() {
        if (done) {
            return 0;
        }

        return minutes;
    }
}
Check your prediction
2
25
0
10

first and selected refer to the same Session, so completing selected also changes what first observes. The other Session stays unchanged. Trace the for-each loop: on each iteration, session is the next Session object, so you can call its methods.

You may also see sessions.get(0).complete(). Read it in two steps: get the object at index 0, then call complete on that object. It does the same work as the selected variable and complete call above. Try replacing those two lines with the shorter call, then run again. Index 0 must exist before either form is safe.

A helper method can be tested separately Java
public class Main {
    public static void main(String[] args) {
        System.out.println(label(25));
        System.out.println(label(10));
    }

    static String label(int minutes) {
        return minutes + " min";
    }
}
Check your prediction
25 min
10 min

A small calculation can be checked separately from file organization. Separating source files should clarify responsibilities; it should not make every minor expression its own class.

Your turn

Exercise

Rebuild the collaboration

In the single-file workspace below, create Main plus a helper class Topic. Topic has a private final String name supplied by its constructor, and public String label() returning Topic: followed by the name. Construct Java and print Topic: Java. Keep both classes in this workspace for this exercise. The supplied browser tabs have fixed filenames; there is no add-tab step. If you later work locally, you can move Topic into Topic.java, change its declaration to public class Topic, and keep Main in Main.java. Splitting this answer into files is optional.

Your workspace Java
public class Main {
    public static void main(String[] args) {
        // Construct and call a Topic.
    }
}

class Topic {
    // Define the field, constructor and label operation.
}
Hint 1: the idea

The class collaboration is the same whether the helper is below Main or in a correctly named separate file.

Hint 2: a first step

Main calls new Topic with a String, then invokes label on that object. The label method needs to read the private field rather than return a fixed Java message.

Show one solution
public class Main {
    public static void main(String[] args) {
        Topic topic = new Topic("Java");
        System.out.println(topic.label());
    }
}

class Topic {
    private final String name;

    Topic(String name) {
        this.name = name;
    }

    public String label() {
        return "Topic: " + name;
    }
}
Topic: Java

Change it and explain

In the two-tab example, deliberately misspell summary in Main: the compiler points to Main.java. Restore it, then misspell minutes inside Session.summary: the diagnostic should name Session.java. Restore each defect before continuing. Never treat a previous successful run as evidence that new edits also work.

There are two useful debugging questions: which file owns the failing behavior, and which call supplied the wrong value? A stack trace or diagnostic helps you follow that connection. The final project will add a collection owner and a separate test entry, keeping the same small-file pattern.

Was this article helpful?

Your response is saved on this device.