In the previous lesson you wrote HelloBiblioTech.java and ran it, but you did so by copying a structure without understanding it. That "I write a magic spell and it works" feeling is uncomfortable and, above all, it blocks progress: when something breaks you will not know where to look. This lesson dissects a Java file word by word: what a package is, what an import is for, what each term in public static void main(String[] args) means, how statements are separated, how code is commented, which naming conventions the whole community follows and, very importantly, how to read compiler messages so that you never get stuck.

Contents

  1. Full anatomy of a Java file
  2. The package declaration
  3. Imports
  4. The class declaration
  5. The main method, word by word
  6. Statements, blocks and semicolons
  7. Comments: line, block and Javadoc
  8. Naming conventions
  9. Indentation and style
  10. Packages and directories in practice
  11. Command-line arguments: args
  12. Typical compilation errors and how to read them
  13. Common Mistakes and Tips
  14. Exercises

  1. Full anatomy of a Java file

Let's start with the map. This is a complete Java file with all its parts, exactly as the start of BiblioTech will look:

package com.nexussoftware.bibliotech;          // 1. Package declaration

import java.util.Scanner;                      // 2. Imports

/**
 * Entry point of BiblioTech, the management system
 * for Nexus Software's internal technical library.
 */
public class BiblioTechApp {                   // 3. Class declaration

    public static void main(String[] args) {   // 4. main method

        System.out.println("BiblioTech v0.1");  // 5. Statements

    }                                          // end of the main method
}                                              // end of the class

The order of these parts is not optional: if there is a package declaration, it comes first; then the imports; then the class. Swapping the order is a compilation error.

flowchart TD
    A["package com.nexussoftware.bibliotech;<br/><i>optional, but at most one and always first</i>"] --> B["import java.util.Scanner;<br/><i>zero or more</i>"]
    B --> C["public class BiblioTechApp {<br/><i>one public class per file</i>"]
    C --> D["public static void main(String[] args) {<br/><i>entry point</i>"]
    D --> E["statements;<br/><i>terminated with a semicolon</i>"]

  1. The package declaration

package com.nexussoftware.bibliotech;

A package is a namespace: it groups related classes and prevents collisions. If two different libraries define a Book class, without packages it would be impossible to use both; with packages they are com.nexussoftware.bibliotech.Book and com.othercompany.Book, and there is no ambiguity.

The concrete rules:

  • It must be the first code statement in the file (only comments may precede it).
  • There can be only one per file.
  • It is written entirely in lowercase, with dots separating levels.
  • By convention the organisation's domain reversed is used: Nexus Software would own nexussoftware.com, so its packages start with com.nexussoftware. Then the project name is appended: com.nexussoftware.bibliotech.

If you leave out the declaration, the class ends up in the default package (unnamed). It works for quick tests, but it is discouraged in any real code: classes in the default package cannot be imported from other packages, which stops the project from growing. In this module you will see both forms; from module 3 onwards, when BiblioTech has several classes, we will always use an explicit package.

  1. Imports

import java.util.Scanner;

An import tells the compiler where to find a class you are going to use, so that you can write Scanner instead of the full name java.util.Scanner every time.

It is important to understand what it does not do: an import does not copy code, nor increase the size of the program, nor slow it down. It is purely a writing convenience. These two versions are equivalent:

// With an import
import java.util.Scanner;
...
Scanner input = new Scanner(System.in);

// Without an import, using the fully qualified name
java.util.Scanner input = new java.util.Scanner(System.in);

Variants:

Form Meaning
import java.util.Scanner; Imports one specific class. This is the recommended form.
import java.util.*; Imports every class in the java.util package (not from its subpackages)
import static java.lang.Math.PI; Static import: lets you write PI instead of Math.PI

Why have you never needed to import String or System? Because the java.lang package is imported automatically into every Java file. It contains the most basic things: String, System, Math, Integer, Object, Exception. Everything else has to be imported.

IDEs manage imports for you (IntelliJ adds them as you type the class name; Ctrl+Alt+O removes the redundant ones), but it is worth knowing what is going on.

  1. The class declaration

public class BiblioTechApp {
    ...
}

In Java, all executable code lives inside a class. There are no free-standing file-level functions as in Python or JavaScript. Even though in this module the class is only a container for main, from module 3 onwards you will see its true purpose: grouping data and behaviour.

Let's dissect the line:

  • public is an access modifier: it says the class is visible from any other package. The alternative is to write nothing (package visibility). Access modifiers are studied in depth in lesson 03-07.
  • class is the reserved word that declares a class.
  • BiblioTechApp is the name. It must start with a letter, $ or _ (in practice, always a letter) and it cannot be a reserved word of the language.
  • { ... } delimit the body of the class. Everything belonging to the class goes inside.

The file-name rule, already mentioned: if the class is public, the file must be named exactly like it plus .java. public class BiblioTechAppBiblioTechApp.java. Java is case-sensitive, so bibliotechapp.java will not do. A file may contain several classes, but only one of them can be public.

  1. The main method, word by word

public static void main(String[] args) {

This signature is a contract with the JVM. When you run java BiblioTechApp, the virtual machine looks in that class for a method with exactly this shape. If it differs in any detail, it will not start. Let's analyse each term:

Word What it means Why it is needed here
public Accessible from anywhere The JVM calls the method from outside your class; if it were private it could not
static Belongs to the class, not to an object When the program starts there is not yet any BiblioTechApp object; without static the JVM would have to create one first
void Returns no value When main finishes, there is nobody in your code to return anything to
main The exact name the JVM looks for It is a convention fixed by the platform, it cannot be anything else
String[] args One parameter: an array of strings It receives the arguments typed on the command line

Details that often come as a surprise:

  • The parameter name can change: String[] arguments is equally valid. What cannot change is its type.
  • String[] args and String args[] are equivalent; the first form is the modern convention.
  • Since Java 21 you can write void main() without modifiers in single-file programs (a preview feature designed for learning). This course uses the full standard form, which is what you will find in all professional code.
  • You may write static public void main(...): the order of the modifiers is free. The convention is public static.

If you get the signature wrong, the program compiles just fine (it is a valid method, it simply is not the main), but when you run it you get:

Error: Main method not found in class BiblioTechApp

That is, a runtime error, not a compilation error. Remembering this will save you some confusion.

  1. Statements, blocks and semicolons

A statement is a complete instruction. In Java every statement ends with a semicolon:

int publicationYear = 2018;
System.out.println("BiblioTech");
double fine = daysLate * 0.25;

A block is a set of statements between braces { }. Blocks define the body of classes and methods, and later on that of conditionals and loops.

The rule that clears up 90 % of the doubts: braces are not followed by a semicolon.

public class BiblioTechApp {   // no ; after the brace
    public static void main(String[] args) {   // no ; here either
        System.out.println("Hello");           // WITH ; : it is a statement
    }                                          // no ;
}                                              // no ;

Java is a free-format language: line breaks and spaces do not change the meaning, only the readability. These three versions compile identically:

// Readable
int publicationYear = 2018;
String title = "Effective Java";

// Equally valid, unreadable
int publicationYear=2018;String title="Effective Java";

// Equally valid, absurd
int
    publicationYear
        =
            2018 ;

The fact that the compiler accepts all three does not mean they are worth the same: code is read many more times than it is written.

  1. Comments: line, block and Javadoc

Comments are text the compiler ignores. Java has three forms and each has its place:

// Single-line comment. Everything after // up to the end of the line is ignored.

/*
   Block comment.
   It can span several lines.
   Useful for temporarily disabling a fragment of code.
*/

/**
 * Javadoc comment. It is written BEFORE a class, method or field
 * and is used to generate HTML documentation with the javadoc tool.
 *
 * @author BiblioTech Team
 * @version 0.1
 */
Type Syntax Typical use
Line // text Clarifying a specific line; the most common one
Block /* text */ Long explanations; temporarily disabling code
Javadoc /** text */ Documenting the public API: what a class or method does

Javadoc is more than a pretty comment: the javadoc tool included in the JDK generates a documentation website from them, and IDEs show them as contextual help when you hover over a class. The official Java API documentation is generated exactly like that. Its most used tags are @param (describes a parameter), @return (describes the returned value) and @author. In this module you will barely use them because you only have main, but from module 3 onwards they will be routine.

A tip about good comments: do not explain what the code does if the code already says it; explain why.

// BAD: redundant, adds nothing
int daysLate = 12;   // assigns 12 to daysLate

// GOOD: adds context the code cannot express
// Nexus Software allows 15 calendar days of loan before applying a fine.
final int LOAN_DAYS = 15;

  1. Naming conventions

Java does not force you to follow them: the compiler accepts class biblio_tech_app. But the Java community is exceptionally uniform on this point, and ignoring the conventions immediately marks code as amateur. They also have functional value: they tell you at a glance what kind of element you are looking at.

Element Convention BiblioTech examples
Class and interface PascalCase (every word capitalised) BiblioTechApp, Book, LoanManager
Method camelCase, starts with a verb calculateFine(), registerLoan()
Variable camelCase title, publicationYear, daysLate
Constant UPPERCASE_WITH_UNDERSCORES DAILY_RATE, LOAN_DAYS
Package all.in.lowercase com.nexussoftware.bibliotech

Why they really matter:

  • When you read Book book = ... you know without thinking that Book is a type and book a variable. The convention removes the ambiguity.
  • When you see DAILY_RATE you know it is a value that does not change, without going to look at its declaration.
  • IDEs, static analysers and code generation tools all assume them.

Additional rules about names:

  • They must start with a letter, _ or $. Never with a digit.
  • They may contain letters, digits, _ and $. Java accepts accented letters and ñ because it supports Unicode, but avoid them: they cause encoding problems between systems. Stick to plain ASCII: write publicationYear, not añoPublicación.
  • They cannot be reserved words (class, int, public, static, new, return…).
  • They are case-sensitive: title, Title and TITLE are three different identifiers.
  • Use descriptive names. daysLate is infinitely better than d or x. A variable's name is the most-read documentation in the code.

  1. Indentation and style

Indentation does not affect compilation, but it is the first thing that separates readable code from unreadable code. The dominant conventions in Java:

  • Four spaces per nesting level (not tabs; configure it in your IDE).
  • Opening brace at the end of the line, not on its own line. It is the official Java style and it differs from the usual C# one.
  • One space around operators: int total = a + b;, not int total=a+b;.
  • Lines of reasonable length (100-120 characters at most).
  • A blank line to separate logical blocks.
// Conventional Java style
public class BiblioTechApp {

    public static void main(String[] args) {
        String title = "Effective Java";
        int publicationYear = 2018;

        System.out.println(title + " (" + publicationYear + ")");
    }
}

Every IDE reformats automatically: in IntelliJ it is Ctrl+Alt+L, in Eclipse Ctrl+Shift+F, in VS Code Shift+Alt+F. Get into the habit of pressing it before you consider a file finished.

  1. Packages and directories in practice

Here is a strict rule that causes many errors for beginners: the package hierarchy must match the directory hierarchy.

If your file declares:

package com.nexussoftware.bibliotech;

then BiblioTechApp.java must live at .../com/nexussoftware/bibliotech/BiblioTechApp.java.

flowchart TD
    A["src/main/java/"] --> B["com/"]
    B --> C["nexussoftware/"]
    C --> D["bibliotech/"]
    D --> E["BiblioTechApp.java<br/>package com.nexussoftware.bibliotech;"]

And this changes how you compile and run from the terminal. Standing in src/main/java:

# Compile by giving the full path of the file
javac com/nexussoftware/bibliotech/BiblioTechApp.java

# Run using the FULLY QUALIFIED name of the class
java com.nexussoftware.bibliotech.BiblioTechApp

Notice the essential difference: when compiling you use slashes (it is a file path); when running you use dots (it is a class name). Typing plain java BiblioTechApp when the class is in a package produces Could not find or load main class, because the class's real name includes its package.

A practical option to keep the .class files out of your source code:

javac -d ../../../out com/nexussoftware/bibliotech/BiblioTechApp.java
java -cp ../../../out com.nexussoftware.bibliotech.BiblioTechApp
  • -d indicates the output directory for the .class files, and javac recreates the package structure there automatically.
  • -cp (classpath) tells the java command where to look for the classes.

When you work in the IDE it handles all of this for you; but when something breaks, you will know what it is doing underneath.

  1. Command-line arguments: args

The String[] args parameter receives the values you type after the class name when running. It is the simplest way to pass data to a program.

public class BiblioTechArguments {
    public static void main(String[] args) {
        // args.length says how many arguments were received.
        System.out.println("Arguments received: " + args.length);

        // args[0] is the first, args[1] the second... (counting from 0)
        System.out.println("First argument: " + args[0]);
        System.out.println("Second argument: " + args[1]);
    }
}

Running it:

javac BiblioTechArguments.java
java BiblioTechArguments "Effective Java" 978-0000000001

Output:

Arguments received: 2
First argument: Effective Java
Second argument: 978-0000000001

Key points:

  • Arguments are separated by spaces. If a value contains spaces (like Effective Java), it has to be wrapped in quotes; otherwise it would arrive as two separate arguments.
  • They always arrive as String, even if you type a number. java Program 2018 makes args[0] the text "2018", not the number. To convert it you use Integer.parseInt(args[0]), which you will see in lesson 01-04.
  • The index starts at 0: the first argument is args[0].
  • If you access an index that does not exist (for example args[1] when you only passed one), the program fails at runtime with ArrayIndexOutOfBoundsException. Checking args.length first requires conditionals, which arrive in module 2; handling the error, in module 6.
  • The class name is not part of args, unlike what happens in C.

They can also be configured in IDEs: in IntelliJ, Run → Edit Configurations… → Program arguments.

  1. Typical compilation errors and how to read them

Learning to read javac messages is a skill that pays for itself. The format is always the same:

File.java:LINE_NUMBER: error: DESCRIPTION
        the code on that line
              ^   <- points to the exact position
1 error

Let's look at the four most frequent ones and their diagnosis.

';' expected

System.out.println("BiblioTech")   // the semicolon is missing
BiblioTechApp.java:4: error: ';' expected
        System.out.println("BiblioTech")
                                        ^

Diagnosis: a semicolon is missing. An important warning: the compiler points at the end of the line before the problem, so if the error seems to make no sense on the line indicated, look at the line above.

cannot find symbol

System.out.println(title);   // 'title' has not been declared
BiblioTechApp.java:5: error: cannot find symbol
        System.out.println(title);
                           ^
  symbol:   variable title
  location: class BiblioTechApp

Diagnosis: the compiler does not know that name. The three causes, by frequency: (1) you have not declared it; (2) you typed it with different capitalisation (Title vs title); (3) a class import is missing.

incompatible types

int publicationYear = "2018";   // text assigned to an integer
BiblioTechApp.java:4: error: incompatible types: String cannot be converted to int
        int publicationYear = "2018";
                              ^

Diagnosis: Java's static typing in action. You are putting a value of one type into a variable of another. It is exactly the kind of error that in a dynamic language would blow up in production.

class X is public, should be declared in a file named X.java

Diagnosis: the file name does not match the public class name. Rename one of the two.

Compilation errors versus runtime errors

This is a fundamental distinction:

Compilation error Runtime error
When it appears When you run javac, before anything executes While the program is running
Who detects it The compiler The JVM
Examples Missing ;, incompatible types, symbol not found ArrayIndexOutOfBoundsException, integer division by zero
Cost Cheap: you see it in seconds Expensive: it may only show up with certain data, even in production

A tip on method: when javac reports twenty errors, fix only the first one and compile again. A single mistake (an unclosed brace, for instance) triggers a cascade of derived errors that disappear on their own.

Common Mistakes and Tips

  • Putting a semicolon after the closing brace of a method or class. It is not a serious error (Java tolerates it as an empty statement in some contexts), but it betrays inexperience and in other places it does cause an error.
  • Writing Main instead of main. Java is case-sensitive: it compiles, but at runtime it says Main method not found.
  • Forgetting that the public class and the file must have the same name.
  • Putting the import before the package. The order is fixed: package, then import, then the class.
  • Running java BiblioTechApp when the class is in a package. You have to use the fully qualified name: java com.nexussoftware.bibliotech.BiblioTechApp.
  • Trusting the error's line number without looking at the previous one. Very typical with ';' expected.
  • Tip: configure your IDE to format on save. You will never argue about style and your code will be uniform from day one.
  • Tip: when you copy code from the internet, type it out. The mistakes you make and fix are the real learning.
  • Tip: unbalanced braces are the most frequent cause of incomprehensible errors. Every IDE highlights the matching brace when you place the cursor on one: use it.

Exercises

Exercise 1: Hunt the errors

The following file, saved as BiblioTechApp.java, contains five errors. Find them, explain why they are errors and write the corrected version.

import java.util.Scanner;
package com.nexussoftware.bibliotech;

public class bibliotechApp {
    public static void Main(String args) {
        System.out.println("BiblioTech - Nexus Software")
        System.out.println("Book: " + Title);
    }
}

Exercise 2: A card with a package and arguments

Create the class BiblioTechCard inside the package com.nexussoftware.bibliotech, with the correct directory structure. The program must receive a book's title and ISBN from the command line and show them under a header. Include a Javadoc comment on the class and a line comment explaining where the data comes from. Also write the exact compilation and execution commands.

Expected run:

java com.nexussoftware.bibliotech.BiblioTechCard "Design Patterns" 978-0000000002

Output:

=== BiblioTech (Nexus Software) ===
Title: Design Patterns
ISBN: 978-0000000002

Exercise 3: Applying the conventions

The following code compiles perfectly, but it breaks every Java convention. Rewrite it applying them and justify each change.

public class loan_app{
public static void main(String[] args){
int DaysLate=12;
double daily_rate=0.25;
final double max_fine=20.0;
String Employee_Name="Marta Ruiz";
System.out.println(Employee_Name+" owes "+(DaysLate*daily_rate));
}
}

Solutions

Solution 1

The five errors:

  1. import before package. The package declaration must be the first statement in the file.
  2. class bibliotechApp does not match the file name BiblioTechApp.java. It also breaks PascalCase.
  3. Main instead of main. With a capital letter it is not the entry point; the program would compile but fail at runtime with Main method not found.
  4. String args instead of String[] args. The parameter must be an array of strings; without the brackets, the signature is not the one the JVM looks for.
  5. The semicolon is missing at the end of the first println. And there is a sixth, knock-on problem: Title is not declared anywhere, which would produce cannot find symbol.

Corrected version:

package com.nexussoftware.bibliotech;   // 1. The package ALWAYS comes first

import java.util.Scanner;               // The import comes after the package

// 2. The public class name matches the file name, in PascalCase
public class BiblioTechApp {

    // 3 and 4. The exact signature the JVM looks for: lowercase main and String[]
    public static void main(String[] args) {

        // We declare the variable before using it (fixes 'cannot find symbol')
        String title = "Effective Java";

        // 5. Every statement ends with a semicolon
        System.out.println("BiblioTech - Nexus Software");
        System.out.println("Book: " + title);
    }
}

Note: if in the end you do not use Scanner, the right thing to do is remove its import. An unused import is not an error, but it is noise.

Solution 2

Directory structure, starting from the project root:

bibliotech/
└── src/
    └── main/
        └── java/
            └── com/
                └── nexussoftware/
                    └── bibliotech/
                        └── BiblioTechCard.java

The file:

package com.nexussoftware.bibliotech;

/**
 * Prints the basic card of a book from the BiblioTech catalog to the console.
 * The data is received as command-line arguments.
 *
 * @author BiblioTech Team - Nexus Software
 * @version 0.1
 */
public class BiblioTechCard {

    public static void main(String[] args) {
        // args[0] and args[1] arrive from the command line, in that order,
        // and they are ALWAYS of type String even if they contain digits.
        String title = args[0];
        String isbn = args[1];

        System.out.println("=== BiblioTech (Nexus Software) ===");
        System.out.println("Title: " + title);
        System.out.println("ISBN: " + isbn);
    }
}

The commands, standing in src/main/java:

# Compile: you give the PATH of the file, with slashes
javac -d ../../../out com/nexussoftware/bibliotech/BiblioTechCard.java

# Run: you give the qualified CLASS NAME, with dots
java -cp ../../../out com.nexussoftware.bibliotech.BiblioTechCard "Design Patterns" 978-0000000002

An important detail: "Design Patterns" goes in quotes because it contains a space. Without them, the system would pass two arguments (Design, Patterns) and the output would be wrong.

Solution 3

/**
 * Calculates the late fine for a BiblioTech loan.
 */
public class LoanApp {                             // PascalCase, not snake_case

    public static void main(String[] args) {
        int daysLate = 12;                         // camelCase for variables
        double dailyRate = 0.25;                   // camelCase, not snake_case
        final double MAX_FINE = 20.0;              // constant: UPPERCASE_WITH_UNDERSCORES
        String employeeName = "Marta Ruiz";        // camelCase, starts lowercase

        double fine = daysLate * dailyRate;        // we extract the calculation into a variable

        System.out.println(employeeName + " owes " + fine + " euros");
        System.out.println("Maximum applicable fine: " + MAX_FINE + " euros");
    }
}

Justification for each change:

Before After Reason
loan_app LoanApp Classes use PascalCase; snake_case is not Java style
DaysLate daysLate Starting with a capital makes it look like a class
daily_rate dailyRate Variables use camelCase
max_fine MAX_FINE It is final, that is, a constant: it is written in uppercase
Employee_Name employeeName It mixed two incorrect conventions at once
No indentation 4 spaces per level Readability
Calculation inside the println A fine variable Separates calculation from presentation and makes it reusable

Conclusion

There are no magic spells left: you know that a Java file is arranged as package, import and class; that public static void main(String[] args) is a contract with the JVM and what each word contributes; that statements end with a semicolon and braces do not; that there are three kinds of comment and that Javadoc generates real documentation; that naming conventions communicate information at a glance; that packages correspond to directories and that this changes how you compile and run; that args brings command-line arguments always as text; and, above all, how to read a compiler error message instead of freezing in front of it.

In the next lesson, Variables and Data Types, we will fill that skeleton with content: the eight primitive types with their ranges, literals, the difference between primitive and reference types, String and its immutability, type conversions, constants with final and inference with var. By the end of it you will be able to model a complete book from the BiblioTech catalog using variables.

Java Programming Course

Module 1: Introduction to Java

Module 2: Control Flow

Module 3: Object-Oriented Programming

Module 4: Advanced Object-Oriented Programming

Module 5: Data Structures and Collections

Module 6: Exception Handling

Module 7: File Input/Output

Module 8: Multithreading and Concurrency

Module 9: Networking

Module 10: Advanced Topics

Module 11: Java Frameworks and Libraries

Module 12: Building Real-World Applications

© Copyright 2026. All rights reserved