Java 객체지향

5. 캡슐의 은닉성과 접근 지정자

캡슐에서 오류가나면 그 캡슐에서 오류가남.
함수가 다른 캡슐의 데이터를 사용하려고 넘겨받으면? 캡슐을 깨뜨리는 것이다.
사용하려면 함수룰 호출하는 방향으로 가야한다.
자바는 보호막을 씌워서 쓰고싶어도 못쓰게 만들 수있다.
캡슐을 깨지 못하게 하는 접근제어지시자가 그 역할을 한다.
키워드를 통해 공개할지 말지를 정할 수 있다.

공개하고 싶지않는 것은 공개하지 않는게 캡슐화에서 중요한 점이다.
데이터 구조에 대해서는 거의 필수로 거는게 낫다.

private Exam[] exams;
private int current;

함수는 공개하고자하는 것, 서비스해야할 것은 public
캡슐이란 자기가 숨겨야할 것에 대해서 은닉시킬 수 있어야하고 그걸 위한 도구가 접근제어지시자가 있다.

6. 생성자(Constructor)

객체 초기화
new ExamList();하면 객체가 생성하면 실체화하는 작업이 이루어지고 데이터구조가 메모리에 올라가게 된다.
이걸 사용하기 위해 이름을 부여하는데 이것이 ExamList list = new ExamList(); 이다.
처음에 만들기 위해서값을 초기화하기 위해서 init을 실행시켯다.
이상태를 가지고 업무적인 동작을 하면 하나씩 계산되게 된다.
그런데 누군가가 중간에 init를 또 호출하면? current가 0이되버린다.
어찌보면 객체 초기화용이아닌 리셋용이 되버린다.
우리가 필요로하는 초기화는
1.객체가 생성되자마자 무조건 제일 먼저 실행되어야한다.
2.생성될때 단 한번만 실행되어야만 한다.
이 함수가 바로 '생성자'이다. 그런데 생성자는 이름을 가지지않아 함수라기엔 애매해서 생성자라고 부르자.
그럼 생성자는 어떻게 생성하나? 객체 생성마다 생성자를 호출하고 있다.
new ExamList();는 new ExamList + ();이게 합쳐져있는 거엿다. 객체를 불러주고 그걸 초기화하는 생성자를 불러달라 이것이다.

생성자는 이름도 반환타입도 없다.
public 클래스이름(){}
정의할때의 함수명은 초기화 할 객체를 한정하기 위한 형식 명칭이다.

public ExamList() {
    exams = new Exam[3];
    current = 0;
}

7. 생성자 오버로드(Constructor Overload)

생성자도 함수의 특징을 가지고 잇어서 오버로드를 할 수있다.

public ExamList(int size) {
    exams = new Exam[size];
    current = 0;
}

->이렇게하면 두가지 방식으로 생성자를 호출 할 수 있다.

주의사항이 있다.
1.오버로드를 만들때 기본생성자를 제거한다면? 기본생성자를 호출할 수 없다.
2.똑같은 기능을 인자를 다르게 하는것이다.오버로드햇다하면 다른것을 줄이고 재호출할 수 있다.
생성자가 생성자를 호출할수 잇냐? 객체를 만들면된다.
public ExamList(){
this(3)
}
재호출함으로써 중복이 된 부분을 해결할 수있다.
3.생성자를 하나도 정의하지 않는다면 컴파일러가 기본생성자를 만들어줌.
참조변수는 null 기본은 0이니 원하지 않는다면 만들어라

8. Getters와 Setters 그리고 이것을 써야하는 이유

Exam 캡슐화 안되어잇다. -> ExamList오류난다.
pirntList가 둘다 사용해서 오류가 난다. 다른 캡슐에 있는 것을 사용하려한다.
보조데이터가 다른 캡슐에 잇는것임. 데이터구조가잇는데 직접사용하는건 무리가 잇으니 함수를 만들어달라고 하는 것이다.

속성을 직접 쓰지 않는 방식으로 받도록하는게 맞다
getter함수를 통해 간접적으로 값을 가져온다.
int kor = exam.getKor()//exam.kor;

public int getKor() {return kor;}
public int getEng() {return eng;}
public int getMath() {return math;}

exam.setKor(kor);
exam.setEng(eng);
exam.setMath(math);

값을 주는 것은 setter함수를 통해 간접적으로 값을 전달한다.

public void setKor(int kor) {
    this.kor = kor;
}

public void setEng(int eng) {
    this.eng = eng;
}

public void setMath(int math) {
    this.math = math;
}

받은 값은 현재 this객체에 넘겨받은 값을 전달하면된다.
그래서 여기서 변수명이 바뀌어도 매개변수로 받아온 것을 전달해주면된다.

getter setter를 왜 만들어야하나??
데이터구조를 직접 가져다쓸때 private라면 에러가 난다.
값을 설정하려면? 캡슐을 setter를 만들고 set를 가져다 사용하게 된다.

비효율적이고 더 복잡한데 왜 사용해야하나?
속성명이 변경되는 것때문 --> 이런일은 별로없다
데이터 구조가 변경되는 것때문임.--> 캡슐화는 데이터 구조를 모아두는 것인데 데이터구조가 변경되면 문제가 발생할 수 있다.
Exam이 처음에는 kor eng math성적밖에 없어는데 언제 봣는지 이름이 추가된다면?
속성이 많아져서 방정리를 다시 해야한다. 이것들을 과목이라는 울타리로 바꿈
그러면 구조가 깊어지고 원래쓰던게 없어짐. 그런데도 변화를 줘야한다.


getter setter를 사용한다면? 내부적으로만 변경될뿐 외부에선 상관이 없게 된다.

9. Exam 클래스의 캡슐화 완성

가져다쓰려면 getter setter를 무조건 작성해야한다.
그래서 이클립스는 이 기능을 자동으로 지원하고 있다.
getXXX / setXXX으로 만들어진다.

total의 경우 책임을 다시 생각해볼필요가잇다. Exam객체의 것을 가져다 사용하고 있기때문임.
int total = exam.total();//kor + eng + math;
float avg = exam.avg(); //total / 3.0f;

public int total() {
    return kor + eng + math;
}
public float avg() {
    return total() / 3.0f;
}

값을 설정할때는 생성자로도 받을 수있다.

public Exam(int kor, int eng, int math) {
    this.kor = kor;
    this.eng = eng;
    this.math = math;
}

kor가 지역변수 kor가 높기때문에 식별하기 위해 this를 꼭 해주자.

10. UI 코드는 분리하는 것이 기본

잘 갖추어진 캡슐을 깨는작업을 할것이다.
하나의 캡슐이라도 분리되어야할게 있다. 이 패턴중에 하나가 ui이다.
input()은 사용자로부터 입력을 받은 것을 / 성적 목록에 추가하는 함수
print()는 목록에서 성적을 꺼내서 / 사용자에게 출력하는 함수

사실 두가지 업무로 나눌 수있다. 이걸 두는게 좋은지 조치를 취하는게좋은지?
입/출력은 사용자와 상호작용한다. 콘솔/윈도우/웹/모바일 등을 사용한다.
어떤 플랫폼을 쓰냐에 따라 ui가 달라질수잇고 다른 함수들은 그대로 사용할 수있다.
그래서 다른 곳에서 재사용할 수 없으니 입출력부분은 콘솔로부터 자유로워야한다.

input() + add() / get() + print()

11. ExamConsole 클래스 구현하기

Console을 구상하기 위해서 ExamList를 사용하고 있다.
Exam은 데이터의 기본단위임. 각각 캡슐을 가지고 있는 것을 has a 상속이다.

import java.util.Scanner;

public class ExamConsole {
    //Composiotion Has A 일체형
    private ExamList list;

    public ExamConsole() {
        list = new ExamList();
    }

    public void printList() {
        printList(list.size());
    }

    public void printList(int size) {
        System.out.println("┌──────────────────────────┐");
        System.out.println("│          성적출력        │");
        System.out.println("└──────────────────────────┘");
        System.out.println();

        for (int i = 0; i < size; i++) {
            Exam exam = list.get(i);//

            int kor = exam.getKor();//exam.kor;
            int eng = exam.getEng();//exam.eng;
            int math = exam.getMath();//exam.math;

            int total = exam.total();//kor + eng + math;
            float avg = exam.avg(); //total / 3.0f;

            System.out.printf("국어: %3d\n", kor);
            System.out.printf("영어: %3d\n", eng);
            System.out.printf("수학: %3d\n", math);

            System.out.printf("총점 : %3d\n", total);
            System.out.printf("평균 : %6.2f\n", avg);
            System.out.println("────────────────────────");
        }
    }

    public void inputList() {
        Scanner sc = new Scanner(System.in);

        System.out.println("┌──────────────────────────┐");
        System.out.println("│          성적입력        │");
        System.out.println("└──────────────────────────┘");
        System.out.println();

        int kor, eng, math;

        do {
            System.out.printf("국어: ");
            kor = sc.nextInt();

            if (kor < 0 || 100 < kor) {
                System.out.println("국어성적은 0~100까지의 범위만 입력이 가능합니다.");
            }
        } while (kor < 0 || 100 < kor);

        do {
            System.out.printf("영어: ");
            eng = sc.nextInt();

            if (eng < 0 || 100 < eng) {
                System.out.println("영어성적은 0~100까지의 범위만 입력이 가능합니다.");
            }
        } while (eng < 0 || 100 < eng);

        do {
            System.out.printf("수학: ");
            math = sc.nextInt();

            if (math < 0 || 100 < math) {
                System.out.println("수학성적은 0~100까지의 범위만 입력이 가능합니다.");
            }
        } while (math < 0 || 100 < math);

        /*
         * Exam exam = new Exam(); exam.setKor(kor);//exam.kor = kor;
         * exam.setEng(eng);//exam.eng = eng; exam.setMath(math);//exam.math = math;
         */

        Exam exam = new Exam(kor, eng, math);
        // ----------------------add-------------------------

        list.add(exam);

        System.out.println("────────────────────────");
    }
}

12. Has A 상속

https://youtu.be/6wKyPg9rxtw 영상
캡슐로 구성되어잇는것들을 어떻게 엮을 것인지가 프로그래밍이다.
Program->ExamCosle->ExamList->Exam이런식으로 이용하고 잇다.
그런데 ExamList이 Exam을 가지고 잇지만 사용하고 있나라고생각하면 아니다.

일반적으로 List는 가져오지만 사용하지 않는다.
ExamConsole이 실질적으로 Exam을 사용한다.
부품을 가지고 있다는 것은 사용하려고 하는 것이다. 그런데 사용을 하지만 그릇만 한다.
실질적인 관계와 구성하는 관계가 맞지 않는다. 일반적으로는 사용관계와 구성관계를 일치하게 만든다.
그래서 사용관계는 구성관계가 같다는 Has A 관계라고 한다.
Has A상속이라고 한다. 상속은 물려받는 건데 가지고 있음으로서 기능을 사용하는 것을의미한다.
이 상속을 부품으로 물려받앗다고 한다.
캡슐이 다른캡슐을 가지고 있는 상태를 의미한다.

나중에 관계를 Has A상속을 햇읍니다 이런것을 이해하자.
객체가 만들어질때 객체가 그 안에서 만들어지는 경우 Composion has a상속햇다.
이런 용어들을 이해하자.

13. 코드 재사용이란?

맨땅에 처음부터 만들면? 어렵다. 재사용은 소스코드를 재사용하는건지? 컴파일러로 한것을 재사용하는건지?
배포된 binary를 재사용하는 것이다. 소스코드는 그냥 사용이다.
배포를 어떻게 재사용하는지 가상의 새로운버전을 만들고 추가해보자.

가상의프로젝트 OOPJavaPrj를 생성해서 Exam을 가져다쓰자. 만들지 않고 가져올 것이다.
1.컴파일 -> Exam.class
2.압축 -> Exam.zip
3.jar -> Exam.jar
의 순서를 거친다. 그런데 이클립스는 이 기능을 알아서 제공하고잇다.

Export하고 선택한다.

buildpath눌러서 add external jars하면된다.

import ch03객체지향.sec04ui코드분리하기.Exam;

public class Program {

    //1. 컴파일 -> Exam.class
    //2. 압축 -> Exam.zip
    //3.jar -> Exam.jar

    public static void main(String[] args) {
        Exam exam = new Exam(1,1,1);

        System.out.println(exam.total());

    }

}

그려면 import가 가능해진다.

14. IS A 상속이란?

객체지향이 있는 언어에선 다 된다.
객체지향에는 3가지 덕목이된다. 캡슐화 상속 다형성
이 중에서 의 상속을 알아보고자 한다.

프로그램을 만들때 원하는 부품들이 다있다면? has a 상속으로 쉽게만들 수있다.
만약 하나의 부품이 없다면? 부담이생긴다. 그런데 딱 내가 원하는 모든 것이 아닌데 어느정도 구성되어있다면?
가져다가 프레임을 사용할 수도 있다. 이렇게 틀로 가져다 쓰는 상속이 is a상속이다.
사진참조
is a상속을 할수있다면 원하는 것에 근접할 수록 소요되는 돈과 시간을 절약할 수있다.
이 틀이 잇으면 틀을가지고 생겅하는것 이 틀이 framework이고 틀이잇다면 생산성이 매우 오른다.
요즘엔 framework를 거의 많이 사용한다. 장점은 생산성이 좋고 단점은 기성품이라 흔하다. 유닉할필요가 없다면 프레임워크를 가져다쓰는게 낫다.
그런데실제로는 성장속도가 빨라서 혼자 다만드는건 거의 불가능하다. 90%쓰고 한 10% 5%만 추가해서 유닉하게 만들려고 한다.

Exam을 IS A 상속하기
Nelec학교에서 NewlecExam을 하는데 거의 흡사하다면 그냥 가져다 사용하면된다.
기존 소스코드는 마음대로 사용할 수없기때문에 상속을 하는것이다.

public class NewlecExam extends Exam { }
Exam이가지고잇는 것들을 NewlecExam이 가지고 있는 것처럼 사용한다.
식별하기 위해서 이름을 붙이는데
부모클래스/상위클래스/기반클래스 <---> 자식클래스/하위클래스/파생클래스 라고 한다.
가장많이 사용하는 것은 이 3가지 이다.

모자란 부분을 확장했는데 재사용하려는데 문제점으로 total을 사용하지 못하는 경우가 발생한다.
이를 해결하기 위해 다양한 방법이 필요하다. 다음장에서 배우고자 한다.

public class NewlecExam extends Exam {
    private int com;

    public int getCom() {
        return com;
    }

    public void setCom(int com) {
        this.com = com;
    }
}

public static void main(String[] args) {
    NewlecExam exam = new NewlecExam();
    exam.setKor(10);
    exam.setEng(10);
    exam.setMath(10);
    exam.setCom(10);

    System.out.println(exam.total()); //30
}

16. Override(우선순위가 높은) 메소드

이를 어떻게 해결하나? 상속받은거로 new를 하면? 객체를 두개 만들어야한다.
Exam을 만들고 com이 있는 NewlexExam을 확장해서 만든다.
전달될때는 내부에서 this를 사용하기 때문에 부모를 super로 지정할 수 있다.

NewlexExam에 total이잇다면 이것을 우선순위로 사용하고 없다면 차선책으로 부모의 것을 사용한다.
이전의 문제를 해결하려면 NewlexExam에 total을 정의하면된다. 이것을 override 메소드라고 한다.
재정의 가리는 함수라고 해서 override method라고 한다.

그럼 우리가 원래 코드의 함수를 알고 재정의해야하나?
컨트롤 스페이스하면 override할 수있도록 목록이 나온다.

@Override
public int total() {
    return super.total() + com;
}

아무것도 안하면 this이기 때문에 super로 부모의 것을 호출하고 com을 더하는 것을 이용하자.
우선순위를 높여서 재정희 한것이라고 알 수있다.

17. 자식 클래스의 객체 초기화

public NewlecExam(int kor, int eng, int math, int com) {
    super(kor, eng, math);
    this.com = com;
}

부모 생성자를 불러와서 알아서 초기화하도록 하고 추가내용만 초기화하면된다.
생성자 오버로드이다.

18. 참조형식과 호출되는 메소드의 관계

참조변수 다참조하는게아니라 부모의 일부만 참조하는 것이 가능하다. 참조하려는 객체가 존재하면 괜찮다.


그런데 여기서 문제가 발생한다. 오버라이드가 되엇을때 nelecc~으로 호출하면 뭐가 호출되나? 당연히 newlec이 호출된다.
Exam exam을 참조형식으로 햇을때 total은 누구것이 호출되나? newlec이 호출된다.
자바는 참조형식의 함수보다 객체 형식의 함수호출을 우선으로 한다.
참조형식이 exam이라해도 객체가 newlec이면 newlec것이 사용된다.

하지만 그래도 참조형식이 가지고있는 메소드가 있어야 호출이 가능하다.

19. 메소드의 관계 연습하기

퀴즈 풀이로 확인 학습하기
https://youtu.be/vlkOwD828yg
부모-부모것을 호출하더라도 오버라이드된게 호출된다.
오버라이드되면 중간에 가로챈다.

20. IS A 상속 쉬어가기

상속을 어디까지 알아야하나? 얼만큼 활용해야하나?
만들겟다보다는 이용해서 만들겠다는 것이다. 윈도우 를 직접작성할수잇나? 자바에서는 프레임워크를 제공하고 잇다.

2023.02.13

is a 상속은 평소에도 알고 있던 것이엇으나 has a 상속이라는 것은 처음들어본다.
사용을 어떻게하는지는 이해가 가지만 이런이름을 가지고 있을 것이라고는 생각을 못해봤다.
내일 부터 학원에서 자바 진도를 나간다고 한다. 미리 앞으로 가면서 앞을 복습하는 느낌으로 가면 좀 더 잘 할 수 있을 것이다.
중요한것은 꺾이지 않는 마음이다.

'기초단계 > JAVA' 카테고리의 다른 글

2023.02.15-1 Java 복습  (0) 2023.02.15
2023.02.14 Java 복습  (0) 2023.02.14
2023.02.12 Java 복습  (0) 2023.02.13
2023.02.11 Java 복습  (0) 2023.02.12
2023.02.10 Java 복습  (0) 2023.02.10

+ Recent posts