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
- Full anatomy of a Java file
- The package declaration
- Imports
- The class declaration
- The
mainmethod, word by word - Statements, blocks and semicolons
- Comments: line, block and Javadoc
- Naming conventions
- Indentation and style
- Packages and directories in practice
- Command-line arguments:
args - Typical compilation errors and how to read them
- Common Mistakes and Tips
- Exercises
- 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 classThe 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>"]
- The package declaration
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 withcom.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.
- Imports
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.
- The class declaration
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:
publicis 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.classis the reserved word that declares a class.BiblioTechAppis 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 BiblioTechApp → BiblioTechApp.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.
- The
main method, word by word
main method, word by wordThis 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[] argumentsis equally valid. What cannot change is its type. String[] argsandString 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 ispublic 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:
That is, a runtime error, not a compilation error. Remembering this will save you some confusion.
- Statements, blocks and semicolons
A statement is a complete instruction. In Java every statement ends with a semicolon:
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.
- 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;
- 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 thatBookis a type andbooka variable. The convention removes the ambiguity. - When you see
DAILY_RATEyou 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: writepublicationYear, notañoPublicación. - They cannot be reserved words (
class,int,public,static,new,return…). - They are case-sensitive:
title,TitleandTITLEare three different identifiers. - Use descriptive names.
daysLateis infinitely better thandorx. A variable's name is the most-read documentation in the code.
- 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;, notint 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.
- 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:
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.BiblioTechAppNotice 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-dindicates the output directory for the.classfiles, andjavacrecreates the package structure there automatically.-cp(classpath) tells thejavacommand 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.
- Command-line arguments:
args
argsThe 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:
Output:
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 2018makesargs[0]the text"2018", not the number. To convert it you useInteger.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 withArrayIndexOutOfBoundsException. Checkingargs.lengthfirst 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.
- 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 errorLet's look at the four most frequent ones and their diagnosis.
';' expected
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
BiblioTechApp.java:5: error: cannot find symbol
System.out.println(title);
^
symbol: variable title
location: class BiblioTechAppDiagnosis: 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
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
Maininstead ofmain. Java is case-sensitive: it compiles, but at runtime it saysMain method not found. - Forgetting that the public class and the file must have the same name.
- Putting the
importbefore thepackage. The order is fixed:package, thenimport, then the class. - Running
java BiblioTechAppwhen 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:
Output:
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:
importbeforepackage. The package declaration must be the first statement in the file.class bibliotechAppdoes not match the file nameBiblioTechApp.java. It also breaks PascalCase.Maininstead ofmain. With a capital letter it is not the entry point; the program would compile but fail at runtime withMain method not found.String argsinstead ofString[] args. The parameter must be an array of strings; without the brackets, the signature is not the one the JVM looks for.- The semicolon is missing at the end of the first
println. And there is a sixth, knock-on problem:Titleis not declared anywhere, which would producecannot 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.javaThe 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-0000000002An 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
- Introduction to Java
- Setting Up the Development Environment
- Basic Syntax and Structure
- Variables and Data Types
- Operators
- Console Input and Output
- Your First Complete Program: BiblioTech
Module 2: Control Flow
- Conditional Statements
- Loops
- Switch Statements
- Break and Continue
- Debugging and Execution Traces
- Project: The BiblioTech Interactive Menu
Module 3: Object-Oriented Programming
- Introduction to OOP
- Classes and Objects
- Methods
- Constructors
- Inheritance
- Polymorphism
- Encapsulation
- Abstraction
- The Object Class: equals, hashCode and toString
Module 4: Advanced Object-Oriented Programming
- Interfaces
- Abstract Classes
- Inner Classes
- Anonymous Classes
- Lambda Expressions
- Functional Interfaces and Method References
- Enums and Records
Module 5: Data Structures and Collections
- Arrays
- The Collections Framework
- ArrayList
- LinkedList
- HashMap
- HashSet
- Queue and Deque
- Stack
- Sorting and Searching Collections
Module 6: Exception Handling
- Introduction to Exceptions
- The Try-Catch Block
- Throw and Throws
- Custom Exceptions
- The Finally Block
- Try-with-resources and AutoCloseable
- Error Handling Strategies and Logging
Module 7: File Input/Output
- Reading Files
- Writing Files
- File Streams
- BufferedReader and BufferedWriter
- Serialization
- The NIO.2 API: Path and Files
- Interchange Formats: CSV and Properties
Module 8: Multithreading and Concurrency
- Introduction to Multithreading
- Creating Threads
- Thread Lifecycle
- Synchronization
- Concurrency Utilities
- Concurrent Collections and Atomic Variables
- Asynchronous Tasks with CompletableFuture
Module 9: Networking
- Introduction to Networking
- Sockets
- ServerSocket
- DatagramSocket and DatagramPacket
- URL and HttpURLConnection
- The Modern HTTP Client
Module 10: Advanced Topics
- Generics
- Annotations
- Reflection
- Java 8 Features: Streams and Optional
- Dates and Times with java.time
- Java 9 and Beyond
- Memory, Garbage Collection and Performance
Module 11: Java Frameworks and Libraries
- Introduction to Java Frameworks
- Spring Framework
- Hibernate
- JUnit
- Maven
- Advanced Testing with Mockito
- Essential Ecosystem Libraries
