Why doesn’t Java allow overriding of static methods

https://stackoverflow.com/questions/2223386/why-doesnt-java-allow-overriding-of-static-methods

Yes. Practically Java allows overriding static method, and No theoretically if you Override a static method in Java then it will compile and run smoothly but it will lose Polymorphism which is the basic property of Java. You will Read Everywhere that it is not possible to try yourself compiling and running. you will get your answer. e.g. If you Have Class Animal and a static method eat() and you Override that static method in its Subclass lets called it Dog. Then when wherever you Assign a Dog object to an Animal Reference and call eat() according to Java Dog’s eat() should have been called but in static Overriding Animals’ eat() will Be Called.

class Animal {
public static void eat() {
System.out.println(“Animal Eating”);
}
}

class Dog extends Animal{
public static void eat() {
System.out.println(“Dog Eating”);
}
}

class Test {
public static void main(String args[]) {
Animal obj= new Dog();//Dog object in animal
obj.eat(); //should call dog’s eat but it didn’t
}
}

Output Animal Eating

According to Polymorphism Principle of Java, the Output Should be Dog Eating.
But the result was different because to support Polymorphism Java uses Late Binding that means methods are called only at the run-time but not in the case of static methods. In static methods compiler calls methods at the compile time rather than the run-time, so we get methods according to the reference and not according to the object a reference a containing that’s why You can say Practically it supports static overring but theoretically, it doesn’t.

Abstract Class & Abstract Method & Polymorphism

Abstract Class (추상 클래스)는 하나 이상의 abstract method(추상메소드)를 가지거나, 추상메소드가 없지만 abstract으로 선언한 클래스. 추상클래스는 객체를 생성할 수 없다.

Abstract Method (추상 메소드)는 선언은 되어 있으나 구현 내용이 없는 메소드이다.

추상클래스를 상속받은 하위클래스는 반드시 추상 메소드를 재정의(method override) 구현해야한다.

Polymorphism (다형성)은 프로그램 실행(run-time execution)시 형(type)이 변화되는 객체.

다형성에 따라서, 클래스 상속관계(Inheritance)에서 메소드 재정의(Method overriding)된 것이 동적 바인딩(dynamic binding)으로 호출되었을 시, 해당 객체에 맞는 재정의된 메소드가 호출되는 것이다.

Shape 추상클래스 has-a ShapeType, ShapeColor, ShapeRect

Shape 추상클래스를 상속받은 Triangle, Rectangle, Square, Circle 클래스는 추상메소드인 area()를 반드시 구현해야한다.

https://www.cs.usfca.edu/~wolber/SoftwareDev/OO/abstractInterfacesIntro.htm

Person Student Class Instance vs Static Method Overriding

PersonStudent-MethodOverriding2

/// Person
class Person {
    private static int count = 0; // static (class) field
    protected String name; // instance field
    protected int age; // instance field

    public Person() {
        //System.out.println("Person Constructor"); // this("", 0); error: call to this must be first statemenht in constructor
        this("", 0);
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        count++;
    }

    public static void printCount() { // static (class) method
        System.out.println("Person Count: " + count);
    }

    public static int getCount() {  // static (class) method
        return count; 
    }

    public String toString() { // Object.toString() overriding
        return "Person Name: " + name + " Age: " + age;
    }

    public void print() { // instance method
        System.out.println("Person Name: " + name + " Age: " + age);
    }
}

/// Student
class Student extends Person {
    private static int scount = 0; // static (class) variables
    protected int id;

    public Student() {
        this("", 0, 5208);
    }

    public Student(String name, int age, int id) {
        super(name, age);
        this.id = id;
        scount++;
    }

    public static void printCount() { // static (class) method overriding
        System.out.println("Student Count: " + scount);
    }

    public String toString() { // Object.toString() overriding
        return "Student Name: " + name + " Age: " + age + " ID: " + id;
    }

    public void print() { // Person.print() method overriding
        System.out.println("Student Name: " + name + " Age: " + age + " ID: " + id);
    }
}

class PersonStudentTest {
    public static void main(String[] args) {
        Person p1 = new Student("K", 20, 2018); // upcasting 1번째 Student 객체 생성
        p1.print(); // dynamic binding Student Name: K Age: 20 ID: 2018
        p1.name = "JAVA";
        p1.age = 1;
        //p1.id = 2017; // cannnot call id because p1 is Person type
        System.out.println(p1); // dynamic binding p1.toString() Student Name: JAVA Age: 1 ID: 1208
        p1.printCount(); // p1 is Person, Person.printCount() Person.count=1
        Person.printCount(); // Person.printCount() Person.count=1
        Student.printCount(); // Student.printCount() Student.scount=1
        System.out.println();

        Student s1 = (Student)p1; // downcasting
        s1.name = "JAVA2"; 
        s1.age = 2;
        s1.id = 2017; // can call id, because s1 is Student type
        s1.print(); // Student Name: JAVA2 Age: 2 ID: 2017
        s1.printCount(); // s1 is Student, Student.printCount() scount=1
        Person.printCount(); // Person.printCount() count=1
        Student.printCount(); // Student.printCount() scount=1
        System.out.println();

        Student s2 = new Student("S", 30, 1207); // 2번째 Student 객체 생성
        s2.print(); // Student Name: S Age: 30 ID: 1217
        s2.printCount(); // s2 is Student, Student.printCount() scount=2
        Person.printCount(); // Person.printCount() count=2
        Student.printCount(); // Student.printCount() count=2
        System.out.println();

        Person p2 = new Person("Park", 1); // 1번째 Person 객체 생성
        System.out.println(p2); // Person Name: Park Age: 1
        p2.printCount(); // p2 is Person, Person.printCount() count=3
        Person.printCount(); // Person.printCount() count=3
        Student.printCount(); // Student.printCount() scount=2
        System.out.println();

        Person p3 = new Person(); // 2번째 Person 객체 생성
        System.out.println(p3); // Person Name: Age: 0
        p3.printCount(); // p3 is Person, Person.printCount() count=4
        Person.printCount(); // Person.printCount() count=4
        Student.printCount(); // Student.printCount() scount=2
        System.out.println();

        System.out.println("Number of Person: " + Person.getCount()); // Person.getCount() count=4
    }
}

BankAccount CheckingAccount SavingAccount Class

BankAccount-InstanceStatic – instance vs static member field & method
BankAccount


public class BankAccount {
    protected String name; // instance field
    protected double balance; // instance field 
    protected static double interestRate; // static field

    public BankAccount() { // default constructor
        this(1000, "JAVA17");
    }

    public BankAccount(String name, double balance) {
        this.name = name;
        this.balance = balance;
        BankAccount.interestRate = 1.0;
    }

    public void deposit(double amount) { // instance method
        balance += amount;
    }

    public void withdraw(double amount) { // instance method
        balance -= amount;
    }

    public void transfer(double amount, BankAccount other) { // instance method
        withdraw(amount);
        other.deposit(amount);
    }

    public double getBalance() { // instance method
        return balance + balance * interestRate * 0.01;
    }

    public void print() { // instance method
        System.out.println(name + " balance=" + getBalance());
    }

    @Override // Object.toString() method override
    public String toString() { // instance method
        return name + " balance=" + getBalance();
    }

    public static void setInterestRate(double interestRate) { // static method
        BankAccount.interestRate = interestRate;
    }
}

public class CheckingAccount extends BankAccount {
    private double overdraftLimit; 

    public CheckingAccount(String name, double balance, double overdraftLimit) { 
        super(name, balance);
        interestRate = 2.0;
        setOverdraftLimit(overdraftLimit); 
    }
	
    public void setOverdraftLimit(double overdraftLimit) {  
        this.overdraftLimit = overdraftLimit;  
    }  

    public double getOverdraftLimit() {  
        return overdraftLimit;  
    }

    @Override // BankAccount.withdraw(double amount) method override 
    public void withdraw(double amount) {  
        if(getBalance() - overdraftLimit < amount)
            System.out.println("CheckingAccount::withdraw Cannot be withdrawn due to overdraftLimit"); 
        else
            super.withdraw(amount);  		
    }

    @Override // BankAccount.print() method override 
    public void print() {
        System.out.println("CheckingAccount: " + name + " balance=" + getBalance());
    }
}

public class SavingAccount extends BankAccount {
    public SavingAccount(String name, double balance) { 
        super(name, balance);
        interestRate = 5.0;
    }

    @Override // BankAccount.withdraw(double amount) method override
    public void withdraw(double amount)  {
        if (getBalance() < amount)  
            System.out.println("SavingAccount::withdraw Amount is larger than current balance.");  
        else
            super.withdraw(amount);
    }

    @Override // BankAccount.print() method override 
    public void print() {
        System.out.println("SavingAccount: " + name + " balance=" + getBalance());
    }
}

public class BankAccountTest {
    public static void main(String[] args) {
        BankAccount b1 = new BankAccount("P", 0);
        b1.deposit(3000);
        b1.withdraw(1000);
        b1.print(); // P balance=2020.0

        BankAccount b2 = new BankAccount("K", 0);
        b1.print(); // K balance=0.0

        System.out.println("transfer 1000 money from b1 to b2");
        b1.transfer(1000, b2);
        System.out.println("b1: " + b1); // b1: P balance=1010.0
        System.out.println("b2: " + b2); // b2: K balance=1010.0

        BankAccount c1 = new CheckingAccount("Checking1", 1000, 500);
        BankAccount c2 = new CheckingAccount("Checking2", 1000, 500);
        c1.withdraw(700); // CheckingAccount::withdraw Cannot be withdrawn due to overdraftLimit
        c2.withdraw(800); // CheckingAccount::withdraw Cannot be withdrawn due to overdraftLimit
        c1.print(); // CheckingAccount: Checking1 balance=1020.0
        c2.print(); // CheckingAccount: Checking1 balance=1020.0

        BankAccount s1 = new SavingAccount("Saving1", 1000);
        s1.deposit(5000);
        s1.withdraw(1000);
        s1.print(); // SavingAccount: Saving1 balance=5250.0

        BankAccount s2 = new SavingAccount("Saving2", 1000);
        s2.withdraw(2000); // SavingAccount::withdraw Amount is larger than current balance.
        s2.print(); // SavingAccount: Saving2 balance=1050.0
    }
}

Car Sedan Class

CarSedan-PublicProtectedPrivate

public class Car {
    // member field
    private boolean disel; // private은 파생 클래스에서 사용하지 못함
    protected boolean gasoline; // protected은 파생 클래스에서 사용 가능하나 외부(다른 패키지)에서는 호출하지 못함
    protected int wheel = 4;

    // constructor
    protected Car() { 
        disel = true; 
        gasoline = true; 
    }
    protected Car(int wheel) { 
        this.wheel = wheel; 
        disel = false; 
        gasoline = false; 
    }

    // method
    protected void move() {
        if (disel) System.out.println("Disel Car");
        System.out.println("Move wheel=" + wheel);
    }
}

public class Sedan extends Car {
    // member field
    private boolean gasoline; // 파생 클래스에서 기반클래스에 멤버필드명이 같다면 default는 자신의 멤버부터 호출, 즉, this.gasoline 부터 사용

    // constructor
    public Sedan() { // 내부적으로 super() 호출, 즉 super.gasoline=true 
        gasoline = false; 
    } 
    public Sedan(int wheel) { // super(int wheel)를 호출, 즉 super.gasoline=false (protected 생성자는 파생클래스에서 호출 가능)
        super(wheel); 
        gasoline = true; 
    } 
    // method
    public void sedanMove() {
        // base의 gasoline과 this의 gasoline을 구분해야하는 경우
        if (super.gasoline) System.out.print("Gasoline Car ");
        if (this.gasoline) System.out.print("Gasoline SedanCar ");
        System.out.println("move wheel=" + wheel);
    }

    public static void main(String[] args) {
        Car myCar = new Car(); // protected 생성자 같은 패키지 내에 사용가능.
        myCar.move(); // protected 메소드 호출 가능
        System.out.println("myCar wheel=" + myCar.wheel); // protected 필드 호출 가능

        // 바퀴 4개 디젤 자동차
        Sedan myCar1 = new Sedan(); // public 생성자 호출 가능
        myCar1.move(); // protected 메소드 호출 가능
        myCar1.sedanMove(); // public 메소드 호출가능

        // 바퀴 6개 가솔린 자동차
        Sedan myCar2 = new Sedan(6); // public 생성자 호출 가능
        myCar2.move(); // protected 메소드 호출 가능
        myCar2.sedanMove(); // public 메소드 호출가능
    }
}

Static vs Instance Initializer Block

Static Initializer Block

  • class 로딩 시 호출
  • instance variable이나 method에 접근 못함
  • static variable 초기화에 사용
public class StaticIntializerBlockTest {
    private static int id = 5;
    static {
        if (id <10) id = 20;
        else id = 100;
    }

    public static int getId() {
        return id;
    }

    public static void print() {
        System.out.println("StaticIntializerBlockTest.id=" + getId());
    }
}

public class StaticInstanceInitializerBlockTest {

    public static void main(String[] args) {
        StaticIntializerBlockTest.print();
    }
}

StaticIntializerBlockTest.id=20 // static block 이 호출되면서 20으로 셋팅

Instance Initializer Block

  • 객체 생성시 호출
  • super 생성자 이후에 실행하고, 생성자보다 먼저 실행
  • instance variable이나 method에 접근 가능
  • 모든 생성자의 공통 부분을 instance initializer block에 넣어줌
class InstanceInitializerBlockSuper {
    public InstanceInitializerBlockSuper() {
        System.out.println("InstanceInitializerBlockSuper");
    }
}

public class InstanceInitializerBlockTest extends InstanceInitializerBlockSuper {
    private int id = 5;
    {
        if (id <10) id = 20;
        else id = 100;
    }

    public InstanceInitializerBlockTest() {
        System.out.println("InstanceInitializerBlockTest.id=" + this.id);
    }

    public InstanceInitializerBlockTest(int id) {
        System.out.println(“InstanceInitializerBlockTest.id=" + this.id);
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public void print() {
        System.out.println("StaticIntializerBlockTest.id=" + getId());
    }
}

public class StaticInstanceInitializerBlockTest {
    public static void main(String[] args) {
        InstanceInitializerBlockTest i = new InstanceInitializerBlockTest();
        i.print();
        i = new InstanceInitializerBlockTest(30);
        i.print();
    }
}

InstanceInitializerBlockSuper // super 생성자 이후에 실행
InstanceInitializerBlockTest.id=20 // instance block 호출되면서 20으로 셋팅
id=20
InstanceInitializerBlockSuper // super 생성자 이후에 실행
InstanceInitializerBlockTest.id=20 // instance block 호출되면서 20으로 셋팅된후 this.id = id를 통해서 30으로 셋팅
id=30

https://stackoverflow.com/questions/12550135/static-block-vs-initializer-block-in-java

singleton design pattern vs static class

공용 데이터를 저장하여 사용하고자 할 때, singleton design pattern이나 inner static class를 사용한다.

싱글톤 패턴 (Singleton design pattern) -싱글톤 패턴이란 single instance object(해당 클래스의 인스턴스 하나)가 만들어지고, 어디서든지 그 싱글톤에 접근할 수 있도록 하기 위한 패턴

public final class SiteStructureSingletonPattern {
    private Object[] data = new Object[10];

    private static SiteStructureSingletonPattern instance = new SiteStructureSingletonPattern(); // private static member인 instance
    public static SiteStructureSingletonPattern getInstance() { // getInstance() 메소드를 사용하여 instance 객체 사용
        return instance;
    }
    private SiteStructureSingletonPattern() {
        System.out.println("SiteStructureSingletonPattern constructor");
        for(int i=0; i<10; i++) {
            data[i] = i+1;
        }
    }
    public void print() {
        System.out.print("SiteStructureSingletonPattern data=");
        for(int i=0; i<10; i++) {
            System.out.print(" " + data[i]);
        }
        System.out.println();
    }
}
public class SiteStructureStaticClass {
    private Object[] data = new Object[10];

    private static class SiteStructureStatic { // inner static class
        private static final SiteStructureStaticClass instance = new SiteStructureStaticClass();
    }
    public static SiteStructureStaticClass getInstance() {
        return SiteStructureStatic.instance;
    }
    private SiteStructureStaticClass() {
        System.out.println("SiteStructureStaticClass constructor");
        for(int i=0; i<10; i++) {
            data[i] = i+1;
        }
    }

    public void print() {
        System.out.print("SiteStructureStaticClass data=");
        for(int i=0; i<10; i++) {
            System.out.print(" " + data[i]);
        }
        System.out.println();
    }
}
public class StaticClassSingletonPatternTest {

    public static void main(String[] args) {
        SiteStructureSingletonPattern instance = SiteStructureSingletonPattern.getInstance();
        instance.print();
        instance = SiteStructureSingletonPattern.getInstance();
        instance.print();

        SiteStructureStaticClass instance2 = SiteStructureStaticClass.getInstance();
        instance2.print();
        instance2 = SiteStructureStaticClass.getInstance();
        instance2.print();
    }
}