Java 이해하기

김태성·2024년 7월 11일

개인 프로젝트-1

목록 보기
9/53
post-thumbnail

서론

우리는 어떤것을 배울때 순서를 잘 정해야 한다.
흐름을 알지 못하고 디테일을 파기 시작하면 어느순간 길을 잃게 되고,
흐름을 잡았음에도 깊이 파고들지 못하면 겉햝기로 끝나게 된다.

그래서 java를 어떻게 공부해야 할까 고민을 하며, 책을 참고해 순서를 정리해봤다.
이전글에 적힌 내용을 포함한 순서이다.
참고 : 스프링 입문을 위한 자바 객체지향의 원리와 이해(개구리책)

  1. JAVA 구동 - JVM, JRE, JDK
  2. JAVA의 특징
  3. 메모리와 멀티 스레드/멀티프로세스 이해 (스태틱, 스택, 힙)
  4. OOP 객체란 무엇인가?
  5. OOP의 특징
  6. SOLID

이후의 스프링 삼각형(IoC/DI , AOP , PSA , POJO)는 다음 글에 쓰도록 하겠다.

위와 같은 순서를 선택한 이유는 다음과 같다.

  • 시작은 전체를 훑어보며 어떤 특징을 가지고, 어떻게 실행되는지를 알아본다.
  • Java를 이해하는데 OOP는 큰 부분을 차지하는데, 이를 이해하기 위해서 하위 단계부터 OOP까지 공부하는게 좋다는 결론을 내렸다.
  • 즉, OOP 특성상 객체들의 집합일 것이고, 객체를 정확하게 이해하는것이 OOP를 이해하는데 큰 도움이 된다고 생각했다.

그럼, 드디어 Java가 무엇인지 공부해보도록 하자.
이 글은 독학으로 쓰여진 글이라 틀린 점이 있을수 있기에 최대한 공신력 있는 사이트를 사용하기 위해 노력할 것이다.

JVM , JRE , JDK

레퍼런스
https://www.geeksforgeeks.org/jvm-works-jvm-architecture/

간단하게 정리하자면 다음과 같다.

  • JDK = JRE , JVM(JRE에 포함) 및 기타 개발 툴을 가진 자바 개발용 도구
  • JRE = JVM과 여러 파일, 라이브러리 등을 가진 실행 환경
  • JVM = 자바 실행용 가상머신

즉, 자바는 JVM을 통해서 실행이 되며, 실행을 돕기 위한 JRE, 자바 개발을 위한 JDK와 연관되어 있다.

JVM

JVM의 아키텍쳐 이다.
JVM의 실행 과정에 따라서 한번 알아보자.

Class Loader Subsystem
클래스 로더는 3가지 역할을 한다.

1. 로딩
클래스 로더는 .class 파일을 읽고, 그에 해당하는 바이너리 데이터를 만들어 메서드 영역에 저장한다.
(이때 generate 라는 단어가 사용되었는데, .class 파일은 이미 바이너리 데이터이다. java코드를 JVM에서 바이너리 코드로 바꾼다고 오해하지 말자.)
설명에 덧붙여서, .class 파일(binary) 코드를 읽고, 아래의 데이터를 포함한 메타데이터를 메서드 영역에 저장한다.
그 데이터의 내용은 다음과 같다.

  • 로드된 클래스의 이름과 부모 클래스
  • .class 파일이 클래스, 인터페이스, 등과 관련이 있는지
  • modefier, variables , method 정보 등등

로딩 후 java.lang 패키지에 미리 정의된 Class 타입의 객체를 생성하여 힙 메모리에 저장한다.
이 Class 객체를 통해 클래스 수준의 정보를 얻을 수 있다.
예를 들어 클래스 이름, 부모 클래스 이름, 메서드 및 변수 정보 등이 포함된다.

일반적으로 3가지 클래스 로더가 있다.

  • BootStrap ClassLoader : JAVA_HOME/jre/lib 디렉토리에 있는 핵심 자바 API 클래스를 로드한다.

  • Extension ClassLoader : JAVA_HOME/jre/lib/ext 또는 java.ext.dirs 시스템 속성에 의해 지정된 다른 디렉토리에 있는 클래스를 로드한다.

  • System/Application ClassLoader : App Class 경로에서 Class를 로드한다. java.class.path에 매핑된 환경변수를 사용한다.

위 클래스 로더는 위가 부모이고, 아래가 자식이다.(Bootstrap -> Extension -> System/Application)

JVM은 클래스를 로드할때 위임-계층 원칙을 따른다.

  1. 요청은 System 클래스 로더가 처음 받는다.
  2. 이 요청은 Bootstrap 클래스 로더까지 전달된다.
  3. Bootstrap 클래스 로더는 Class를 찾는다. 이때 찾으면 클래스를 로드하고, 찾지 못하면 자식으로 요청을 내려보낸다.
  4. Extension 클래스로더도 마찬가지로 찾는다면 로드, 찾지 못한다면 자식 클래스로더로 요청을 내려보낸다.
  5. 이때 System/Application 클래스 로더에서도 클래스를 로드하지 못한다면 java.lang.ClassNotFoundException 런타임 예외가 발생한다.

2. 링킹
검증, 준비, 해석(선택) 단계를 수행한다.

  • 검증 : .Class 파일이 올바른지 확인한다. 검증 실패시 런타임 예외가 발생한다. 검증이 끝나면 컴파일 준비가 완료된다.
  • 준비 : JVM은 클래스 정적 변수에 대한 메모리를 할당하고, 메모리를 기본값으로 초기화한다.
  • 해석 : 타입의 상징적 참조를 직접 참조로 대체한다.

3. 초기화
모든 정적 변수에 코드에서 정의된 값과 정적 블록(있을때만)이 할당된다. 클래스 위에서 아래, 부모에서 자식순으로 실행된다.

JVM Memory

  • 메서드 영역 : 클래스 이름, 직계 부모 클래스 이름(immediate), 메서드 및 변수 정보 등 모든 클래스 수준의 정보와 정적 변수가 저장된다. JVM당 하나의 메서드 영역이 있고, 공유 자원이다.

  • 힙 영역 : 모든 객체의 정보가 저장된다. JVM당 하나의 힙 영역이 있고 공유 자원이다.

  • 스택 영역 : JVM은 각 스레드마다 하나의 런타임 스택을 생성하여 저장한다. 스레드 종료시 제거되며, 공유자원이 아니다.

  • PC 레지스터 : 스레드의 현재 실행중인 명령어의 주소를 저장한다. 각 스레드 별로 PC 레지스터를 가진다.

  • 네이티브 메서드 스택 : 네이티브 메서드 정보를 저장한다.

Execution Engine
.Class 코드를 실행한다. 바이트 코드를 한줄씩 읽고, 메모리 영역의 데이터와 정보를 사용하여 실행한다.
3가지로 분류 가능하다.

  • 인터프리터 : 바이트 코드를 한줄씩 읽고 실행한다. 여러번 호출되었을때 매번 재해석 해야한다.

  • JIT 컴파일러 : 인터프리터의 효율을 높히는데 사용된다. 전체 바이트 코드를 컴파일하고 이를 네이티브 코드로 변경하여 인터프리터가 반복 호출된 메서드가 재해석 되지 않고도 실행시킬 수 있게 도와준다. 이로 인해 효율성이 향상된다.

  • 가비지 컬렉터 : 우리가 잘 아는 가비지 컬렉터이다. 메모리 단편화를 방지하고 최적화 한다.

Java Native Interface(JNI)

JNI는 네이티브 메서드 라이브러리와 상호 작용하며 실에 필요한 네이티브 라이브러리(C,C++)를 제공한다.
이를 통해서 JVM이 C/C++ 라이브러리를 호출하고, C/C++ 라이브러리에서 호출될 수 있게 한다.

Native Method Library
네이티브 라이브러리(C,C++)의 모음이며 Execution Engine에 필요하다.

JRE , JDK

JRE , JDK에 대해서는 깊게 알아보진 않을 것이다.

JRE는 말 그대로 런타임 환경을 조성해 주는 것이고
JDK도 말 그대로 자바 개발을 위한 kit이다.

약간 다른 점이 있다면
JRE는 자바를 실행하기 위한 최소한의 라이브러리만을 가져 자바 실행 환경을 조성하는데만 사용되고,
JDK는 자바를 개발하기 위한 많은 개발 도구를 포함하고 있다는 점이다.

JRE의 Libraries에는

  • lang , i/o , date-time 등이 있고

JDK의 Tools에는

  • java , javac , javadoc , jar ... 등이 있다.

이 글의 목적은 자바의 실행 과정임으로, 런타임 환경과 jdk에 대해서는 여기까지 하겠다.

자바의 특징

래퍼런스 : https://www.geeksforgeeks.org/introduction-to-java/

  1. 플랫폼 독립적, 이식성, 어디에서도 실행 가능
  • JAVA 코드는 바이트 코드로 변환 후 JVM으로 실행한다.
  • 컴퓨터의 하드웨어/OS와 통신하지 않고 JVM을 통해 실행됨으로, 환경에 상관없이 일정된 값을 출력한다.
  1. 객체 지향 (OOP) , 유연성
  • 주요 개념 : 추출, 캡슐화, 계승, 다양성
  • 객체 지향형이라 상당히 유연하게 동작한다고 한다. 또한 C,C++로 이루어진 네이티브 메서드를 지원한다.
  1. 간단하고 고성능
  • 포인터, 연산자 오버로딩, 다중상속, 명시적 메모리 할당과 같은 복잡한 기능이 없어서 쉽다.
  • 어디까지나 C, C++ 수준에 비해서 쉽다고 설명하는거 같다.
  • JIT 컴파일러를 통해 오버헤드를 효과적으로 줄였다.
  1. 안전 , 샌드박스 실행 , 견고함
    참고)노마드코더 : https://www.youtube.com/watch?v=wFRr-RNhyG0
  • 포인터가 없어서 범위를 벗어난 배열에 엑세스 할 수 없다.
  • C/C++에서는 포인터를 활용한 취약점이 많이 발생하는데, 이러한 점을 말하는거 같다.
  • 또한 바이트코드 검증기를 통해서 실행되기에 추가적인 보안을 제공한다.
  • 컴파일러가 오류를 잘 잡아낸다.
  • 가비지컬렉터, 예외처리, 메모리 할당은 JAVA를 견고하게 만들어준다.
  1. 분산(distribution) - EJB
  • 원격 메서드 호출 및 Enterprise Java Beans가 분산 에플리케이션을 만드는데 사용된다.
  • EJB는 기업환경 시스템을 구현하기 위한 서버측 컴포넌트 모델이다. - wiki
  • 더 자세한 내용은 추후 다루도록 하겠다.(양이 엄청 많고 이해가 안된다..)
  1. 멀티스레딩
  • JAVA는 멀티스레딩을 지원한다(야호!)

Java 메모리 / 멀티스레드

메모리

위에서 봤던 메모리 영역이다.
Method Area에는 정적변수, 클래스 데이터 등이 들어가고
Stack 영역에는 런타임 스택을
Heap 영역에는 객체의 정보가 저장된다.

코드 실행 영역에는 위를 제외한 PC레지스터, 네이티브 메서드 스택 등이 포함된다.

이제 프로그램 실행에 따른 메모리 구조를 살펴보자.

(Static은 Method Area에 포함된다.)

  1. 프로그램이 실행되면 JVM을 실행한다.
  2. JVM은 목적파일을 받아 실행한다.
  3. 모든 자바 프로그램은 java.lang을 반드시 포함해야 한다. 따라서 Static에 java.lang을 포함한 필요 패키지를 추가한다.
  4. 모든 클래스, import 패키지를 스태틱 영역에 추가한다.
  5. 코드를 실행한다. 이때 실행되는 메서드들은 Stack에 추가한다.
  6. 메서드를 실행하면서 추가되는 객체들은 Heap에 추가한다.

여기서 추가적으로 주의해야 할 점들이 있다.

  • 메서드 스택들은 서로 접근하지 못한다.(다른 메서드의 변수에 접근X)
  • 메서드에 if 와 같은 블록 스택 프레임이 생길 수 있다. 이때 내부 스택 프레임은 외부 스택 프레임에 접근이 가능하지만, 반대는 불가능하다.
  • 전역변수는 스태틱에 저장된다. 하지만 사용하지 않는걸 권장한다. 이는 C++, JS, Python 등등 모든 언어에 통용되는 말이다.

멀티스레드/멀티프로세스

멀티 스레드를 설명할때 항상 나오는 말이다.
Java에서도 똑같이 통용이 된다.

  • Java의 멀티스레드는 static, heap을 공유하고, stack을 각각 처리한다.
  • 이는 스레드의 기능에서 알 수 있는데, 스레드는 단순히 하나의 명령을 처리하는 작은 단위체일 뿐이다.
  • 그렇기에 하나의 프로세스에는 스레드마다 스택이 있고, 각자 실행하는 것이다.

멀티프로세스는 말 그대로 여러개의 프로세스를 한번에 같이 돌리는 것이다.
컨텍스트 스위칭을 통해서 병렬적으로 돌린다.

안타깝게도 자바는 멀티프로세스를 지원하지 않지만, 멀티스레드는 지원한다.

다행히 관련 교육용 프로그램을 만져본 경험이 있어서 이해가 빨랐다.

OOP 객체란 무엇인가?

객체 지향 프로그래밍은 컴퓨터 프로그램을 명령어의 목록으로 보는 시각에서 벗어나 여러 개의 독립된 단위, 즉 "객체"들의 모임으로 파악하고자 하는 것이다. - wiki

관련 내용은 레퍼런스에 상세하고 정확하게 나와있으니 참고 바란다.
레퍼런스1 : 스프링 입문을 위한 자바 객체지향의 원리와 이해
레퍼런스2 : https://ko.wikipedia.org/wiki/객체 (컴퓨터 과학)
(2번 링크는 주소를 그대로 복사하셔야 합니다.)
레퍼런스3 : CSAPP
각각에 대해서는 짧게 설명해 보겠다.

객체란?

  • 우리가 보고 인지할 수 있는 그 모든것.(이라고 설명하면 도무지 이해가 되질 않는다.)

=> 클래스에서 정의한 것을 토대로 메모리(실제 저장공간)에 할당된 것으로, 프로그램에서 사용되는 데이터 또는 식별자에 의해 참조되는 공간을 의미한다. -wiki

코드를 하나 살펴보자.


public class Gamer{

	public String name;
	public void say(){
    	System.out.println(name+"는 근본 프로게이머!");
    }

}




public class Human{
	public static void main(String[] args){
    	Gamer 페이커 = new Gamer();
        
        페이커.name = "대상혁";
        페이커.say();
        
        Gamer 엠비션 = new Gamer();
        엠비션.name = "강찬용;
        엠비션.say();
        }
}

위에서 사용했던 메모리를 다시 보자.

현재 상황은, JVM을 실행하고 main() 함수를 읽은 상황이다.
그리고 Gamer이라는 Class를 static에 넣었음을 알 수 있다.
왜 Gamer는 main 안에 생성자로 쓰였기 때문에 Static에 미리 넣는 것이다.

이후, 생성자를 실행해보자.

Heap 영역에 각각의 객체들이 추가될 것이다.
여기서 잘 생각을 해야 하는 것이, Stack은 단순히 스레드의 런타임 데이터를 저장하는것 뿐이다. 멀티 스레드를 하게 될 경우, 각각의 스레드들은 개인의 스택 프레임을 가지게 된다. 이때 발생하는 중복/순서의 개념은 여기서 설명하진 않을 것이다.

그러니까, 간단히 생각하면 된다.
복잡하게 생각할 필요가 없다.

Stack은 스레드의 런타임 데이터이고
Static은 스레드가 동작하기 위한 정적변수, Class 데이터 등이며
Heap은 스레드가 동작할때 발생하고, 동작하기위해 참고하는 '객체'들이다.

왜 그런가?에 대해서 말을 하자면 그렇게 만들었기 때문이다.

뜬금없겠지만 C언어를 살펴 보자.
C언어에서 동적 메모리 할당을 위해 Heap을 사용한다.(CSAPP 9장)
즉, 프로그램 실행 시 동적인 데이터가 얼마일지 가늠이 안되니 일단 실행은 하되, 들어오는 변수를 보고 MALLOC을 통해 Heap 영역을 조절하겠다는 것이다.

여기서, 말을 조금 다르게 해석해보자.

  • 동적인 데이터 -> 객체

여기서 무엇을 말하고 싶은지 이해 가는가?
C나 Java나 둘다 특정 데이터를 Heap영역에 집어넣고,
한쪽은 동적인 데이터, 다른 한쪽은 객체라고 부르는 것이다.

다시 한번 사전적인 의미를 보자.

  • 클래스에서 정의한 것을 토대로 메모리(실제 저장공간)에 할당된 것으로, 프로그램에서 사용되는 데이터 또는 식별자에 의해 참조되는 공간을 의미한다. -wiki

그러니 여기에서 객체란

  • 페이커의 박스
  • 엠비션의 박스

가 객체가 될 것이고,
name은 각 객체의 인스턴스 변수가 될 것이다.

OOP 특징

레퍼런스 : https://www.codestates.com/blog/content/객체-지향-프로그래밍-특징
레퍼런스2 : https://www.oracle.com/java/technologies/javase/seccodeguide.html
총 4개가 있고, 다음과 같다.

  • Abstraction - 추상화
  • Encapsulation - 캡슐화
  • Inheritance - 재사용
  • Polymorphism - 다형성

1. 추상화

우리는 CS를 공부할때도 추상화라는 것을 많이 본다.

위 그림은 추상화 계층 이다. - wiki(추상화 계층)
컴퓨터는 저런 구조로 되어있습니다~ 라고 보여주는 것이다.

위 그림은 단순히 구조를 보여줄 뿐, 세부적인 내용은 보여주지 않는다.
추상화는 복잡한 자료, 모듈, 시스템 등으로부터 핵심적인 개념 또는 기능을 간추려 내는 것을 말한다. - wiki

OOP에서도 똑같은 의미로 추상화가 쓰인다.
여러 Class들에서 공통적으로 쓰이는 것들을 상위 클래스로 만들어 사용하는 것이다.
하위 클래스들은 공통부분을 가진 상위 클래스를 받아서 변형을 한 후 사용하는 것이다.


예를 들어보자.

  • 사람은 털이 있고, 온혈이며 입으로 음식을 먹고 산다.
  • 판다는 털이 있고, 온혈이며 입으로 음식을 먹고 산다.
  • 이 외 다른 ~~~들도 털이 있고, 온혈이며 입으로 음식을 먹고 산다.

그럼 이러한 공통부분들을 모아서 상위 클래스를 만들어보자.

  • 포유류는 털이 있고, 온혈이며 입으로 음식을 먹고 산다.

위의 사례에서 보면 사람/판다/~~ 들의 공통적인 부분을 가지고
포유류 라는 추상화를 그려낸 것이다.

2. 상속

위의 내용과 연결되는 이야기며, 같은 이야기라고도 볼 수 있다.

새로운 포켓몬을 한마리 만들어야 한다고 상상해보자.
그리고 이 포켓몬을 포유류 로 만들고 싶다고 가정해 보자.

포유류를 그려라고 했는데, 위 처럼 입도 없고, 털도 없으며 냉혈 동물을 그려버리면 안된다는 것이다.

이처럼, 포유류의 속성들을 이어받아 새로운 Class를 만드는것이 상속이다.
당신은 포유류 포켓몬을 만들기 위해 적어도

  • 털이 있고
  • 온혈이며
  • 입으로 음식을 먹는

포켓몬을 그려야 할 것이다.

하나 더 알아야 할 것이
추상화는 interfaceClass로 나타낼 수 있는데,
interface는 그대로 가져와야 하고
Class는 오버라이딩을 사용하여 변형 후 사용할 수 있다는 차이점이 있다.


3. 다형성

당신은 포유류에 2종류 이상 되는 생물이 포함된 다는 것을 알고있다.
그리고 그 각각의 생물들은 공통적인 부분을 가지고 있다는 것을 안다.

당신이 코딩을 한다고 가정해 보자.
당신은 어떤 포유류가 '털이있는지, 온혈인지, 입으로 밥을 먹는지' 가 궁금할 것이다.
그래서 코딩을 할려고 하는 순간 고민에 빠지게 된다.

  • 10만마리를 판별하려면 각각 예외처리를 해줘야 하나?

이러한 생각을 하겠지만, OOP에는 다형성이라는것을 지원한다.

위의 그림과 같이 특정한 class 별로 하나하나 함수를 실행시키게 된다면
종류(N)에 대하여 O(N)의 시간복잡도가 나오게 될 것이다.

다시 한번 생각해보면

  • 10만마리를 판별하려면 각각 예외처리를 해줘야 하나?

다형성을 적용시킨 예시를 보면

위와 같이 같은 추상화를 상속받은 자식 class들을 포유류 라는 상위 Interface/Class로 매개변수 타입을 지정해 줄 수 있다는 것이다.

추가로, 각각의 class마다 예외처리를 많이 하는 것을
결합도가 높다고 한다.


4. 캡슐화

캡슐화는 한줄로 설명할 수 있다.

  • 정보를 외부로 유출하지 마라

지극히 당연한 말이다.
서버를 만들었는데 정보가 술술 세어나가면 신뢰할 수 있는가?

이 캡슐화를 이해하기 위해서는 2가지를 기억하면 된다.

첫번째는 접근 제어자 이다.
말 그대로 접근 제어자는 어디에서 접근 할 수 있는지 명시하는 것이다.

  • public class ~~~

  • 접근 제한이 없는 Class입니다.

위 두 표현은 같은 표현이다.
public은 접근 제한이 없는 것
그리고 Class를 만들겠다고 선언하는 것이다.

그렇다면, 이 접근 제어자를 어떻게 써야 하는지가 주된 내용이다.
위에 있는 어린왕자의 상자를 기억하는가?
그 상자의 내용물은 작가조차 모른다.
그 뜻은, 무언가 일어나고 있다는 추상적인 내용을 알 뿐, 정확하게 무엇이 있는지는 모른다는 것이다.

만약 내가 상자에 맛있는 음식이 있으면 좋겠다고 생각해 보자.
작가조차 내가 알고있는 맛있는 음식 중 어떤것이겠거니 생각할 것이다.
하지만 정확하게 어떤 음식이, 얼마나, 온도는 몇도고 양념은 얼마나 했고
등등의 디테일한 정보는 절대 알 수 없다는 것이다.

이제 예시를 들어보자.

당신이 현금을 송금한다고 생각해 보자.
그런데 개발자가 너무나 개을러서 서버에 당신의 개인정보가 다 나온다고 가정해 보자.

모든 데이터가 private class 안에서 public 하게 오픈되어 있다는 것이다.
이때 해커가 들어와 데이터를 변조한다면...

이렇게 의도하지 않은 곳에 의도하지 않은 돈이 송금 될 수 있다는 것이다!
이 얼마나 무서운 일인가...

그래서 이렇게 보안 가이드에 하지마라고 적혀져 있다.
(출처 - seccodeguide)

SOLID

레퍼런스 : https://ko.wikipedia.org/wiki/SOLID_(객체_지향_설계)
(주소 전체를 복사해서 검색해야됨)

드디어 마지막인 SOLID에 왔다.
SOLID는 개념적인 내용이라 최대한 간결하게 설명해 보겠다.

S : 단일 책임 원칙

Single responsibility principle
하나의 클래스는 하나의 책임만 가져야 된다.

하나의 클래스가 여러 일을 하고 있다면, 기능을 분화해야 한다.
클래스 하나로 여러개 다 하지 말고 나누라는 뜻이다.

O : 개방 폐쇄 원칙

Open/closed principle
소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다.

위의 포유류 예시를 생각해보자.
판다, 사람, 기린 등등 여러가지 동물들을 추가하면 의존도가 높아지게 된다.
그럼으로 포유류 라는 상위 class를 둠으로써, 의존도를 줄이고 확장성을 높혀야 한다.

L : 리스코프 치환 원칙

Liskov substitution principle
프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다. 계약에 의한 설계를 참고하라.

간단하게 말해서

  • 판다의 상위 class에는 포유류가 있다.
  • 이때 하위 class인 판다는 상위 class인 포유류로 바꿀 수 있어야 한다.

I : 인터페이스 분리 원칙

Interface segregation principle
특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다.

첫번째 단일 책임 원칙과도 비슷하다.
하나의 인터페이스는 하나의 일만 하라는 것이다.
여러개 쓰면 복잡해져서 유지/보수하는것도 힘들어진다.

D : 의존관계 역전 원칙

Dependency inversion principle
프로그래머는 추상화에 의존해야지, 구체화에 의존하면 안된다. 의존성 주입은 이 원칙을 따르는 방법 중 하나다.

구체적인 것에 의존성을 부여하지 마라는 것이다.
위의 포유류 예시를 다시한번 사용하면
판다, 사람, 등등 하위 객체에 의존성을 집어넣지 말고
포유류에 의존성을 넣어라는 것이다.









여담

드디어... 드디어 끝이 났다...
여기까지 적는데 온갖 사이트를 뒤지면서 이게 무슨말인지, 맞는 말인지 이해하고 검증하는데 시간이 장난아니게 걸렸다.

이제 기본적인 java에 대한 이해는 끝났으니, Spring 프레임워크를 공부해 보자.

profile
닭이 되고싶은 병아리

0개의 댓글