티스토리 뷰

Java

6. 상속

kingsubin 2020. 12. 23. 11:27

목표

자바의 상속에 대해 학습하세요.

학습할 것 (필수)

  • 자바 상속의 특징
  • super 키워드
  • 메소드 오버라이딩
  • 다이나믹 메소드 디스패치 (Dynamic Method Dispatch)
  • 추상 클래스
  • final 키워드
  • Object 클래스

1. 자바 상속의 특징

Inheritance is an important pillar of OOP. It is the mechanism in java by which one class is allow to inherit the features(fileds and methods) of another class.

 

Important facts about inheritance in Java

Default superclass: Except Object class, which has no superclass, every class has one and only one direct superclass (single inheritance). In the absence of any other explicit superclass, every class is implicitly a subclass of Object class.

Superclass can only be one: A superclass can have any number of subclasses. But a subclass can have only one superclass. This is because Java does not support multiple inheritnace with classes. Although with interfaces, multiple inheritance is supported by java.

Inheriting Constructors: A subclass inherits all the members (fields, methods, and nested calsses) from its superclass. Constructors are not members, so they are not inherited by subclasses, but the constructor of the superclass can be invoked from the subclass.

Private member in inheritance: A subclass does not inherit the private members of its parent class. However, if superclass has public or protected methods(like getters and setters) for accessing its private fields, these can also be used by the subclass.


2. super 키워드

1. Use of super with variables: This scenario occurs when a derived class and base class has same data members.

In that case there is a possiblility of ambiguity for the JVM.

class Vehicle {
  int maxSpeed = 120;
}

class Car extends Vehicle {
  int maxSpeed = 200;
  void display() {
    System.out.print.ln(super.maxSpeed);
  }
}

// 120

 

2. Use of super with methods: This is used when we want to call parent class method. So whenever a parent and child class have same named methods then to resolve ambiguity we use super keyword.

class Person {
  void message() System.out.println("This is person class");
}

class Student extends Person {
  void message() System.out.println("This is student class");
  void display() {
    message();
    super.message();
  }
}

// This is student class
// This is person class

 

3. Use of super with constructors: super keyword can also be used to access the parent class constructor. One more important thing is that, "super" can call both parametric as well as non parametric constructors despending upon the situation. 

class Person {
  Person() System.out.println("Person class Constructor");
}

class Student extends Person {
  Student() {
    super();
    System.out.println("Student class Constructor");
  }
}

// Person class Constructor
// Student class Constructor

3. 메소드 오버라이딩

In any object-oriented programming language, Overriding is a feature that allows a subclass or child class to provide a specific implementation of a method that is already provided by one of its super-classes or parent class.

class Parent {
  void show() System.out.println("Parent's show()");
}

class Child extends Parent {
  @Override
  void show() System.out.println("Child's show()");
}

Parent.show(); // Parent's show()
Child.show(); // Child's show()

 

Rules for method overriding:

1. Overriding and Access-Modifiers: The access modifier for an voerriding method can allow more, but not less, access than the overriden method.

class Parent {
  // private methods are not overriden
  private void m1() System.out.println("parent m1");
  protected void m2() System.out.println("parent m2");
}

class Child extends Parent {
  // new m1() method
  // unique to Child class
  private void m1() System.out.println("child m1"); 
  
  // overriding method with more accessibilty
  @Override
  public void m2() System.out.println("child m2"); 
}

 

2. Final methods can not be overridden: If we don't want a method to be overriden, we declare it as final.  

class Parent {
  // Cant be overridden
  final void show() {}
}

class Child extends Parent {
  // This would produce error
  void show() {}
}
// error: show() in Child cannot override show() in Parent

 

3. Static methods can not be overridden (Method Overriding vs Method Hiding): When you define a static method with same signature as a static method in base class, it is known as method hiding.

class Parent {
  // static method in base calss which will be hidden in subclass
  static void m1() System.out.println("parent static m1()"); 
  void m2()  System.out.println("parent non-static(instance) m2()"); 
}

class Child extends Parent {
  // this method hides m1() in Parent
  static void m1() System.out.println("child static m1()");
  @Override
  public void m2() System.out.println("child non-static(instance) m2()"); 
}

Child.m1(); // parent static m1()
Child.m2(); // child non-static(instance) m2()

 

4. Private methods can not be overriden: Private methods cannot be overridden as they are bonded during compile time. Therefore we cant even override private methods in a subclass.

 

5. The overriding method must have same return type (or subtype): child’s return type should be sub-type of parent’s return type.

class A {}
class B extends A {}

class Base {
  A fun() {
    System.out.println("Base fun()");
    return new A();
  }
}

class Derived extends Base {
  B fun() {
    System.out.println("Derived fun()"); 
    return new B();
  }
}

public class Main { 
  public static void main(String args[]) { 
    Base base = new Base(); 
    base.fun(); // Base fun()
  
    Derived derived = new Derived(); 
    derived.fun(); // Derived fun()
  } 
} 

 

6. Invoking overridden method from sub-class: We can call parent class method in overriding method using super keyword.

class Parent {
  void show() System.out.println("Parent's show()"); 
}

class Child extends Parent {
  @Override
  void show() {
    super.show();
    System.out.println("Child's show()");   
  }
}

Child.show(); 
// Parent's show()
// Child's show()

 

7. Overriding and constructor: We can not override constructor as parent and child class can never have constructor with same name(Constructor name must always be same as Class name).

 

8. Overriding and Exception-Handling:

8-1) If the super-class overriden method does not throw an exception, subclass overriding method can only throws the unchecked exception, throwing checked exception will lead to compile-time error.

class Parent {
  void m1() System.out.println("From parent m1()"); 
  void m2()  System.out.println("From parent  m2()"); 
}

class Child extends Parent {
  @Override
  // no issue while throwing unchecked exception
  void m1() throws ArithmeticException {
    System.out.println("From child m1()"); 
  }
  
  @Override
  // compile-time error issue while throwin checked exception
  void m2() throws Exception {
    System.out.println("From child m2"); 
  }
}

// error: m2() in Child cannot override m2() in Parent
// overridden method does not throw Exception

8-2) If the super-class overridden method does throws an exception, subclass overriding method can only throw same, subclass exception. Throwing paretn exception in Exception hierarchy will lead to compile time error.

class Parent {
  void m1() throws RuntimeException {
    System.out.println("From parent m1()");
  }
}

class Child1 extends Parent {
  @Override
  // no issue while throwing same exception
  void m1() throws RuntimeExcpetion {
    System.out.println("From child1 m1()"); 
  }
}
class Child2 extends Parent {
  @Override
  // no issue while throwing subclass exception 
  void m1() throws RuntimeExcpetion {
    System.out.println("From child1 m1()"); 
  }
}
class Child3 extends Parent {
  @Override
  // no issue while not throwing any exception 
  void m1() throws RuntimeExcpetion {
    System.out.println("From child1 m1()"); 
  }
}
class Child4 extends Parent {
  @Override
  // compile-time error issue while throwing parent exception
  void m1() throws RuntimeExcpetion {
    System.out.println("From child1 m1()"); 
  }
}

// error: m1() in Child4 cannot override m1() in Parent
// overridden method does not throw Exception

 

9. Overriding and abstract method: Abstract methods in an interface or abstract class are meant to be overridden in derived concrete class otherwise a compile-time error will be thrown.

 

# Overriding vs Overloading:

1. Overloading is about same method have different signatures. Overriding is about same method, same signature but different classes connected through inheritance.

2. Overloading is an example of compiler-time polymorphism and overriding is an example of run time polymorphism.

https://www.geeksforgeeks.org/overriding-in-java/

 

Why Method Overriding?

Polymorphism is essential to object-oriented programming for one reason: it allows a general class to specify methods that will be common to all of its derivatives while allowing subclasses to define the specific implementation of some or all of those methods. Overridden methods are another way that Java implements the “one interface, multiple methods” aspect of polymorphism.

Overridden methods allow us to call methods of any of the derived classes without even knowing the type of derived class object.

 

When to apply Method Overriding ?

by combining inheritance with overridden methods, a superclass can define the general form of the methods that will be used by all of its subclasses.

class Employee {
  public static int base = 10000; 
  int salary() return base; 
}

class Manager extends Employee { 
  int salary() return base + 20000; 
} 
  
class Clerk extends Employee { 
  int salary() return base + 10000;
} 

class Main { 
  static void printSalary(Employee e) { 
    System.out.println(e.salary()); 
  } 
  
  public static void main(String[] args) { 
    Employee obj1 = new Manager(); 
    System.out.print("Manager's salary : "); 
    printSalary(obj1); // Manager's salary : 30000
  
    Employee obj2 = new Clerk(); 
    System.out.print("Clerk's salary : "); 
    printSalary(obj2); // Clerk's salary : 20000
  } 
} 

4. 다이나믹 메소드 디스패치 (Dynamic Method Dispatch)

Dynamic method dispatch is the mechanism by which a call to an overridden method is resolved at run time, rather than compile time.

 

- When an overridden method is called through a superclass reference, Java determines which version (superclass/subclass) of that method is to be executed based upon the type of the object being referred to at the time the call occurs. Thus, this determination is made at run time.

- At run-time, it depends on the type of the object being referred to (not the type of the reference variable) that determines which version of an overridden method will be executed.

- A superclass reference variable can refer to a subclass object. This is also known as upcasting. Java uses this fact to resolve calls to overridden methods at run time.

Therefore, if a superclass contains a method that is overridden by a subclass, then when different types of objects are referred to through a superclass reference variable, different versions of the method are executed.

class A {
  void m1() System.out.println("Inside A's m1 method"); 
}

class B extends A {
   void m1() System.out.println("Inside B's m1 method");
}

class C extends A {
  void m1() System.out.println("Inside C's m1 method"); 
}

class Dispatch {
  public static void main(String args[]) { 
    // object of type A 
    A a = new A(); 
  
    // object of type B 
    B b = new B(); 
  
    // object of type C 
    C c = new C(); 
  
    // obtain a reference of type A 
    A ref; 
          
    // ref refers to an A object 
    ref = a; 
  
    // calling A's version of m1() 
    ref.m1(); 
  
    // now ref refers to a B object 
    ref = b; 
  
    // calling B's version of m1() 
    ref.m1(); 
  
    // now ref refers to a C object 
    ref = c; 
  
    // calling C's version of m1() 
    ref.m1(); 
  } 
}

// Inside A's m1 method
// Inside B's m1 method
// Inside C's m1 method

 

Runtime Polymorphism with Data Members

In Java, we can override methods only, not the variables(data members), so runtime polymorphism cannot be achieved by data members.

class A {
  int x = 10;
}

class B extends A {
  int x = 20;
}

A a = new B(); // objecdt of type B
// Data member of class A will be accessed
a.x // 10

5. 추상 클래스

1. In Java, an instance of an abstract class cannot be created, we can have references of abstract class type though.

abstract class Base { 
  abstract void fun(); 
} 

class Derived extends Base { 
  void fun() System.out.println("Derived fun() called");
} 

class Main { 
  public static void main(String[] args) {  
    Base b = new Derived(); 
    b.fun();  
  } 
} 

// Derived fun() called

 

 

2. A constructor of abstract class is called when an instance of a inherited calss is created. 

abstract class Base {
  Base() System.out.println("Base Constructor Called");
  abstract void fun();
}

class Derived extends Base {
  Derived() System.out.println("Derived Constructor Called");
  void fun() System.out.println("Derived fun() called");
}

class Main {
  public static void main(String[] args) {
    Derived d = new Derived();
  }
}

// Base Constructor Called
// Derived Constructor Called

 

3. In Java, we can have an abstract class without any abstract method. This allows us to create classes that cannot be instantiated, but can only be inherited.

abstract class Base {
  void fun() System.out.println("Base fun() called");
}

class Derived extends Base {}

class Main {
  public static void main(String args[]) {  
    Derived d = new Derived(); 
    d.fun(); 
  } 
}

// Base fun() called

 

4. Abstract classes can also have final methods (methods that cannot be overridden).

// An abstract class with a final method 
abstract class Base { 
  final void fun() System.out.println("Derived fun() called");
} 
   
class Derived extends Base {} 
   
class Main { 
  public static void main(String args[]) {  
    Base b = new Derived(); 
    b.fun(); 
  } 
}  

// Derived fun() called

 

5. For any abstract java class we are not allowed to create an object i.e, for abstract calss instanitiation is not possible.

abstract class Test { 
  public static void main(String[] args) { 
    // Try to create an object 
    Test t = new Test(); 
  } 
}  

// Compile time error. Test is abstract; 
// cannot be instantiated Test t=new Test();

6. final 키워드

final keyword is used in different contexts. First of all, final is a non-access modifier applicable only to a variable, a method or a class. 

https://www.geeksforgeeks.org/final-keyword-java/

 

Final variables

When a variable is declared with final keyword, its value can't be modified, essentially, a constant. This also means that you must initialize a final variable. 

It is good practice to represent final variables in all uppercase, using underscore to separate words.

// a final variable
final int THRESHOLD = 5;
// a blank final variable
final int THRESHOLD;
// a final static variable PI
static final double PI = 3.141592653589793;
// a  blank final static  variable
static final double PI;

 

Initializing a final variable:

We must initialize a final variable, otherwise compiler will throw compile-time error. A final variable can only be initialized once, either via an initializer or an assignment statement.

1. You can initialize a final variable when it is declared.

2. A blank final variable can be initialized inside instance-initializer block or inside constructor. If you have more than one constructor in your class then it must be initialized in all of them, otherwise compile time error will be thrown.

3. A blank final static variable can be initiailized inside static block.

class Gfg { 
  // a final variable direct initialize 
  final int THRESHOLD = 5; 
      
  // a blank final variable 
  final int CAPACITY; 
      
  // another blank final variable 
  final int  MINIMUM; 
      
  // a final static variable PI direct initialize 
  static final double PI = 3.141592653589793; 
      
  // a blank final static variable 
  static final double EULERCONSTANT; 
      
  // instance initializer block for  
  // initializing CAPACITY 
  { 
    CAPACITY = 25; 
  } 
      
  // static initializer block for  
  // initializing EULERCONSTANT 
  static{ 
    EULERCONSTANT = 2.3; 
  } 
      
  // constructor for initializing MINIMUM 
  // Note that if there are more than one 
  // constructor, you must initialize MINIMUM 
  // in them also 
  public GFG() { 
    MINIMUM = -1; 
  }      
} 

 

When to use a final variable:

Final variables must be used only for the values that we want to remain constant throughout the execution of program.

 

Reference final varaible:

When a final variable is a reference to an object, the this final variable is called reference final variable.

As you know that a final variable cannot be re-assign. but in case of a reference final variable, internal state of the object pointed by that reference variable can be changed. Note that this is not re-assingning.

class Gfg { 
  public static void main(String[] args) { 
  // a final reference variable sb 
  final StringBuilder sb = new StringBuilder("Geeks"); 
          
  System.out.println(sb); 
          
  // changing internal state of object 
  // reference by final reference variable sb 
  sb.append("ForGeeks"); 
          
  System.out.println(sb); 
  }     
} 

// Geeks
// GeeksForGeeks

 

Final classes

When a class is declared with final keyword, it is called a final class. A final class cannot be extended(inherited).

1. One is definitely to prevent inheritance, as final classes cannot be extended. For example, all Wrapper Classes like Integer, Float etc. are final classe. We can not extend them.

final class A {
  // methods and fields
}

// The following class is illegal.
class B extends A { 
  // COMPILE-ERROR! Can't subclass A
}

2. The other use of final with classes is to create an immutable class like the predefined String class.

You can not make a class immutable without making it final.

 

Final methods

When a method is declared with final keyword, it is called a final method. A final method cannot be overridden.

class A {
  final void m1() System.out.println("This is a final method.");
}

class B extends A {
  void m1() { 
    // COMPILE-ERROR! Can't override.
    System.out.println("Illegal!");
  }
}

7. Object 클래스

Object class is present in java.lang package. Every class in Java is directly or indirectly derived from the Object class.

If a class does not extend any other class then it is direct child class of Object and if extends other class then it is an indirectly derived. Therefore the Obejct class methods are available to all Java classes. Hence Obejct class acts as a root of inheritance hierarchy in any Java Program. 

 

Using Object class methods

1. toString(): toString() provides String representation of an Object and used to convert an object to String. 

// Default behavior of toString() is to print class name, then
// @, then unsigned hexadecimal representation of the hash code
// of the object
public String toString() {
  return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

Note: Whenever we try to print any Object reference, then internally toString() method is called.

Student s = new Student();

// Below two statements are equivalent
System.out.println(s);
System.out.println(s.toString());

 

2. hashCode(): For every object, JVM generates a unique number which is hashcode. It returns distinct integers for distinct objects. The hashCode() method is native because in Java it is impossible to find address of an object, so it uses native languages like C/C++ to find address of the object.

 

Use of hashCode() method: Returns a hash value that is used to search object in a collection. JVM uses hashcode method while saving objects into hashing related data structures like HashSet, HashMap, Hashtable etc. Th main advantage of saving objects based on hash code is that searching becomes easy.

Note:Override of hashCode() methods needs to be done such that for every object we generate a unique number.

ublic class Student { 
  static int last_roll = 100;  
  int roll_no; 
  
  // Constructor 
  Student() { 
    roll_no = last_roll; 
    last_roll++; 
  } 
  
  // Overriding hashCode() 
  @Override
  public int hashCode() { 
    return roll_no; 
  } 
  
  public static void main(String args[]) { 
    Student s = new Student(); 
  
    System.out.println(s); 
  } 
} 

// Student@64
// Note that 4*16^0 + 6*16^1 = 100

 

3. equals(Obejct obj): Compares the given object to "this" object (the object on which the method is called).

It gives a generic way to compare objects for equality. It is recommended to override equals(Object obj) method to get our own equality condition on Objects.

 

4. getClass(): Returns the class object of "this" object and used to get actual runtime class of the object.

public class Test { 
  public static void main(String[] args) { 
    Object obj = new String("GeeksForGeeks"); 
    Class c = obj.getClass(); 
    System.out.println("Class of Object obj is : " + c.getName()); 
  } 
} 

// Class of Object obj is : java.lang.String

 

5. finalize(): This method is called just before an object is garbage collected. It is called by th Garbage Collector on an object when garbage collector determines that there are no more references to the object.

public class Test { 
  public static void main(String[] args) { 
    Test t = new Test(); 
    System.out.println(t.hashCode()); 
  
    t = null; 
  
    // calling garbage collector  
    System.gc(); 
  
    System.out.println("end"); 
  } 
  
  @Override
  protected void finalize() { 
    System.out.println("finalize method called"); 
  } 
} 

// 366712642
// finalize method called
// end

 

6. clone(): It returns a new object that is exactly the same as this object. 

 

7. wait(), notify(), notifyAll() are related to Concurrency.

 

 


※ 출처

www.geeksforgeeks.org/java/?ref=leftbar

 

※ 스터디

github.com/whiteship/live-study/issues/6

'Java' 카테고리의 다른 글

8. 인터페이스  (0) 2021.01.09
7. 패키지  (0) 2021.01.02
5. 클래스  (0) 2020.12.19
4. 제어문  (0) 2020.12.04
3. 연산자  (0) 2020.11.28