클래스와 객체

kailyn·2024년 8월 26일

JAVA

목록 보기
6/17

1. 클래스와 객체

1. 클래스란?

💡 서로 다른 타입의 데이터와 메소드를 정의하여 사용자 정의의 타입을 만들 수 있는데, 이것을 클래스라고 한다. 즉, 클래스는 사용자 정의의 자료형이다.

import java.util.Arrays;

public class Application1 {
    /* 필기.
     *  객체지향프로그램(OOP)란?
     *   OOP(Object Oriented Programming language)
     *   추상화, 캡슐화, 상속, 다형성을 적용하여 유지보수성을 고려한 응집력 높고 결합도 낮은
     *   객체 위주의 개발 방식을 적용한 프로그래밍을 말한다.
     *
     * 필기.
     *  객체지향의 특징(4대 특징)
     *  - 추상화(Abstraction)(추상화를 제외한 3대 특징)
     *  - 캡슐화(Encapsulation)(필드 및 메소드 은닉)
     *  - 상속(Inheritance)
     *  - 다양성(Polymorphism)
     *
     * 필기.
     *  정보은닉(imfomation hiding)
     *  - 필드 및 메소드 은닉
     *  - 타입 은닉
     *  - 구현 은닉
    * */
    public static void main(String[] args) {

        /* 수업목표. 클래스가 무엇인기 이해하고 작성할 수 있다. */
        /* 설명.
         *  회원 정보를 관리하기 위해 회원의 여러 정보(아이디, 비밀번호, 이름, 나이, 성별, 취미)를 취급하여 관리 하려고 한다.
         *  이 자료를 취급할 수 있는 방법을 생각해 보자.
        * */

        /* 목차. 1. 변수를 이용한 회원 데이터 관리 */
        String id = "user01";
        String pwd = "pass01";
        String name = "홍길동";
        int age = 20;
        char gender = '남';
        String[] hobby = new String[]{"축구", "볼링", "테니스"};

        System.out.println("id = " + id);
        System.out.println("pwd = " + pwd);
        System.out.println("name = " + name);
        System.out.println("age = " + age);
        System.out.println("gender = " + gender);
        System.out.println("hobby = " + Arrays.toString(hobby));

        /* 필기
         *  이렇게 변수들로만 관리할 떄 발생하리 수 있는 문제점
         *  1. 많은 변수명들을 관리하기 힘들 수 있다.
         *  2. 메소드의 전달인자로 전달 할 값이 너무 많아 회원과 관련된 기능을 호출할 때 매개변수가 많아진다.
         *  3. 메소드의 반환형으로 회원이라는 개념을 반환할 수 없다.(메소드의 반환형은 하나여야 하기 떄문에)
        * */

        Member member = new Member();
        Member member2 = new Member();

        System.out.println(member.name);
        System.out.println(member2.name);

        member.name = "김철수";
        member2.name = "김영희";

        System.out.println(member.name);
        System.out.println(member2.name);

        /* 설명. 김철수씨만 완성해 보기 */
        member.age = 30;
        member.gender = '남';
        member.hobby = new String[]{"볼링", "하키"};
        member.id = "user03";
        member.pwd = "pwd03";

        /* 설명. 회원 정보를 하나의 전달인자로 전달하고 하나의 매개변수로 받을 수 있다. */
        /* 설명. 또한 메소드의 반환형으로 한번에 회원 정보를 반환할 수 있고 반환 받은 곳에서 반횐된 객체에 접근도 가능하다. */
        System.out.println(printMember(member).name);

        Member renameMember = printMember(member);
        System.out.println(renameMember.age);
        System.out.println(Arrays.toString(renameMember.hobby));

    }

    private static Member printMember(Member member) {
        System.out.println("개명 시작을 시작합니다.");
        member.name = "박길동";

        return  member;
    }
}
/* 설명. 여러가지 타입의 값을 하나의 타입(Member 클래스)으로 묶을 수 있다.(c++의 구조체에 가까움) */
public class Member { // 배열과 다르게 인덱스 대신 변수로 올라가고 그에 대한 값은 다 다름
    String id;
    String pwd;
    String name;
    int age;
    char gender;
    String[] hobby;

}

2. 캡슐화

💡 캡슐화는 유지보수성 증가(낮은 결합도)를 위해 필드의 직접 접근을 제한하고, public 메소드를 이용하여 간접적으로 접근하여 사용할 수 있도록 클래스를 작성하는 기법이다. 클래스를 작성할 시 특별한 목적이 아닌 이상 캡슐화가 기본적인 원칙으로 사용되고 있다.
  • 기억해두기! 필드에 private 입력해야 함
    public class Application {
        public static void main(String[] args) {
    
            /* 수업목표. 접근제어자와 캡슐화에 대해 이해하고 직접 필드 접근을 막는 이유를 이해할 수 있다. */
            Monster monster = new Monster();
    //        monster.name = "드라큘라";
    //        monster.hp = 1000;
    
            /* 설명. 필드에 직접 접근 대신 메소드를 통해 우회하여 접근한다. */
            monster.setInfo1("드라큘라");
            monster.setInfo2(1000);
    
            /* 필기.
             *  캡슐화(encapsulation)란?
             *    캡슐화는 유지보수를 증가시키기 위해 필드의 직접 접근을 제한하고
             *    public 메소드를 이용해서 간접적으로(우회해서) 접근하여 사용할 수 있도록 만든 기술이다.
             *    클래스 작성시 특별한 목적이 있지 않다면 캡슐화를 적용하는 것을 기본 원칙으로 하고 있다.
            * */
        }
    }
    public class Monster {      // class의 접근 제어자는 public 또는 default(안 적힘) 두 가지 밖에 없다.
    
        /* 필기. 접근제어자 private을 활용하여 캡슐화를 적용한다.(같은 클래스를 제외한 곳에서는 접근 불가) */
        private String name;
        private int hp;
    
        public void setInfo1(String info1) {
            this.name = info1;
        }
    
        public void setInfo2(int info2) {
            this.hp = info2;
        }
    }

2-1. 필드에 직접 접근 시 발생할 수 있는 문제점

A. 필드에 올바르지 않는 값이 들어가도 통제가 불가능하다.
B. 필드의 이름이나 자료형을 변경할 때 사용하는 쪽에도 수정 영향을 미친다.

2-2. 캡슐화를 적용하여 문제점 해결

A. 필드에 직접 접근하지 못하도록 private 접근 제어자 사용

구분같은 클래스같은 패키지자식 클래스전체
publicOOOO
protectedOOO
(default)OO
privateO

B. public 메소드를 이용하여 간접적으로 접근하도록 사용

C. 메소드 내에 값을 검증하는 로직 추가

public class Application {
    public static void main(String[] args) {

        /* 수업목표. 접근제어자와 캡슐화에 대해 이해하고 직접 필드 접근을 막는 이유를 이해할 수 있다. */
        Monster monster = new Monster();
//        monster.name = "드라큘라";
//        monster.hp = 1000;

        /* 설명. 필드에 직접 접근 대신 메소드를 통해 우회하여 접근한다. */
        monster.setInfo1("드라큘라");
        monster.setInfo2(1000);

        /* 필기.
         *  캡슐화(encapsulation)란?
         *    캡슐화는 유지보수를 증가시키기 위해 필드의 직접 접근을 제한하고
         *    public 메소드를 이용해서 간접적으로(우회해서) 접근하여 사용할 수 있도록 만든 기술이다.
         *    클래스 작성시 특별한 목적이 있지 않다면 캡슐화를 적용하는 것을 기본 원칙으로 하고 있다.
        * */
    }
}
public class Monster {      // class의 접근 제어자는 public 또는 default(안 적힘) 두 가지 밖에 없다.

    /* 필기. 접근제어자 private을 활용하여 캡슐화를 적용한다.(같은 클래스를 제외한 곳에서는 접근 불가) */
    private String name;
    private int hp;

    public void setInfo1(String info1) {
        this.name = info1;
    }

    public void setInfo2(int info2) {
        this.hp = info2;
    }
}

3. 추상화

3-1. 추상화란?

💡 유연성을 확보하기 위해 공통적인 것을 추출하고 공통적이지 않은 것을 제거하는 것을 의미한다. 추상화 과정을 통해 객체(Object)가 도출이 되며, 이러한 객체를 생성하기 위해 클래스를 설계하게 된다. 즉, 추상화는 현실세계의 복잡한 사건을 단순화하여 새로운 객체 지향 세계를 창조해나가는 과정이다.

3-2. 객체 지향 프로그래밍이란?

💡 현실 세계의 모든 사건(event)는 객체와 객체의 상호작용에 의해 일어난다는 세계관을 프로그램을 만들 때 이용하여 새로운 세계를 창조하는 방법론이다. 객체가 서로 상호작용을 하며 사건이 일어난다는 개념은 현실 세계와 닮아있지만, 다른 점은 무생물이나 개념 같은 존재들도 하나의 주체로 본인의 상태를 스스로 제어하고 행동한다는 것이 다른 점이다. (의인화 기법 → 캡슐화를 의미한다.) 또한 모든 객체는 너무 많은 수행할 수 있는 것이 아닌 적절한 책임을 가지고 있어야 한다. 만약 너무 많은 것을 수행해야 하는 책임이 존재한다면 이는 또 다른 객체에게 책임을 나눈다. (단일 책임의 원칙)

3-3. 객체간의 상호작용

💡 객체와 객체는 메세지(메소드 호출)를 통해 서로 상호작용을 한다. 보내는 쪽을 송신자, 받는 쪽을 수신자라고 하며, 수신자는 메세지를 전달 받아 그 메세지에 해당하는 내용을 처리하는 방법을 스스로 결정한다. 스스로 결정한 방식대로 명령어를 순차적으로 기술한 것을 메소드(method)라고 한다. 상호작용에 중요한 것은 메세지를 수신하여 응답하는 상호작용을 설계하는 것을 의미한다. 객체 지향 프로그래밍에서 중요한 것은 이러한 상호작용을 설계하는 것이다. (즉, 필드보다는 메소드를 중점으로 추상화 기법을 적용하여 객체를 설계하는 것이 중요하다.)

3-4. 행위를 중심으로 추상화

A. 시스템 요구사항

1. 카레이서는 시동걸기, 엑셀레이터 밟기, 브레이크 밟기, 시동 끄기를 할 수 있다.
2. 자동차는 시동걸기, 앞으로가기, 멈추기 , 시동끄기를 할 수 있다.
3. 자동차는 처음에 멈춘 상태로 대기하고 있는다.
4. 카레이서는 먼저 자동차에 시동을 건다. 이미 걸려있는 경우 다시 시동을 걸 수 없다.
5. 카레이서가 엑셀레이터를 밟으면 시동이 걸린 상태일 경우 자동차는 시속이 10km/h 증가하며 앞으로 나간다.
6. 자동차가 달리는 중인 경우 브레이크를 밟으면 자동차의 시속은 0으로 떨어지며 멈춘다.
7. 브레이크를 밟을 때 자동차가 달리는 중이 아니라면 이미 멈춰있는 상태라고 안내한다.
8. 카레이서가 시동을 끄면 더 이상 자동차는 움직이지 않는다.
9. 자동차가 달리는 중이라면 시동을 끌 수 없다.

B. 필요한 객체

카레이서, 자동차

C. 상호작용

- 카레이서가 수신할 수 있는 메세지 (카레이서가 해야 하는 일)
1. 시동을 걸어라
2. 엑셀레이터를 밟아라
3. 브레이크를 밟아라
4. 시동을 꺼라

- 자동차가 수신할 수 있는 메세지 (자동차가 해야 하는 일)
1. 시동을 걸어라
2. 앞으로 가라
3. 멈춰라
4. 시동을 꺼라

1. 시동 걸기
 ---------                           ----------                           --------
 | 사용자 |  ---> 시동을 걸어라 ---> | 카레이서 | ---> 시동을 걸어라 --->   | 자동차 |
 ---------  <-------------------     ---------- <---------------------    ---------

2. 가속 하기
  ---------                                 ----------                         --------
  | 사용자 |  ---> 엑셀레이터를 밟아라 ---> | 카레이서 | ---> 앞으로 가라 --->   | 자동차 |
  ---------  <------------------------     ---------- <--------------------   ---------

3. 정지하기
  ---------                               ----------                     --------
  | 사용자 |  ---> 브레이크를 밟아라 ---> | 카레이서 | ---> 멈춰라 --->   | 자동차 |
  ---------  <-----------------------    ----------   <--------------   ---------

4. 시동 끄기
  ---------                        ----------                          --------
  | 사용자 |  ---> 시동을 꺼라 ---> | 카레이서 | ---> 시동을 꺼라 --->   | 자동차 |
  ---------  <-------------------   ---------- <------------------     ---------

D. 클래스 설계하기

1. 카레이서 클래스
 - 속성 : 자동차
 - 행위 : 시동을걸어라, 엑셀레이터를 밟아라, 브레이크를 밟아라, 시동을 꺼라.

2. 자동차 클래스
 - 속성 : 시동상태, 현재시속
 - 메소드 : 시동을걸어라, 앞으로 가라, 멈춰라, 시동을 꺼라

3-5. 데이터를 중심으로 추상화

💡 객체지향에서 중요한 것은 행위(메소드)이지만, 데이터를 중심으로 추상화 하여 객체 및 클래스를 설계하는 경우도 존재한다. 대표적으로 DTO 같은 클래스가 있다.
import java.util.Scanner;

public class Application {
    public static void main(String[] args) {

        /* 수업목표. 객체지향 프로그래밍에 대해 이해하고 객체와 클래스를 설계하여 프로그래밍 할 수 있다. */
        /* 필기.
         *  추상화란?
         *   공통된 부분을 추출하고 공통되지 않고 불필요한 부분을 제거한다는 의미를 가지며, 추상화의 목적은 유연성을 확보하기 위함이다.
         *   유연성 확보는 여러 곳에서 적용될 수 있는 유연한 객체를 의미하며, 재사용성이 높아질 수 있게 한다는 의미이다.
        * */

        /* 필기.
         *  프로그램 개요
         *    카레이서가 자동차를 운전하는 프로그램
         *
         * 필기.
         *  시스템 요구사항
         *   1. 카레이서는 시동걸기, 엑셀레이터 밟기, 브레이크 밟기, 시동 끄기를 할 수 있다.
         *   2. 자동차는 시동걸기, 앞으로 가기, 멈추기, 시동끄기를 할 수 있다.
         *   3. 자동차는 처음에 멈춘 상태로 대기하고 있는다.
         *   4. 카레이서는 먼저 자동차에 시동을 건다. 이미 걸려있는 경우 다시 시동을 걸 수 없다.
         *   5. 카레이서가 엑셀레이터를 밟으면 시동이 걸린 상태일 경우 자동차는 시속이 10km/h 증가하며 앞으로 나간다.
         *   6. 자동차가 달리는 중인 경우 브레이크를 밟으면 자동차의 시속은 바로 0km/h로 떨어지며 멈춘다.
         *   7. 브레이크를 밟을 때 자동차가 달리는 중이 아니면 이미 멈춰있는 상태라고 안내한다.
         *   8. 카레이서가 시동을 끄면 더 이상 자동차는 움직이지 않는다.
         *   9. 자동차가 달리는 중이라면 시동을 끌 수 없다.
        * */

        /* 설명. 카레이서 한 명이 등장 */
        CarRacer racer = new CarRacer();

        Scanner sc = new Scanner(System.in);

        /* 설명. 카레이서에게 명령을 하기 위한 메뉴 출력 */
        while (true) {
            System.out.println("=========== 카레이싱 프로그램 ===========");
            System.out.println("1. 시동 걸기");
            System.out.println("2. 전진");
            System.out.println("3. 정지");
            System.out.println("4. 시동 끄기");
            System.out.println("9. 프로그램 종료");
            System.out.println("메뉴 선택: ");
            int input = sc.nextInt();

            switch (input) {
                case 1: racer.startUp();
                    break;
                case 2: racer.stepAccelator();
                    break;
                case 3: racer.stepBreak();
                    break;
                case 4: racer.turnOff();
                    break;
                case 9: System.out.println("프로그램을 이용해 주셔서 감사합니다.");
                    break;
                default:
                    System.out.println("잘못된 메뉴를 선택하셨습니다.");
            }

            if(input == 9) break;
        }
    }
}
public class CarRacer {

    /* 설명. 차 한대(Car 객체 하나)를 필드로 가진다. */
    private Car myCar = new Car();

    /* 설명. 자신이 보유한 차량에 시동을 건다. */
    public void startUp() {
        this.myCar.startUp();
    }

    public void stepAccelator() {
        myCar.go();
    }

    public void stepBreak() {
        myCar.stop();
    }

    public void turnOff() {
        myCar.turnOff();
    }
}
public class Car {

    private boolean isOn = false;
    private int speed;

    /* 설명. 시동이 걸려있는 상태인 경우에는 할 일이 없고, 시동이 걸려있지 않은 상태인 경우 시동을 건다. */
    public void startUp() {
        if (isOn) {         // 시동이 걸려 있으면
            System.out.println("이미 시동이 걸려있습니다.");
        } else {            // 시동이 걸려 있지 않다면
            isOn = true;    // isOn 앞에는 this.이 있는 거지만 this.을 생략해도 자동으로 적힌다.
            System.out.println("시동을 걸었습니다. 부릉부릉~!");
        }
    }

    /* 설명. 시동이 걸린 상태인 경우에만 호출시 마다  10km/h씩 속도를 증가시킨다. */
    public void go() {
        if(isOn) {      // 시동이 켜져있어 달릴 수 있음
            System.out.println("차가 앞으로 움직입니다.");
            speed += 10;
            System.out.println("현재 차의 시속은 " + speed + "km/h 입니다.");
        }else {         // 시동이 꺼져있어 달릴 수 없음
            System.out.println("차의 시동이 걸려있지 않습니다. 시동을 먼저 걸어주세요.");

        }
    }

    /* 설명. 시동이 걸려있고 달리는 상태인 경우에만 멈출 수 있다. */
    public void stop() {
        if (isOn) {
            if(speed > 0) {
                speed = 0;
                System.out.println("급 브레이크를 밟았습니다. 차를 멈춥니다.");
            }else {
                System.out.println("차는 이미 멈춰있는 상태입니다.");
            }
        } else {
            System.out.println("차의 시동이 걸려있지 않습니다. 시동을 먼저 걸어주세요.");
        }
    }

    /* 설명. 시동이 걸려있는 상태인 경우 시동을 끄고, 이미 꺼지 상태라면 끌 수 있고 주행중이라면 끌 수 없다. */
    public void turnOff() {
        if (isOn) {
            if(speed > 0) {
                System.out.println("달리는 상태에서 시동을 끌 수 없습니다. 차를 우선 멈춰주세요.");
            } else {
                isOn =false;
                System.out.println("시동을 끕니다. 다시 운행하시려면 시동을 켜주세요.");
            }
        }else {
            System.out.println("이미 시동이 꺼져 있는 상태입니다. 시동 상태를 확인해 주세요.");
        }
    }
}

4. 생성자

💡 인스턴스를 생성할 때 초기 수행할 명령이 있는 경우 미리 작성해두고, 인스턴스를 생성할 때 단 한 번 호출되는 함수이다. 지금까지 인스턴스를 생성할 때 클래스명 레퍼런스변수 = new 클래스명(); 이렇게 사용해왔다. new 뒤에 클래스명은 사실 생성자(constructor)라는 메소드를 호출하는 구문이었다. 생성자 함수에 매개변수가 없는 생성자를 기본생성자(default constructor)라고 하며, 기본 생성자는 compiler에 의해 자동으로 추가되기 때문에 지금까지 명시적으로 작성하지 않고 사용할 수 있었다.
  • 실행결과
    User [id=null, pwd=null, name=null, enrollDate=null]
    User [id=user01, pwd=pass01, name=홍길동, enrollDate=null]
    User [id=user02, pwd=pass02, name=이순신, enrollDate=Sun May 07 15:52:53 KST 2023]
    User [id=user02, pwd=pass02, name=이순신, enrollDate=Sun May 07 15:52:53 KST 2023]

5. 오버로딩

5-1. 오버로딩이란?

💡 동일한 클래스 내에는 동일한 이름의 생성자 혹은 메소드를 작성하지 못한다. 하지만 매개변수의 타입, 갯수, 순서를 다르게 작성하면 서로 다른 생성자나 메소드로 인식하기 때문에 동일한 이름의 생성자나 메소드를 여러 개 작성할 수 있게 해준다. 이것을 오버로딩(overloading)이라고 한다.

5-3. 매개변수로 사용 가능한 자료형

💡 기본자료형, 기본자료형 배열, 클래스 자료형, 클래스 자료형 배열, 가변인자 이렇게 5가지 형태의 매개변수 사용이 가능하다. 💡 오버로딩과 오버라이딩의 차이를 잘 알아두자!!

오버로딩(overloading) : 같은 클래스 내부에서 메소드를 확장하기 위한 개념

오버라이딩(overriding) : 부모 클래스의 메소드의 동작 방법을 변경(재정의)하여 우선적으로 사용하는 개념


6. static과 final 키워드

6-1. static

💡 정적 메모리 영역에 프로그램이 start될 시 할당 하고자 할 때 사용하는 키워드이다. static 필드나 메소드는 인스턴스 생성 없이 **클래스명.을 통해 접근**해서 사용이 가능한다. 여러 인스턴스가 **공유해서 사용할 목적인 속성이나 필드**의 예약어 자리에 추가한다.

  • 위 그림을 잘 기억해두기

6-2. final

💡 **변경 불가의 의미**를 담고 있는 키워드이다. (상속과 관련하여 클래스나 메소드의 예약어 자리에 쓰이면 더이상 하위 클래스에서 가지지 못하는 마지막 클래스나 메소드를 의미하게 된다.)
  • 클래스 필드의 final 변수는 선언과 동시에 초기화 하거나 생성자를 통한 초기화를 해야 한다.

7. 변수의 종류

💡 클래스에서 쓰이는 변수는 크게 클래스 변수, 인스턴스 변수(클래스의 필드), 지역 변수로 구분된다. **클래스 변수**는 static 키워드를 가지고 필드에 선언하는 변수로 메모리의 static영역을 사용한다. **인스턴스 변수**는 static 키워드 없이 클래스의 필드에 선언하는 변수로 메모리의 heap영역을 사용한다. **지역 변수**는 메소드, 생성자, 초기화 블록 내부에서 선언하는 변수로 다른 변수들보다 우선권을 가진다.
변수생성시기소멸시기
클래스 변수프로그램 시작 시프로그램 종료 시
인스턴스 변수인스턴스 생성 시참조하지 않을 시
(GC 소관)
지역 변수메소드 호출 시메소드 종료 시

8. 초기화 블럭

💡 초기화 블럭은 해당 클래스의 인스턴스 생성 시 어떤 생성자를 활용해서 인스턴스를 생성하더라도 공통적으로 실행 될 코드를 작성할 수 있는 블럭이다.
  • 인스턴스 변수

  • 클래스 변수


2. 객체 배열(클래스 자료형 배열)

1. 객체 배열이란?

💡 레퍼런스 변수에 대한 배열로 동일한 타입의 여러 인스턴스들을 배열로 관리할 수 있다.
Car[] carArray = new Car[5];
carArray[0] = new Car("페라리", 300);
carArray[1] = new Car("람보르기니", 350);
carArray[2] = new Car("롤스로이스", 250);
carArray[3] = new Car("부가티베이론", 400);
carArray[4] = new Car("포터", 500);

for (int i = 0; i < carArray.length; i++) {
	carArray[i].driveMaxSpeed();
}
  • 실행결과
    페라리이() 최고시속 300km/h로 달려갑니다.
    람보르기니이() 최고시속 350km/h로 달려갑니다.
    롤스로이스이() 최고시속 250km/h로 달려갑니다.
    부가티베이론이() 최고시속 400km/h로 달려갑니다.
    포터이() 최고시속 500km/h로 달려갑니다.
profile
나는 할 수 있다...!

0개의 댓글