
Introduction
This guide assumes you are an experienced programmer who understands OOP principles, has Java 25 installed, and knows how to manage a project environment. Let’s skip the fluff and dive into the syntax.
Program Structure: The Java 25 Shift
Historically, Java required significant boilerplate just to print text. As of Java 25 (September 2025), the language has streamlined its entry point.
The Evolution of “Hello World”
You likely expect the verbose, explicit structure, but modern Java allows for much leaner code.
1. The Modern Script (Java 25+)
void main() {
IO.println("Hello World!");
}
2. The Classic Enterprise Structure
package com.louiseyousre.helloworld;
class Main {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
What Changed?
The “Modern Script” above isn’t a shortcut; it’s a first-class feature called Implicit Classes and Instance Main Methods.
- Implicit Classes: If you don’t declare a class, the compiler wraps your code in one automatically. This is ideal for scripts but means the class cannot be referenced by name elsewhere.
- Instance Main: The
mainmethod no longer requiresstaticorString[] args. The JVM simply instantiates the class and runs the method. - The
IOClass: Part ofjava.lang(auto-imported),IO.println()is a shorthand forSystem.out.println().
When to Use Which Style
Experienced devs need to know when to drop the ceremony and when to keep it.
| Use Case | Recommended Style | Rationale |
|---|---|---|
| Scripts & CLI tools | void main() | Speed and readability. |
| Interviews | Either (but mention both) | Shows you are current with Java 25. |
| Production Apps | Explicit classes + packages | Required for dependency injection and multi-file linking. |
| Library Dev | Explicit classes + packages | Others cannot import an implicit class. |
The “Package” Threshold
In Java 25, the choice of syntax determines your “Mode” of development.
1. The Unnamed Package (Script Mode)
When you use implicit classes or omit the package declaration, your code lives in the unnamed package.
- The Limit: Classes in the unnamed package cannot be imported by any other package.
- The Use Case: One-off utility scripts, data cleaners, or competitive programming.
2. Named Packages (Project Mode)
As soon as your project grows beyond a single file, you must transition to named packages (package com.yourname.project;).
The Decision Tree for Pros:
- Single, self-contained file? Use the simple
void main()syntax in the unnamed package. - Multiple files or external dependencies? Use an explicit class, a named package, and standard build tools (Maven/Gradle).
Introduction to The Core
Let’s strip away the “Enterprise” structure to focus on the core. By the core I mean Variables, Logic and Flow …etc.
Modern Input/Output (IO)
Before diving into anything else, let’s see the modern IO class in action. It’s the standard tool for simple console interaction in Java 25, replacing the old Scanner and System.out ceremony for many tasks.
To keep our code readable, we use comments—notes written for humans that the computer ignores.
Types of Comments in Java:
- Single-line (
//): Everything from the//to the end of that line is ignored. This is the most common way to add quick notes or labels to your steps. - Multi-line (
/* ... */): Everything between the opening/*and closing*/is ignored. This is perfect for longer explanations or temporarily “turning off” a block of code.
Note: There is a third type called Javadoc used for professional documentation. Because it has its own special syntax and power, we will cover it in its own dedicated article later.
Here’s a complete script demonstrating its key methods and examples of how to write comments:
void main() {
/* The IO class is the modern way to handle console interaction.
It is simpler and cleaner than the older "Scanner" approach.
*/
// [1] Print "Hello, World" followed by a new line.
IO.println("Hello World!");
// [2] Print a new line; just to add more spacing.
IO.println();
// [3] Print a prompt without a new line at the end.
IO.print("Enter Your First Name: ");
// [4] Read the user's input as a String and assign it to a variable.
final var firstName = IO.readln();
// [5] Print a customized message using string concatenation.
IO.println("Nice to meet you, " + firstName + ".");
// [6] A more concise pattern: prompt and read in one line.
final var lastName = IO.readln("Enter Your Last Name: ");
// [7] Print the result.
IO.println("So your full name is " + firstName + " " + lastName + ".");
}
Key Methods of IO:
IO.print(x): Printsxwithout a trailing newline.IO.println(x)/IO.println(): Printsxwith a trailing newline, or just a newline if called without an argument.IO.readln(): Reads a full line of input from the console, returning it as aString.IO.readln(prompt): A convenience method that performsIO.print(prompt);and then returnsIO.readln().
What you should note (will be explained later in more details):
final var: This declares a variable (firstName,lastName) whose type is inferred by the compiler (Stringin this case) and whose value cannot be changed after assignment (final). We’ll explore this next.- No Imports: The
IOclass is part ofjava.langand is automatically available, keeping script code clean. - Flow: The program executes sequentially, waiting at each
readln()for user input.
This example gives you a feel for modern Java’s streamlined syntax and sets the stage for understanding its core better, which we’ll continue to cover next.
Variables, Constants and the Type System
Java is a statically typed language, which ensures catch-at-compile-time safety as you know. However, it has evolved to reduce verbosity.
Declaring Variables
A variable declaration in Java consists of a type, a name, and optionally an initializer.
int count; // `int` is the type, `count` is the name and there's no initializer.
int total = 10; // here `= 10` is the initializer.
Java also supports local variable type inference using var (introduced in Java 10):
var message = "Hello"; // inferred as String
var sum = 42; // inferred as int
Type inference does not make Java dynamically typed—the inferred type is still fixed at compile time.
Variable Naming Rules & Conventions
Rules (enforced by the compiler)
- Must begin with a letter,
_, or$. - Cannot start with a digit.
- Cannot be a reserved keyword.
- Case-sensitive.
Conventions (by community standards)
- Use camelCase.
- Use meaningful, descriptive names.
- Avoid abbreviations unless widely understood.
int userAge;
String accountStatus;
Immutability (final)
In Java, immutability at the variable level is expressed using the final keyword.
A final variable cannot be reassigned after it has been initialized.
final int retries = 3;
final String name = "Alice";
This guarantees that the variable will always refer to the same value or object reference.
Data Types
Java is considered:
- Statically typed: types are known and checked at compile time.
- Strongly typed: incompatible types cannot be mixed implicitly.
This strictness:
- Prevents many runtime errors
- Improves readability and maintainability
- Enables powerful compiler optimizations
Data types in Java fall into two main categories: primitive types and reference types.
Primitive (Basic) Types
Numeric Types
// Integer types (whole numbers):
byte b = 10; // 8-bit
short s = 300; // 16-bit
int i = 42; // 32-bit (most commonly used)
long l = 100000L; // 64-bit
// Floating-point types (decimal numbers):
float f = 3.14f; // 32-bit (requires `f` suffix)
double d = 3.14; // 64-bit (default for decimals)
int and double are the default choices for most numeric operations unless memory or precision constraints apply.
NOTE: In Java, numeric literals are
intby default and decimal literals aredoubleby default.
- Use the
fsuffix to indicate afloatliteral (e.g.,3.14f).- Use the
Lsuffix to indicate alongliteral (e.g.,100000L).- Omitting these suffixes may lead to compilation errors or unintended type conversions.
Boolean Type
The boolean type represents logical truth values:
boolean isActive = true;
boolean hasAccess = false;
Java booleans are not interchangeable with integers (unlike some other languages).
Character Type
The char type represents a single Unicode character and uses single quotes:
char letter = 'A';
char symbol = '@';
Internally, char is a 16-bit unsigned value representing a UTF-16 code unit.
Reference Types (like String)
Reference types store references to objects, not the actual value. It could be not referencing to anything too (at any time) by holding the value null.
Primitive types cannot be null.
int x = null; // ❌ compile-time error
String (as an example):
String is a reference type, not a primitive, but it is used frequently and treated specially by the language.
String greeting = "Hello";
Key characteristics of String:
- Immutable
- Stored as an object
- Supports many built-in methods (
length(),substring(), etc.)
String name = null;
Note:
Stringis just an example of a reference type. The concept of objects and how reference types work in general will be explained in more detail later.
The Object Type (Base of All Reference Types)
In Java, every reference type ultimately inherits from Object. This means that any object — a String, for example, can be stored in a variable of type Object.
Object o = "Hello"; // String stored as Object
This works because of the magic of OOP:
String, and all classes extendObject. (More about OOP later…)- Java allows upcasting (storing a subclass instance in a superclass reference). ⏳🫠🤌🏻💗
Arrays
An array in Java is a reference type that stores a fixed-size sequence of elements of the same type.
- Arrays are objects
- Their size is fixed once created
- Elements are stored in contiguous memory
- Indexing is zero-based
Declaring Arrays
int[] numbers;
String[] names;
boolean[] flags;
There’s an alternative syntax (C-Style) although the type[] name style is the preferred one for clarity. and it is:
int numbers[];
Creating Arrays
You can create an array filled with the default values (which is 0 for numbers). You must specify the size when creating an array:
int[] numbers = new int[5]; // zero(es)
String[] names = new String[3]; // null(s)
The default values are:
| Type | Default Value |
|---|---|
int | 0 |
double | 0.0 |
boolean | false |
char | '\u0000' |
| Reference types | null |
For example:
int[] arr = new int[3];
IO.println(arr[0]); // 0
Array Initialization
Both the following ways are nearly the same just syntax differences. As it will create the exact same array in memory.
Inline:
int[] nums = {1, 2, 3, 4, 5};
String[] fruits = {"Apple", "Banana", "Orange"};
With new:
int[] nums = new int[] {1, 2, 3}; // You don't put the length this way.
Java requires the type information when an array is created outside a declaration. Here’s just an example:
// Will work...
for (int i : new int[] {1,2,3}){
IO.println(i);
}
// Won't work... Java 25 (It may get changed later .. who knows?!).
for (int i : {1,2,3}){
IO.println(i);
}
Knowing the length .length
int[] nums = {1, 2, 3};
IO.println(nums.length); // 3
Accessing & Modifying Elements:
int[] nums = {10, 20, 30};
int first = nums[0]; // 10
nums[1] = 99; // modify element
NOTE: Accessing an invalid index causes a runtime error:
nums[3]; // ❌ ArrayIndexOutOfBoundsException
Multidimensional Arrays:
int[][] matrix = new int[2][3];
// or inline initialization
int[][] matrix = {
{1, 2, 3},
{4, 5, 6}
};
IO.println(matrix[1][2]) // 6
NOTE: Java multidimensional arrays are not true matrices; each row can have a different length.
We will discuss more and more about arrays later for example java.util.Arrays and sorting …etc.
Type Inference (Java 10+)
Java supports local variable type inference using var (local made you curious right 😂 waiiit).
var count = 10; // int
var name = "Alice"; // String
Important notes:
varcan only be used for local variables.- The inferred type is fixed at compile time.
varrequires an initializer.
var x; // ❌ invalid
var y = null; // ❌ invalid (type cannot be inferred)
Type Conversion
Java distinguishes between implicit (widening) and explicit (narrowing) conversions.
Implicit Conversion (Widening)
Safe conversions where data loss cannot occur:
byte b = 10;
int i = b; // byte → int, safe
char c = 'A';
int code = c; // char → int, safe
float f = 3.14f;
double d = f; // float → double, safe
long l = 1000L;
float g = l; // long → float, safe (may lose precision for very large values, but no truncation)
| From | To (Widening) |
|---|---|
byte | short, int, long, float, double |
short | int, long, float, double |
char | int, long, float, double |
int | long, float, double |
long | float, double |
float | double |
Note: Widening does not truncate the value, so you won’t lose magnitude. Floating-point conversions (
int→float) are considered widening, though very large integers may lose precision in the least significant digits—but the language still treats it as safe.
Explicit Conversion (Casting)
Required when converting to a smaller or less precise type.
double d = 9.7;
int i = (int) d; // result: 9
Casting may result in:
- Loss of precision
- Overflow
Operators
Operators in Java are symbols used to perform operations on values or variables. They are grouped into different categories:
Arithmetic Operators
int a = 10;
int b = 3;
int sum = a + b; // Addition: 10 + 3 = 13
int diff = a - b; // Subtraction: 10 - 3 = 7
int prod = a * b; // Multiplication: 10 * 3 = 30
int div = a / b; // Integer division: 10 / 3 = 3
int rem = a % b; // Modulus (remainder): 10 % 3 = 1
double preciseDiv = 10.0 / 3.0; // Floating-point division: 3.3333
Notes:
- Integer division truncates the decimal.
- Modulus
%is commonly used to check divisibility or cycle through values.
Assignment Operators
int x = 5;
x += 3; // x = x + 3 -> 8
x -= 2; // x = x - 2 -> 6
x *= 2; // x = x * 2 -> 12
x /= 3; // x = x / 3 -> 4
x %= 3; // x = x % 3 -> 1
int x = 5; // 0101
x &= 3; // x = x & 3
x |= 2; // x = x | 2
x ^= 1; // x = x ^ 1
x <<= 1; // x = x << 1
x >>= 1; // x = x >> 1
x >>>= 1;
Notes:
- Assignment operators store a value in a variable.
- Compound assignment (
+=,-=, etc.) is shorthand forx = x <op> y.
Comparison Operators
int a = 10;
int b = 3;
boolean eq = (a == b); // false
boolean neq = (a != b); // true
boolean gt = (a > b); // true
boolean lt = (a < b); // false
boolean gte = (a >= b); // true
boolean lte = (a <= b); // false
Notes:
- Comparison operators always return a boolean (
true/false).- For objects (like
String), use.equals()instead of==.
Logical Operators
int a = 10;
int b = 3;
boolean andOp = (a > 5 && b < 5); // true (both conditions true)
boolean orOp = (a > 5 || b > 5); // true (at least one condition true)
boolean notOp = !(a > b); // false (negates true)
Notes:
&&evaluates to true only if both expressions are true.||evaluates to true if at least one expression is true.!inverts the boolean value.&&and||are Short-Circuit which means they don’t evaluate all sides if not needed to determine the final result.
Ternary Operator
int a = 10;
String result = (a % 2 == 0) ? "Even" : "Odd"; // "Even"
Notes:
- Syntax:
condition ? valueIfTrue : valueIfFalse;- It’s a shorthand for simple
if-elsestatements.
Bitwise Operators:
int bitA = 5; // 0101 in binary
int bitB = 3; // 0011 in binary
int andBits = bitA & bitB; // 0101 & 0011 = 0001 -> 1
int orBits = bitA | bitB; // 0101 | 0011 = 0111 -> 7
int xorBits = bitA ^ bitB; // 0101 ^ 0011 = 0110 -> 6
int notBits = ~bitA; // ~0101 = 1010 (two's complement) -> -6
int leftShift = bitA << 1; // 0101 << 1 = 1010 -> 10
int rightShift = bitA >> 1; // 0101 >> 1 = 0010 -> 2
int unsignedRightShift = bitA >>> 1; // 0101 >>> 1 = 0010 -> 2
Notes:
- Bitwise operators work on binary representations of integers.
- Used in flags, low-level programming, or optimization tasks.
- Non-Short-Circuit Logical Operators (
&,|) also work with booleans, but the difference is that they do evaluate both sides.
Unary Operators
int x = 5;
IO.println(x); // 5
IO.println(x++); // 5 (post-increment: use first, then increment)
IO.println(x); // 6
IO.println(++x); // 7 (pre-increment: increment first, then use)
IO.println(x); // 7
IO.println(x--); // 7 (post-decrement: use first, then decrement)
IO.println(x); // 6
IO.println(--x); // 5 (pre-decrement: decrement first, then use)
IO.println(x); // 5
Notes:
++xandx++are not the same in expressions.- Common source of bugs in loops and expressions.
String Concatenation Operator (+):
+ is overloaded in Java for String concatenation**.
String name = "Java";
int version = 21;
String result = name + " " + version; // "Java 21"
Notes:
- If either operand is a String, Java converts the other to String.
- Evaluation is left to right.
instanceof Operator
Checks if an object belongs to a type.
Object obj = "Hello";
boolean isString = obj instanceof String; // true
Operator Precedence
Operators are evaluated in a specific order, unless parentheses override it. Understanding precedence avoids unexpected results.
Java operator precedence (simplified, highest → lowest):
- Parentheses:
() - Unary operators:
+,-,++,--,!,~ - Multiplication, division, modulus:
*,/,% - Addition, subtraction:
+,- - Bitwise shift:
<<,>>,>>> - Relational:
<,>,<=,>=,instanceof - Equality:
==,!= - Bitwise AND:
& - Bitwise XOR:
^ - Bitwise OR:
| - Logical AND:
&& - Logical OR:
|| - Ternary:
? : - Assignment:
=,+=,-=,*=,/=,%=
Example:
int result1 = 5 + 3 * 2; // 5 + (3*2) = 11
int result2 = (5 + 3) * 2; // (5+3) * 2 = 16
int result3 = 10 > 5 && 3 < 2; // true && false -> false
Tip: Always use parentheses to clarify intent and avoid mistakes.
Special Notes
- Integer division truncates decimals; use
doublefor precise division. - Use parentheses for clarity when combining multiple operator types.
// Object comparison: use .equals() for Strings, not ==
String s1 = "Hello";
String s2 = "Hello";
boolean stringEq = s1.equals(s2); // true (content)
boolean stringRefEq = (s1 == s2); // true due to string interning, but not reliable
Control Flow (Decision Making)
Control flow statements allow a program to make decisions and execute different code paths based on conditions.
if / else
The if statement executes a block only if a condition is true.
int age = 20;
if (age >= 18) {
IO.println("Adult");
}
Use else to handle the false case:
if (age >= 18) {
IO.println("Adult");
} else {
IO.println("Minor");
}
Notes:
- Conditions must evaluate to a
boolean.- Curly braces
{}are optional for single statements but strongly recommended.
else if
Used to check one or more alternative conditions in sequence.
int score = 85;
if (score >= 90) {
IO.println("Excellent");
} else if (score >= 75) {
IO.println("Good");
} else if (score >= 60) {
IO.println("Pass");
} else {
IO.println("Fail");
}
Notes:
- Conditions are evaluated top to bottom.
- The first matching condition stops further checks.
switch Statement (Classic)
Traditional switch compares a value against fixed constants.
int day = 3;
switch (day) {
case 1:
IO.println("Monday");
break;
case 2:
IO.println("Tuesday");
break;
case 3:
IO.println("Wednesday");
break;
default:
IO.println("Unknown day");
}
Notes:
breakis required to prevent fall-through.- Works with
int,enum,String, etc.
Modern switch Expressions (Java 14+)
switch can now return a value and use arrow syntax.
int day = 3;
String dayName = switch (day) {
case 1 -> "Monday";
case 2 -> "Tuesday";
case 3 -> "Wednesday";
default -> "Unknown";
};
Notes:
- No
breakneeded.- Safer and more expressive than classic
switch.
switch with yield (Multi-statement cases)
When a case needs multiple statements, use yield.
int score = 85;
String grade = switch (score / 10) {
case 10, 9 -> "A";
case 8 -> {
IO.println("Good performance");
yield "B";
}
case 7 -> "C";
default -> "F";
};
Pattern Matching in switch (Java 21+)
You can switch on types, not just values.
Object obj = "Hello";
String result = switch (obj) {
case String s -> "String of length " + s.length();
case Integer i -> "Integer: " + i;
case null -> "Null value";
default -> "Unknown type";
};
Notes:
- Eliminates manual casting.
nullmust be handled explicitly.
Guarded Patterns (when clauses)
Add extra conditions to pattern matches.
Object obj = 42;
String description = switch (obj) {
case Integer i when i > 0 -> "Positive integer";
case Integer i when i < 0 -> "Negative integer";
case Integer i -> "Zero";
default -> "Not an integer";
};
Notes:
- Guards make
switchas powerful as complexif-elsechains.- Evaluated only after the pattern matches.
Nested Conditions
Conditions can be nested, but readability can suffer.
int age = 25;
boolean hasLicense = true;
if (age >= 18) {
if (hasLicense) {
IO.println("Can drive");
} else {
IO.println("Needs license");
}
}
Tip: Prefer early returns or guards to reduce nesting.
Short-Circuit Evaluation
Logical operators && and || stop evaluating as soon as the result is known.
int x = 0;
if (x != 0 && 10 / x > 1) {
IO.println("Safe");
}
Notes:
10 / xis never evaluated ifx != 0is false.- Prevents errors like division by zero.
- Improves performance.
instanceof with Pattern Matching (Java 16+)
Simplifies type checks and casting.
Object obj = "Java";
if (obj instanceof String s) {
IO.println(s.toUpperCase());
}
Notes: Variable
sis only available inside theifblock.
Summary Tips
- Prefer switch expressions over classic
switch. - Use pattern matching to avoid casting.
- Use guard clauses to flatten logic.
- Rely on short-circuit evaluation for safety.
- Avoid deep nesting whenever possible.
Loops & Iteration
Loops allow a program to repeat execution of a block of code while a condition holds or while elements remain to be processed.
for Loop (Classic)
Used when the number of iterations is known in advance.
for (int i = 0; i < 5; i++) {
IO.println(i);
}
Structure:
for (initialization; condition; update) { }
Notes:
- Initialization runs once.
- Condition is checked before every iteration.
- Update runs after each iteration.
for Loop with Multiple Variables
for (int i = 0, j = 10; i < j; i++, j--) {
IO.println(i + ", " + j);
}
Notes:
- Useful for two-pointer techniques.
- Variables must be of the same type.
Enhanced for Loop (foreach)
Used to iterate over arrays and collections.
int[] numbers = {1, 2, 3};
for (int n : numbers) {
IO.println(n);
}
Notes:
- Read-only access (no index).
- Safer and cleaner than classic
for.
var in Enhanced for (Java 10+)
Type inference improves readability.
for (var n : numbers) {
IO.println(n);
}
Unnamed Loop Variables (Java 21+)
Use _ when the loop variable is intentionally unused.
for (var _ : numbers) {
IO.println("Hello");
}
Notes:
- Improves intent clarity.
- Prevents unused-variable warnings.
while Loop
Used when the number of iterations is unknown.
int count = 3;
while (count > 0) {
IO.println(count);
count--;
}
Notes:
- Condition is checked before each iteration.
- Can execute zero times.
do-while Loop
Guarantees at least one execution.
int attempts = 0;
do {
IO.println("Attempt " + attempts);
attempts++;
} while (attempts < 3);
Notes:
- Condition checked after the loop body.
- Useful for menus, retries, user input.
Loop Control Statements
break
Exits the loop immediately.
for (int i = 0; i < 10; i++) {
if (i == 5) break;
}
continue
Skips the current iteration.
for (int i = 0; i < 5; i++) {
if (i == 2) continue;
IO.println(i);
}
Labeled break and continue
Control outer loops from inner loops.
outer:
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i == 1 && j == 1) break outer;
}
}
Notes:
- Powerful but should be used sparingly.
- Often replaceable with refactoring or early returns.
Nested Loops
Loops inside loops.
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
IO.println(i + ", " + j);
}
}
Notes:
- Time complexity multiplies (
O(n²)).- Be careful with performance.
Infinite Loops
A loop that never ends unless explicitly stopped.
while (true) {
// runs forever
}
for (;;) {
// infinite loop
}
Common causes:
- Missing update
- Always-true condition
- Logic errors
How to avoid:
- Ensure termination conditions
- Use timeouts or counters
- Prefer bounded loops when possible
Summary Tips
- Use classic
forwhen you need indexes. - Prefer enhanced
forfor collections. - Use
while/do-whilewhen conditions are dynamic. - Avoid deep nesting and accidental infinite loops.
- Use unnamed variables (
_) to express intent clearly.
Functions / Methods
Functions (also called methods in Java) are reusable blocks of code that perform a specific task. They help organize programs, avoid repetition, and make code more readable.
Defining a Method
int add(int a, int b) {
return a + b;
}
Notes:
intis the return type (voidif no value is returned).addis the method name.(int a, int b)are parameters.
When inside a class , public and other access modifiers (visibility) are required.
public int add(int a, int b) {
return a + b;
}
Calling a Method
int sum = add(3, 5);
IO.println(sum); // 8
Notes:
- Pass arguments matching parameter types.
- Return value can be stored or used directly.
Parameters & Arguments
- Parameters: Variables defined in the method signature.
- Arguments: Actual values passed during the call.
void greet(String name) {
IO.println("Hello, " + name);
}
void main() {
greet("Louise"); // Hello, Louise
}
Return Values
- Use
returnto send a value back. - Methods with
voiddo not return a value.
double square(double x) {
return x * x;
}
void main() {
IO.println(square(4)); // 16
}
Notes:
- Returning a value ends the method immediately.
- Multiple return statements can exist in a method.
Method overloading and Default Parameters (Java does not support them natively)
Method overloading means multiple methods with same name but different parameters. Java requires method overloading to simulate default parameters.
void greet() {
greet("Guest");
}
void greet(String name) {
IO.println("Hello, " + name);
}
void main() {
greet(); // Hello, Guest
greet("Louise"); // Hello, Louise
}
Return type alone cannot differentiate methods. So the following wouldn’t compile.
int getSomething() {
return 1;
}
String getSomething() {
return "Hello";
}
Named vs Positional Parameters
- Java only supports positional arguments.
- Arguments are assigned in order to parameters.
void printCoords(int x, int y) {
IO.println("x=" + x + ", y=" + y);
}
void main() {
printCoords(10, 20); // x=10, y=20
}
Method Scope
- Variables defined inside a method are local and destroyed after the method ends.
- Parameters are also local to the method.
void example() {
int localVar = 5; // accessible only inside example()
}
Pure vs Impure Methods
Pure
No side effects; same inputs → same outputs.
int multiply(int a, int b) {
return a * b; // pure
}
Impure
Modifies external state or depends on it.
int x = 0;
void increment() {
x += 1;
}
void main() {
IO.println("x=" + x);
increment();
IO.println("x=" + x);
}
Anonymous Functions / Lambdas (Java 8+)
- Lambdas allow inline, unnamed functions.
- Typically used with functional interfaces (more about interfaces later).
// More about lists later....
List<Integer> nums = List.of(3, 4, 5);
// More about streams later...
var squaredNums = nums.stream().map(n -> n*n).toList();
for (int squaredNum : squaredNums) {
IO.println(squaredNum);
// Expected Output:
// 9
// 16
// 25
}
Notes:
n -> n * nis a lambda expression.- Can replace short single-method classes.
- Often used with streams or event handlers.
Conclusion
In this guide, we’ve covered the core essentials of modern Java:
- Leaner program structure with implicit classes and
void main()scripts. - Modern input/output with the
IOclass. - Variables, constants, and type system including primitives, reference types, and arrays.
- Type inference with
varand safe conversions. - Operators: arithmetic, assignment, comparison, logical, bitwise, unary, ternary, and
instanceof. - Control flow:
if/else,switch(classic and modern), pattern matching, and guarded conditions. - Loops & iteration:
for,while,do-while, nested loops, enhancedfor, and loop control statements. - Functions / methods: defining, calling, parameters, return values, overloading, scope, purity, and lambdas.
You now have a solid foundation to write clean, modern Java code and understand the core language mechanics.
What’s Next?
In future articles, we’ll dive into Object-Oriented Programming in Java and beyond, including:
- Classes, objects, and constructors.
- Inheritance, interfaces, and polymorphism.
- Encapsulation and access modifiers.
- Advanced concepts like records, sealed classes, and pattern matching.
- Functional programming with streams, lambdas, and higher-order operations.
By mastering these fundamentals first, you’ll be ready to tackle more advanced Java topics with confidence.
Stay tuned for the next part of the Java Journey where we explore OOP, streams, functional programming, and deeper language features.