Home [Java 프로그래밍] 4강 - 클래스와 상속
Post
Cancel

[Java 프로그래밍] 4강 - 클래스와 상속

💡해당 게시글은 방송통신대학교 김희천 교수님의 'Java 프로그래밍' 강의를 개인 공부 목적으로 메모하였습니다.



학습 개요


  • 클래스를 정의하고, 정의된 클래스를 사용하는 것에 관해 살펴봄
  • static필드와 메소드 및 final필드와 메소드의 의미 학습
  • 객체의 생성과 초기화 및 메소드의 오버로딩 학습
  • 기존 클래스를 이용해 새로운 클래스 정의하기 위한 클래스 상속 개념과 오버라이딩 이해



학습 목표


  • 클래스 정의 문법 숙지할 수 있음
  • staticfinal키워드의 의미와 사용법을 설명할 수 있음
  • 메소드 오버로딩과 오버라이딩 구분하고 적용 가능함
  • 상속 개념 활용해 클래스 정의 가능함



강의록


클래스 정의와 사용

static필드

  • static필드

    Untitled

    1
    2
    3
    4
    5
    
      class Circle {
          int x, y;
          int radius;
          static int count;
      }
    
  • 정적 필드 or 클래스 변수

    • 클래스의 모든 객체가 공유하는 데이터
      • 객체의 생성이 없어도 항상 사용 가능
      • 어떤 객체도 값을 변경할 수 있음
    • 사용 방법
      • 클래스이름.정적필드
        • 객체변수.정적필드도 가능

static메소드

  • static메소드

    1
    2
    3
    4
    5
    
      class Math {
          public static double sqrt(double a) {
            
          }
      }
    
  • 정적 메소드 or 클래스 메소드
    • non-static 메소드와 달리 객체와 무관하게 호출되고 실행
      • 메소드의 몸체에서 this사용 불가
      • static메소드는 static필드를 다룸
  • 호출 방법
    • 클래스이름.정적메소드()
    • 객체와 무관하므로 클래스 이름으로 호출 가능
    • 객체변수.정적메소드()도 가능
  • 정적 메소드를 호출하는 예시

    1
    
      Math.sqrt(2.0);
    
    1
    
      Integer.parseInt("102");
    

final필드와 final메소드

  • final필드
    • 상수 데이터
    • 선언할 때 초기 값 지정 필요
    • static과 자주 함께 사용

      1
      
        final static double PI = 3.141592;
      
  • final메소드
    • 자식 클래스로 상속은 가능하나 자식 클래스에서 재정의(오버라이딩) 할 수 없는 메소드

필드의 초기화

  • 객체 생성 시 데이터 필드에 초기 값 지정하는 것
    • 데이터 필드는 자동으로 초기 값(0,false 또는 null)이 주어질 수 있음
    • 클래스 변수(static필드)는 프로그램 시작 시에 초기화 됨
  • 객체 초기화를 위해 초기화 블록을 사용할 수 있음
    • 초기 값 지정을 위한 코드로, 클래스 정의에서 메소드 바깥의 임의 위치에 들어갈 수 있음
    • static필드는 static초기화 블록을 사용
  • 필드 초기화 방법의 실행 순서

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
      class IniTest {
          static int nStatic = 0;
          int nValue  = 1; // 초기값 선언하지 않을경우 0으로 자동 초기화
            
          { // 초기화 블록
              nValue  = 2; // 초기값 변경
          }
            
          static {
              nStatic = 1; // 클래스 변수 초기화 블록
          }
            
          public IniTest() { // 생성자에서 초기화
              nValue = 3; // 초기값 변경
          }
      }
    
    1. static필드의 선언문에서 초기화
    2. static초기화 블록 실행
    3. non-static 필드의 선언문에서 초기화
    4. non-static 초기화 블록 실행
    5. 생성자 실행
  • 필드 초기화 코드

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
      import java.awt.Point;
        
      class Rectangle {
          public int width = 0; // non-static 변수 초기화
          public int height = 0;
          public Point origin; // Point 클래스 : 좌표상의 어떤 위치를 나타내는데 사용하는 클래스
    
          public Rectangle() {
              origin = new Point(0,0);
          }
    
          public Rectangle(Point p, int w, int h) { // 생성자를 사용해 데이터 필드 초기화
              origin = p;
              width = w;
              height = h;
          }
      }
    
    1
    2
    3
    4
    5
    6
    
      public class Main {
          public static void main(String args[]) {
              Point originOne = new Point(23, 94); // Point 클래스에 x와 y좌표값을 저장하기 위한 멤버 변수 존재
              Rectangle rectOne = new Rectangle(originOne, 100, 200);
          }
      }
    

    Untitled

메소드 오버로딩

1
2
3
4
5
6
7
8
9
class PrintStream {
    public void println() {

    }

    public void println(String x) {

    }
}
  • 매개 변수의 개수나 매개 변수의 자료형이 다르면 같은 이름의 메소드를 한 클래스에서 여러 개 정의 가능
    • 매개 변수의 개수와 자료형이 일치하면 중복 정의 불가
    • 리턴형과 접근 제어자는 구분의 기준이 되지 못함
    • 메소드 시그니처를 가지고 메소드 구분
      • 메소드의 이름, 매개 변수 리스트
  • 메소드를 호출할 때, 가장 가까운 매개 변수 목록을 가진 메소드 호출
    • ex)

      1
      2
      3
      4
      5
      
        System.out.println(); // 인자 없음
        System.out.println("문자열"); // 인자는 String
        System.out.println(241); // 인자는 int
        System.out.println(34.5); // 인자는 double
        mc.add(10, 10.5); // add(int, int)와 add(double, double) → 자동 형변환 후 add(double, double) 호출
      

클래스와 객체의 사용 예

1
2
3
4
5
6
7
8
9
10
11
12
13
class Cylinder {
    private Circle c; // 원 (Circle 클래스 변수 정의)
    private int h; // 높이

    public Cylinder(Circle a, int b) {
        c = a;
        h = b;
    }

    public double getVolume() {
        return c.getArea() * h;
    }
}
1
2
3
4
5
6
7
8
9
10
11
class Circle {
    double radius;

    public Circle(double a) {
        radius = a;
    }

    public double getArea() {
        return radius * radius * 3.14;
    }
}
1
2
3
4
5
6
7
public class Main {
    public static void main(String args[]) {
        Circle c = new Circle(3); // Circle 객체 생성
        Cylinder cy = new Cylinder(c, 10);
        System.out.println(cy.getVolume());
    }
}

상속

클래스의 재사용

  • 합성

    1
    2
    3
    
      class Line { 
          Point begin, end; 
      }
    
    • 기존 클래스를 새로운 클래스 정의에서 데이터 필드의 자료형으로 사용
    • has-a 관계
  • 상속

    • 기존 클래스(부모)를 사용해 새로운 클래스(자식)를 정의
    • 코드의 중복 작성을 줄이고 프로그램의 확장성 증가
    • 기존 클래스를 확장 or 특화하는 것
    • 자식 is -a 부모의 관계

클래스의 상속

  • 상속은 부모 클래스와 자식 클래스 간의 관계
    • 자식 클래스가 부모 클래스의 필드와 메소드를 상속 받음
    • 기존 클래스를 상속 받을 때 키워드 extends사용
      • ex)

        1
        2
        3
        4
        
          class Manager extends Employee {
              // 자식 클래스(새로운 클래스) Manager 
              // 부모 클래스(기존 존재 클래스) Employee
          }
        
    • 자식 클래스에서 상속받은 메소드를 오버라이딩(재정의) 할 수 있음
  • 클래스의 상속단일 상속만 가능
    • 인터페이스 상속의 경우는 다중 상속 가능
  • 클래스의 상속

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
      class CSuper {
          private int f1;
          public int f2;
            
          public void setPrivate(int i) { 
              f1 = i; 
          }
            
          public void setPublic(int i) { 
          f2 = i; 
          }
            
          private void mPrivate() {
              f1 = 30; 
          }
      }
        
      class CSub extends CSuper {
          private int f3;
          public int f4;
      }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
      public class Main {
          public static void main(String args[]) {
              CSub sub  = new CSub();
    
              // sub.f1 = 40; // 오류 발생
              sub.f2 = 50;
              // sub.f3 = 60; // 오류 발생
              sub.f4 = 70;
    
              sub.setPrivate(10);
              sub.setPublic(20);
              // sub.mPrivate(); // 오류 발생
          }
      }
    

메소드 오버라이딩

  • 부모로부터 상속 받은 메소드의 몸체를 자식 클래스에서 재정의하는 것
  • 부모와 자식에서 같은 이름의 메소드가 다른 기능 수행하게 됨
  • 오버라이딩 방법
    • 메소드의 이름, 인자의 개수와 자료형, 반환형이 같은 메소드를 정의
    • 단, 반환형은 서브 타입(상속 관계에서 자식 클래스)도 가능함
    • 접근 제어자의 가시성(접근 범위)은 같거나 커져야 함
      • protected인 경우 protected또는 public
      • public인 경우 public만 가능
  • 메소드 오버라이딩

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
      class Shape { // 부모 클래스
          public double getArea(double h, double w) { 
              return h * w; 
          }
      }
        
      class Triangle extends Shape { // 자식 클래스
          public double getArea(double h, double w) { 
              return h * w * 0.5; 
          }
      } // getArea 메소드 재정의 -> 메소드 오버라이딩
    
    1
    2
    3
    4
    5
    6
    
      public class Main {
          public static void main(String args[]) {
              Shape t = new Triangle(); // Shape 자식 클래스 Triangle 객체 생성
              System.out.println(t.getArea(3.0, 4.0)); // 3.0 * 4.0 * 0.5 = 6.0 출력
          }
      }
    

this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Circle {
    static double PI = 3.14;
    double radius = 2.0; // 반지름

    public double getArea() {
        return this.radius * this.radius * PI; // c1.radius 호출 시 this값은 c1값을 가지게 됨
    }
    public void display() {
        System.out.println("반지름:" + this.radius + " 면적:" + this.getArea());
    }

    public static void main(String args[]) {
        Circle c1 = new Circle();
        Circle c2 = new Circle();
        c2.radius = 5.0; // c2의 반지름을 5.0으로 변경

        c1.display();
        c2.display();
    }
}
  • 메소드 호출 시, 숨은 인자로 this가 메소드에 전달됨
    • this현재 객체에 대한 참조 값을 가지고 있음
    • c1.display()c2.display()의 결과가 다른 이유임
  • 인스턴스 메소드나 생성자에서 필드를 참고하거나 메소드를 호출할 때 사용 가능(생략 가능)
    • this.필드 이름
    • this.메소드 이름(인자)

super

1
2
3
4
5
6
7
class CSuper {
    public double x = 0;

    public void f() {
        System.out.println("CSuper");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
class CSub extends CSuper {
    public double x = 1;

    public void f() {
        System.out.println("CSub");
    }

    public void print() {
        System.out.println("super.x");
        super.f();
    }
}
  • this와 같으나 자료형이 부모 클래스 유형임
  • 자식 클래스의 인스턴스 메소드나 생성자에서 사용됨
    • this와 마찬가지로 static메소드에서 사용할 수 없음
  • 부모 클래스에서 오버로딩 당한 메소드를 호출하거나 상속되었으나 감춰진 필드에 접근할 때 필요함
    • super.필드 이름
    • super.메소드 이름(인자)
  • thissuper의 사용

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
      class CSuper { // 부모 클래스
          public double x;
      }
        
      class CSub extends CSuper { // 자식 클래스
          public double x;
    
          public CSub(double new_x) {
              this.x = new_x; // 자식 클래스의 x 필드 값 지정
              super.x = new_x * 10; // 부모 클래스의 x 필드 값 지정
          }
            
          public double getSuper() {
              return super.x; // 상속받은 x 리턴
          }
    
          public double getSub() {
              return this.x; // 재정의한 x 리턴
          }
      }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
      public class Main {
          public static void main(String args[]) {
              CSub sub = new CSub(10.0);
              System.out.println(sub.x); // 10.0
            
              System.out.println(sub.getSuper()); // 100.0 출력
            
              System.out.println(sub.getSub()); // 10.0 출력
          }
      }
    

상속과 생성자

  • this()
    • 같은 클래스의 다른 생성자를 호출하는 것
  • super()
    • 자식 클래스의 생성자에서 부모 클래스의 생성자를 호출하는 것
      • 상속받은 데이터 필드를 초기화하기 위해 사용
    • 자식 클래스의 생성자 몸체에서 부모 클래스 생성자의 명시적 호출이 없다면, 인자가 없는 생성자인 super()가 자동 호출됨
  • this()super()생성자 몸체의 맨 앞에 위치해야 함

supersuper()의 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class Cyliner extends Circle {
    private double height;
    static final double PI = 3.14;

    public Cylinder() {
            super(); // 부모클래스의 생성자 초기화
            height = 1.0;
    }

    public Cylinder(double radius, double h) {
            super(radius); // double값을 가지는 인자로 부모클래스의 생성자 초기화
            this.height = h;
    }

    public double getHeight() {
        return height;
    }

    public void setHeight(double h) {
        this.height = h;
    }

    /**
     * 원기둥의 겉넓이 계산
     * 2 * 3.14 * 반지름 * 높이 + 2 * 원의 면적
     */
    public double getArea() { // Cylinder의 getArea() 메소드
        return 2 * PI * getRadius() * height + 2 * super.getArea(); // 상속받은 this.getRadius() 호출
    }

    /**
     * 원기둥의 부피 계산
     * 원의 면적 * 높이
     */
    public double getVolume() {
        return super.getArea() * height; // Circle의 getArea() 메소드 호출
    }

    public String toString() {
        return "Cylinder of radius = " + getRadius() + " height = " + height;
    }
}
1
2
3
4
5
public class Main {
    public static void main(String args[]) {
        System.out.println(new Cylinder(3, 10).getVolume());
    }
}

메소드 체이닝

  • 하나의 명령문에서 동일 객체에 대해 연속적으로 메소드 호출을 하는 프로그래밍 기법
    • 메소드 체이닝에 사용되는 메소드는 현재 객체의 참조 값(this)을 반환해야 함
  • 메소드 체이닝은 프로그램의 가독성을 향상 시키고, 코딩을 단순화 함
  • ex)

    1
    
      p.setName("홍길동").setAge(30).setAddress("서울");
    
  • 메소드 체이닝의 사용

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
      class People {
          String name;
          int age;
          String address;
            
          public String getName() {
              return name;
          }
            
          public void setName(String name) {
              this.name = name;
          }
      }
        
      public class Main {
          public static void main(String[] args) {
              People p = new People();
                
              p.setName("홍길동");
              p.setAge(30);
              p.setAddress("서울");
          }
      }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
      class People {
          String name;
          int age;
          String address;
            
          public String getName() {
              return name;
          }
            
          public People setName(String name) {
              this.name = name;
              return this;
          }
      }
    
    1
    2
    3
    4
    5
    6
    
      public class Main {
          public static void main(String[] args) {
              People p = new People();
              p.setName("홍길동").setAge(30).setAddress("서울");
          }
      }
    



학습 정리


  • 클래스를 정의할 때, 클래스의 모든 객체가 공유하는 데이터는 static데이터 필드로 정의함
  • 클래스 정의에 있는 데이터 필드의 선언문, 초기화 블록, 생성자를 통해서 객체 생성 시 필요한 데이터 필드의 초기값 지정 가능
  • 메소드 오버로딩이란 한 클래스에서 이름이 같은 여러 메소드가 존재하는 상황을 말함
    • 이때 오버로딩된 메소드끼리는 매개 변수 목록이 달라 구별될 수 있음
  • 부모로부터 상속받은 메소드의 몸체를 자식 클래스에서 다시 정의하는 것을 메소드 오버라이딩이라고 함
  • 인스턴스 메소드와 생성자에서 숨은 인자인 this, super사용할 수 있음
  • this()는 같은 클래스에 있는 다른 생성자를, super()는 자식 클래스 생성자에서 부모 클래스 생성자를 호출하기 위한 것



연습 문제


  1. 키워드 final에 관한 설명으로 틀린 것은?

    a. final클래스의 객체를 생성할 수 없다.

    • 키워드 final
      • final클래스의 자식 클래스를 정의할 수 없음
      • 부모 클래스의 final메소드는 자식 클래스로 상속될 때 재정의될 수 없음
      • final변수는 상수로 사용됨
  2. Circle 클래스에서 원주율 PI를 상수로 선언하기 위해 밑줄 친 ㉠에 들어갈 적당한 내용은 무엇인가?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
     class Circle {
     	// ㉠
     	private double radius; 
     	public Circle(double radius) {
     		// ㉡
     	}
     	public double getArea() {
     		return radius * radius * PI;
     	}
     }
    

    a. static final double PI = 3.14;

  3. Circle 클래스의 생성자에서 밑줄 친 ㉡에 들어갈 적당한 내용을 작성하시오

    a. this.radius = radius;

[데이터베이스 시스템] 3강 - 관계형 모델

[파이썬 프로그래밍 기초] 4강 - 파이썬 시작하기