March 27, 202612 min read

Java for Beginners: Write Your First Program (Without the Enterprise Trauma)

Learn Java from scratch. Variables, OOP, methods, arrays, ArrayList, inheritance, and common gotchas explained with clear examples and zero jargon.

java beginners programming oop tutorial
Ad 336x280

Java has a reputation. Verbose. Enterprise-y. The language of 500-line XML configs and AbstractSingletonProxyFactoryBean. That reputation isn't entirely undeserved, but it misses the bigger picture.

Java runs on over 3 billion devices. It powers Android apps, backend services at Netflix and LinkedIn, trading systems at banks, and Minecraft. It's the most-taught language in university CS programs. It's fast, it's mature, and jobs are everywhere.

The verbosity is real, but modern Java (version 17+) has gotten significantly leaner. And the upside of all that structure is that once you learn it, you understand concepts that transfer to almost every other object-oriented language.

Let's write some Java.

Installing Java

You need the JDK (Java Development Kit). Download it from adoptium.net -- it's free and open source. Get the latest LTS version (Java 21 as of this writing).

Verify the installation:

java --version
javac --version
java runs programs. javac compiles them. Java is a compiled language -- you write .java files, compile them to .class bytecode, and the JVM (Java Virtual Machine) runs that bytecode. This is why Java is "write once, run anywhere."

For an editor, VS Code with the "Extension Pack for Java" works great. IntelliJ IDEA Community Edition is the gold standard if you want a full IDE.

Hello World

Create a file called HelloWorld.java:

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

Compile and run:

javac HelloWorld.java
java HelloWorld

Let's dissect every piece of this because nothing here is optional:

  • public class HelloWorld -- Java requires every file to have a class, and the filename must match the class name. HelloWorld.java contains class HelloWorld.
  • public static void main(String[] args) -- the entry point. public means accessible from outside, static means it belongs to the class (not an instance), void means it returns nothing, String[] args accepts command-line arguments.
  • System.out.println() -- prints text followed by a newline. System.out.print() skips the newline.
Yes, it's more ceremony than Python's print("Hello"). You get used to it.

Variables and Data Types

Java is statically typed. Every variable needs a declared type:

public class Variables {
    public static void main(String[] args) {
        // Primitive types
        int age = 25;                  // Whole numbers
        double price = 19.99;          // Decimal numbers
        boolean isActive = true;       // true or false
        char grade = 'A';              // Single character (single quotes)

// Reference type
String name = "Alice"; // Text (double quotes, capital S)

// Type inference (Java 10+)
var count = 42; // Compiler infers int
var message = "Hello"; // Compiler infers String

System.out.println(name + " is " + age + " years old.");
System.out.println("Price: $" + price);
}
}

Key primitive types:

TypeSizeRange / Purpose
int32 bits-2.1 billion to 2.1 billion
long64 bitsVery large whole numbers
double64 bitsDecimal numbers
float32 bitsSmaller decimals (rarely used)
boolean1 bittrue or false
char16 bitsSingle Unicode character
String is not a primitive -- it's a class. That distinction matters later.

Constants

Use final for values that shouldn't change:

final double TAX_RATE = 0.08;
final String COMPANY = "CodeUp";
// TAX_RATE = 0.10;  // Compile error

Conditionals

if / else

int score = 85;

if (score >= 90) {
System.out.println("Grade: A");
} else if (score >= 80) {
System.out.println("Grade: B");
} else if (score >= 70) {
System.out.println("Grade: C");
} else {
System.out.println("Grade: F");
}

switch

String day = "Wednesday";

switch (day) {
case "Monday":
case "Tuesday":
case "Wednesday":
case "Thursday":
case "Friday":
System.out.println("Weekday");
break;
case "Saturday":
case "Sunday":
System.out.println("Weekend");
break;
default:
System.out.println("Invalid day");
}

Modern Java (14+) has a cleaner switch expression:

String type = switch (day) {
    case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" -> "Weekday";
    case "Saturday", "Sunday" -> "Weekend";
    default -> "Invalid";
};

Loops

for loop

for (int i = 0; i < 5; i++) {
    System.out.println("Iteration: " + i);
}

while loop

int countdown = 5;
while (countdown > 0) {
    System.out.println(countdown);
    countdown--;
}

do-while loop

Runs at least once, checks the condition after:

int attempts = 0;
do {
    System.out.println("Attempt " + (attempts + 1));
    attempts++;
} while (attempts < 3);

Enhanced for loop (for-each)

String[] fruits = {"Apple", "Banana", "Cherry"};
for (String fruit : fruits) {
    System.out.println(fruit);
}

Methods

Methods are reusable blocks of code:

public class Calculator {
    // Method that returns a value
    static int add(int a, int b) {
        return a + b;
    }

// Method that returns nothing
static void greet(String name) {
System.out.println("Hello, " + name + "!");
}

// Method with a default-like pattern using overloading
static double calculateTax(double amount) {
return calculateTax(amount, 0.08);
}

static double calculateTax(double amount, double rate) {
return amount * rate;
}

public static void main(String[] args) {
int result = add(10, 20);
System.out.println("Sum: " + result);

greet("Alice");

System.out.println("Tax: $" + calculateTax(100.00));
System.out.println("Tax: $" + calculateTax(100.00, 0.10));
}
}

Method overloading (same name, different parameters) is Java's way of providing flexibility without default arguments.

Arrays

Fixed-size collections:

// Declare and initialize
int[] numbers = {10, 20, 30, 40, 50};
String[] names = new String[3];  // Empty array of size 3

names[0] = "Alice";
names[1] = "Bob";
names[2] = "Carol";

// Access elements
System.out.println(numbers[0]); // 10
System.out.println(numbers.length); // 5

// Loop through
for (int i = 0; i < numbers.length; i++) {
System.out.println(numbers[i]);
}

// Or use for-each
for (int num : numbers) {
System.out.println(num);
}

Arrays have a fixed size. Once created, you can't add or remove elements. For that, you need ArrayList.

ArrayList: Dynamic Arrays

import java.util.ArrayList;

public class ShoppingList {
public static void main(String[] args) {
ArrayList<String> items = new ArrayList<>();

// Add items
items.add("Milk");
items.add("Bread");
items.add("Eggs");

// Access by index
System.out.println("First item: " + items.get(0));

// Size
System.out.println("Total items: " + items.size());

// Remove
items.remove("Bread");
// or by index: items.remove(1);

// Check if something exists
if (items.contains("Milk")) {
System.out.println("Don't forget the milk!");
}

// Loop
for (String item : items) {
System.out.println("- " + item);
}
}
}

Notice ArrayList -- the angle brackets specify what type the list holds. This is called generics. You can have ArrayList, ArrayList, etc. (Note: use Integer not int -- ArrayList requires wrapper classes for primitives.)

Object-Oriented Programming

This is where Java shines. OOP organizes code into classes (blueprints) and objects (instances of those blueprints).

Classes and Objects

public class Book {
    // Fields (instance variables)
    String title;
    String author;
    double price;
    int pages;

// Constructor
public Book(String title, String author, double price, int pages) {
this.title = title;
this.author = author;
this.price = price;
this.pages = pages;
}

// Method
public String describe() {
return title + " by " + author + " ($" + price + ")";
}

public boolean isLong() {
return pages > 400;
}
}

Using it:

public class Bookstore {
    public static void main(String[] args) {
        Book book1 = new Book("Dune", "Frank Herbert", 9.99, 688);
        Book book2 = new Book("1984", "George Orwell", 8.99, 328);

System.out.println(book1.describe());
System.out.println("Is it long? " + book1.isLong());

System.out.println(book2.describe());
System.out.println("Is it long? " + book2.isLong());
}
}

this refers to the current object. In the constructor, this.title = title distinguishes the field from the parameter.

Encapsulation

In practice, you make fields private and provide getters and setters:

public class BankAccount {
    private String owner;
    private double balance;

public BankAccount(String owner, double initialBalance) {
this.owner = owner;
this.balance = initialBalance;
}

public double getBalance() {
return balance;
}

public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}

public boolean withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
return true;
}
return false;
}
}

The private keyword prevents outside code from directly changing balance. All modifications go through methods that enforce rules (no negative deposits, no overdrafts).

Inheritance

Classes can extend other classes to inherit their fields and methods:

public class Product {
    protected String name;
    protected double price;

public Product(String name, double price) {
this.name = name;
this.price = price;
}

public String getInfo() {
return name + " - $" + price;
}
}

public class DigitalProduct extends Product {
private double fileSizeMB;

public DigitalProduct(String name, double price, double fileSizeMB) {
super(name, price); // Call parent constructor
this.fileSizeMB = fileSizeMB;
}

@Override
public String getInfo() {
return super.getInfo() + " (" + fileSizeMB + " MB download)";
}
}

extends creates an "is-a" relationship: a DigitalProduct is a Product. super() calls the parent constructor. @Override tells the compiler you're intentionally replacing a parent method.

Interfaces

Interfaces define a contract -- what methods a class must implement, without specifying how:

public interface Searchable {
    boolean matches(String query);
}

public interface Printable {
String toPrintFormat();
}

public class Article implements Searchable, Printable {
private String title;
private String content;

public Article(String title, String content) {
this.title = title;
this.content = content;
}

@Override
public boolean matches(String query) {
return title.toLowerCase().contains(query.toLowerCase())
|| content.toLowerCase().contains(query.toLowerCase());
}

@Override
public String toPrintFormat() {
return "=== " + title + " ===\n" + content;
}
}

A class can implement multiple interfaces but can only extend one class. Interfaces are how Java achieves a form of multiple inheritance without the complexity.

Building Something: A Simple Task Manager

Let's combine everything into a working program:

import java.util.ArrayList;
import java.util.Scanner;

public class TaskManager {
private ArrayList<String> tasks = new ArrayList<>();
private ArrayList<Boolean> completed = new ArrayList<>();

public void addTask(String task) {
tasks.add(task);
completed.add(false);
System.out.println("Added: " + task);
}

public void completeTask(int index) {
if (index >= 0 && index < tasks.size()) {
completed.set(index, true);
System.out.println("Completed: " + tasks.get(index));
} else {
System.out.println("Invalid task number.");
}
}

public void showTasks() {
if (tasks.isEmpty()) {
System.out.println("No tasks yet.");
return;
}
for (int i = 0; i < tasks.size(); i++) {
String status = completed.get(i) ? "[x]" : "[ ]";
System.out.println((i + 1) + ". " + status + " " + tasks.get(i));
}
}

public void showStats() {
long done = completed.stream().filter(c -> c).count();
System.out.println("Total: " + tasks.size()
+ " | Done: " + done
+ " | Remaining: " + (tasks.size() - done));
}

public static void main(String[] args) {
TaskManager manager = new TaskManager();
Scanner scanner = new Scanner(System.in);

System.out.println("Task Manager (type 'help' for commands)");

while (true) {
System.out.print("> ");
String input = scanner.nextLine().trim();

if (input.equals("quit")) {
break;
} else if (input.equals("help")) {
System.out.println("Commands: add <task>, done <number>, list, stats, quit");
} else if (input.startsWith("add ")) {
manager.addTask(input.substring(4));
} else if (input.startsWith("done ")) {
int num = Integer.parseInt(input.substring(5)) - 1;
manager.completeTask(num);
} else if (input.equals("list")) {
manager.showTasks();
} else if (input.equals("stats")) {
manager.showStats();
} else {
System.out.println("Unknown command. Type 'help'.");
}
}

scanner.close();
System.out.println("Goodbye!");
}
}

Compile and run it: javac TaskManager.java && java TaskManager. You've got a working interactive program with state management, user input, and multiple methods working together.

Common Gotchas

== vs .equals()

This trips up every Java beginner:

String a = new String("hello");
String b = new String("hello");

System.out.println(a == b); // false (compares references)
System.out.println(a.equals(b)); // true (compares values)

== checks if two variables point to the same object in memory. .equals() checks if their values are the same. For strings and other objects, always use .equals().

There's a subtle catch: string literals are cached (interned), so "hello" == "hello" is true. But don't rely on this -- use .equals() every time.

NullPointerException

The most common Java runtime error:

String name = null;
System.out.println(name.length());  // NullPointerException!

You're calling a method on something that doesn't exist. Always check for null before using an object:

if (name != null) {
    System.out.println(name.length());
}

Or in modern Java, use Optional:

import java.util.Optional;

Optional<String> maybeName = Optional.ofNullable(getName());
maybeName.ifPresent(n -> System.out.println(n.length()));

Array Index Out of Bounds

int[] nums = {1, 2, 3};
System.out.println(nums[3]);  // ArrayIndexOutOfBoundsException

Arrays are zero-indexed. An array of size 3 has indices 0, 1, and 2. Index 3 doesn't exist. Use .length to stay in bounds.

Forgetting break in switch

switch (value) {
    case 1:
        System.out.println("One");
        // Missing break! Falls through to case 2
    case 2:
        System.out.println("Two");
        break;
}

Without break, execution falls through to the next case. Use the modern switch expression (with ->) to avoid this entirely.

What to Learn Next

You've covered the fundamentals. Here's the roadmap:

  • Collections framework -- HashMap, HashSet, LinkedList, and when to use each
  • Exception handling -- try/catch/finally, creating custom exceptions
  • File I/O -- reading and writing files
  • Streams API -- functional-style operations on collections (filter, map, reduce)
  • Multithreading -- running code in parallel
  • Build tools -- Maven or Gradle for managing dependencies
  • Spring Boot -- if you're heading toward backend development
Java's verbosity is the price of clarity. Every type is declared, every exception is handled, every access level is specified. That explicitness makes large codebases maintainable. Once you internalize the patterns, you stop noticing the boilerplate and start appreciating the structure.

Explore more tutorials and guides at CodeUp.

Ad 728x90