ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [자바의 정석] 상속 & 오버라이딩 & package & import
    JAVA 2022. 10. 17. 18:39
    728x90
    반응형

    상속 (Inheritance)

    기존의 클래스를 재사용해서 새로운 클래스를 작성하는 것을 말하며 이렇게 작성된 클래스와 기존 클래스는 서로 부모와 자식으로 관계를 맺어지게 됩니다.

    • 자식은 부모의 모든 멤버를 상속받습니다. (단, 생성자 및 초기화 블록은 제외됩니다.)
    • 자식의 멤버개수는 부모보다 적을 수 없습니다.
    • 단일 상속을 원칙으로 한다.
    class Point{
        int x;
        int y;
    }
    
    class Point3D extends Point{
        int z;
    }
    
    Point3D p = new Point3D();
    p.x = 1; // OK
    p.y = 2; // OK
    p.z = 3; // OK

    상속 관계

    공통 부분은 부모에서 관리하고 개별 부분은 자식에서 관리하게 만들며 부모의 변경은 자식에게 영향을 미치지만 자식의 변경은 부모에게 아무런 영향을 미치지 않는다.

    포함 (composite)

    한 클래스의 멤버 변수로 다른 클래스를 선언하는 것을 말하며 작은 단위의 클래스를 먼저 만들고 이 들을 멤버 변수로 조합하여 하나의 커다란 클래스를 만든다.

    • 여러 개의 클래스를 상속받고 싶다면 비중이 높은 클래스를 상속관계로 나머지는 포함관계로 작성한다.
    class Point{
        int x;
        int y;
    }
    
    class Circle{
        Point p = new Point();
        int r;
    }
    
    Circle c = new Circle();
    c.p.x = 1; // OK
    c.p.y = 2; // OK
    c.r = 3;   // OK

    클래스 간의 관계 결정하는 방법

    객체지향을 제대로 활용하기 위해서는 가능한 많은 관계를 맺어주어 재사용성을 높이고 관리하기 쉽게 만들어야 합니다.

    상속 관계는 "~은 ~이다"
    포함 관계는 "~은 ~을 가지고 있다."

    예제로 확인하기

    1. Circle은 Shape이다. (상속 관계)
    2. Circle은 Point를 가지고 있다. (포함 관계)
    class Shape{
        String color = "blue";
        void draw(){}
    }
    
    class Point{
        int x;
        int y;
        
        Point(){this(0,0);}
        Point(int x, int y){
            this.x = x;
            this.y = y;
        }
    }
    
    class Circle extends Shape{ // 상속관계
        Point center; // 포함관계
        int r;
        
        Circle(){
            this(new Point(0,0), 100);
        }
        Circle(Point center, int r){
            this.center = center;
            this.r = r;
        }
    }
    
    Circle c = new Circle(new Point(100,50), 30);
    System.out.println(c.center.x); // 100
    System.out.println(c.center.y); // 50
    System.out.println(c.r); // 30
    
    class Triangle extends Shape{
        Point[] p;
        
        Triangle(Point[] p){
            this.p = p;
        }
        
        Triangle(Point p1, Point p2, Point p3){
            p = new Point[]{p1, p2, p3};
        }
    }
    
    Point[] p = {
        new Point(100,100),
        new Point(140,50),
        new Point(200,180),
    };
    Triangle t = new Triangle(p);
    System.out.println(t.p[0].x); // 100
    System.out.println(t.p[1]); // 배열 주소 반환
    System.out.println(t.p[2].y); // 180

    Object 클래스 - 모든 클래스의 최고 조상

    조상이 없는 클래스는 자동적으로 Object클래스를 상속받게 되며, 모든 클래스는 Object클래스에서 정의된 11개의 메서드를 상속받게 됩니다.

    • toString(), equals(Object obj), hashCode() 등등
      • equals(Object obj)는 문자열을 비교할 때 사용됩니다.

    오버라이딩 (Overriding)

    오버 라이딩은 부모 클래스로부터 상속받은 메서드의 내용을 상속받은 자식 클래스에 맞게 변경하는 것을 오버 라이딩이라고 합니다.

    • 오버 라이딩과 오버 로딩은 아무런 연관이 없다.
    오버로딩(overloading) : 기존에 업는 새로운 메서드를 정의하는 것(new)
    오버라이딩(overriding) : 상속받은 메서드의 내용을 변경하는 것(change)
    class Point{
        int x;
        int y;
        String getLocation(){
            return "x : " + x + ", y : " + y;
        }
    }
    
    class Point3D extends Point{
        int z;
        String getLocation(){ // Overriding
            return "x : " + x + ", y : " + y + ", z : " + z;
        }
    }

    오버 라이딩의 조건

    오버 라이딩을 하기 위해서는 몇 가지 지켜야 할 조건들이 있다.

    1. 선언부가 같아야 한다.
    2. 접근 제어자를 좁은 범위로 변경할 수 없다.
    3. 조상 클래스의 메서드보다 많은 수의 예외를 선언할 수 없다.
    class Parent{
        // 예외를 2개 선언, 접근 제어자 default
        void parentMethod() throws IOException, SQLException{
            // ....
        }
    }
    
    class Child1 extends Parent{
        // 예외를 1개 선언, 접근제어자 protected
        protected void parentMethod() throws IOException{
            // ....
        }
    }
    
    class Child2 extends Parent{
        // 예외를 2개 선언, 접근제어자 public
        public void parentMethod() throws IOException, SQLException{
            // ....
        }
    }

    super( )  - 부모의 생성자 호출

    부모로부터 상속받은 자식 클래스의 인스턴스를 생성하면 자식 멤버와 부모의 멤버가 합쳐져서 하나의 인스턴스가 생성되는데 자식의 멤버는 자식 클래스에서 생성자를 통해 초기화가 가능하지만 부모의 멤버는 초기화를 할 수 없기 때문에 사용된다.

    • 자식 클래스의 생성자 첫 문장에서 부모의 생성자를 호출해야 한다.
    • 상속 시 부모의 생성자 및 초기화 블록은 상속되지 않는다.
    • Object 클래스를 제외한 모든 클래스의 생성자 첫 줄에는 같은 클래스의 다른 생성자 또는 부모의 생성자를 꼭 호출해야 한다.
      • 그렇지 않을 경우 컴파일러가 자동적으로 "super();"를 생성자의 첫 줄에 삽입한다.
    class Point{
        int x;
        int y;
        Point(){this(0,0);}
        Point(int x, int y){
            this.x = x;
            this.y = y;
        }
    }
    
    // 위에 코드에서 생략된 생성자 및 최고 조상을 모두 입력하면 아래와 같다.
    class Point extends Object{
        int x;
        int y;
        Point(){this(0,0);}
        Point(int x, int y){
            super();
            this.x = x;
            this.y = y;
        }
    }

    예제를 통해 super( )를 이해하기

    class Point{
        int x;
        int y;
        
        Point(int x, int y){
            this.x = x;
            this.y = y;
        }
        
        String getLocation(){
            return "x : " + x + ", y : " + y;
        }
    }
    
    class Point3D extends Point{
        int z;
        
        Point3D(int x, int y, int z){
            super(x, y); // 부모의 생성자 Point(int x, int y) 호출
            // 만약 생략했다면 어떻게 될까?
            // 정답은 Error!!!!
            // 이유는 컴파일러가 super()를 삽입하는데 부모의 생성자에는 Point()가 없기 때문이다.
            this.z = z;
        }
        
        String getLocation(){ // Overriding
            return "x : " + x + ", y : " + y + ", z : " + z;
        }
    }

    package와 import

    package는 서로 관련된 클래스와 인터페이스들의 묶음을 의미하며 클래스가 물리적으로 클래스 파일(*. class)인 것처럼 패키지는 물리적으로 폴더라고 생각하면 편하다.

    • 패키지는 서브 패키지를 가질 수 있으며 "."으로 구분한다.

    클래스의 실제 이름은 패키지 명이 포함되어 있다.

    • 예를 들어 String 클래스의 실제 이름은 java.lang.String이다.

    rt.jar는 Java API의 기본 클래스들을 압축한 파일이다.

    • JDK 설치 경로\jre\lib에 위치해 있다.

    package의 선언

    패키지는 소스파일에 첫 번째 문장으로 단 한번 선언하며 하나의 소스파일에 두 개 이상의 클래스가 포함된 경우 모두 같은 패키지에 속하게 됩니다.

    • 만약 패키지를 선언하지 않았다면 자동적으로 클래스들은 Unnamed Package에 속하게 된다.

    import 사용

    사용할 클래스가 속한 패키지를 지정하는 데 사용되며 import 문을 사용하면 클래스를 사용할 때 패키지 명을 생략할 수 있다.

    • 단, java.lang 패키지의 클래스는 import 하지 않고도 패키지 명을 생략할 수 있다.
      • 예를 들어 String, Object, System, Thread 등등
    class ImportTest{
        java.util.Date today = new java.util.Date();
    }
    
    // 위에 코드를 import 문을 사용해서 패키지 명을 생략할 수 있다.
    import java.util.Date
    
    class ImportTest{
        Date today = new Date(); // import문을 사용해서 패키지명 생략
        
        void langTest(){
            java.lang.System.out.println("hello java");
            System.out.println("HELLO JAVA"); // java.lang패키지는 import하지 않아도 생략 가능
        }
    }

    import 문의 선언

    import문은 패키지 선언과 클래스 선언의 사이에 선언을 하게 되며 import문은 컴파일 시에 처리되므로 몇 개를 선언하더라도 프로그램 성능에는 아무런 영향을 미치지 않는다.

    1. package 문 선언
    2. import 문 선언
    3. 클래스 선언

    import 문을 선언하는 방법에는 2가지가 있다.

    1. import.packageName.className;
    2. import.packageName.*;

    이름이 같은 클래스가 속한 패키지가 있을 경우에는 클래스 앞에 패키지명을 붙여줘야 한다.

    import java.sql.*;
    import java.util.*;
    
    public class ImportTest{
        public static void main(String[] args){
            // java.util은 생략이 가능하지만 sql 패키지에도 Date 클래스가 있기 때문에
            // 헷갈리는 것을 방지하기 위해 패키지명을 작성해주는게 좋다.
            java.util.Date today = new java.util.Date();
        }
    }
    728x90
    반응형
Designed by Tistory.