V
Vel·ToolKit
Simple · Fast · Ready to use
EN
Chapter 6 of 20

Classes & Objects

Constructors, fields, encapsulation, static, final, nested classes

Class Declaration

"A class is a blueprint for objects": it describes which fields (state) and methods (behavior) objects have. new ClassName() creates an instance, and each instance has its own field values.

// User.java
public class User {
    // fields (instance variables)
    String name;
    int age;

    // methods
    void hello() {
        System.out.println("Hi, I'm " + name);
    }

    public static void main(String[] args) {
        User u = new User();   // create an object
        u.name = "Alice";       // assign fields
        u.age = 30;
        u.hello();
    }
}

Constructors and this

A constructor is a special method called automatically when an object is created: same name as the class, no return type. this refers to the current object, often used to disambiguate when a field and a parameter share a name.

// User.java
public class User {
    String name;
    int age;

    public User(String name, int age) {
        this.name = name;   // this.name is the field, name is the parameter
        this.age = age;
    }

    public User(String name) {
        this(name, 0);       // call another constructor (must be the first line)
    }

    public static void main(String[] args) {
        User a = new User("Alice", 30);
        User b = new User("Bob");
        System.out.println(a.name + " " + a.age);
        System.out.println(b.name + " " + b.age);
    }
}

Field Initialization Order

Fields are initialized in declaration order: zero values first, then literal initializers and initializer blocks, finally the constructor. static fields are initialized once at class loading.

// InitOrder.java
public class InitOrder {
    int a = init("field a", 1);

    {
        // instance initializer block (runs on every new)
        System.out.println("instance block");
    }

    static int s = init("static s", 99);

    static {
        // static initializer block (runs once at class loading)
        System.out.println("static block");
    }

    public InitOrder() {
        System.out.println("constructor");
    }

    static int init(String label, int v) {
        System.out.println("init " + label);
        return v;
    }

    public static void main(String[] args) {
        System.out.println("--- new 1 ---");
        new InitOrder();
        System.out.println("--- new 2 ---");
        new InitOrder();
    }
}

Encapsulation: private Fields + getter/setter

"Java favors "private fields + public methods" encapsulation: external access to object state must go through methods, making it easy to add validation, locking, and logging, and leaving room to change the implementation.

// Account.java
public class Account {
    private String owner;
    private long balance; // unit: cents

    public Account(String owner, long initial) {
        if (initial < 0) throw new IllegalArgumentException("initial < 0");
        this.owner = owner;
        this.balance = initial;
    }

    public String getOwner() { return owner; }
    public long getBalance() { return balance; }

    public void deposit(long amount) {
        if (amount <= 0) throw new IllegalArgumentException("amount <= 0");
        this.balance += amount;
    }

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

    public static void main(String[] args) {
        Account a = new Account("Alice", 10000);
        a.deposit(5000);
        System.out.println(a.getBalance()); // 15000
        System.out.println(a.withdraw(20000)); // false
        System.out.println(a.getBalance()); // 15000
    }
}

static Fields and Methods

Fields and methods marked static belong to the class, not an instance; all objects share one copy. Access via ClassName.member; a static method cannot directly access instance fields. Common for utility methods, constants, and counters.

// Counter.java
public class Counter {
    static int totalCount = 0;     // shared by all instances
    int instanceId;

    public Counter() {
        totalCount++;
        this.instanceId = totalCount;
    }

    static int getTotal() {        // static method
        return totalCount;
    }

    public static void main(String[] args) {
        new Counter();
        new Counter();
        new Counter();
        System.out.println(Counter.getTotal());      // 3
        System.out.println(Counter.totalCount);      // 3
    }
}

final Fields (Immutable)

A final field can be assigned only once (in the constructor or a literal initializer). Common for building "immutable objects" — thread-safe and safe as a Map key.

// Point.java
public final class Point {  // the class itself can also be final, forbidding subclassing
    private final double x;
    private final double y;

    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public double getX() { return x; }
    public double getY() { return y; }

    public Point translate(double dx, double dy) {
        return new Point(x + dx, y + dy);   // doesn't modify itself, returns a new object
    }

    @Override
    public String toString() {
        return "(" + x + ", " + y + ")";
    }

    public static void main(String[] args) {
        Point p = new Point(1, 2);
        Point q = p.translate(3, 4);
        System.out.println(p);  // (1.0, 2.0)
        System.out.println(q);  // (4.0, 6.0)
    }
}

Nested and Anonymous Classes

Classes can be nested. Three common kinds: static nested class (an independent namespace), inner class (holds a reference to the outer class), anonymous class (a one-off interface/class implementation). Java 14+ also has record (see Chapter 10) replacing many anonymous-class uses.

// Nested.java
public class Nested {

    // static nested class: only a namespace relation to the outer class
    static class Pair {
        final String key;
        final int value;
        Pair(String k, int v) { this.key = k; this.value = v; }
    }

    // inner class: each instance holds a reference to the outer Nested instance
    class Inner {
        void show() {
            System.out.println("outer count = " + count);
        }
    }

    int count = 10;

    public static void main(String[] args) {
        // use the static nested class
        Pair p = new Pair("x", 1);
        System.out.println(p.key + "=" + p.value);

        // use the inner class
        Nested outer = new Nested();
        Inner inner = outer.new Inner();
        inner.show();

        // anonymous class: a one-off Runnable implementation
        Runnable r = new Runnable() {
            @Override public void run() {
                System.out.println("running");
            }
        };
        r.run();
    }
}