Overriding :

A obj = new A("asasdfad");
System.out.println(obj); // → com.hitarth.oop.A@5f184fc6 // some random stuff is printed out
// we don't want this hash value to be printed but want the name and number to be printed out.
// for that we use @Override

class A {  
    final int num = 10;  
    String name;  
  
    A(String name)  
    {        
	    this.name = name;  
    }  
}

Internally, when we try to print obj the .toString method is called upon the object.
Pasted image 20240104210917.png
If the .toString method is NOT present in the object (see class A above, there is no .toString() method present there) then the default .toString method is called which looks like this :
Pasted image 20240104211211.png

Packages :

Packages are basically containers for classes. It is used to keep the class names compartmentalized.
In simple terms, packages are just folders.
com.kunal means com/kunal i.e. kunal folder lives inside com folder

Pasted image 20240104215417.png

  • Here we don't have to use import as both Human and Main classes are in the same folder

Static :

Properties which are not really related to a specific object but are common to all the object of the class, we declare those as static.

public class Human {
    int age;
    String name;
    int salary;
    boolean married;
    long population=0; // population is not dependent on the object, for 2 different humans the population will remain same so we declare it as static.
	//constructor
    public Human(int age, String name, int salary, boolean married) {
        this.age = age;
        this.name = name;
        this.salary = salary;
        this.married = married;
        this.population +=1;
    }
}

Now if we perform this :

		Human kunal = new Human(22, "Kunal Kushwaha", 10000, false);
        Human raka = new Human(22, "raka", 10000, false);
        System.out.println(kunal.population); // → 1
        System.out.println(raka.population); // → 1

we get the output as 1 and 1, to correct this behavior we'll use static
Using static : static long population=0;

public class Human {
    int age;
    String name;
    int salary;
    boolean married;
    static long population=0; // population is not dependent on the object, for 2 different humans the population will remain same so we declare it as static.
	//constructor
    public Human(int age, String name, int salary, boolean married) {
        this.age = age;
        this.name = name;
        this.salary = salary;
        this.married = married;
        this.population +=1;
    }
}
  • Now we'll get 2 and 2 as the output

Now since static is not really related to the object, so why are we using this keyword in this.population +=1; as this refers to the object. Since this population variable is common to all the human beings, therefore we can use Class_name instead of this keyword here.
So the code now becomes :

package com.hitarth.oop;

public class Human {
    int age;
    String name;
    int salary;
    boolean married;
    static long population=0; // population is not dependent on the object, for 2 different humans the population will remain same so we declare it as static.

    public Human(int age, String name, int salary, boolean married) {
        this.age = age;
        this.name = name;
        this.salary = salary;
        this.married = married;
        Human.population +=1; // removed the "this" keyword
    }
}
  • Using both Human and this keyword will give same results, the only difference is that with this keywords then it'll first look for whether population exists in the object or not, since it doesn't so then it'll look for it in the class Human and then update it. With Human. keyword it'll directly look in the class for the required static variable.
  • Pasted image 20240104223207.png
    Notice that the population variable does not exist in the objects, it only resides in the class.
    Also, don't use object name when using static variables, use class names instead, like this :
		Human kunal = new Human(22, "Kunal Kushwaha", 10000, false);
        Human raka = new Human(22, "raka", 10000, false);
        System.out.println(Human.population); // → 2
        System.out.println(Human.population); // → 2

NOTE:

when a member is declared as static it can be accessed before any of the object of the class is being created and without referencing to that object (no kunal.population) needed.

So, even if we haven't created any object, we can still access the population variable using Human.population;

We can create static methods as well, one of the common static methods is psvm : public static void main.

  • psvm is static so that we can use this method/function without actually creating an object of that class first
  • main (not Main) is the very first thing that is run in the program
public class Main {
    public static void main(String[] args) {
    }
}
  • In order to run anything that is inside a class (here public class Main {), you have to create an object of that class, but how can you run the program to create an object if main i.e. public static void main(String[] args) { is the very first thing running.
  • Hence, you should be able to run the main method without creating any object of the class Main , this is why we use static in psvm.
package com.hitarth.oop;

public class Main {

    public static void main(String[] args) {
        greeting();
    }
    
    // NOT using static below will give us this error :
    // "Non-static method 'greeting()' cannot be referenced from a static context"
    // we also know that something which is static belongs to an object
    static void greeting() {
        System.out.println("Hello World");
    }
}
package com.hitarth.oop;

public class Main {

    public static void main(String[] args) {
    fun(); // no error shown  
	fun2(); // gives an error : Non-static method 'fun2()' cannot be referenced from a static context  
	Main funn = new Main();  
	funn.fun2(); // no error shown
    
    }
    
    // this is not dependent on objects 
    static void fun(){
        greeting(); // this will give an ERROR
        // you can't use this because it requires an instance
        // but the function you are using it in does not depend on instances
        //in simple terms fun() does not depend on instances so how can you have something that depends on instances inside of fun() ? You can't.

	// you can't access non static stuff without referencing their instances in a static context
	// hence, here we are referencing it : 
	Main obj = new Main();
	obj.greeting(); // no this won't give any errors.
	
    }

	void fun2() {
		greeting(); // here we won't get any errors cuz we know that directly or indirectly fun2() will get called from `main` so we know that object for `fun2()` will be created. i.e why we won't have to manually create an object for it.
	}
    
    // we know that something which is not static belongs to an object
    void greeting() {
	    System.out.println("hello");
    }
}

Initialization of Static Variables

  • As soon as a class is loaded, all the static elements are loaded first.
package com.hitarth.oop;

// this is a demo to show initialisation of static variables
public class StaticBlock {
    static int a =4;
    static int b;

    // static block: will get executed exactly once when the class is loaded
    static {
        System.out.println("I'm in a static block");
        b = a*5;
    }

    public static void main(String[] args) {
        StaticBlock obj = new StaticBlock();
        System.out.println(StaticBlock.a + " " + StaticBlock.b);
    }
}
  • Output :
I'm in a static block
4 20
package com.hitarth.oop;

// this is a demo to show initialisation of static variables
public class StaticBlock {
    static int a =4;
    static int b;

    // static block: will get executed exactly once when the class is loaded
    static {
        System.out.println("I'm in a static block"); // → I'm in a static block
        b = a*5;
    }

    public static void main(String[] args) {
        StaticBlock obj = new StaticBlock();
        System.out.println(StaticBlock.a + " " + StaticBlock.b); // → 4 20

        StaticBlock.b +=3;

        StaticBlock obj2 = new StaticBlock();
        System.out.println(b); // → 23 // so when we created a new object it did not run the static block again, hence the static things are only run once when the first object is created. Even if we had not created objects "obj" and "obj2" we'd still be able to perform the same calculations.
    }
}

Inner Classes :

Class inside a class.
NOTE : Outside classes (The classes which are not inside any classes, for e.g. InnerClasses class in the code block below) can't be static.

  • Therefore, outside classes can't be static, only inner classes can be static.
  • This is because the outside class is itself not dependent on any other class, so how can you put it as static.
  • Static methods are resolved during compile time, whereas objects are resolved during run time.
package com.hitarth.oop;

/* ------------------------------------------------------ */

// since this class is an outside class it needs not be a static class.
class TestOutside {
    static String text;

    public TestOutside(String text) {
        this.text = text;
    }
}

/* ------------------------------------------------------ */

public class InnerClasses {

    // this "Test" class is inside a class so, it can be static.
    // with static, this class is not dependent on the class outside, it does require an object of the container class to be created
    static class Test {
        String name;

        public Test(String name) {
            this.name = name;
        }
    }

    public static void main(String[] args) {

        //Test
        Test a = new Test("some name");
        Test b = new Test("changed name");
        System.out.println(a.name); // → some name
        System.out.println(b.name); // → changed name

        //TestOutside
        TestOutside random = new TestOutside("alkasdd");
        TestOutside random_2 = new TestOutside("jhgfa");
        System.out.println(random.text); // → jhgfa
        System.out.println(random_2.text); // → jhgfa
        // hence the same static variable was changed
    }
}

Singleton Class

  • When you want ONLY 1 instance of a class to be created, you'll use singleton classes.
    Pasted image 20240105170905.png

Above code in text format : (Both files are in the same folder)

Main.java :

package com.hitarth.oop;

public class Main {

    public static void main(String[] args) {
        Singleton obj = Singleton.getInstance();
        Singleton obj2 = Singleton.getInstance();
        Singleton obj3 = Singleton.getInstance();
        // so, every time someone asks for a new instance we are giving it the same instance again and again, hence no more than 1 instance is being created.
        // therefore all 3 ref variables are pointing to just one object
        // since the constructor is private therefore we can't call a constructor here either, hence we can't create more than 1 objects
    }
}

Singleton.java :

package com.hitarth.oop;

public class Singleton {

    // constructor
    private Singleton() {
        // private class is basically a class only accessible to this file
        // whatever thing is private will only run in this class
    }

    // since this instance is not going to be dependent on the class "SingletonClass", we aren't going to create an object of that outside class right, it is not allowed, so we are only going to be returning the "instance" object created below, so we can put it as static.

    private static Singleton instance;

    public static Singleton getInstance() {
        // first, check whether 1 obj only is created or not
        // instance == null will be true if no object had been created, i.e. reference variable will be null and will not be assigned to any pointer.
        if (instance == null) {
            // if no object had been created, we'll make one
            instance = new Singleton(); // constructor can be called here since it is in the same file
        }
        return instance;
    }

}