Java Basics: Syntax, Data Types, Control Flow, and OOP Explained
Java is one of the most widely used programming languages in the world, powering everything from Android apps to enterprise server farms. Its “write once, run anywhere” philosophy, enforced by the Java Virtual Machine (JVM), makes it a portable and reliable choice for developers of all levels. This tutorial covers everything you need to build a solid foundation: syntax, primitive types, control flow, object-oriented programming (OOP), and the tools that make Java development productive.
Java Syntax and Program Structure
Every Java application begins with a class definition. The Java Language Specification (JLS, §7.6) requires that the source file name matches the public class name. Consider the classic Hello World:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, ExcellentWiki!");
}
---The public keyword controls access (JLS §6.6). static binds the method to the class rather than an instance. void indicates no return value. The String[] args parameter captures command-line arguments.
Case sensitivity is strict — Main and main are different identifiers. Semicolons terminate statements. Blocks are delimited by curly braces { }. These conventions, documented in the Oracle Java Code Conventions, ensure readability across teams.
Primitive Types and Variables
Java has eight primitive types defined in JLS §4.2:
| Type | Size | Range |
|---|---|---|
| byte | 8-bit | -128 to 127 |
| short | 16-bit | -32,768 to 32,767 |
| int | 32-bit | -2^31 to 2^31-1 |
| long | 64-bit | -2^63 to 2^63-1 |
| float | 32-bit | IEEE 754 ±3.4E±38 |
| double | 64-bit | IEEE 754 ±1.8E±308 |
| boolean | 1-bit | true or false |
| char | 16-bit | Unicode character (0 to 65,535) |
Variables must be declared before use. Local variables require explicit initialization — the compiler rejects uninitialized reads (JLS §16). Type inference with var (Java 10+) allows the compiler to deduce the type:
var count = 42; // int
var message = "Hello"; // String
var list = new ArrayList<String>();Per the Oracle Java Tutorials, var improves readability when the right-hand side makes the type obvious. Avoid it in API signatures or when the inferred type is ambiguous.
Control Flow Statements
Java provides standard control structures familiar to C-family developers.
Conditionals use if-else and switch:
if (score >= 90) {
grade = 'A';
--- else if (score >= 80) {
grade = 'B';
--- else {
grade = 'F';
---The enhanced switch (Java 14+) supports arrow syntax and yields values:
String category = switch (status) {
case 200 -> "OK";
case 404 -> "Not Found";
case 500 -> "Server Error";
default -> "Unknown";
---;Loops include for, while, and do-while. The enhanced for-each loop iterates over arrays and collections:
for (String name : names) {
System.out.println(name);
---The Streams API (Java 8+) offers a functional alternative for collection processing. See our Java Streams API guide for details.
Object-Oriented Programming
Java is fundamentally object-oriented. The core principles — encapsulation, inheritance, polymorphism, and abstraction — are enforced by the language.
Classes and Objects: A class is a blueprint. An object is an instance. Fields hold state; methods define behavior. Constructors initialize objects:
public class Book {
private String title;
private String author;
public Book(String title, String author) {
this.title = title;
this.author = author;
}
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
---Encapsulation hides internal state via access modifiers. Joshua Bloch in Effective Java (Item 15) advises minimizing accessibility — prefer private over public for fields.
Inheritance uses the extends keyword. Java supports single class inheritance but multiple interface inheritance (JLS §8.1.4):
public class EBook extends Book implements Downloadable {
private long fileSize;
public EBook(String title, String author, long fileSize) {
super(title, author);
this.fileSize = fileSize;
}
@Override
public void download() {
System.out.println("Downloading " + getTitle());
}
---Abstract Classes and Interfaces: Abstract classes can have both abstract and concrete methods. Interfaces (Java 8+) can have default and static methods. Prefer interfaces for defining contracts (Bloch, Item 22).
Polymorphism enables methods to behave differently based on the runtime type. The JVM uses dynamic dispatch (vtable) for instance methods (JLS §15.12).
Packages and Imports
Packages organize classes into namespaces (JLS §7). The package declaration must be the first statement in a file. The import statement brings types into scope:
package com.excellentwiki.util;
import java.util.List;
import java.util.ArrayList;Oracle recommends reverse-domain naming (com.example.project). This convention prevents name collisions in the global classpath.
Key Java Standard Library Classes
The java.lang package is implicitly imported. Key classes include:
String— immutable character sequences with rich methods (substring,replace,matchesfor regex)StringBuilder— mutable strings for efficient concatenationMath— static methods formax,min,sqrt,pow, trigonometric functionsScanner— reading primitive types and tokens from input streams
Baeldung’s tutorials on these classes provide thorough coverage of common patterns.
Arrays and Enhanced for Loops
Arrays are fixed-length containers declared with bracket syntax:
int[] numbers = new int[5]; // all zeros
String[] names = {"Alice", "Bob"};
int first = numbers[0]; // indexed access
numbers[numbers.length - 1] = 42; // last elementThe enhanced for-each loop iterates over arrays and any Iterable:
for (String name : names) {
System.out.println(name);
---For indexed iteration, use the traditional for loop with length. The Arrays utility class (java.util.Arrays) provides sort(), binarySearch(), fill(), and toString() for array manipulation.
Generics and Type Safety
Generics (JLS §4.5) enable compile-time type checking for collections and classes:
List<String> strings = new ArrayList<>();
strings.add("hello");
// strings.add(42); // compile error
String s = strings.get(0); // no cast neededType erasure removes generic type information at runtime. This is why new ArrayList<String>() compiles but new T() does not. Bounded type parameters restrict type arguments:
public <T extends Comparable<T>> T max(T a, T b) {
return a.compareTo(b) > 0 ? a : b;
---Wildcards (? extends, ? super) handle covariance and contravariance. The PECS principle (Producer Extends, Consumer Super) from Bloch (Item 31) guides their use.
Enumerated Types
enum types (JLS §8.9) define a fixed set of named constants with methods and fields:
public enum HttpStatus {
OK(200, "OK"),
NOT_FOUND(404, "Not Found"),
INTERNAL_SERVER_ERROR(500, "Server Error");
private final int code;
private final String reason;
HttpStatus(int code, String reason) {
this.code = code;
this.reason = reason;
}
public int getCode() { return code; }
public String getReason() { return reason; }
public static HttpStatus fromCode(int code) {
for (HttpStatus status : values()) {
if (status.code == code) return status;
}
throw new IllegalArgumentException("Unknown code: " + code);
}
---Enums provide type safety over int or String constants, and switch statements can exhaustively match all values. The compiler warns when a new enum value is added without updating all switch statements.
FAQ
Q: What is the difference between == and .equals() in Java?
A: == compares reference identity (memory address), while .equals() compares logical value. Always use .equals() for string comparison.
Q: Why is main declared public static void?
A: public lets the JVM access it. static allows invocation without creating an instance. void means the program exits via System.exit().
Q: What is the JVM and why is it important? A: The Java Virtual Machine interprets compiled bytecode, providing platform independence, garbage collection, and JIT compilation for performance.
Q: Should I learn Java 11 or Java 21 as a beginner? A: Start with Java 21 LTS. It includes all modern features like records, sealed classes, and pattern matching while maintaining backward compatibility.
Q: What IDE should I use for Java development? A: IntelliJ IDEA Community Edition is the most popular choice. Eclipse and VS Code with Java extensions are also solid alternatives.
Java’s type system includes autoboxing — automatic conversion between primitives and their wrapper types (Integer, Long, Boolean). While convenient, autoboxing in hot loops creates hidden object allocations that pressure the garbage collector. Use primitive types in performance-critical code and prefer IntStream over Stream<Integer>.
Java’s try-with-resources statement (Java 7+) ensures that any object implementing AutoCloseable is closed reliably after use. This eliminates the need for explicit finally blocks when working with files, database connections, and network sockets. Multiple resources can be declared in a single try statement, and each is closed in reverse declaration order regardless of whether an exception occurs.
Java’s switch expression (Java 14+) yields a value, supports multiple case labels per arm, and enforces exhaustiveness when used with enums or sealed types. Unlike the traditional switch statement, the expression form eliminates accidental fall-through and makes control flow explicit. Combined with pattern matching (Java 21+), it becomes one of the most expressive constructs in the language.
Regular practice on platforms like HackerRank, LeetCode, and CodinGame reinforces these fundamentals. Building small projects — a calculator, a to-do list, a file parser — transforms abstract concepts into muscle memory. Pair programming and code reviews further accelerate the learning curve.
Mastering Java basics opens doors to enterprise development, Android, big data, and cloud-native applications. Continue with our Java Collections Framework guide and explore the Java Streams API for functional data processing.