FirstHack Learn
Log in Sign up free
Lessons in this course 0/6 All courses Java Fundamentals

CSE

Progress0 / 6 lessons
  1. 1. JVM, JDK and JRE explained properly
  2. 2. Classes, objects and the main method
  3. 3. Primitives, objects and autoboxing
  4. 4. String immutability and why == fails
  5. 5. Collections: ArrayList and HashMap
  6. 6. Exceptions and try-with-resources

Courses › Java Fundamentals

Classes, objects and the main method

What each word in public static void main means, and how objects come to life.

11 min read · Lesson 2 of 6 · Free

Everything lives in a class

Java has no free-standing functions. Every method belongs to a class. That is why even a one-line program needs a class around it.

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

Reading the main signature word by word

Students memorise this line. Understand it instead — each word is examinable.

public — visible from anywhere. The JVM is outside your class and must be able to call main, so it cannot be private.

static — belongs to the class, not to an object. When the JVM starts, no object of your class exists yet, so main must be callable without one.

void — returns nothing. The exit status goes through System.exit(int) instead.

main — the exact name the JVM looks for. Main with a capital M will compile and then fail at run time with "Main method not found".

String[] args — command line arguments. Running java Hello one two fills args with {"one", "two"}.

Java
public class Args {
    public static void main(String[] args) {
        System.out.println("Got " + args.length + " arguments");
        for (String a : args) {
            System.out.println(a);
        }
    }
}

java Args red green prints Got 2 arguments, then red, then green. Note that unlike C, args[0] is the first argument, not the program name.

A class is a blueprint, an object is a thing

Java
public class Student {
    private String name;
    private int roll;
    private double cgpa;

    public Student(String name, int roll, double cgpa) {
        this.name = name;
        this.roll = roll;
        this.cgpa = cgpa;
    }

    public String getName() { return name; }
    public double getCgpa() { return cgpa; }

    public boolean isEligible() {
        return cgpa >= 7.0;
    }

    public void printCard() {
        System.out.println(roll + " " + name + " CGPA " + cgpa);
    }

    public static void main(String[] args) {
        Student a = new Student("Adnan", 21, 8.4);
        Student b = new Student("Priya", 22, 6.5);

        a.printCard();                 // 21 Adnan CGPA 8.4
        b.printCard();                 // 22 Priya CGPA 6.5
        System.out.println(a.isEligible());   // true
        System.out.println(b.isEligible());   // false
    }
}

Student describes what a student has and can do. a and b are two separate objects, each with its own copy of name, roll and cgpa.

new Student(...) does three things: allocates memory on the heap, runs the constructor, and returns a reference to the object.

this, and why it is needed here

Inside the constructor, the parameter is called name and the field is also called name. The parameter wins, so plain name = name assigns the parameter to itself and the field stays null. this.name says "the field of this object". Writing this. is how you break the tie.

⚠️

Forgetting this. in a constructor is a classic silent bug. It compiles, produces no warning by default, and your object comes out with null and 0 in every field. If your printout shows null 0 0.0, look at the constructor first.

Constructors

Java
public class Rectangle {
    private double length;
    private double breadth;

    public Rectangle() {            // no-argument constructor
        this(1.0, 1.0);             // calls the other one
    }

    public Rectangle(double side) { // a square
        this(side, side);
    }

    public Rectangle(double length, double breadth) {
        this.length = length;
        this.breadth = breadth;
    }

    public double area() { return length * breadth; }

    public static void main(String[] args) {
        System.out.println(new Rectangle().area());        // 1.0
        System.out.println(new Rectangle(3).area());        // 9.0
        System.out.println(new Rectangle(3, 4).area());     // 12.0
    }
}

A constructor has the class name, no return type, and runs once when the object is created. You can have several, distinguished by their parameters — that is constructor overloading. this(...) calls another constructor of the same class and must be the first statement.

If you write no constructor at all, Java gives you a free empty one. The moment you write any constructor, that free one disappears — which is why adding a parameterised constructor can suddenly break new Rectangle() elsewhere in your code.

static versus instance

Java
public class Counter {
    private static int totalCreated = 0;   // one copy, shared by the class
    private int id;                        // one copy per object

    public Counter() {
        totalCreated++;
        this.id = totalCreated;
    }

    public int getId() { return id; }

    public static int getTotalCreated() { return totalCreated; }

    public static void main(String[] args) {
        Counter a = new Counter();
        Counter b = new Counter();
        Counter c = new Counter();

        System.out.println(a.getId());              // 1
        System.out.println(c.getId());              // 3
        System.out.println(Counter.getTotalCreated());  // 3
    }
}

A static field exists once, no matter how many objects you make. An instance field exists once per object. A static method is called on the class (Counter.getTotalCreated()), an instance method on an object (a.getId()).

⚠️

A static method cannot use this or touch instance fields, because there is no object. This is the source of "non-static variable cannot be referenced from a static context" — the error every beginner meets when they declare a variable in the class body and use it inside main. Either make the variable static, or create an object and use it through that.

Encapsulation, and why fields are private

Java
public class BankAccount {
    private double balance;

    public BankAccount(double opening) {
        if (opening < 0) throw new IllegalArgumentException("negative opening");
        this.balance = opening;
    }

    public double getBalance() { return balance; }

    public void deposit(double amount) {
        if (amount <= 0) throw new IllegalArgumentException("deposit must be positive");
        balance += amount;
    }

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

    public static void main(String[] args) {
        BankAccount acc = new BankAccount(1000);
        acc.deposit(500);
        System.out.println(acc.withdraw(2000));    // false, not enough
        System.out.println(acc.getBalance());      // 1500.0
    }
}

If balance were public, any code anywhere could write acc.balance = -99999; and no rule would stop it. Private fields with public methods mean every change goes through code that can check it. That is encapsulation, and it is not a formality — it is the only reason you can trust the state of an object you did not write.

💡

Default habit: fields private, methods public only when something outside genuinely needs them. Add a getter when it is needed, not automatically for every field. A class with a getter and setter for everything has the same problem as public fields, with more typing.