Prerequisites: Basic knowledge of Java
Table of Contents
- 3.1 Create correct control instructions
- 3.2 Demo: Fix errors in control structures and variables
- 3.3 Refactoring for readability and maintainability
- 7.1 OutboundProcess.java
- 7.2 InboundOrder.java
- 7.3 OrderProcess.java
- 7.4 Bucket.java
- 7.5 Samples.java
1. Training overview
Debugging Java training teaches how to apply debugging techniques throughout the software development process. When writing code, you inevitably write bugs. Knowing how to quickly identify and correct these bugs is essential to producing quality work and moving forward efficiently.
Main objectives
- Understand common issues inside and outside of code
- Learn how to collect information from errors to fix bugs
- Use the tools of an IDE (Integrated Development Environment)
What you will learn
At the end of this training, you will have a wide range of tools and techniques applicable both when writing code and when problems appear, to debug your Java code quickly and efficiently.
Prerequisites
A basic understanding of Java is required before beginning this training.
2. Développer un état d’esprit de débogage
2.1 Write bugs and fix code
Whether you’re just starting out or have been working with Java for a while, debugging is an integral part of writing code. Learning to debug more effectively can really boost your productivity and reduce your frustration levels.
Java Versions and Applicability
This course was created with specific versions of Java (Java 17 and later as a base), but since debugging is as much about strategy and concepts as it is about syntax, the principles presented apply to all versions of Java.
The three key moments to find and fix bugs
- At the time of entry
- Spelling and syntax errors recognized immediately
- Iteration cycle of a few seconds
- IDEs with real-time compilation facilitate immediate detection
- At compile time
- The compiler takes into account the entire file or project
- IDEs report potential input errors using real-time compilation
- On large team projects, broken dependencies and bugs may only appear during the full build
- At runtime (runtime)
- Unexpected behavior visible in test or production environment
- Some errors only appear under certain execution conditions
- Sometimes related to data used or external dependencies
Iteration cycle (iteration cycle)
The iteration cycle is the time between discovering a bug and verifying the fix:
| Moment of discovery | Iteration cycle |
|---|---|
| Input (typos, syntax) | A few seconds |
| Local compilation | A few minutes |
| Shared Integrated Test Environment | Several minutes to several days |
Improving your knowledge of the language helps shorten the iteration cycle, allowing you to fix more bugs in less time.
Common issues outside of application code
These issues do not concern the business logic of the code itself, but its configuration or dependencies:
- Database configuration: no connection to the database, or connection to the wrong database
- API Endpoints: endpoint unavailable, or API contract (what we send and receive) has changed
- Environment variables: configuration pointing to the wrong environment (e.g.: dev instead of test)
- Code deployment (code deployment): the application is still running on the old version of the code after a deployment
- Network Issues: Unexpected behavior due to network infrastructure issues
Recognizing these common issues outside of application code can save you hours of searching in the wrong places.
2.2 Understanding verification and validation
Car and boat analogy
Suppose you build a perfectly functional car. The customer is unhappy because he needed a boat to travel on water, not on land.
-
Verification (Verification): Did you build the thing correctly? (Did you build it right?) → Does the car have all the components of a car and do they work as expected?
-
Validation (Validation): Did you build the right thing? (Did you build the right thing?) → The car is not the right thing — a boat was needed.
The three categories of bugs
| Category | Description | Solution |
|---|---|---|
| Verification error | The code is incorrect (bad syntax, bad implementation logic) | Fix the code |
| Validation error | The code is syntactically correct but does the wrong thing | Change code behavior |
| New feature | There is no code for the desired functionality | Implement the feature |
Most verification bugs are detected by the compiler. Validation focuses on the needs of stakeholders.
Causes of validation errors
- Unclear or ambiguous requirements
- Frequently requested visual changes to data presentation
- Modification of business logic (business logic) (e.g.: updating tax rates)
- Missing features not obvious before using the system
- Change of opinion of stakeholders
3. Avoid Common Mistakes
3.1 Create correct control instructions
Common syntax errors
Most missing values generate an immediately detectable syntax error:
- Forgot a semicolon (
;) at the end of a statement - Forgot to close parentheses, braces, square brackets
- Forgot to close single or double quotes
Multi-line text blocks (text blocks)
Old method (error-prone):
String multi1 =
"this is an example\n" +
"of a multi-line string\n" +
"be sure to remember newlines\n";
New method with text blocks (Java 13+):
String multi2 =
"""
this is another way
to create a multi-line string.
It's easier to create and edit.
""";
text blocks reduce the risk of errors when creating and editing multi-line strings.
if statements — Formats and pitfalls
Java supports multiple formatting styles for if statements. All are syntactically valid, but some are more readable and less risky:
// Style recommandé — lisible et clair
if (condition) {
doTrue();
} else {
doFalse();
}
// Style avec accolades sur leur propre ligne
if (condition)
{
doTrue();
}
else
{
doFalse();
}
// Sans accolades — RISQUÉ lors de l'ajout de lignes
if (condition)
doTrue();
else
doFalse();
In Java, indentation does not matter to the compiler. All of these styles do the same thing.
Assignment operator vs comparison operator in an if
Common BUG — use = instead of ==:
boolean a = true;
boolean b = false;
// BUG : assigne b à a (a vaut maintenant false), n'effectue PAS une comparaison
if (a = b) {
doTrue();
} else {
doFalse(); // Ce bloc s'exécute TOUJOURS
}
// Effet de bord : a vaut maintenant false !
Correct code:
// Correct : compare a et b
if (a == b) {
doTrue();
} else {
doFalse();
}
For booleans, it is even more readable to simply write
if (freeShipping)orif (!freeShipping)without the==operator.
.equals() vs == for objects
The .equals() method compares the values of an object’s attributes (depending on its implementation). The == operator compares references (if two variables point to the same instance).
Bucket one = new Bucket(10, "red");
Bucket two = new Bucket(10, "red");
// true — même taille et couleur (comparaison par valeur)
one.equals(two)
// false — instances différentes (comparaison par référence)
one == two
// true — même référence
Bucket three = one;
one == three
Typically use
.equals()to compare objects by their attribute values.
switch instructions — Forgetting break (fall-through)
Current BUG — switch without break:
String color = "red";
// BUG : sans break, tous les cases après "red" s'exécutent aussi
switch (color) {
case "red":
System.out.println("In red"); // Exécuté
case "orange":
System.out.println("In orange"); // Exécuté aussi !
case "yellow":
System.out.println("In yellow"); // Exécuté aussi !
default:
System.out.println("In default");// Exécuté aussi !
}
Correct code with break:
switch (color) {
case "red":
System.out.println("In red");
break;
case "orange":
System.out.println("In orange");
break;
case "yellow":
System.out.println("In yellow");
break;
default:
System.out.println("In default");
break;
}
Modern solution — switch with arrow statements (Java 14+):
// Pas de fall-through possible avec la syntaxe flèche
switch (color) {
case "red" -> System.out.println("In red");
case "orange" -> System.out.println("In orange");
case "yellow" -> System.out.println("In yellow");
default -> System.out.println("In default");
}
Arrow syntax (
->) inswitcheliminates fall-through behavior and simplifies the code.
Loops — Common Errors
off-by-one errors (offset of one)
These errors often occur with array indices (0-based in Java):
String[] topOrders = new String[topSize];
// BUG : i commence à 1 — le premier élément (index 0) est manqué
for (int i = 1; i < topSize; i++) {
topOrders[i] = orderList[i]; // topOrders[0] restera null
}
// Correct : commencer à 0
for (int i = 0; i < topSize; i++) {
topOrders[i] = orderList[i];
}
Infinite loops (infinite loops)
Common causes:
| Cause | Example |
|---|---|
| Bad update of control variable | Increment instead of decrement |
| Bad test expression | Use > instead of < |
| Forgot to initialize or update | Control variable never modified |
Case sensitivity of variables (case sensitivity)
In Java, variable names are case sensitive:
// "value" et "Value" sont deux variables DIFFÉRENTES
int value = 10;
int Value = 20;
Variable scope (variable scope)
Scope defines where a variable is visible in a program. Classes, methods, and control structures have different scopes.
Common BUG — confusion between a class variable and a method parameter:
public class OrderProcess {
// Variable de classe — portée classe
private int topSize = 3;
// BUG : paramètre "topsize" (minuscule) ≠ "topSize" (capital S)
// La variable de classe topSize = 3 est utilisée, PAS le paramètre !
public String[] getTopOrders(String[] orderList, int topsize) {
String[] topOrders = new String[topSize]; // utilise la variable de CLASSE (= 3)
// ...
}
}
Correction:
// Corriger la casse du paramètre
public String[] getTopOrders(String[] orderList, int topSize) {
String[] topOrders = new String[topSize]; // utilise maintenant le PARAMÈTRE
// ...
}
3.2 Demo: Fix control structure and variable errors
Scenario 1 — Assignment operator bug (OutboundProcess)
Background: The accounting department has detected an unusual drop in shipping charges collected.
Symptom: Even with freeShipping=false, the system used a special carrier and did not apply shipping charges.
Cause: In the assignCarrier method, the assignment operator = was used instead of the comparison operator == in the if statement.
// BUG : assigne true à freeShipping, qui passe ensuite à recordShippingCharge
if (freeShipping = true) { // assigne true à freeShipping !
out("Use special carrier.");
} else {
out("Use regular carrier.");
}
Correction:
// Option 1 : utiliser ==
if (freeShipping == true) { ... }
// Option 2 (recommandée pour les booléens) : utiliser directement la variable
if (freeShipping) {
out("Use special carrier.");
} else {
out("Use regular carrier.");
}
Scenario 2 — switch bug without break (InboundOrder)
Context: Commands should have only one tag applied, but multiple tags are attached simultaneously.
Cause: The tagOrder method had a switch without break instructions, causing fall-through behavior.
Correction: Add break after each case, or convert to arrow syntax:
public void tagOrder(String customerType) {
switch (customerType) {
case "bronze" -> out("Tag: level2");
case "silver" -> out("Tag: level3");
case "gold" -> out("Tag: level4");
default -> out("Tag: level1");
}
}
Scenario 3 — off-by-one error in getTopOrders (OrderProcess)
Context: Top order reports were missing items based on inventory.
Cause: The count variable i was initialized to 1 instead of 0, causing an off-by-one error.
// BUG : i commence à 1, le premier élément du tableau est manqué
for (int i = 1; i < topSize; i++) { ... }
// Correction : commencer à 0
for (int i = 0; i < topSize; i++) { ... }
Scenario 4 — Variable scope/case confusion (OrderProcess)
Context: All top order lists have the same length even though they should be different depending on the department.
Cause: The parameter topsize (all lowercase) did not match the class variable topSize (capital S). Java is case sensitive, so the class variable topSize=3 was always used.
private int topSize = 3; // variable de CLASSE
// BUG : paramètre "topsize" avec s minuscule ≠ variable de classe "topSize"
public String[] getTopOrders(String[] orderList, int topsize) {
String[] topOrders = new String[topSize]; // utilise TOUJOURS la variable de classe = 3
}
// Correction : harmoniser la casse
public String[] getTopOrders(String[] orderList, int topSize) {
String[] topOrders = new String[topSize]; // utilise maintenant le paramètre
}
3.3 Refactoring for readability and maintainability
Quote from Brian Kernighan
“Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.”
There is real value in making your code clear and easy to understand. Even if you are the only one maintaining a project, after several months the code might as well have been written by someone else.
Techniques to improve readability
Code organization
- Prefer several small methods with targeted objectives rather than one giant method that does 10 things
- Use packages and classes to group code by related functionality
- Good organization makes debugging easier
Whitespace
- Add blank lines, indentation and spaces to group methods, operators and code blocks
- Make code structure and flow easier to follow visually
Descriptive variable names
// Mauvais — noms incompréhensibles
public double calcTot(double prc, double rt, double dr) {
double disc = prc * dr;
double subTot = prc - disc;
double tx = subTot * rt;
double tot = subTot + tx;
return tot;
}
// Bon — noms descriptifs, code auto-documenté
public double calculateTotal(double price, double taxRate, double discountRate) {
double discount = price * discountRate;
double subTotal = price - discount;
double tax = subTotal * taxRate;
double total = subTotal + tax;
return total;
}
Java is not faster with shorter variable names. The limit on the length of a variable name is much larger than you will practically use.
Practical rules:
- Name an order array
orders, notarray - Avoid excessive abbreviations
- Names must represent the value or role of the variable
Coding standards (coding standards)
Coding standards guide the implementation of:
- Indentation
- Spacing
- Naming conventions
- Other aspects affecting maintainability
The goal is to create a consistent experience across the entire codebase (codebase), rather than having hundreds of variations for each developer.
Automatic formatting tools allow these standards to be defined and applied automatically.
Use standard libraries (standard libraries)
Before writing code from scratch, consider using the Java standard libraries:
- These widely used libraries have had much more time for their bugs to be found and fixed
- They save development time
- They reduce the number of potential bugs in your own code
4. Trouver le problème, corriger le problème
“Find the problem, fix the problem.” — Advice from a senior developer, useful for working through endless logs
Debugging is an iterative process. The first thing you fix doesn’t always make the whole problem go away. Sometimes fixing one bug reveals two more. Other times, fixing one bug eliminates three at once.
4.1 Read stack traces and logs
Anatomy of a simple stack trace
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 8 out of bounds for length 7
at com.globomantics.orders.OrderProcess.getTopOrders(OrderProcess.java:8)
at com.globomantics.orders.OrderProcess.main(OrderProcess.java:24)
The stack trace reads from bottom to top:
- Bottom: entry point (
main) - Top: exact line where the exception occurred
Navigate a complex stack trace
In real applications with frameworks and application servers, stack traces are much longer. Strategy for analyzing them:
- Identify the exception at the top of the stack trace (e.g.:
NullPointerException) - Use package names as map:
org.apache.tomcat,coyote,catalina→ application server related, probably not your code to fixjavax.servlet→ servlet API, probably not your codeorg.springframework→ Spring framework, probably not your codejava.base→ Java platform APIs, probably not your codecom.globomantics→ your application code → where to look for bugs
-
Avoid line wrapping (line wrapping): seeing the stack trace without newlines makes it easier to filter packages
-
Focus on application packages: zoom in on the lines corresponding to your code
Example of localization with a complex stack trace
com.globomantics.OrderService.processOrder(OrderService.java:13)
Line 13 of
OrderService.javais the best place to start. The actual problem line may be a line or two above or below, but it’s a great place to start.
System.out.println vs logging frameworks
| Characteristic | System.out.println | Logging framework |
|---|---|---|
| Exit | Always on | Controllable by log level |
| Context | None | Timestamp, level, source class |
| Granularity | None | TRACE, DEBUG, INFO, WARN, ERROR |
| Recommended use | Fast debugging | Production applications |
Log levels (log levels)
From most verbose to least verbose:
| Level | Typical usage |
|---|---|
TRACE | Very detailed information, the most output |
DEBUG | Debug Info |
INFO | General information about the process |
WARN | Non-blocking warnings |
ERROR | Actual errors, used least often |
Logging frameworks provide a standard way to manage the information that goes into your logs, and allow you to control what is displayed through the use of log levels.
4.2 Fix common exceptions
Runtime exceptions in Java are not checked by the compiler. The code may compile perfectly, but a problem may occur only at runtime, and sometimes only under certain conditions.
Most common runtime exceptions
| Exception | Typical cause |
|---|---|
NullPointerException | Calling a method or accessing a field on a null reference |
ArrayIndexOutOfBoundsException | Accessing a non-existent array index |
IndexOutOfBoundsException | Accessing an out-of-bounds index of a collection |
ArithmeticException: / by zero | Division by zero |
These exceptions are among the most common during debugging because they only appear at runtime.
4.3 Demo: Find bugs using stack traces
This demo uses the getTopOrders method to demonstrate several common runtime exceptions and how to fix them.
Bug 1 — NullPointerException
Symptom: Exception thrown when calling toString() on a null object.
Cause: Initializing the loop to i = 1 (off-by-one error) caused topOrders[0] to remain null.
Fix utility method out:
// Version robuste de la méthode out
public static void out(Object o) {
System.out.println(o); // println gère déjà le null
}
Note: System.out.println already converts the object to a string and handles null values — so an explicit call to toString() is not necessary.
Bug 2 — ArithmeticException: / by zero
Symptom: Exception thrown when calculating topPercent when totalOrders = 0.
// BUG : division par zéro si totalOrders = 0
double topPercent = (double)topSize / (double)totalOrders;
Fix — protect against division by zero:
double topPercent = 0;
if (totalOrders != 0) {
topPercent = (double)topSize / (double)totalOrders;
}
Bug 3 — Integer division (integer division)
Symptom: topPercent is 0 even with topSize = 2 and totalOrders = 3.
Cause: Both operands were int. Integer division in Java truncates fractions.
// BUG : division entière — 2 / 3 = 0 (pas 0.66)
double topPercent = topSize / totalOrders;
Fix — cast to double to get floating point division:
double topPercent = (double)topSize / (double)totalOrders;
// Résultat : 0.6666... (deux tiers)
This bug illustrates what often happens: when looking for an exception (
/ by zero), we find another bug (the division did not work as expected).
Bug 4 — IndexOutOfBoundsException
Symptom: Exception thrown when topSize > orderList.length.
Cause: Attempted to access an index that does not exist in the table.
java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
Correction — validate input:
public String[] getTopOrders(String[] orderList, int topSize, int totalOrders) {
// Protéger contre un topSize supérieur à la taille de la liste
if (topSize > orderList.length) {
topSize = orderList.length;
}
// ... reste de la méthode
}
The input must always be validated to ensure it is valid for the code. Business clarifications (business requirements) may be necessary to define the exact expected behavior.
5. What else can go wrong?
This module covers more advanced issues that may arise when running more complex Java programs.
5.1 Dealing with bad configurations
Integration with external systems
As your application grows, you begin to integrate with additional systems. Example: a database to store customer and carrier information.
Managing multiple environments
Most systems have separate environments:
| Environment | Features |
|---|---|
| Development | Masked/fictitious data (names and account numbers changed to protect privacy) |
| Test | Shared environment to validate integration |
| Production | Real and precise data |
Imagine if your phone number was part of a test that runs multiple times a day — this is why development environments use dummy data.
Configuration via configuration files or environment variables:
- Development activities must use the development database
- Production activities must use the production database
First reflexes when debugging a connection problem:
- Check environment configuration
- Ensure that all integrated systems connect to the correct instances for the environment where they are deployed
Deployment issues (deployment)
Symptom: You make a change to your code, but the application behavior does not change.
Possible cause: An old version of the code is still running.
Possible solutions:
- Restart application to load new changes
- Perform a build and deploy
- Use hot reload tools available in IDEs (changes automatically pushed to running application)
The hot reload shortens the iteration cycle by avoiding having to stop the process, recompile and restart.
5.2 Handle memory exceptions
heap and garbage collection
- Heap (heap): general amount of memory available for your Java program from the system
- Can be viewed as a box with some amount of empty space
- Garbage collection (GC): process that runs to remove unused objects from the heap
- Can be viewed as sorting the box to remove certain items
OutOfMemoryError
If the heap is completely filled, you will get an OutOfMemoryError.
Common causes:
| Cause | Description | Solution |
|---|---|---|
| Heap too small | The allocated memory is not sufficient for the needs of the program | Increase maximum heap size (-Xmx) or run on hardware with more memory |
| Memory leak (memory leak) | Code creating objects that cannot be collected by the GC | Use memory profiling tools to find the faulty code |
| GC too slow | Garbage collector not running fast enough | Increase processing power or memory speed |
Real story: A persistent OutOfMemoryError issue in production was resolved by taking the servers out of factory-enabled eco mode (power saving). The application was very intensive and needed all the server’s power for the garbage collector.
stop-the-world event
A stop-the-world GC event is a situation where program execution is suspended until the garbage collector has finished.
Impact:
- The application may appear to be frozen (frozen)
- An operation that normally takes 50ms may suddenly take 5 seconds
- Inconsistent performance may indicate garbage collection issues
Inconsistent performance can be difficult to reproduce: when the bug was reported, the GC may have been running, but when you attempted to reproduce, the operation completed within the expected time.
6. Increase productivity with an IDE
6.1 Use IDE features to debug faster
IDEs (Integrated Development Environments) have many tools that make it easier to write and debug code.
Preventive features (avoid bugs)
Real-time syntax checking (real-time syntax checking)
The IDE runs real-time compiler checks while typing:
- Highlighting unclosed parentheses and braces
- Reporting misspelled variables
- Detecting syntax errors before even attempting to execute code
Reduces the iteration cycle between writing a bug and fixing it.
Autocomplete (autocomplete)
- Suggest valid variables in the current context (in scope)
- Does not suggest out-of-scope variables or incomplete control statements
- Avoid typing every character, especially useful for long, descriptive variable names
Autocomplete suggestions are syntactically valid, but are not guaranteed to meet the requirements of the written code.
Debug Features
Run mode vs Debug mode
| Fashion | Description |
|---|---|
| Run | Just run the code |
| Debug | Runs code storing additional information to enable debugging features |
To debug, you must always launch in Debug mode.
Breakpoints
| Type | Description |
|---|---|
| Static breakpoint | Pause execution on a specific line |
| Conditional Breakpoint | Stops only when a condition is true (eg: i > 3) |
| Exceptional breakpoint | Triggers when certain exceptions are thrown |
Stepping
After stopping at a breakpoint, you can navigate the code:
| Action | Description |
|---|---|
| Step Over | Execute the current line and move on to the next one |
| Step Into | Enters the method called on the current line |
| Step Out | Exits the current method without having to go through each line |
| Run to Cursor | Runs to cursor position |
| Summary | Resumes execution until next breakpoint |
Watch variables and expressions
- Watches: Selected variables automatically update in a dashboard — easy to monitor as they change
- Expressions: allows you to create expressions in valid Java syntax that are evaluated during debugging (e.g.:
topSize * totalOrders) - Modification of values: possibility of changing the value of a variable during a debugging session
Launch profiles (launch profiles)
Allows you to configure:
- The Java SDK to use
- Program arguments (program arguments)
- Environment variables (environment variables)
- Which project or class to launch
Particularly useful if you work on multiple projects and multiple environments.
Reload functionality (reload)
Allows changes to be taken from the source code and introduced into the running session.
- Compile and Reload File: reloads the modified file in the current debug session
- Helps quickly iterate on solutions while debugging
- Limitation: only certain types of changes are supported during reload; for unsupported changes, you must restart the debugging session
6.2 Demo: Debugging in IntelliJ
This demo demonstrates the functionality shown using the IntelliJ IDEA IDE.
Note: This is an overview of the debugging tools and not an exhaustive demo of all possible options in the debugger.
Demo — Real-time syntax checking
When adding a calculateShipping method:
- The word
calculateShippingis highlighted in red — missing return type - After adding the
doublereturn type, the closing brace is set to red — missingreturnstatement - Once the method is complete, the red lines disappear
Demo — Autocomplete
When entering the body of the calculateShipping method:
- IDE suggests
distancevariable (method parameter, in scope) - Pressing Enter inserts suggestion automatically
costFactorvariable is also suggested
Autocompletion is especially useful with long, descriptive variable names — no need to type every character.
Demo — Launch Profiles
The launch profiles menu remembers each class for which a main method has been executed, allowing these classes to be restarted at any time without navigating to the file.
Demo — Breakpoint on NullPointerException
- The IDE offers to create a breakpoint for the
NullPointerExceptionduring execution - Clicking on the suggestion opens the Breakpoints menu
- A breakpoint on Java Exception Breakpoints is created for
NullPointerException - Run in Debug mode
- Execution stops on the line where the exception is thrown
- Fix the problem, then restart the process
Demo — Static Breakpoint and Step-by-Step Navigation
// Cliquer dans la gouttière (gutter) à gauche de la ligne
// Un point rouge apparaît et la ligne est surlignée
Navigation:
Step Over: executes one line at a timeRun to Cursor: useful for quickly passing a loopStep Into: enters the called method (e.g.:calculateShipping)Step Out: exits the method without going through each lineResume: resumes until the next breakpoint
Demo — Conditional Breakpoint
// Condition : i > 3
// Avec topSize = 3 : le breakpoint ne s'arrête pas (i ne dépasse jamais 3)
// Avec topSize = 5 : le breakpoint s'arrête quand i = 4
Creating a conditional breakpoint:
- Create a standard breakpoint (click in the gutter)
- Right click on the breakpoint to open its properties
- Enter any valid Java expression in the condition field
Demo — Variables and expressions in the Debug window
- Variable values appear next to running lines of code
- The Debug window displays the list of variables (e.g.:
topSize,totalOrders) - Add a watch: enter
iin the list of watches to see it at the top of the list - Create an expression: enter
topSize * totalOrders, press Enter — the result is displayed
Demo — Change a value while debugging
Select a variable (eg: in orderList) and set a new value — this value is now changed for this execution.
Demo — Compile and Reload File
To modify code during a debug session:
- Right click in the source file
- Select Compile and Reload File
- Check the message confirming that the class has been reloaded
Only certain changes are supported by this feature. For unsupported changes, restart the debug session.
6.3 Summary
This training covered:
-
The different types of bugs and how some are not caused by bad code (configuration issues, external dependencies)
-
Common errors in Java:
- off-by-one errors in loops
- Operator
=vs== switchwithoutbreak- Case sensitivity of variables
- Variable scope
-
The importance of code readability to facilitate debugging (descriptive names, organization, coding standards)
-
Reading stack traces to determine the error and where to look in the code
-
Garbage collection: concept of cleaning up memory space in the heap where the Java code executes
-
IDE tools for:
- Avoid writing bugs (real-time checking, autocompletion)
- Debug more efficiently (breakpoints, stepping, variable watches, expressions, reload)
7. Code files — Globomantics Project
The examples in this training use a fictitious order processing system for the company Globomantics.
Project structure:
materials/
└── src/
└── main/
└── java/
└── com/
└── globomantics/
├── orders/
│ ├── InboundOrder.java
│ └── OrderProcess.java
├── shipping/
│ └── OutboundProcess.java
└── slides/
├── Bucket.java
└── Samples.java
Note:
Samples.javais used for training slides and is not really part of a working system. Some files contain intentional bugs created for demonstration purposes. The files presented here represent the final corrected version after the demos.
7.1 OutboundProcess.java
Manages outbound order processing, including assigning carrier and recording shipping charges.
package com.globomantics.shipping;
public class OutboundProcess {
public static void main(String[] args) {
OutboundProcess testOutboundProcess = new OutboundProcess();
boolean freeShipping = false;
out("free Shipping: " + freeShipping);
testOutboundProcess.assignCarrier(freeShipping);
}
public void assignCarrier(boolean freeShipping) {
if (freeShipping) {
out("Use special carrier.");
} else {
out("Use regular carrier.");
}
recordShippingCharge(freeShipping);
}
public void recordShippingCharge(boolean freeShipping) {
if (!freeShipping) {
out("Add $5 shipping charge");
} else {
out("No shipping charge");
}
}
public static void out(Object o) {
System.out.println(o.toString());
}
}
Key points:
assignCarrier(boolean freeShipping): directly uses thefreeShippingboolean in theifstatement (corrected version — no=operator vs==)recordShippingCharge(boolean freeShipping): uses the!operator for the negation of the booleanout(Object o): display utility method
7.2 InboundOrder.java
Manages inbound order processing, including application of tags based on customer type.
package com.globomantics.orders;
public class InboundOrder {
public static void main(String[] args) {
InboundOrder testInboundOrder = new InboundOrder();
String customerType = "silver";
out("customer type: " + customerType);
testInboundOrder.tagOrder(customerType);
}
public void tagOrder(String customerType) {
switch (customerType) {
case "bronze" -> out("Tag: level2");
case "silver" -> out("Tag: level3");
case "gold" -> out("Tag: level4");
default -> out("Tag: level1");
}
}
public static void out(Object o) {
System.out.println(o.toString());
}
}
Key points:
tagOrder(String customerType): final version using arrow syntax (->) for theswitch- No
breakneeded with arrow syntax — no fall-through possible - Client type
default→ taglevel1
Tag levels by customer type:
| Customer Type | Tag applied |
|---|---|
bronze | level2 |
silver | level3 |
gold | level4 |
| Other (default) | level1 |
7.3 OrderProcess.java
Manages order processing, including retrieving top orders and calculating shipping costs.
package com.globomantics.orders;
public class OrderProcess {
public String[] getTopOrders(String[] orderList, int topSize, int totalOrders) {
if (topSize > orderList.length){
topSize = orderList.length;
}
String[] topOrders = new String[topSize];
for (int i = 0; i < topSize; i++) {
topOrders[i] = orderList[i];
}
//newMethod();
double topPercent = 0;
if (totalOrders != 0){
topPercent = (double)topSize / (double)totalOrders;
}
out(topPercent);
calculateShipping(5, .1);
return topOrders;
}
public void newMethod(){
out("In new method");
}
public double calculateShipping(double distance, double costFactor){
double result = distance * costFactor;
return result;
}
public static void main(String[] args) {
String[] orderList = {"shirt", "pants", "shoes", "socks", "belt", "coat", "gloves"};
int topSize = 5;
int totalOrders = 7;
OrderProcess orderProcess = new OrderProcess();
String[] topList = orderProcess.getTopOrders(orderList, topSize, totalOrders);
for (String order: topList) {
out(order);
}
}
public static void out(Object o) {
System.out.println(o);
}
}
Key points:
getTopOrders(String[], int, int)— final version corrected with:- Validation:
topSize > orderList.length→ adjustment to the size of the list - Loop starting at
i=0(fixed fromi=1) - Protection against division by zero:
if (totalOrders != 0) - Cast to
(double)for floating point division:(double)topSize / (double)totalOrders calculateShipping(double distance, double costFactor): demonstration of Step Into in the IDE demosout(Object o): usesSystem.out.println(o)— no need for explicit call totoString()becauseprintlnhandles it
7.4 Bucket.java
Example class to illustrate the difference between .equals() and ==.
package com.globomantics.slides;
import java.util.Objects;
public class Bucket {
private int size;
private String color;
public Bucket(int size, String color) {
this.size = size;
this.color = color;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Bucket bucket = (Bucket) o;
return size == bucket.size && Objects.equals(color, bucket.color);
}
public int getSize() { return size; }
public void setSize(int size) { this.size = size; }
public String getColor() { return color; }
public void setColor(String color) { this.color = color; }
@Override
public int hashCode() {
return Objects.hash(size, color);
}
}
Key points:
- Implemented
equals(Object o)to comparesizeandcolorby value - Using
Objects.equals(color, bucket.color)to handlenullvalues securely - Implementation of
hashCode()withObjects.hash(size, color)— good practice: always redefinehashCode()when redefiningequals() - Check
this == wherefirst for optimization (same reference → automatically equal)
Usage demonstration:
Bucket one = new Bucket(10, "red");
Bucket two = new Bucket(10, "red");
Bucket three = one;
one.equals(two) // true — même taille et couleur (comparaison par valeur)
one == two // false — instances différentes (comparaison par référence)
one == three // true — même référence
7.5 Samples.java
Demo class used for training slides. Illustrates different code patterns and their behaviors.
package com.globomantics.slides;
public class Samples {
public static void main(String[] args) {
// Méthode main vide — point d'entrée pour les tests
}
// --- Text blocks ---
public void stringExample(){
// Ancienne méthode (concaténation avec \n)
String multi1 =
"this is an example\n" +
"of a multi-line string\n" +
"be sure to remember newlines\n";
// Nouvelle méthode avec text blocks (Java 13+)
String multi2 =
"""
this is another way
to create a multi-line string.
It's easier to create and edit.
""";
}
// --- Switch statements ---
// Switch avec break — correct
public void switchSample1(){
String color = "red";
switch (color) {
case "red":
System.out.println("In red");
break;
case "orange":
System.out.println("In orange");
break;
default:
System.out.println("In default");
break;
}
}
// Switch sans break — BUG (fall-through)
public void switchSample2(){
String color = "red";
switch (color) {
case "red":
System.out.println("In red"); // fall-through vers tous les cases suivants !
case "orange":
System.out.println("In orange");
default:
System.out.println("In default");
}
}
// Switch avec syntaxe flèche — pas de fall-through
public void switchSample3(){
String color = "red";
switch (color) {
case "red" -> System.out.println("In red");
case "orange" -> System.out.println("In orange");
default -> System.out.println("In default");
}
}
// --- If statements ---
// Démonstration de l'opérateur = (assignation) vs == (comparaison)
public void ifWithAssignmentOperator(){
boolean a = true;
boolean b = false;
if (a = b) { // BUG : assigne b à a — a vaut maintenant false !
doTrue();
} else {
doFalse(); // S'exécute TOUJOURS
}
// a vaut maintenant false (effet de bord)
}
public void ifWithEqualityOperator(){
boolean a = true;
boolean b = false;
if (a == b) { // Correct : compare a et b
doTrue();
} else {
doFalse(); // S'exécute car a != b
}
// a vaut toujours true (pas d'effet de bord)
}
// --- Equals vs == ---
public void equalsTest() {
Bucket one = new Bucket(10, "red");
Bucket two = new Bucket(10, "red");
one.equals(two) // true — comparaison par valeur (size et color)
one == two // false — instances différentes
Bucket three = one;
one == three // true — même référence
}
// --- Nommage : abréviation vs descriptif ---
// Mauvais — noms abrégés, difficile à comprendre
public double calcTot(double prc, double rt, double dr){
double disc = prc * dr;
double subTot = prc - disc;
double tx = subTot * rt;
double tot = subTot + tx;
return tot;
}
// Bon — noms descriptifs, code auto-documenté
public double calculateTotal(double price, double taxRate, double discountRate){
double discount = price * discountRate;
double subTotal = price - discount;
double tax = subTotal * taxRate;
double total = subTotal + tax;
return total;
}
private void doTrue() { System.out.println("true"); }
private void doFalse() { System.out.println("false"); }
}
Concepts illustrated:
| Method | Illustrated concept |
|---|---|
stringExample() | Text blocks Java 13+ vs concatenation with \n |
switchSample1() | Standard switch with break — correct behavior |
switchSample2() | Switch without break — fall-through behavior (bug) |
switchSample3() | Switch with arrow syntax — no fall-through |
ifWithAssignmentOperator() | Bug: = instead of == in an if + side effect |
ifWithEqualityOperator() | Correct: == in an if — without side effects |
equalsTest() | Difference between .equals() (value) and == (reference) |
calcTot() | Bad naming — incomprehensible abbreviations |
calculateTotal() | Good naming — descriptive names, readable code |
Search Terms
java · debugging · backend · architecture · full-stack · web · bug · bugs · errors · stack · debug · fix · scenario · variable · breakpoint · exceptions · features · syntax · trace · variables · assignment · autocomplete · avoid · break