3장의 내용 중 중요한 내용들을 정리했다.
클래스와 객체의 개념
흔히 얘기하는 붕어빵과 붕어빵틀처럼 생산수단과 생산물에 초점을 맞추는 것이 중요한게 아니다.
클래스는 분류에 대한 개념이고, 객체는 실체이다. 따라서 올바른 예시를 들자면, 사람과 강호동 같은?
class Book{
public String name;
public int price;
}
public class Main{
public static void main(String[] args){
Book springBook = new Book();
}
}
정의한 클래스대로 만들어져 힙 메모리에 올라간 객체를 "클래스의 인스턴스"라고 한다.
위 코드에서 new Book()으로 힙에 올린 객체를 Book클래스의 인스턴스라고 하고,
인스턴스 변수 springBook으로 인스턴스의 주소값을 받아와 스택영역에서 힙영역으로 접근한다.
추상화는 모델링
클래스를 정의할때는 객체들의 공통되는 특성이나 속성을 추출해서 설계해야하는데 이를 추상화라고 한다.
class CheckCard{
public String cardNumber;
public String cvcNumber;
public String validity;
public CheckCard(String cardNumber, String cvcNumber, String validity){
this.cardNumber = cardNumber;
this.cvcNumber = cvcNumber;
this.validity = validity;
}
}
public class Main{
public static void main(String[] args){
CheckCard myCard = new CheckCard("1234", "123","2025-12-05");
CheckCard yourCard = new CheckCard("5678", "456","2025-11-05");
}
}
예를 들면 내 체크 카드와 친구의 체크카드 등 발급된 체크 카드들의 정보는 모두 다르지만 체크 카드 클래스의 속성에는 카드 번호, CVC 번호 등을 갖도록 설계하는 것처럼.
근데 클래스를 정의할 때도 이용하는 서비스에 따라 분류의 폭을 좁히면서 맞춰 상태와 행동을 정의해줘야하는데 이를 애플리케이션 컨텍스트에 맞춰 추상화 한다고 한다. 예를 들어 체크카드를 만드는데 사용자의 대학교, 주민번호 같은 정보는 필요가 없다. 수많은 구체적인 속성들 중에 관심영역(애플리케이션 컨텍스트)에 있는 특성만 가지고 조합하는 것이 모델링이다.
따라서 클래스 설계에 진행되는 추상화는 모델링이고, 자바에서는 이를 class 키워드로 지원한다.
모델링에 스태틱 키워드 녹이기
보통 클래스에 정의된 상태들은 실체화될 때 각자 다른 값을 소유하게 되는데 이를 인스턴스 멤버로 정의한다.
클래스의 인스턴스들이 모두 같은 값을 가지는 개념은 스태틱 멤버(클래스 멤버)로 정의한다.
예를 들면 체크카드의 카드 번호는 사용자마다 다를 것이라 인스턴스 변수로 정의하지만, 카드 번호의 자릿 수 같은 모든 체크카드가 동일한 개념을 스태틱 멤버로 정의한다.
지역변수는 초기화 하지 않으면 쓰레기값을 갖지만 클래스 필드는 자동으로 초기화해주는데 이는 어디서 사용될지 모르는 공유변수 이기 때문이다.
스태틱 멤버는 클래스의 인스턴스에서 접근하면 코드영역 > 스택 > 힙 > 메서드순으로 접근한다.
메모리 낭비를 줄이기 위해 코드영역 > 메서드로 접근해서 사용한다.
상속 = 재사용 + 확장
저자가 상속이라는 단어를 싫어한다. 이유는 클래스 - 객체의 개념에 아버지 - 자식 같은 오해를 불러일으키기 때문이다.
위에서 말한 것과 같이 클래스와 객체와 마찬가지로 부모클래스, 자식클래스는 상위 개념, 하위 개념에 가깝다.
상위로 갈수록 추상화, 하위로 갈수록 구체화 됐다고 한다.
자바에서 상속을 사용할때 extends 키워드를 쓰는데 이는 하위개념이 상위개념에 포함된 객체지향의 상속을 정확히 나타낸다.
public class Animal {
public String name;
public void callName() {
System.out.println(this.name);
}
}
public class Penguin extends Animal {
public Penguin(){
this.name = "Penguin";
}
}
public class Lion extends Animal{
public Lion() {
this.name ="Lion";
}
}
public class Main{
public static void main(Stirng[] args){
Penguin pigu = new Penguin();
Lion scar = new Lion();
pigu.callName(); // "Penguin";
scar.callName(); // "Lion";
}
}
클래스를 정의할 땐 분류명스럽게, 인스턴스 변수를 정의할 땐 구체화한 객체처럼 해야한다.
위의 코드 처럼 사자는 동물의 종류이고, 펭귄도 동물의 종류이기 때문에 자식클래스가 될 수 있는 것이다.
클래스의 다중 상속 안되는 이유는?
클래스를 모델링 할 때 애플리케이션 컨텍스트에 따라 추상화를 하는 개념과 상충된다.
클래스를 상속하려면 자식 클래스는 부모클래스의 종류여야 하는데
위와 같이 인어와 같이 사람, 물고기를 다중상속을 받으면 동물에서 사람으로 구체화된 특성과 동물에서 물고기로 구체화 된 특성을 모두 물려받아 인어에게 수영하라고 하면 사람처럼 팔, 다리로 할지물고기처럼 지느러미로 할지 알수 없는 문제가 생긴다.
자바에선 이 문제를 상속을 사용하는 클래스 대신 구현을 강제하는 인터페이스로 해결했다.
다형성 : 오버라이딩
class Animal{
public void call(){
System.out.println("Animal");
}
}
class Lion extends Animal{
@Override
public void call(){
System.out.println("Lion");
}
}
public class Main{
public static void main(Stirng[] args){
Animal animal = new Animal();
Liong scar = new Lion();
animal.call(); // "Animal"
scar.call(); // "Lion"
Animal scar = new Lion();
simba.call(); // "Lion"
}
}
오버라이딩은 부모 클래스의 메서드와 같은 이름의 메서드를 재정의해 사용하는 것이다.
자식클래스를 생성하면 부모클래스도 같이 생성되는데 이때 인스턴스 변수를 부모클래스에 지정해도 자식 클래스에서 오버라이딩 된 메서드를 기준으로 실행을 시키기 때문에 자식클래스가 어떻게 구현이 되었는지 몰라도 부모 클래스에서 사용할 수 있다.