[Java] 클래스와 상속, 추상 클래스

Yunhye Park·2024년 1월 27일
0
post-thumbnail
post-custom-banner

클래스

  • 자바에서 클래스는 사용자가 정의하는 자료형 혹은 설계도, 틀이라고 볼 수 있다.
  • 이렇게 틀을 통해서 생성된 실체가 '객체', 또 다른 말로 '인스턴스'이다.
  • 클래스에서 객체를 만든다 => 인스턴스화 한다.

예를 들어, '사람'이라는 클래스를 정의했다고 하자.

여기엔 사람이라면 갖고 있을 공통 속성(ex. 먹고 자고 쉬는 행위)이 존재하고, 이와 동시에 사람1과 사람2를 구분 지을 수 있는 특성(ex. 생김새)도 있다. 그렇기에 하나의 클래스로도 여러 인스턴스 생성이 가능하다.

관련 용어

  • 멤버(Member) : 클래스의 구성요소

    • 필드(field) : 변수 - 클래스 내에서 값을 저장
    private String name;
    private int age;
    • 메서드(method) : 동작 - 객체 내부 함수, 호출하여 실행
    public void eat() {
          System.out.println("밥을 먹는다.");
    }
  • 생성자(Constructor) :

    • 생성된 객체(인스턴스)를 초기화
    • 객체 생성 시 자동 호출
    • 클래스 내부에서 클래스와 동일한 이름으로 메서드 설정
     class Person {
    	public Person (String name, int age) {
      	this.name = name;
          this.age = age;
      }
    }

클래스 예제

📢 가로(width), 세로(height)를 받아 직사각형의 넓이를 구하시오.

우선 객체를 생성한다는 건 어떤 말인가?

// 생성자 호출
Rectangle rectangle = new Rectangle();

메모리에 할당한다는 것인데, new 키워드로 생성자를 호출한다.

클래스 내에서 필드와 메서드를 만들어 주고 이를 main에서 호출해 결과값을 출력한다.

public class Rectangle {
	// 필드 선언
	private int width;
    private int height;
    
    // 생성자로 필드 초기화
    public Rectangle(int width, int height) {
    	this.width = width;
        this.height = height;
    }
    
    // 메서드 생성
    public int getArea() {
    	return width * height;
    }
        public static void main(String[] args) {
        // Rectanngle 생성자는 두 개의 매개변수를 받음(width, height)
        // 객체 생성 후 메서드 호출하여 넓이 구함
        int area = new Rectangle(5, 7).getArea();

		// 결과 출력
        System.out.println(area);
    }
  }

상속

상속은 클래스에서 빼놓을 수 없는 개념이다. 단어 뜻 그대로 상위 클래스의 필드와 메서드를 하위 클래스에서 상속 받는 걸 말한다. 기존 클래스를 재사용하기 때문에 코드의 변경과 유지보수에 유리하다.

사용법은 단순하다.

class Child extends Parents {
	...
}

자바는 단일 상속만 가능해서 단 하나의 상위 클래스만 상속 받을 수 있다. 그렇다고 해서 바로 부모격만 상속 가능하다는 게 아니다. 예를 들어 (상위 클래스를 상속 받는) 하위 클래스1을 상속받는 하위 클래스2는 사용 가능하다.

상속 예제

📢 Animal 클래스를 상속받는 Cat, Dog 클래스를 만들고 각각 메서드 오버라이딩하기.

먼저 상위 클래스 Animal.java

package _04_class._04_inheritance.ex3;

public class Animal {
	// 필드 선언
    private String species;
    private String name;
    private int age;

	// 메서드 정의
    public void makeSound() {
        System.out.println("동물 소리");
    }

	// 생성자로 선언한 필드 초기화
    public Animal(String species, String name, int age) {
        this.species = species;
        this.name = name;
        this.age = age;
    }

	// toString 오버라이딩
    @Override
    public String toString() {
        return species + " 이름은 " + name + ", 나이는 " + age;
    }

}
  • 필드는 private으로 설정하여 외부에서 직접 값을 변경할 수 없도록

    • 하위 클래스에서 name, species, age 필드 직접 접근 불가
  • 그럼 어떻게 해당 필드 값을 출력할 수 있나?

    • 1) getter, setter를 따로 정의해서 사용
    • 2) 혹은 toString 메서드 오버라이딩 <-- 선택 ✅
// Dog.java

package _04_class._04_inheritance.ex3;

public class Dog extends Animal {
    public Dog (String name, int age) {
        super("강아지", name, age);
    }

    @Override
    public void makeSound() {
        System.out.println("왈왈왈!");
    }

    public static void move() {
        System.out.println("챡 챡 챡");
    }
}
  • extends로 상위 클래스 받아옴

    • Animal 클래스의 생성자에 매개변수가 두 개 존재하므로 super()에 넘겨주어 생성자 호출
    • 생성자는 부모 -> 자식 순으로 호출됨
  • makeSound를 override* 해서 덮어씌움

    자바의 override
    -상위 클래스의 메서드의 반환 타입, 매개변수 개수 등을 똑같이 작성하여 상위 클래스의 메서드가 아닌 하위 클래스의 메서드를 호출하게 함.
    -쉽게 말해 덮어씌우는 것!
    -어노테이션(@Override)을 사용해 명시적으로 오버라이딩을 표시할 수 있음 (생략 가능)

  • static 키워드*로 메서드 정의

    static 멤버 (필드, 메서드)
    -객체마다 생성되지 않고, 클래스에 1개의 static 생성
    -클래스가 생성되는 순간에 메모리를 할당 받음
    객체를 생성하지 않아도 static 멤버에 접근 가능
    static 메서드에서 non-static 멤버에 접근 불가 (반대는 가능)
    -같은 메모리 공간을 공유해서 동일한 클래스의 모든 인스턴스 값 동일
    -유틸리티성 메서드 등 공유 목적으로 자주 사용
    -static 메서드에서는 this 사용 불가! (대신 getter, setter)

같은 방식으로 Cat 클래스도 만들어줬다.

// Cat.java

package _04_class._04_inheritance.ex3;

public class Cat extends Animal {
    public Cat(String name, int age) {
        super("고양이", name, age);
    }

    @Override
    public void makeSound() {
        System.out.println("미야오옹~");
    }

    public static void move() {
        System.out.println("자박 자박");
    }
}

이 모든 내용을 호출할 AnimalEx.java

package _04_class._04_inheritance.ex3;

public class AnimalEx {
    public static void main(String[] args) {
        Cat cat = new Cat("연님", 5);
        Dog dog = new Dog("태평", 6);

        // 인스턴스 값 출력 :
        System.out.println(cat.toString());
        // static 변수는 인스턴스 없이 생성자로 호출 가능
        Cat.move();

        System.out.println(dog.toString());
        Dog.move();
    }
}

추상 클래스

  • 하나 이상의 추상 메서드(선언 O, 구현 X)를 포함하는 클래스

    • 추상 메서드, 일반 메서드 모두 가질 수 있음
  • 상속 관계에서 상위 클래스 역할

  • 서브 클래스에서 반드시 추상 메서드를 구현해야 함

    • 추상 클래스(상위) : 객체의 공통적인 특징을 추상화해서 정의
    • 서브 클래스(하위) : 메서드 오버라이딩으로 추상 메서드를 구체적으로 구현
  • 추상 클래스는 new 연산자를 통해 인스턴스 객체 생성 불가

    • 대신 서브 클래스로 객체 생성

설계와 구현을 분리함으로써 하나의 클래스에서 여러 타입을 구현할 수 있는 다형성을 갖춘다.

추상 클래스 예제

📢 Shape 추상 클래스를 상속 받는 Circle, Triangle 클래스 생성

🔶🔷상위 클래스 Shape

  • 넓이는 도형마다 구현 내용이 달라지니까 추상 메서드(calculateArea)로 만든다.
  • 추상 메서드가 1개 이상 포함된 클래스니까 Shape 클래스는 추상 클래스가 된다.
package _04_class._05_abstract.ex3;

public abstract class Shape {
    private String color;
    
    // 생성자
    public Shape(String color) {
        this.color = color;
    }

    // 추상 메서드 : 도형 넓이 구하기 - 오버 라이드
    abstract public void calculateArea();

	// 일반 메서드 : 도형 색상 출력
    public void getColor() {
        System.out.println("도형 색상: " + color);
    }
}
  • 추상 메서드는 강제성이 있기 때문에 모든 하위 클래스에서 사용하되 오버라이드 해서 사용할 만한 것으로 만든다.
  • 색상은 출력 형태가 동일해서 일반 메서드로 만들어줬다.

🔹하위 클래스1 Triangle

package _04_class._05_abstract.ex3;

public class Triangle extends Shape {
    private int width;
    private int height;
    
    @Override
    public void calculateArea() {
        double result = width * height * 0.5;
        System.out.println("도형 넓이: " + result);
    }
    
    // 생성자
    public Triangle(String color, int width, int height) {
        super(color);
        this.width = width;
        this.height = height;
    }
}
  • 생성자에 매개변수가 있는 상위 클래스를 상속 받으면, super()*에 매개변수를 넘겨줘야 한다.
    Shape에서 String color를 받았으므로 같은 매개변수를 입력한다.

    • 위 과정을 거치지 않으면 오류가 발생한다 :
      There is no default constructor available in '_04_class._05_abstract.ex3.Shape'

상위(부모) 생성자 호출하기 : super()
• 모든 객체는 생성자를 호출해야 생성된다.
• 상위 클래스가 기본 생성자라면? (=매개변수 없음)
하위 클래스 생성자 컴파일 과정에서 자동으로 추가되면서 부모 클래스의 기본 생성자를 호출.
• BUT 매개변수를 갖는 생성자만 있다면?
super()에 매개변수를 넣어주어야 한다.

  • 추상 메서드로 선언한 calculateArea를 오버라이드 해주어 도형 넓이를 구한다.
    • 넓이를 구하려면 width와 height이 필요하므로 각각 private 필드로 선언한 후에 초기화했다.

똑같은 방식으로 Circle 클래스를 구현해준다. 다른 건 오버라이드 한 메서드 내용뿐!

🔸하위 클래스2 Circle

package _04_class._05_abstract.ex3;

public class Circle extends Shape {
    private int width;
    private int height;
    
    @Override
    public void calculateArea() {
        System.out.println("도형 넓이: " + width * height);
    }
    
    // 생성자
    public Circle(String color, int width, int height) {
        super(color);
        this.width = width;
        this.height = height;
    }
}

결과 보기

package _04_class._05_abstract.ex3;

public class ShapeEx {
    public static void main(String[] args) {
        Triangle triangle = new Triangle("green", 49, 5);
        Circle circle = new Circle("yellow", 40, 12);

        System.out.println("=== 삼각형 정보 ===");
        triangle.getColor();
        triangle.calculateArea();
        System.out.println();

        System.out.println("=== 직사각형 정보 ===");
        circle.getColor();
        circle.calculateArea();
    }
}
profile
일단 해보는 편
post-custom-banner

0개의 댓글