ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • (김영한의 실전 자바 - 기본편) 4 - 내용 정리
    카테고리 없음 2024. 5. 28. 15:15

    [1. 클래스와 데이터]

    클래스에 정의된 변수들 = 멤버변수, 필드
    멤버 변수 : 특정 클래스에 소속된 멤버이기 때문에
    필드 : 데이터 항목을 가르키는 전통적인 용어

    사용자가 직접 정의하는 사용자 정의 타입을 위해서는 설계도가 필요한데,
    그게 바로 클래스이다. 

    클래스를 사용해서 실제 메모리에 만들어진 실체를 객체 또는 인스턴스라 한다. 

    new 를 사용하면 객체(인스턴스)를 만들 수 있다. 

    Student student1 에서 Student 는 student1의 변수 타입이다. 

    객체는 클래스에서 정의한 속성과 기능을 가진 실체이다. 

    인스턴스랑 객체랑 같은 말이지만 관계를 명확히 할 떄는 인스턴스라는
    말을 많이 사용한다. 
    ex) student1은 Student 의 인스턴스이다. 

    모든 인스턴스는 객체이지만 우리가 인스턴스라고 부르는 순간은 
    특정 클래스로부터 그 객체가 생성되었음을 강조하고 싶을 떄

     

    [2. 기본형과 참조형]
    * 변수 종류
    - 멤버 변수(필드) : 클래스에 선언

    ※ 멤버 변수는 추후 static 여부에 따라 클래스 변수, 인스턴스 변수 2가지로 나뉜다. 

    - 지역 변수 : 메서드에 선언, 매개변수도 지역 변수의 한 종류

     

    [3. 객체 지향 프로그래밍]

    * 모듈화
    레고 블럭과 같다. 즉, 필요한 블럭을 가져다 꼽아서 사용할 수 있다. 

    * 캡슐화
    속성과 기능을 하나로(마치 하나의 캡슐에 쌓여있는 것처럼) 묶어서 필요한 기능을 메서드를 통해 외부에 제공하는 것

    즉, 외부에 보일 필요가 없는 데이터나 메서드는 숨기는 것이 좋다. 

     

    [4. 생성자]
    * 생성자
    생성자 이름은 클래스 이름과 같아야 한다. (대문자 시작)
    생성자 반환 타입 없다.
    이외는 메서드와 같다.

    제약 - 생성자는 호출 필수이다.
    직접 정의한 생성자가 있다면 반드시 호출해야 한다. 

    생성자는 반드시 호출되어야 한다.
    생성자가 없으면 기본 생성자가 제공된다.
    생성자가 하나라도 있으면 기본 생성자가 제공되지 않는다. 
    따라서 개발자가 정의한 생성자를 직접 호출해야 한다. 

    this() 기능 사용하면 생성자 내부에 자신의 생성자 호출 가능하다. 
    this 는 인스턴스 자신의 참조값을 가리킨다. 

     this()는 생성자 코드의 첫 줄에만 작성할 수 있다.

     

    [6. 접근제어자]
    접근제어자를 통해 데이터를 온전히 통제할 수 있다. 

    - private : 모든 외부 호출을 막는다. 
    - default(package-private) : 같은 패키지 안에서 호출은 허용한다. (아무것도 적지 않으면 디폴트)
    - protected : 같은 패키지 안에서 호출은 허용한다. 패키지가 달라도 상속 관계의 호출은 허용한다. 
    - public : 모든 외부 호출을 허용한다. 

     private(모두 차단, 클래스) -> default(패키지) -> protected(패키지 + 상속) -> public(모두 허용)

    클래스, 필드, 생성자, 메서드 접근제어자 사용 가능
    지역변수에서는 불가능

    클래스 레벨의 접근제어자는 public, default만 사용 가능하다. 
    public 클래스는 파일 이름이랑 같아야 한다. 
    하나의 자바 파일에 public 클래스는 하나만 등장 가능
    하나의 자바 파일에 default 접근 제어자를 사용하는 클래스는 무한정 만들 수 있다. 

     

    [7. 자바 메모리 구조와 static]
    - 메서드 : 클래스 정보 보관, 클래스 실행 코드, 필드, 메서드와 생성자 코드 ex) 붕어빵 틀

    ex) 클래스 정보(클래스, 메서드 등의 실행코드), static 영역, 상수 풀
    - 스택 : 실제 프로그램이 실행되는 영역, 메서드 실행 시 하나씩 쌓인다.

    ex) 지역변수(매개 변수 포함)
    - 힙: 객체(인스턴스) 생성 영역 , new 명령어, 배열도 해당 영역 생성 ex) 붕어빵 

    ex) 객체(인스턴스), 배열 -> 가비지 컬렉션(GC) 더이상 참조되지 않는 객체 제거

     

    인스턴스의 변수는 힙영역에 저장되지만(값이 다 다르기 때문)
    메서드는 메서드 영역에 저장된다. 

    - 스택 (LIFO) 후입선출 : 마지막에 넣은 게 가장 먼저 나온다.
    - 큐(FIFO) 선입선출 : 가장 먼저 넣은 게 가장 먼저 나오다. ex) 선착순

    메서드 호출에는 스택 구조가 적합

    자바는 스택 영역을 사용해서 메서드 호출과 지역 변수(매개 변수)를 관리
    지역 변수(매개 변수 포함)는 스택 영역에서 관리한다. 


    static은 주로 멤버변수(필드)와 메서드에서 사용
    static 키워드 사용 시 공용으로 함께 사용하는 변수 만들  수 있다. 
    static 변수 = 정적 변수 = 클래스 변수 (클래스 영역에서 관리해서 클래스 변수라 불림)

    Data3.count 클래스에 직접 접근해서 쓰는 것처럼 보인다. (클래스명 + . + 변수명)

    static이 붙은 멤버 변수는 메서드 영역에서 관리한다 (힙 영역x)

    static 변수는 클래스인 붕어빵 틀이 관리하는 특별히 관리하는 변수이다.
    붕어빵 틀은 1개이므로 클래스 변수도 하나만 존재한다. 

    멤버변수는 2가지로 나뉜다. 
    1. 인스턴스 변수 : static이 붙지 않은 멤버 변수
    2. 클래스 변수 : static이 붙은 멤버 변수
    클래스에 바로 접근 가능하여 사용 가능하고, 

    클래스 자체에 소속 여러 곳에서 공유하는 목적으로 사용

    * 변수와 생명주기

    - 클래스 변수 : 메서드 영역 / static 영역 보관 / JVM --> 프로그램 종료까지, 가장 길다.
    - 지역변수(매개변수) : 스택영역  --> 메서드 호출 끝나면 사라짐
    - 인스턴스변수 : 힙 영역 / GC  활동  --> 인스턴스 생존 기간(소속 인스턴스가 GC 되기 전까지 존재)

    static 메서드는 객체 생성 없이 클래스에 있는 메서드 바로 호출 가능
    static 메서드에서 인스턴스 변수 및 메서드에 접근 불가능  --> 인스턴스에 대한 참조값을 모르기 때문이다. 

    인스턴스 변수 및 메서드에서 static 변수 및 메서드 접근 가능

    * 멤버 메서드 : 클래스에 정의된 메서드
    1. 인스턴스 메서드 : static이 붙지 않은 멤버 메서드
    2. 클래스 메서드 : static이 붙은 멤버 메서드

    정적 메서드 활용
    정적 메서드는 객체 생성 필요 없이 메서드의 호출만으로 필요한 기능을 수행할 떄
     
    main()은 정적 메서드이다.
    따라서 main 안에서 호출하는 메서드는 정적 메서드만 호출이 가능하다. 

     

    ※ 예제 문제 참고
    최솟값 최댓값 비교할때는 첫 번째 값과 비교한 후 그 값을 대체시키자!!

     

    [8. final]
    final 키워드가 붙으면 더 값을 변경할 수 없다. 

    static final 은 변하지 않는(final) 공통된 값(static) = 상수

    상수 : 변하지 않고 항상 일정한 값을 갖는 수 = static final
    1. 상수값을 이용하면 값을 매번 변경하지 않고 중앙 관리적으로 값을 변경하여 사용 가능 ex) MAX_USER
    2. 변수명만 보고도 추론 가능

    참조형 변수에 final이 붙어도 참조 대상의 객체 값은 변경할 수 있다. 
    final Data data = new Data();
    data = new Data() 불가
    data.value = 10;
    data.value = 20; 가능
    즉 참조 대상 자체를 다른 대상으로 변경 못하는 것이고, 참조하는 대상의 값은 변경 가능하다. 

     

    [9. 상속]
    - 메서드 오버로딩(=과적) : 같은 이름의 메서드를 여러개 정의
    - 메서드 오버라이딩 : 무언가를 넘어서 타는 것 , 자식의 새로운 기능이 
    부모의 기존 기능을 넘어 타서 기존 기능을 새로운 기능으로 덮어버린다. 
    무언가를 다시 정의
    오버라이딩 : 부모에게 상속받은 기능을 자식이 재정의 한다. 

    * 메서드 오버라이딩 조건
    메서드 이름이 같아야 함
    메서드 매개변수(파라미터) 타입 순서 ,개수가 같아야 한다.
    반환 타입이 같아야 한다. 
    접근제어자 : 오버라이딩 메서드 접근제어자는 상위클래스의 메서드보다 제한적이어서는 안된다.
    ex) 상위가 protected 이면 하위 클래스에서 protected public 는 가능하나 private default 이면 안된다.  
    예외 : 오버라이딩 메서드는 상위 클래스의 메서드보다 더 많은 체크 예외를 throws 로 선언할 수 없다. (추후 배운다)
    static, final, private 키워드가 붙은 메서드 오버라이딩 안된다.
    static 은 클래스 레벨에서 작동하므로 인스턴스 레벨에서 사용하는 오버라이딩 의미가 없다. 
    생성자 오버라이딩 할 수 없다. 

    * super (= 부모 참조)
    부모 클래스에 대한 참조를 나타낸다.
    this 는 자기 자신(인스턴스)의 참조
    super는 부모 참조를 나타낸다.

     

    *  클래스와 메서드에 사용되는 final
    class에 final : 상속 끝 -> 확장 불가
    메서드에 final : 오버라이딩 끝 -> 오버라이드 불가

    좀 더 추상화 된 개념을 상속화 시켜서 같은 기능을 같이 사용

     

    [10. 다형성1]

    다형성이란 한 객체가 여러 타입의 객체로 취급될 수 있는 능력

    ★다형적(다양하게, 여러개) 참조

    부모 변수가 자식 인스턴스 참조(다형적 참조)
    부모 타입은 자식 변수를 참조할 수 있다. 

     


    Parent poly = new Child();

    Child 인스턴스를 생성해서 메모리 상 Child 와 Parent 모두 생성
    생성된 참조값 x001을 Parent 타입의 변수 poly 에 담아둔다.

    부모는 자식을 담을 수 있다. 
    반대로 자식 타입은 부모 타입을 담을 수 없다.  

    따라서 Parent에 있는 parentMethod 만 사용 가능하다.
    즉, 자식의 기능은 호출할 수 없다. 

    자바에서 부모 타입은 물론이고, 자신을 기준으로 모든 자식 타입을 참조할 수 있다. 

    바로 다양한 형태를 참조할 수 있다 해서 다형적 참조라 한다. 

    poly.childMethod() 가 안되는 이유는 poly가 Parent 타입이다. 
    따라서 Parent 클래스부터 시작해서 필요한 기능을 찾는다.
    그러나 상속 관계는 부모 방향으로 찾아 올라가는 것은 가능하지만,
    자식 방향으로 내려갈 수는 없다.
    Parent는 부모 타입이고 상위에 부모가 없어서 childMethod()를 찾을 수 없다.

    즉, 다형적 참조의 핵심은 부모는 자식을 품을 수 있다. 

     

    여기서 childMethod()를 사용하고 싶다면 다운캐스팅이 필요하다. 

    다운캐스팅(부모 타입 -> 자식 타입)
    특정 타입으로 바꾼다(캐스팅)

    Child child = (Child) poly
    이렇게 Child 타입으로 다운캐스팅 한다고 해서 Parent의 poly 타입이 변하는 것이 아니다.

    poly의 타입은 Parent 로 기준과 같이 유지한다.

     

    ※ 업캐스팅 vs 다운캐스팅
    - 업캐스팅: 부모 타입으로 변경
    - 다운캐스팅: 자식 타입으로 변경

    일시적 다운캐스팅
    ((Child) poly).childMethod() 

    업캐스팅(생략 가능)
    현재 타입을 부모 타입으로 바꾸는 것
    Parent parent1 = (Parent) child; //괄호는 생략가능

    자바에서 부모는 자식을 담을 수 있지만 반대는 안된다. 

     



    자바에서는 사용할 수 없는 타입으로 다운캐스팅 하는 경우
    ClassCastException 예외를 발생시킨다. 

     

    다음과 같은 사진에서는 Parent parent2 = new Parent(); 를 하여 Child 인스턴스는 아예 존재하지 않는다.

    따라서 child2.childMethod(); 를 하게 되면 컴파일 오류가 발생한다. 

    다운캐스팅은 인스턴스에 존재하지 않는 하위타입으로 캐스팅 하는 경우 문제 발생한다. 

     

    ※ 오류
    컴파일 오류 
    변수명 오타, 잘못된 클래스 이름 사용 (IDE에서 즉시 확인 가능)
    런타임 오류
    프로그램 실행 시점에 발생하는 오류.

     

    * instanceof

     

     

     

    if(parent instanceof Child)
    Child 로부터 만들어진 인스턴스인가?

    instanceof
    다형성에서 참조형 변수는 다양한 자식을 대상으로 참조하는데,
    참조하는 대상이 다양하기 때문에 어떤 인스턴스를 참조하는지 확인하는 것이 바로 instanceof 이다. 

    parent instanceof Child 에서 첫 번째 실행문은 아래와 같다.
    new Parent() instanceof Child

    따라서 if 문은 실행되지 않는다. 

     

    반대로 그 밑에 실행문은 다음과 같다. 
    new Child() instanceof Child

    따라서 if 문이 실행된다.


    만약 이해하기 어렵다면 오른쪽에 있는 타입(Child)에

    왼쪽에 있는 인스턴스의 타입이 들어갈 수 있는지 대입해보면 된다. 

     

    즉 new Parent() instanceof Child 의 경우에는 Child 안에 Parent 가 들어가지 않으므로 false,

    new Child() instanceof Child 의 경우에는 Child 안에  Child 가 들어가기 때문에 true 이다. 


    ※ Pattern Matching for instanceof - 자바 16부터

     

     

    instanceof를 사용하면서 동시에 변수 선언 가능
    parent instanceof Child child (parent가 Child 타입이라는 전제하)

    메서드 오버라이딩에서 기억할 점은
    오버라이딩 된 메서드가 항상 우선권을 가진다는 점이다. ★
    더 하위 자식의 오버라이딩 된 메서드가 우선권을 가진다. 

    변수는 오버라이딩이 되지 않지만,
    메서드는 오버라이딩이 된다. 

    * 다형성 핵심 이론 2가지
    1. 다형적 참조 : 하나의 변수 타입으로 다양한 자식 인스턴스를 참조할 수 있는 기능
    ex) Parent parent = new Parent();
    Parent parent = new Child();
    2. 메서드 오버라이딩 : 기존 기능을 하위 타입에서 새로운 기능으로 재정의  

    * 다형적 참조의 한계
    Parent poly = new Child();
    poly.childMethod 불가능하다. 왜냐하면 poly의 타입은 Parent 타입이다.
    따라서 Parent 클래스부터 시작해 필요한 기능을 찾는다. 


    그런데 상속관계에서는 부모 방향으로 올라갈 수는 있지만 
    자식 방향으로 찾아 내려갈 수 없다. 
    Parent는 부모 타입이고 상위에 부모가 없다. 따라서 childMethod()찾을 수 없어
    컴파일 오류가 뜬다. 

     

    [11. 다형성2]


    추상적인 부모를 참조하면 코드 변경없이 유지가 가능하다. 

    * 추상클래스
    동물과 같이 부모 클래스는 제공하지만, 실제 생성되면 안되는 클래스를 추상 클래스
    실체 인스턴스는 존재하지 않고 대신 상속을 목적으로 사용되고, 부모 클래스 역할을 담당한다. 

    abstract class AbstractAnimal() {}

    추상이라는 의미 abstract 키워드를 붙여준다. new Animal() 불가능하다. 
    직접 인스턴스 생성이 안되는 제약이 생긴다. 

    * 추상메서드
    부모 클래스를 상속 받는 자식 클래스가 반드시 오버라이딩 해야 하는 메서드를 부모 클래스에서 정의하는 것.
    추상적인 개념을 제공하는 메서드로 실체가 존재하지 않고, ★메서드 바디가 없다. 

    public abstract void sound();

    추상 메서드가 하나라도 있는 클래스는 추상 클래스가 돼야 한다. 
    추상 메서드는 상속 받는 자식 클래스가 반드시 오버라이딩 해서 사용해야 한다. 

    추상 클래스를 통해 인스턴스화 하지 말아야 할 클래스를 생성하는 일을 방지해준다.
    추상메서드를 통해 실수로 개발해야할 메서드를 빠뜨리는 일이 없다. (무조건 오버라이딩 해야 해서)

    - 순수 추상 클래스 : 모든 메서드가 추상 메서드인 추상 클래스

    순수 추상 클래스는 다형성을 위한 부모 타입으로써 껍데기 역할만 제공한다. 

    * 순수 추상 클래스
    1. 인스턴스 생성X
    2. 상속 시 자식은 모든 메서드를 오버라이딩 해야 한다.
    3. 주로 다형성을 위해 사용된다.

     



    인터페이스(interface) 를 사용하고 순수 추상 클래스와 같다.
    인터페이스는 public abstract 단어 생략 가능하다. 
    인터페이스는 다중 구현(다중 상속) 지원한다.

     

     


    인터페이스 멤버 변수는 public, static, final이 모두 포함되어 있다고 간주한다.
    static final 사용해 정적이면서 고칠 수 없는 변수 = 상수
    상수 정의

    인터페이스는 구현이라고 부른다. (점선 사용) 
    부모 코드를 모두 만드는 것(재정의)이기 때문에 상속이 아닌 구현이라 한다. (모든 메서드 구현)

    인터페이스는 implements 를 한다. (구현이라는 뜻이다.)

    ※ 상속과 구현
    상속은 부모의 기능을 믈려 받는 것이 목적이다.
    하지만 인터페이스는 모든 메서드가 추상 메서드이다.
    따라서 물려받을 기능이 없고, 오히려 인터페이스에서 정의한 모든 메서드를 자식이
    오버라이딩 해서 기능을 구현해야 한다. 따라서 구현한다고 표현한다. 

    인터페이스는 메서드 이름만 있는 설계도이고, 하위 클래스에서 모두 구현해야 한다.

    ※ 인터페이스를 사용해야 하는 이유?
    1. 제약 : 인터페이스를 구현하는 곳에서 인터페이스의 메서드를 반드시 구현하라는 규약(제약)을 주는 것. 
    그러나 순수 추상 클래스는 실행 가능한 메서드를 나중에 끼워넣을 수 있다.
    더는 순수 추상 클래스가 아니게 된다. 
    인터페이스는 모든 메서드가 추상 메서드로 이런 문제를 방지할 수 있다. 

    2. 다중 구현: 부모를 여러명 두는 다중 구현이 가능하다. 

    인터페이스 다중 구현한 이유는 모두 추상 메서드로 이루어져 있기 때문이다. 
    왜냐면 인터페이스에서는 기능 구현이 없고 자식에서 기능 구현을 하기 때문이다. 

     

    [12. 다형성과 설계]

    객체 지향 프로그래밍은 명령어의 목록으로 보는 시각에서 벗어나 여러개의 독립된 단위, 

    즉 객체들의 모임으로 파악하고자 하는 것이다. 

    * 좋은 객체 지향 프로그래밍이란?

     

    역할과 구현으로 분리
    클라이언트는 대상의 역할(인터페이스)만 알면 된다.
    구현 대상의 내부 구조를 몰라도 되고,
    내부 구조가 변경되어도 영향 받지 않는다.
    구현 대상 자체를 변경해도 영향 받지 않는다. 

     

    즉, 여기서는 운전자는 엑셀을 밟고 브레이크를 밟고 이런 자동차의 역할(인터페이스)만 알면 된다.

    구현 대상(K3, 아반떼, 테슬라 등)의 엔진의 구성이 어떻게 되었는지 내부 구조를 몰라도 되고,

    내부 구조가 변경되어도 동일하게 자동차의 역할만 알면 된다.


    역할 = 인터페이스
    구현 = 인터페이스를 구현한 클래스, 구현 객체

    클라이언트 : 요청하는 자
    서버 : 응답하는 자

    > 역할과 구현을 분리하면 유연하고 변경이 용이하다.
    확장 가능한 설계(구현체 계속 생성 가능)
    클라이언트 영향 주지 않는 변경 가능

    클래스 상에서 어떤 클래스를 알고 있다 = 의존한다. 
     
    * OCP원칙(Open Closed Principle)
    Open for extension : 새로운 기능 추가나 변경 사항이 생겼을 때 기존 코드는 확장할 수 있어야 한다. 
    Closed for modification : 기존의 코드는 수정되지 않아야 한다. 

    기존 코드의 수정 없이 새로운 기능을 추가할 수 있다는 뜻이다. 

    Driver의 코드는 수정 없이 차량의 종류가 계속 늘어남.

    ※ 전략 패턴
    : 알고리즘을 클라이언트 코드의 변경 없이 쉽게 교체할 수 있다,

Designed by Tistory.