상속
상속이란 부모 클래스의 멤버 변수와 기능을 자식 클래스로 복제하는 것이다.
이는 유사한 클래스를 공통 분모 기반 아래 빠르고 일관성 있게 생산하는데 도움이 된다.
문법
//@ package com.user.character;
public class Character {
protected String name;
protected int id;
protected int secret =20;
public Character(String name, int id) {
// TODO Auto-generated constructor stub
this.name = name;
this.id = id;
System.out.println("character 클래스 생성자 호출");
}
public void roar() {
System.out.println(name + "가 포효를 시전했다.");
}
}
//@ com.user.character.Babarian;
public class Babarian extends Character {
public Babarian(String name, int id) {
super(name, id);
System.out.println("babarian 클래스 생성자 호출");
}
}
//@ Main 클래스
import com.user.character.Babarian;
public class Main {
public static void main(String[] args) {
Babarian user1 = new Babarian("charchar", 1);
user1.roar();
}
}
//출력
//character 클래스 생성자 호출
//babarian 클래스 생성자 호출
//charchar가 포효를 시전했다.
extends
클래스 선언 단계에서 특정 클래스를 상속한다는 걸 의미
super
피상속 객체에서 상속 객체를 호출하거나 참조할 때 사용한다.
피상속 객체는 생성자 함수에서 super로 상속객체를 반드시 명시적으로 호출하여야 한다.
static에서는 사용불가하다.
protect
접근제한자의 한 종류.
외부에서 접근이 제한되나, 상속받은 클래스나 동일 패키지 내에서 접근 가능
작동과정
하위 객체인 babarian을 호출하면 super() 에 의해서 모객체인 character의 생성자 함수가 호출된다.
그 결과로 모객체의 멤버변수와 메소드는 babarian에게 상속된다.
상속 규칙
만약 상속 체인이 2개 이상이면 최상위 모객체부터 호출이 된다.
자바는 단일 상속만 지원하며. 다중 상속은 지원하지 않는다.
객체 간에 작동 관계를 보여주는 도식표인 다이어그램에서는 상속관계를 부모 방향으로 가는 실선으로 표시함.
상속의 특징
오버라이드(override)
오버라이드란, 상위 클래스의 메소드를 하위 클래스의 메소드가 재정의하는 것이다.
//@ Character
package com.user.character;
public class Character {
protected String name;
public Character(String name, int id) {
this.name = name;
}
public void roar() {
System.out.println(name + "가 포효를 시전했다.");
}
}
//@ Babarian
package com.user.character;
public class Babarian extends Character {
public String name;
public Babarian(String name, int id) {
super(name, id);
}
@Override
public void roar() {
System.out.println(super.name + "가 강력한 포효를 시전했다!");
}
// 오버라이드
}
//@ Main
import com.user.character.Babarian;
public class Main {
public static void main(String[] args) {
Babarian user1 = new Babarian("charchar", 1);
user1.roar();
}
}
// 출력
// charchar가 강력한 포효를 시전했다!
@ annotation
@을 이용한 주석 방식
프로그램 실행에 직접적 영향을 끼치진 않으나, 컴파일이나 빌드 과정에서 유용한 경고와 에러를 제공함
(ex: @Override)
재정의, 은닉
하위 클래스가 상위 클래스의 메소드와 동일한 이름의 메소드를 만들면 그 메소드는 오버라이드된다.
그럼 멤버 변수는 이 경우에 어떻게 될까?
하위 클래스에서 상위 클래스와 동일한 멤버변수를 선언할 경우, 상위 클래스의 멤버 변수와 독립적으로 하위 클래스의 것이 만들어진다.
이런 경우 외부에서는 하위 클래스를 통해 상위 클래스의 멤버 변수에 접근할 수 없다.
반면, 하위 클래스에서는 super로 상위 클래스의 멤버 변수에 접근가능하다.
이를 재정의, 혹은 은닉이라 부를 수 있다.
멤버 변수는 상위 클래스의 은닉이 가능하지만,
정적 메소드는 상위 클래스의 메소드를 은닉할 수 없다.
이 경우 컴파일 에러가 발생한다.
다형성
하위 클래스 인스턴스는 상위 클래스 타입의 변수에 할당 가능함.
서로 다른 하위 클래스를 동일한 상위 클래스로 다룰 때 그리고 하위 클래스의 속성 및 메소드에 접근할 필요가 없을 때 유용하다.
Human user1 = new Warrior(100, "charchar", 0);
Human[] user2= new Human[2];
user2[0]= new Warrior(0, null, 0);
// 다형성에 따라 상위 타입을 하위 클래스의 타입으로 선언
Object 클래스
모든 클래스의 최상위 클래스로 자동 상속된다.
따라서, 모든 클래스의 데이터 타입은 Object로 통일 가능하지만, 타입 검사 기능을 제대로 사용못하므로 지양한다.
이클립스에서 상속관계를 아는 단축키
ctrl t
내부 클래스
클래스 내부에서 선언된 클래스를 의미한다.
이는 일반적으로 독립적인 클래스 파일을 생성 후 결합하는 것과 대비된다.
내부 클래스는 외부 클래스와 유일한 관계를 가질 때 사용한다.
//@ Car
public class Car {
private String name;
// 내부 클래스
class Handle {
public void combine() {
// 내부 클래스는 외부 클래스의 private 변수에 자유롭게 접근 가능
System.out.println(name + "+ handle");
}
}
// 내부 클래스
class Wheel {
}
public Car(String name) {
this.name = name;
Handle handle = new Handle();
Wheel wheel = new Wheel();
handle.combine();
}
}
//@ main
public class Main {
public static void main(String[] args) {
Car car1 = new Car("super car");
}
}
장점
논리적 직관성
개발자의 관점에서 내부 클래스는 외부 클래스와 유일한 관계임을 이해하기 편리하다.
캡슐화의 용이성
내부 클래스는 외부 클래스의 private 멤버 변수에 자유롭게 접근가능하다.
따라서 외부로의 보안과 내부에서의 자유로운 조작의 이점을 가진다.
두 장점의 결과로 유지 보수 및 가독성의 이점을 가진다.
(단, 구조가 너무 복잡해질 수 있다.)
내부 클래스의 종류
종류가 다양하지만 선언 위치나 static에 따른 차이일 뿐이다.
변수의 선언 방식을 내부 클래스에 적용했다고 생각하면 된다.
멤버 내부 클래스 | 외부 클래스의 멤버 변수로 선언
스태틱 내부 클래스 | 외부 클래스의 정적 멤버 변수로 선언. 다만, new를 사용할 수 있으며 일반적인 정적 변수나 정적 메소드와 메모리 구조가 상이함.
지역 내부 클래스 | 메소드의 내부에서 선언. 해당 메소드 내에서만 사용
멤버 내부 클래스
멤버 클래스는 외부 클래스의 멤버 변수이다. 따라서 외부 클래스를 인스턴스로 생성한 후에 new 키워드로 내부 클래스를 호출할 수 있다. 하지만 잘 사용하진 않는다. 애초에 내부 클래스의 목적 자체가 외부 클래스에서만 사용하려 하기 때문이다.
불가피하게 외부 클래스의 바깥에서 내부 클래스의 작업을 할 때 사용된다.
외부 클래스의 멤버로 취급되므로 static 멤버는 선언 불가하다.
외부 클래스를 가리키는 방법 | <<클래스명.this>>(정규화된 this)의 형태로 가리킬 수 있다.
public class Car {
private String name;
class Wheel {
private String maker;
private String date;
// 멤버 클래스에서는 static 변수 선언 불가
// static String language;
// final을 붙여 상수로는 static 가능
static final String language="ko";
// 내부 메소드에서 외부 클래스의 메소드 호출 방법
public void wheelCheck() {
Car.this.run();
}
}
public Car(String name) {
}
}
public class Main {
public static void main(String[] args) {
Car car1 = new Car("super car");
Car.Wheel wheel1 = car1.new Wheel();
}
}
스태틱 내부 클래스
일반적으로 static 변수나 메서드는 클래스의 인스턴스가 아니라 클래스 자체에 한번 생성된다. 따라서 메모리 상으로 한번만 할당된다.
하지만, 스태틱 내부 클래스는 외부 클래스의 인스턴스 생성 없이 바로 생성이 가능하다는 차이를 제외하곤 멤버 내부 클래스처럼 매번 새로운 인스턴스가 생성된다.
멤버 내부 클래스 vs 스태틱 내부 클래스
내부 클래스가 외부 클래스의 인스턴스를 참조해야 한다면 전자.
그렇지 않다면 후자가 성능상으로 더 좋다.
왜냐하면 전자는 외부 참조를 가지므로 메모리를 더 먹고 느리며 GC대상에서 제외되어 메모리 누수 문제를 일으킬 수 있기 때문이다.
익명 클래스
재사용가능성이 없는 단발성의 객체를 만들 때 사용한다. (자바스크립트의 IIFE 같은 느낌인듯)
주로 메소드를 재정의해서 사용하는 목적이다.
new Ano(){
@Override
public void printt() {
System.out.println("i m new ano!"+Ano.id);
}
}.printt();
인터페이스
인터페이스는 클래스처럼 객체의 청사진이자 타입 역할을 한다.
하지만 new 생성자를 통한 객체 생성이나 메소드 호출을 할 순 없다.
클래스의 청사진 역할
원래 자바는 단일 상속이므로 다중 상속이 불가능하다.
하지만 클래스는 복수의 인터페이스를 상속할 수 있으므로 객체에 유연성을 준다.
선언
인터페이스는 멤버변수 대신, 클래스의 메소드, static final 상수, static 메소드만 선언가능하다.
클래스의 메소드는 선언만 가능하며 구현은 상속받는 메소드가 담당한다.
상수와 static 메소드는 상속받는 클래스가 아니라 인터페이스의 정적 구성 요소로 간주된다. 따라서 재정의가 불가능하다.
public interface ICake {
public void makeCake();
public static final int interfaceId = 1;
public static void printICakeStatic() {
System.out.println(interfaceId);
}
}
상속 및 사용
클래스는 implements로 복수의 인터페이스를 상속할 수 있다
다른 클래스를 상속하는 동시에 implements로 인터페이스 상속이 가능하다.
인스턴스 생성 시에도 인터페이스를 타입으로 명시 가능하다.
package com.cake;
public class Cake implements ICake{
@Override
public void makeCake() {
// TODO Auto-generated method stub
System.out.println("make cake");
}
}
추상 클래스
추상 클래스는 일반적인 클래스처럼 멤버 변수, 메소드, 생성자가 존재하며, extend로 상속한다.
차이점은 인터페이스처럼 메소드 시그니처로서 메소드의 선언만 하고 정의 구현을 피상속 클래스에 강제할 수 있다는 점이다. 또한 직접 인스턴스를 만들 수 없다.
public abstract class Aclass1 {
public int id;
public Aclass1(int id) {
this.id = id;
}
public void fuff() {
System.out.println("fuck seeeexxxxy!");
}
// 메소드 시그니처
abstract public void fuff2();
}
public class MainClass extends Aclass1 implements IA, IB {
//@override;
public void fuff2(){};
}
클래스 상속 vs 추상 클래스 상속 vs 인터페이스 구현
공통 역할
상속 시스템은 파생 클래스를 빠르게 만들고 일관성을 갖추는 데 좋다.
하지만, 단일 상속 시스템의 한계로 다형성을 갖추려면 오버라이드를 해야 함.
추상 클래스는 오버라이드를 강제하므로 문법 오류를 줄여 유지 보수를 용이하게 함.
인터페이스는 다양한 구현 메소드를 강제하는 시그니처 역할로서 다형성을 향상
클래스 vs 추상 클래스, 인터페이스
클래스는 그 자체로 인스턴스 객체 생성 가능
반면, 추상클래스와 인터페이스는 독립적인 인스턴스 객체 생성 불가
추상 클래스 vs 인터페이스
추상 클래스는 단일 상속 체제, 메소드 시그니처 외에도 자신의 멤버 변수와 메서드를 상속함. super 호출 가능
인터페이스는 다중 상속 체제, 메소드 시그니처 역할만 가능함.
참고자료
'코드 디자인 패턴 > java' 카테고리의 다른 글
[java & 디자인 패턴] Observer 패턴 학습 정리 (0) | 2024.07.14 |
---|---|
[java & 디자인 패턴] Builder 패턴 학습 정리 (0) | 2024.07.14 |
[java & 디자인 패턴] flyweight 패턴 학습 내용 정리 (0) | 2024.07.14 |
[java & 디자인 패턴] singleton 패턴 학습 내용 정리 (0) | 2024.07.07 |
[java/객체 지향]1. 객체 지향 프로그래밍이란, class의 기본 구성 (0) | 2024.04.16 |