서론
자바의 상속과 오버라이딩 개념을 공부한 후, 예제를 만들어 이를 적용해 보았다. 이 과정을 통해 배운 내용을 보다 명확하게 이해할 수 있었으며, 개념을 다시 정리함으로써 더욱 확실히 내것으로 만들 수 있었다.
또한 알고리즘 스터디 시간에 발표를 진행하였고 발표때 망신 당하지 않기 위해 발표 준비를 열심히 하였다. 발표는 잘못했지만 발표 준비하는 과정에서 배열과, ArraysList, LinkedList의 개념을 이해하고 여러 문제를 풀어보며 개념을 익혔다. 열심히 하다보면 언젠간 잘하는날이 오겠지 뭐..
본론
상속
상속 이라는 단어는 일반적으로 누군가에게 무엇인가를 물려받는 것을 의미한다.
자바 객체 지향 프로그래밍에서 말하는 상속은 기존 클래스의 속성와 기능을 새로운 클래스에 물려주는것을 의미한다. 말 그대로 속성과 기능을 그대로 물려주는 것이다. 여기서 속성과 기을 물려주는 클래스를 부모 클래스 (슈퍼 클래스) 라고 표현하며 속성과 기능을 상속받는 클래스자식 클래스 (서브 클래스) 라고 표현한다.
용어 정리
- 부모 클래스 (슈퍼 클래스) : 상속을 통해 자신의 속성과 기능을 물려주는 클래스
- 자식 클래스 (서브 클래스) : 부모 클래스로 부터 속성과 기능을 상속받는 클래스
상속을 사용하면 부모 클래스의 정의한 속성과 기능을 자식 클래스에서 재사용할 수 있다. 덕분에 동일한 코드를 반복해서 작성할 필요가 없고 개발 효율성이 높아진다. 또한 부모의 기능을 물려받으면서 자신만의 고유한 기능을 추가할 수 있어 확장성이 높아지고 계층적으로 표현할 수 있다는 장점이 존재한다.
예를 들어보자, 기본 캐릭터 클래스에는 이름과 체력이라는 속성이 있고, 기본적인 공격을 할 수 있는 기능이 들어 있다. 여기서 마법사 캐릭터를 만든다고 가정해보자. 만약 상속을 사용하지 않으면, 마법사 캐릭터 클래스를 새로 만들 때 이미 기본 캐릭터 클래스에 들어있는 이름, 체력, 공격 기능 등을 또다시 작성해야 한다.
하지만 상속을 사용하면, 마법사 캐릭터 클래스는 기본 캐릭터 클래스에서 이미 정의된 이름, 체력, 공격 기능을 그대로 물려받을 수 있다. 덕분에 중복 코드를 작성할 필요 없이, 마법사 캐릭터 클래스에는 마법을 사용하는 기능만 추가하면 된다. 이렇게 하면 코드가 더 깔끔하고 관리하기 쉬워진다.
자바의 상속은 extends 키워드를 사용하여 상속을 받을 수 있다. 또한 자바의 상속은 단일 상속 원칙으로 하고 있어 하나의 부모에게만 상속을 받을 수 있다.
상속 관계에서 자식 클래스가 new 키워드를 통해 인스턴스를 생성하게되면 힙 메모리 영역에 부모 클래스의 인스턴스도 같이 생성된다. 즉, 단순히 속성과 기능만 물려받는것이 아니라 부모 타입 인스턴스 까지 포함해서 같이 생성하게 된다. 참조값은 하나이지만 그 안에는 자식 타입과 부모입의 정보가 담겨 있다.
왜?
자식 클래스는 부모 클래스의 속성과 메서드를 상속받아 사용한다. 부모 타입의 인스턴스가 같이 생성되지 않는다면 자식 클래스에서 부모타입의 필드나 메서드로 접근할 수 없다.
상속 관계에서 중복되는 속성과 기능이 존재할 수 있기 때문에 먼저 자바에서는 호출자의 타입을 통해 어떤 메서드를 사용하지 선택한다. 호출하는 변수의 타입의 따라 해당 클래스에서 메서드를 먼저 검색하며, 메서드가 재정의 되어있는지 확인한다. 만약 현재 클래스에서 원하는 메서드를 찾지못한다면 상위 클래스 (부모클래스)로 올라가며 메서드를 찾으며 찾지 못하는 경우 컴파일 오류가 발생하게 된다.
오버라이딩
부모 클래스에게 상속받은 기능을 자식이 재정의하는것을 메서드 오버라이딩라고한다. 따라서 상속관계에서 사용하며. 부모의 기능을 자식이 재정의하는것이라고 이해할 수 있다.
오버라이딩 조건
- 메서드 이름 : 메서드 이름이 같아야한다.
- 메서드 매개변수 : 매개변수 타입순서, 개수가 같아야한다.
- 반환타압 : 반환타입이 같아야한다. -> 자식 클래스 OK
- 접근 제어자 : 접근 제어자는 상위 클래스의 메서드보다 더 제한적이면 안된다.
- 예외 : 상위 클래스보다 더 많은 체크예외를 `throws`로 선언할 수 없다.
- 생성자는 오버라이딩 불가능하다.
예제
자바의 상속과 메서드 오버라이딩을 이용하여 간단한 예제를 만들어보았고 다음과 같다. 먼저 `Character` 클래스는 캐릭터의 이름과 체력, 마력을 속성으로 가지며 공격, 이동과 같은 기능을 가지고있다. 이는 모든 캐릭터가 공통적으로 가지는 속성과, 기능을 의미한다.
class Character {
String name;
int hp;
int mp;
public Character(String name, int hp, int mp) {
this.name = name;
this.hp = hp;
this.mp = mp;
}
void attack() {
System.out.println("공격");
}
void move() {
System.out.println(toString() + ": 이동");
}
@Override
public String toString() {
return "Character 클래스";
}
}
그 후 `Character` 클래스를 상속받아 구체적인 캐릭터의 유형인 `Warrior` 클래스를 정의하였다.
기본적인 속성과 기능은 부모 클래스로 부터 물려받았고 전사 캐릭터만의 특정 기능인 `dash`를 선언하였다. 또한, 메서드 오버라이딩을 통해 부모 클래스에서 물려받은 공격 기능을 전사 캐릭터에 맞게 수정하였다.
class Warrior extends Character {
public Warrior(String name, int hp, int mp) {
super(name, hp, mp);
}
void dash() {
System.out.println("대쉬");
}
@Override
void attack() {
System.out.println("강력한 공격");
;
}
}
메인 코드는 다음과 같다. new 연산자를 이용하여 Warrior 인스턴스를 생성해주었고 Warrior 타입의 변수에 참조값을 저장하였다. 그 후 `warrior` 변수를 이용하여 이동, 대쉬, 공격등의 기능을 호출하였다.
public class Main {
public static void main(String[] args) {
Warrior warrior = new Warrior("전사", 100, 100);
warrior.move();
warrior.dash();
warrior.attack();
}
}
여기서 warrior.move() 메서드를 호출하게되면 어떻게 될까? 먼저 호출자의 타입을 통해 어떤 메서드를 호출할지 결정한다고 하였다. 여기서는 Warrior 타입이므로 Warrior 클래스에서 `move()` 메서드가 있는지 확인한다.
하지만 이전 코드에서 보다시피 Warrior 클래스에서 `move()` 메서드를 정의하지 않았고 메서드를 재정의 또한 하지 않았다. 그렇기 때문에 자바는 이제 상위 클래스 (부모클래스)로 올라가며 메서드를 찾는다. `Character` 클래스에서 `move()` 메서드가 정의되었기 때문에 `Character` 클래스의 `move()` 메서드가 실행된다.
이제 warrior.dash() 메서드를 호출하면 어떻게 될까? 먼저 호출자의 타입을 통해 어떤 메서드를 호출할지 결정한다. 그렇기 때문에 Warrior 클래스에서 `dash()` 메서드가 있는지 확인한다, 있다 `dash()` 메서드가 호출되기 때문에 상위 타입으로 갈필요가 없다. `attack()` 메서드도 마찬가지로 자식 클래스에서 메서드를 재정의하였기 때문에 상위 타입으로 올라가 찾지 않아도 된다.
스터디 발표 자료 - Arrays, ArrayList, LinkedList
배열은 기본적인 자료구조중 하나로 동일한 타입의 데이터를 연속적인 메모리 공간에 저장하는 방식을 사용합니다. 배열의 특징으로는 생성 시에 크기가 고정되며 이후에는 크기를 변경할 수 없다는 특징을 가지고 있고 동일한 데이터 타입과 연속적인 메모리 공간에 저장되기 때문에 인덱스를 사용하여 특정한 요소에 매우 빠르게 접근할 수 있습니다.
ArrayList는 크기가 가변적인 배열을 기반으로하는 자료구조입니다. 여기서 가변적이라는 의미는 배열의 크기가 동적으로 조절될 수 있다는 것을 뜻합니다. 요소를 추가하거나 제거할때에는자동으로 내부 배열의 크기를 변경하여 동작하며 동작합니다. ArrayList의 특징으로는 배열과 달리 고정된 크기를 가지지 않고 유연하게 사용할 수 있다는 특징이 있습니다. 또한 배열기반의 자료구조로 배열과 똑같이 특정 요소에 접근할 때 매우 빠른 속도를 가진다는 특징이 있습니다.
링크드 리스트는 각 요소들이 다음 요소의 참조를 가지고 있는 자료구조입니다.
배열과 arrayList와 달리 각각의 요소를 노드라고 표현하며 노드안에는 데이터를 저장하는 데이터필드와 참조를 저장는 포인트 필드가 있습니다. 포인트의 필드의 개수와 연결방식을 활용하여 단일 연결리스트, 이중 연결리스트, 원형 연결리스트를 구현할 수 있으며. 연결리스트는 노드의 참조를 통해 하나의 리스트를 구성하기 때문에 데이터를 추가 , 삭제 연산이 상대적으로 빠르다는 특징이 존재하고 데이터를 중간에 추가 삭제할 때 단순히 노드의 참조만 변경하면 되므로 속도가 빠르다는 장점이 있습니다. 하지만 연속적인 공간에 저장되어있지 않기 때문에 특정 인덱스의 요소에 접근하기 위해서는 순차적으로 접근해야된다는 특징이 존재합니다.
스터디 발표 자료 - 백준 요세푸스
이 문제는 1부터 N까지의 사람이 원으로 둘러 앉아 있을때 순서대로 k번째 사람의 제거하며 제거 순서를 반환하는 문제로 이해하였습니다. 1부터 N까지의 사람의 번호가 적혀 있는 리스트를 선언하였고 이 리스트가 비어있지 않을때까지 반복하며 k번째 사람을 제거하는 방식으로 접근하였습니다.
String[] inputData = br.readLine().split(" ");
int N = Integer.parseInt(inputData[0]);
int K = Integer.parseInt(inputData[1]);
LinkedList<Integer> linkedList = new LinkedList<>();
for (int i = 0; i < N; i++) {
linkedList.add(i + 1);
}
StringBuffer answer = new StringBuffer();
ListIterator<Integer> iterator = linkedList.listIterator();
answer.append("<");
while (!linkedList.isEmpty()) {
for (int i = 0; i < K; i++) {
if (!iterator.hasNext()) {
iterator = linkedList.listIterator();
}
if (i == K - 1) {
answer.append(iterator.next());
} else {
iterator.next();
}
}
iterator.remove();
if (!linkedList.isEmpty()) {
answer.append(", ");
}
}
answer.append(">");
br.close();
bw.write(answer.toString());
bw.flush();
bw.close();
}
}
ListIterator
이 과정속에서 ListIterator의 개념을 배울 수 있었다. ListIterator는 리스트를 양방향으로 순회가 가능하며 리스트를 순회하는동안 요소를 추가하거나 제거할 수 있는 기능제공한다. 직접 사용해 보면서 개념과 사용법을 익혔다.
ListIterator의 주요 메서드
- hasNext(): 리스트의 현재 위치 다음에 요소가 있는지 확인한다.
- next(): 리스트에서 다음 요소를 반환하고 위치를 이동한다.
- hasPrevious(): 리스트의 현재 위치 이전에 요소가 있는지 확인한다.
- previous(): 리스트에서 이전 요소를 반환하고 위치를 이동한다.
- add(E e): 리스트의 현재 위치에 요소를 추가한다.
- remove(): 현재 위치의 마지막으로 반환된 요소를 리스트에서 제거한다.
- set(E e): 현재 위치의 마지막으로 반환된 요소를 새로운 요소로 대체한다.
결론
나만의 언어로 정리하면 시간은 오래걸리지면 기억에 더 잘남는거 같다.
'TIL' 카테고리의 다른 글
[TIL | 2024-08-28] 예외, 예외 처리 (0) | 2024.08.29 |
---|---|
[TIL | 2024-08-27] 추상 클래스, 추상 메서드, 인터페이스 (0) | 2024.08.29 |
[TIL | 2024-08-24] 메서드, 프로그램의 기능적 분할, 객체 지향 프로그래밍 (0) | 2024.08.23 |
[TIL | 2024-08-23] 배열, 반복문 (0) | 2024.08.22 |
[TIL | 2024-08-22] 변수, 기본 타입, 참조 타입, 오토박싱, 언박싱 (1) | 2024.08.22 |