SW Books/스프링 입문을 위한 자바 객체지향의 원리와 이해

[자바 객체지향 원리] 3장 - 자바와 객체 지향

빵빵0 2023. 8. 10. 19:11

객체 지향은 인간 지향이다

프로그래밍 언어 발전사 = 개발자를 더욱 편하고 이롭게 하기 위함, 하이 레벨의 인간을 배려하기 위한 과정

절차적/구조적 프로그래밍 -> 인간이 기계를 이해하려는 노력 필요. 특히 포인터

 

"왜 우리가 기계 종속적인 개발을 해야 하나?"

"우리가 눈으로 보고, 느끼고, 생활하는 현실 세계처럼 프로그래밍할 수 없을까?"

-> 객체 지향 개념 탄생

 

구조적 프로그래밍에서 가장 중요한 것 "함수"

함수: 코드를 논리적인 단위로 구분, 분할해서 정복

명령어를 논리적인 단위로 나누어 블록화해서 작성

 

객체 지향 프로그래밍에서 가장 중요한 것 "객체"(Object)

모든 만물은 객체들의 합

객체 지향은 직관적; 현실 세계를 인지하는 방식으로 프로그래밍 할 수 있기 때문 

  • 세상에 존재하는 모든 것은 사물, 즉 객체
  • 각각의 사물은 고유
  • 사물은 속성을 가짐
  • 사물은 행위를 함

사물을 하나하나 이해하기 보다 사물을 분류(class)해서 이해하는 것이 인간의 인지법

ex) 사람 -> 김종민, 한효주, 김연아(object)

사람이라는 분류 안의 객체들은 나이, 몸무게, 키 등의 속성(property)

먹다, 자다, 울다, 싸다 등의 행위(method)

 

객체 지향의 4대 특성 - 캡! 상추다

객체 지향의 4대 특성; 캡상추다

캡 - 캡슐화(Encapsulation): 정보 은닉(information hiding)

상 - 상속(Inheritance ): 재사용

추 - 추상화(Abstraction): 모델링

다 - 다형성(Polymorphism): 사용 편의

 

객체 지향의 4대 특성은 클래스 또는 객체를 통해 구현됨


클래스 vs 객체 = 붕어빵틀 vs 붕어빵 ???

이 비유를 버리자!!!!!!!

클래스 객체명 = new 클래스();

붕어빵틀 붕어빵 = new 붕어빵틀(); // ??? 새로운 붕어빵 틀을 만들었더니 붕어빵이 되었다 ???

 

붕어빵틀은 붕어빵을 만드는 팩토리! -> 디자인 패턴 공부! (6장)

 

클래스와 객체를 구분하는 방법은 property를 물어보는 것!

클래스는 분류에 대한 개념

객체는 실체!

 

추상화 Abstraction: 모델링

추상: 여러가지 사물이나 개념에서 공통되는 특성이나 속성 따위를 추출하여 파악하는 작용

사실적인 모습이 아니라 각 부분의 특징을 잘 표현할 수 있도록 특징을 극대화

구체적인 것을 분해해서 관찰자가 관심있는 특성만 가지고 재조합

 

추상화는 모델링이다

"객체 지향의 추상화는 곧 모델링이다"

 

Object는 객체 보다는 "개체"라고 이해하는게 나음

객체지향에서의 객체: 세상에 존재하는 유일무이(unique)한 사물

객체는 속성과 기능을 가지고 있음

 

객체지향에서의 클래스: 같은 특성을 지닌 여러 객체를 총칭하는 집합의 개념

ex) 클래스 : 객체 = 펭귄 : 뽀로로 = 사람 : 김연아 = 쥐 : 미키마우스

 

세상에 존재하는 유일무이한 객체를 특성(속성+기능)에 따라 분류해보니 객체를 통칭할 수 있는 집합적 개념, 즉 클래스(분류)가 나옴!!

 

사람 홍길동 = new 사람();

-> 사람이라는 클래스(분류)를 이용해 유일무이하고 새로운 하나의 사람(객체)을 만들어 홍길동(객체 참조 변수)이라는 이름을 지어준 것

 

클래스를 이용해 object를 만들었다는 것을 강조할 때는 클래스의 인스턴스(instance) 라는 표현을 씀

객체(object) = 클래스의 인스턴스

(붕어빵과 붕어빵 관계는 클래스와 객체의 이런 일면만 설명하는 메타포! 전부를 표현하는 메타포가 아님)

 

객체지향 프로그래밍을 할 때 클래스 먼저 설계

속성: 명사로 표현되는 특성, 값을 가질 수 있음

기능/행위: 동사로 표현되는 특성, 수행 절차 또는 로직이라고 함, 객체 지향에서는 메서드

 

"내가 만들고자 하는 애플리케이션은 어디에서 사용될 것인가?"

애플리케이션 경계(Context)에 따라 클래스 설계가 달라진다.

ex) 병원 애플리케이션이라고 특정하면 사람 클래스에서 필요 없는 특성이 보임 -> 사람이 '환자'로 좁혀짐

 

IT 용어로 추상화를 정의하면,

구체적인 것을 분해해서 관심 영역(애플리케이션 경계, Application Boundary)에 있는 특성만 가지고 재조합 하는 것 = 모델링

 

모델은 실제 사물 정확히 복제 X, 목적에 맞게 관심 있는 특성만을 추출해서 표현하는 것!!

모델은 추상화를 통해 실제 사물을 단순하게 묘사하는 것

-> 객체 지향에서 클래스 설계, 데이터베이스의 테이블을 설계할 때 필요한 기법

 

요약

  • OOP의 추상화는 모델링
  • 클래스 : 객체 = 펭귄 : 뽀로로
  • 클래스 설계에서 추상화가 사용됨
  • 클래스 설계를 위해서는 애플리케이션 경계부터 정해야 함
  • 객체 지향에서 추상화의 결과는 클래스

추상화의 개념을 넓게 본다면,

  • 상속을 통한 추상화, 구체화
  • 인터페이스를 통한 추상화
  • 다형성을 통한 추상화

 

In Java,

1. 자바는 class 키워드를 통해 객체 지향의 추상화를 지원하고 있다!

2. 클래스 객체_참조_변수 = new 클래스(); // 클래스와 객체 관계 표현

클래스: 객체_참조_변수의 자료형(Type)

객체_참조_변수: 생성된 객체를 참조할 수 있는 변수

=: 할당문

new: 새로운

클래스: 만들고자 하는 객체의 분류

(): 메서드

 

new 클래스(): 클래스의 인스턴스, 즉 객체를 생성하기 위해 객체 생성자를 호출

=> 새로운 객체를 하나 생성해 그 객체의 주소값(포인터)객체 참조 변수에 할당

 

추상화와 T메모리

1. 애플리케이션 경계 설정

2. 클래스의 객체들로부터 공통 특성 뽑아내 클래스 설계 => 추상화를 통해 관심 있는 특성만!!

But, 추상화는 주관적! 추상화의 결과가 설계자마다 다를 수 있음

추상화의 결과물은 모델, 모델은 Java에서 클래스로 표현됨

3. 논리적 설계를 물리적 설계로 바꿈

논리적 설계: 개발 환경(언어 등)에 영향 받지 않는 설계

물리적 설계: 개발 환경에 맞춰진 설계

 

 

객체는 Static 공간에 만들어짐. name, age, countOfTail는 이름만 있고 변수 저장공간이 스태틱에 없음!

Mouse 클래스에 속한 속성이 아닌 Mouse 객체에 속한 속성이기 때문

객체가 생성돼야만 속성의 값을 저장하기 위한 메모리 공간이 스태틱 영역이 아닌 힙 영역에 할당됨

 

MouseDriver의 main() 메서드에는 밑줄 O, Mouse 클래스의 sing() 메서드에는 밑줄 X (UML 표기법)

main()메서드는 클래스의 멤버 메서드, sing()은 객체의 멤버 메서드이기 때문

클래스 멤버객체 멤버 구분 키워드 in JAVA: static

 

Mouse mickey: Mouse 객체에 대한 참조 변수 mickey => main() 메서드 스택 프레임 안에 생성됨

new Mouse(): Mouse 클래스의 인스턴스를 하나 만들어 힙에 배치 => 생성된 객체는 힙 영역에 배치됨

대망의 할당문만 남았다! 모든 메모리는 각자의 주소를 가지고 있음!

힙 영역에 저장된 Mouse 클래스의 인스턴스(Mouse Object, Mouse 객체)의 시작 주소가 100번지라고 하면,

mickey 객체 참조 변수에 할당되는 값은? 100!

객체 참조 변수 mickey가 Mouse 객체(클래스의 인스턴스)에 대한 참조 변수! 

 

String은 객체, immutable -> 수정 불가능, 값 변경되면 주소값도 변경됨

참조연산자(.) -> 힙 상의 객체 접근, name 속성에 값 할당

 

객체 참조 변수 mickey에 null 할당 -> 힙 영역에 존재하는 Mouse 객체(:Mouse)를 참조하지 않음

-> 가비지 컬렉터(Garbage Collector)가 아무도 참조해 주지 않는 Mouse 객체(:Mouse)를 쓰레기로 인지하고 수거해감

-> Mouse 객체가 사용했던 힙 영역의 메모리 공간이 할당 해제됨

 

이전과 같은 과정을 거쳐서 새로운 Mouse 객체가 생성되고, (이전의 그 Mouse 객체와는 다름!)

객체 참조 변수 jerry에 그 주소값이 할당됨

 

Static: "고정된"; 스태틱 영역에 올라간 정보는 main() 메서드 시작 전에 올라가서 main() 메서드 종료된 후에 내려옴, 그 때까지 고정!

Stack: LIFO 자료구조 참고!

Heap: 대용량 자료를 저장할 수 있도록 메모리를 사용하는 방식, 자료구조 참고!

 

클래스 멤버 vs 객체 멤버 = static 멤버 vs 인스턴스 멤버

객체: 유일무이하게 존재하는 실체 -> 속성에 값을 가지고 있음

클래스: 개념, 분류 체계 -> 속성에 값을 가질 수는 없음

 

But, 쥐의 꼬리는 몇개인가요? -> 미키마우스의 꼬리 개수, 제리의 꼬리 개수 처럼 답이 있음!

꼬리 개수는 객체의 속성이지만, 모든 객체가 같은 값을 가지고 있기에 클래스를 통해 질문해도 답이 나옴!

-> 인스턴스 객체 수 만큼 메모리를 낭비하지 말고, 클래스에 값을 저장하자!

-> 변수 선언을 할 때 'static' 키워드 붙이기!! (그 속성은 클래스 멤버 속성이 된다. 안붙으면 객체 멤버 속성)

-> 메서드도 static 키워드 유무에 따라 클래스 멤버 메서드/객체 멤버 메서드로 분류(main()은 항상 static 붙어 있어서 클래스 멤버 메서드, 아래 sing()은 객체 멤버 메서드)

package abstraction02;

public class Mouse {
	public String name;
	public int age;
	public static int countOfTail = 1;
	// public final static int countOfTail = 1;

	public void sing() {
		System.out.println(name + " 찍찍!!!");
	}
}

-> countOfTail 속성은 T 메모리의 static 영역에 단 하나의 저장 공간을 갖게 됨!

-> 객체_참조_변수.countOfTail 로 접근 가능

 

package abstraction02;

public class MouseDriver {
    public static void main(String[] args) {
        Mouse.countOfTail = 1;

        Mouse mickey = new Mouse();
        Mouse jerry = new Mouse();
        Mouse mightyMouse = new Mouse();

        System.out.println(mickey.countOfTail);
        System.out.println(jerry.countOfTail);
        System.out.println(mightyMouse.countOfTail);

        System.out.println(Mouse.countOfTail);
    }
}

클래스명.countOfTail vs 객체명.countOfTail

멤버(속성, 메서드로 이뤄짐)

클래스 멤버: static 키워드와 함께 사용되고, Static 영역에 상주하게 됨 = static(정적) 멤버

객체 멤버: 객체가 클래스의 인스턴스 = 인스턴스 멤버 = 오브젝트 멤버

* 필드 = 속성 = 프로퍼티(property)

* 함수(function) = 메서드(method)

* 변수 공간 = 메모리 공간

혼용하고 있는데 적응하자!

 

정적 멤버: 객체 아닌 클래스에 속해 있음. 클래스는 JVM 구동 시 T메모리의 Static 영역에 바로 배치 -> 객체 존재 여부에 관계 없이 쓸 수 있음

정적 멤버 속성: 해당 클래스의 모든 객체들이 같은 값을 가질 때 사용하는게 기본!

정적 메서드: 객체들의 존재 여부에 관계없이 쓸 수 있는 메서드

- main은 반드시 정적 메서드여야 함. T 메모리가 초기화된 순간 객체는 하나도 존재하지 않음, 객체 멤버 메서드를 바로 실행할 수 없다!)

- 정적 변수에 대한 접근자 메서드(getter), 설정자 메서드(setter)

- 실무에서는 클래스의 인스턴스를 만들지 않고 사용하게 되는 유틸리티성 메서드를 주로 정적 메서드로 구현 ex. Math 클래스

- UML 표기법에서는 정적 멤버에 대해 밑줄 사용해 표시

- 정적 속성인 경우 T 메모리의 Static 영역에 클래스가 배치될 때, 클래스 내부에 메모리 공간이 확보됨!!

-> 객체 속성은 속성명만 있지 실제 메모리 공간은 확보하지 않음

-> 객체 속성은 힙 영역에 객체가 생성되면 바로 그때 힙 영역의 각 객체 안에 멤버 속성을 위한 메모리 공간이 할당

 

지역 변수: Stack 영역에 생기는 변수들

-> 개발자가 별도로 초기화하지 않으면 쓰레기 값(더미 값) 갖게 됨

 

클래스 속성 & 객체 속성

-> 별도의 초기화를 해주지 않아도 타입에 따라 자동으로 아래와 같이 초기화해줌.

정수형 0

부동 소수점 0.0

논리형 false

객체 null

 

지역 변수는 별도 초기화/ 멤버 변수(속성)는 왜 자동 초기화??

-> 지역 변수는 한 지역에서 쓰는 변수 But, 멤버 변수는 공유 변수의 성격을 가지고 있기 때문

 

객체 변수: 하나의 객체 안에서 다수의 객체 메서드가 공유하는 변수

클래스 변수: 전역 변수로서 프로그램 어디서든 접근 가능한 공유 변수

 

이러한 공유 변수의 초기화는 누가?

보통, 객체 멤버 => 생성자를 통해, 정적 멤버 => 정적 실행 영역을 통해 초기화

But, 공유변수를 딱히 누가 초기화해야한다고 규정할 수는 없음

그래서 공유 변수는 별도로 초기화를 해주지 않아도 기본값으로 초기화됨

 

지역변수는 당연히 한 지역에서만 사용되고 소멸되는 변수이기에 그 지역에서 초기화하는 것이 논리적으로 맞음.

 


상속: 재사용 + 확장

객체 지향의 상속에 대한 잘못된 예 - 계층도/조직도(부모-자식이 아님!!!)

영어 단어를 그대로 옮기면서 생긴 오해

 

재사용의 확장으로 이해하는 것이 맞다!!!

객체 지향 상속에 대한 올바른 예 - 분류도

객체 지향에서의 상속은 상위 클래스의 특성을 하위 클래스에서 상속(특성 상속)하고, 거기에 필요한 특성을 추가, 즉 확장해서 사용할 수 있다는 의미(확장, 세분화)

 

상속은 부모 클래스 - 자식 클래스보다는

상위 클래스-하위 클래스 or 슈퍼 클래스 - 서브 클래스 라고 표현하자!!

 

상위 분류-하위 분류 or 집합(포함, 세분화, 분류 -> 벤 다이어그램)이라고 이해해도 됨

상위 클래스 쪽으로 갈수록 추상화, 일반화됐다고 말하고,

하위 클래스 쪽으로 갈수록 구체화, 특수화 됐다고 함

 

상속 관계에서 반드시 만족해야할 문장

  • 하위 클래스는 상위 클래스다.

ex) 포유류는 동물이다. 고래는 포유류다. 고래는 동물이다.

 

객체 지향 설계 5원칙 가운데 LSP(리스코프 치환 원칙)를 나타내는 말(자세한건 5장)

 

동물 뽀로로 = new 펭귄();

// 펭귄을 낳으니 동물 역할을 하는 뽀로로라 이름 지었다.

 

extends in Java 키워드~

 

상속의 강력함

객체 참조 변수는 결국 객체를 참조하게 되는데 객체 특성은 유일무이한 것

클래스명은 클래스 답게, 객체 참조 변수명은 객체답게 지어야 함!

즉, 클래스명은 분류스럽게, 객체 참조 변수명은 유일무이한 사물처럼 작명해야함.

 

이런 작은 논리들이 모여 결국 큰 프로그램을 만들게 됨!

영어 잘하는 분들이 Bird는 집단, 즉 클래스, bird라고 소문자로 쓰면 객체라고 한다면 수긍한다고 한다ㅋㅋㅋㅋㅋ

필자가 존경하는 한 프로그래머(미쿡사람)는 Bird aBird 또는 Bird theBird 형태로 코딩한다고 함ㅋㅋㅋㅋㅋㅋㅋㅋㅋ신박하다!!!

 

package inheritance01;

public class Driver03 {
	public static void main(String[] args) {
		동물[] animals = new 동물[7];

		animals[0] = new 동물();
		animals[1] = new 포유류();
		animals[2] = new 조류();
		animals[3] = new 고래();
		animals[4] = new 박쥐();
		animals[5] = new 참새();
		animals[6] = new 펭귄();

		for (int index = 0; index < animals.length; index++) {
			animals[index].showMe();
		}
	}
}

상속으로 인한 극강의 감동!!

하위 클래스는 상위 클래스다!! 포유류를 한마리 동물이라 하는데 이견 있음??

 

클래스 상속 구조에서 최상위 클래스 Object

모든 클래스는 결국 Object의 특성을 물려 받음.

어떤 클래스의 인스턴스든 상관없이 개발자는 toString() 메서드를 사용할 수 있음!

 

상속은 is a 관계를 만족해야 한다?

 

하위 클래스 is a 상위 클래스??

ex) 펭귄 is a 동물 -> "펭귄은 한 마리 동물이다"

펭귄도 클래스, 동물도 클래스, '한 마리 동물'은 객체! ?!

 

하위 클래스는 분류/집단. 상위 클래스도 분류/집단

그러나 "하나의 상위 클래스"는 하나의 객체!

삼단 논법에 의거, "하위 클래스는 하나의 객체(유일무이한 존재)다" ?! 이렇게 되면 논리가 성립되지 않음!

 

is a 관계는 객체(클래스의 인스턴스)와 클래스의 관계로 오해될 소지가 많음

상속 관계의 더 명확한 영어 표현은 is a kind of

하위 클래스 is a kind of 상위 클래스

ex) 펭귄 is a kind of 조류 -> 펭귄은 조류의 한 분류다.

 

정리하자면,

  • 객체 지향의 상속은 상위 클래스의 특성을 재사용하는 것
  • 객체 지향의 상속은 상위 클래스의 특성을 확장하는 것
  • 객체 지향의 상속은 is a kind of 관계를 만족해야함

 

다중 상속과 자바

왜 자바는 다중 상속을 지원하지 않을까?!

C++를 계승/발전/단순화하면서 다중 상속을 빼버린 이유에 대해 살펴보자

 

다중 상속의 다이아몬드 문제

ex)인어는 사람과 물고기를 상속한다고 생각해보자

사람도 수영할 수 있고, 물고기도 수영할 수 있음.

인어에게 "수영해!"라고 한다면 사람처럼 팔과 다리를 저어 수영해야 할까? 아니면 물고기처럼 가슴, 등, 꼬리 지느러미로 헤엄쳐야 할까?

 

다중 상속은 득실 관계에서 실이 더 많았기에 자바와 C#은 과감히 다중 상속을 포기!

대신, C++에는 없는 인터페이스를 도입해 다중 상속의 득은 취하고 실은 과감히 버림

 

상속과 인터페이스

 

상속 관계: 하위 클래스 is a kind of 상위 클래스

해석: 하위 클래스는 상위 클래스의 한 분류다.

ex) 고래는 동물의 한 분류다.

 

인터페이스: 구현 클래스 is able to 인터페이스

해석: 구현 클래스는 인터페이스할 수 있다

ex) 고래는 헤엄칠 수 있다.

 

인터페이스는 be able to, 즉 "무엇을 할 수 있는"이라는 표현 형태로 만드는 것이 좋다!!

자바 API에서도 이러한 be able to 형식의 인터페이스를 많이 볼 수 있음

Serializable 인터페이스: 직렬화할 수 있는

Cloneable 인터페이스: 복제할 수 있는

Comparable 인터페이스: 비교할 수 있는

Runnable 인터페이스: 실행할 수 있는

 

상위 클래스하위 클래스에게 특성(속성과 메서드)을 상속해주고,

인터페이스클래스가 '무엇을 할 수 있다'라고 하는 기능을 구현하도록 강제함.

-> 상위 클래스는 물려줄 특성이 풍성할 수록 좋고, (LSP 리스코프 치환 원칙에 따른 이유)

-> 인터페이스는 구현을 강제할 메서드의 개수가 적을수록 좋다 (ISP 인터페이스 분할 원칙에 따른 이유)

 

package inheritance02;

public class 고래 extends 포유류 implements 헤엄칠수있는 {
	고래() {
    	myClass="고래";
    }
    
    @Override
    public void swim() {
    	System.out.println(myClass + " 수영 중, 어프!!! 어프!!!");
    }
}
package inheritance02;

public class Driver {
	public static void main(String[] args) {
		날수있는 날라리1 = new 박쥐();
		날라리1.fly();

		날수있는 날라리2 = new 참새();
		날라리2.fly();

		헤엄칠수있는[] 맥주병들 = new 헤엄칠수있는[2];

		맥주병들[0] = new 고래();
		맥주병들[1] = new 펭귄();

		for (헤엄칠수있는 맥주병 : 맥주병들) {
			맥주병.swim();
		}
	}
}

객체 참조 변수의 자료형이 상위 클래스(포유류)가 아니라 인터 페이스(날수있는)네...???

-> fly() 메서드는 인터페이스에 있기 때문!

 

상속과 UML 표기법

두 클래스 간의 상속 표현: 하위 클래스에서 상위 클래스 쪽으로 화살표 그림. 화살표는 속이 비어있고, 닫힌 삼각형 머리, 실선 꼬리

클래스가 인터페이스를 구현한 경우: 인터페이스를 구현하는 클래스에서 인터페이스 쪽으로 화살표. 꼬리가 점선. 약식 표기로 막대사탕을 사용하기도 함

 

상속과 T 메모리

package inheritance03;

public class Driver {
	public static void main(String[] args) {
		Penguin pororo = new Penguin();

		pororo.name = "뽀로로";
		pororo.habitat = "남극";

		pororo.showName();
		pororo.showHabitat();

		Animal pingu = new Penguin();

		pingu.name = "핑구";
		// pingu.habitat = "EBS";

		pingu.showName();
		// pingu.showHabitat();

		// Penguin happyfeet = new Animal();
	}
}

힙 영역에 Penguin 클래스의 인스턴스 뿐 아니라 Animal 클래스의 인스턴스도 함께 생김!!

하위 클래스의 인스턴스가 생성될 때 상위 클래스의 인스턴스도 함께 생성된다!!!

그림에서는 생략했지만 모든 클래스의 최상위 클래스인 Object 클래스의 인스턴스도 함께 생성됨!

 

pingu 객체 참조 변수가 가리키고 있는 것은 Animal 인스턴스!

pingu 객체 참조 변수는 사실 펭귄이지만, 자신이 펭귄이라는 사실을 모르고 있음.

따라서 펭귄의 habitat 속성과 showHabitat() 메서드를 사용할 수 없음

(명시적 형변환 연산(Casting), 암묵적 형변환(Promotion)) -> 추후 게시글에 정리!


다형성: 사용 편의성

객체 지향에서 다형성: 오버라이딩(overriding), 오버로딩(overloading)

물론, 상위 클래스와 하위 클래스 사이에서도 다형성 이야기 할 수 있음. 인터페이스와 그것의 구현 클래스 사이에서도 다형성 얘기 가능.

But, 가장 기본은 오버라이딩과 오버로딩!

(참고: 오버로딩이 다형성인지 아닌지에 대해서는 이견 있음)

 

 

오버라이딩? 오버 로딩?

 

오버 라이딩(ride: 올라타다): 같은 메서드 이름, 같은 인자 목록으로 상위 클래스의 메서드를 재정의

오버 로딩(load: 적재하다): 같은 메서드 이름, 다른 인자 목록으로 다수의 메서드를 중복 정의

 

인공위성에서 내려볼 때 오버라이딩(올라타기)된 경우, 맨 위에 올라탄 존재만 보임

오버로딩(옆으로 적재하기)된 경우, 옆으로 적재된 적재물이 다 보임

package polymorphism01;

public class Penguin extends Animal {
	public String habitat;	

	public void showHabitat() {
		System.out.printf("%s는 %s에 살아\n", name, habitat);
	}

	//오버라이딩 - 재정의: 상위클래스의 메서드와 같은 메서드 이름, 같은 인자 리스트
	public void showName() {
		System.out.println("어머 내 이름은 알아서 뭐하게요?");
	}

	// 오버로딩 - 중복정의: 같은 메서드 이름, 다른 인자 리스트
	public void showName(String yourName) {
		System.out.printf("%s 안녕, 나는 %s라고 해\n", yourName, name);
	}
}

 

다형성과 T 메모리

Penguin 클래스가 상위 클래스인 Animal 클래스의 showName() 메서드를 오버라이딩(재정의)

showName(yourName: String) 메서드를 오버로딩(중복 정의)함

 

pingu 객체 참조 변수 타입은 Animal

그럼에도 그림에서 보면 Animal 객체의 showName()은 Penguin 객체의 showName()에 의해 가려져 있음

따라서, pingu.showName() 메서드를 실행하면 Penguin 객체에 의해 재정의된 showName() 메서드가 실행됨

=> 상위 클래스 타입의 객체 참조 변수를 사용하더라도 하위 클래스에서 오버라이딩(재정의)한 메서드가 호출된다!

 

다형성이 지원되지 않는 언어

다형성이 지원되지 않으면 같은 이름의 함수를 추가로 만들 수 없음.

addIntDouble(int, double)

addDouleInt(double, int)

* 리턴 타입은 오버로딩에 영향주지 않음

* 매개 변수 순서는 오버로딩으로 간주!

* 메소드 시그니처(method signature)가 같아야함: 메소드의 선언부에 명시되는 매개변수의 리스트(매개 변수의 개수, 타입, 그 순서까지 같다면 두 메소드의 시그니처는 같다고 할 수 있음)

 

오버로딩은 함수명 하나를 가지고 인자 목록만 달리하면 됨! -> 사용 편의성

자바 5에서 추가된 제네릭을 이용하면 하나의 함수만 구현해도 다수의 함수를 구현한 효과를 낼 수 있음

 

오버라이딩도 하위 클래스가 재정의한 메서드를 알아서 호출해줌으로써

형변환이나 instanceof 연산자를 써서 하위 클래스가 무엇인지 신경쓰지 않아도 됨.

상위 클래스 타입의 객체 참조 변수에서 하위 클래스가 오버라이딩한 메서드를 자동으로 호출해줌으로써 깔끔한 코드 유지 가능

 

-> 다형성은 개발자가 프로그램을 작성할 때 사용 편의성을 줌


캡슐화: 정보 은닉

접근 제어자 in JAVA: private, [default], protected, public/ 접근자(getter), 생성자(setter) 메서드

접근 제어자가 객체 멤버(인스턴스 멤버), 정적 멤버(클래스 멤버)와 함께 쓰일 때를 비교!

객체 멤버의 접근 제어자

당연한 얘기지만, 자신의 멤버가 아닌 다른 객체의 멤버에 접근하는 경우에는 다른 객체를 생성한 후 접근해야함.

UML 표기법에서 private 접근 제어자(-), [default] 접근 제어자(~), protected 접근 제어자(#), public 접근 제어자(+)

속성이나 메서드 아래 밑줄을 사용한 경우는 정적 멤버(Static)

 

 

정적 속성인 priSt, defSt, proSt, pubSt

 

접근제어자는 절대 단순하지 않음!

객체 멤버에 대한 접근인가, 정적 멤버에 대한 접근인가에 따라 생각할게 많아짐

protected는 자신과 상속 관계에 있는 서브 클래스만 접근 가능할 뿐만 아니라 같은 패키지라면 한 집에 산다고 생각하기에 접근 가능함!!

 

aaa.jar 파일 안에 packageOne 패키지가 있고, bbb.jar 파일 안에 같은 이름을 가진 packageOne 패키지가 있음

-> bbb.jar 파일 내부의 packageOne 패키지 내 클래스나 객체에서,

aaa.jar 파일 내부의 packageOne 패키지 내 클래스나 객체가 가진 public 멤버, [default] 멤버, protected 멤버에 자유롭게 접근할 수 있음

  • 상속을 받지 않았다면, 객체 멤버는 객체를 생성한 후 객체 참조 변수를 이용해 접근해야 함
    • 상속 여부와 상관없이, static 함수에서, 객체 멤버는 객체를 생성한 후 객체 참조 변수를 이용해 접근해야함
    • 객체 참조 변수를 통해 정적 멤버도 접근 가능하지만, 권장하지 않음
    • 상속 받았다면, default 함수는 그냥 변수 접근 가능, this.변수 도 가능(private 변수는 본인 제외 접근 불가, default 변수는 같은 패키지 아니면 접근 불가)
    • 참고링크
  • 정적 멤버 클래스명.정적멤버 형식으로 접근하는 것을 권장
    • public 정적 속성인 pubSt의 경우, 각 위치별 객체 멤버 메서드에서 접근할 수 있는 방법은 무려 세가지!
      • 클래스를 통해 접근, 그냥 변수 호출, this로 접근
      • 즉, "클래스명.정적멤버" 형식으로 접근해야하는 이유: 일관된 형식으로 접근하기 위해서
      • 객체 생성한 경우, "객체참조변수명.정적멤버" 형태로도 접근할 수 있음. But 권장 X

 

  • 메모리의 물리적 접근에 따른 이유!
    • 클래스명. 정적 멤버로 접근하는게 더 가깝고 빠름.

참조 변수의 복사

Call By Value(값에 의한 호출): 두 개의 변수는 서로에게 영향을 주지 않음

a가 가진 값이 단순히 b에 복사됨 -> a, b 변수는 아무런 관계가 없음

package reference;

public class CallByValue {
	public static void main(String[] args) {
		int a = 10;
		int b = a;
		
		b = 20;
		
		System.out.println(a); // 10
		System.out.println(b); // 20
	}
}

 

기본 자료형이 아닌 객체를 저장하고 있는 객체 참조 변수를 복사하는 경우는??

Call By Reference(참조에 의한 호출) 또는 Call By Address(주소에 의한 호출)

package reference;

public class CallByReference {
	public static void main(String[] args) {
		Animal ref_a = new Animal();
		Animal ref_b = ref_a;
		
		ref_a.age = 10;
		ref_b.age = 20;
		
		System.out.println(ref_a.age); // 20
		System.out.println(ref_b.age); // 20
	}
}

class Animal {
	public int age;
}

 

Call By Value와 Call By Reference는 본질적인 차이는 X

차이라면, 기본 자료형 변수는 저장하고 있는 값을 그 값 자체로 해석

객체 참조 변수는 저장하고 있는 값을 주소로 해석!

=> 결국, 변수가 가진 값이 그대로 복사됨

화살표 대신 주소값(ex. 100번지)이 저장되어 있다고 보면 된다.

 

객체에 대한 고유값을 보여주는 코드

ref_a, ref_b는 결국 같은 값을 가지고 있음을 알 수 있음!

package reference;

public class CallByReference2 {
	public static void main(String[] args) {
		Animal ref_a = new Animal();
		Animal ref_b = ref_a;

		System.out.println(ref_a); // reference.Animal@15db9742
		System.out.println(ref_b); // 바로 위와 같은 값이 출력된다.
	}
}

ref_a, ref_b는 완전히 다른 변수! 다만, 같은 값을 가지고 있고, 컴퓨터는 그 값을 주소로서 활용

-> 두 변수가 같은 객체를 참조하고 있을 뿐! 참조하고 있는 객체가 같으니, 참조하고 있는 객체의 변화에 함께 영향 받음

 

Call By Value, Call By Reference를 다르다고 이해하기 보다,

기본 자료형 변수는 저장하고 있는 값을 그 값 자체로 판단하고,

참조 변수는 저장하고 있는 값을 주소로 판단한다고 이해하는 것이 쉽다!!

 

변수를 메서드의 인자, 반환값으로 사용하는 경우도 동일!

 

  • 기본 자료형 변수는 값을 그 자체로 판단
  • 참조 자료형 변수는 값을 주소, 즉 포인터로 판단
  • 기본 자료형 변수를 복사할 때, 참조 자료형 변수를 복사할 때 일어나는 일은 같음.
    • 즉, 가지고 있는 값을 그대로 복사해서 넘겨준다

 

정리: 자바 키워드와 OOP 4대 특성

캡 상추다!!!