2.3~4.Java - 객체 지향 프로그래밍, 생성자

sein lee·2024년 5월 4일
0

java-study

목록 보기
11/12
post-custom-banner

객체지향프로그래밍

절차지향프로그래밍

  • 절차지향 프로그래밍
    실행 순서를 중요시 하는 프로그래밍, 프로그램의 흐름을 순차적으로 따르며 처리하는 방식
  • 객체 지향 프로그래밍
    객체를 지향한다. 쉽게 이야기해서 객체를 중요하게 생각하는 방식
    객체 지향 프로그래밍은 실제 세계의 사물이나 사건을 객체로 보고, 이러한 객체들 간의 상호작용을 중심으로 프로그래밍하는 방식
  • 차이점 : 절차지향은 데이터와 해당 데이터에 대한 처리 방식이 분리되어 있다. 객체지향은 데이터와 그 데이터에 대한 행동(메서드)이 하나의 '객체' 안에 포함되어 있다.

음악 플레이어 만들기

절차 지향 프로그래밍을 만들고 객체 지향으로 바꿔보자!

<MusicPalyerMain1.java>

package oop1;

public class MusicPalyerMain1 {
    public static void main(String[] args) {
        int volume = 0;
        boolean isOn = false;

        //음악 플레이어 켜기
        isOn = true;
        System.out.println("음악 플레이어를 시작합니다");

        //볼륨 증가
        volume++;
        System.out.println("음악 플레이어 볼륨:" + volume);

        //볼륨 증가
        volume++;
        System.out.println("음악 플레이어 볼륨:" + volume);

        //볼륨 감소
        volume--;
        System.out.println("음악 플레이어 볼륨:" + volume);

        //음악 플레이어 상태
        System.out.println("음악 플레이어 상태 확인");
        if (isOn) {
            System.out.println("음악 플레이어 ON, 볼륨:" + volume);
        } else {
            System.out.println("음악 플레이어 OFF");
        }

        //음악 플레이어 끄기
        isOn = false;
        System.out.println("음악 플레이어를 종료합니다");

    }
}

<결과>

데이터 묶음

MusicPlayerData 라는 클래스로 사용되는 데이터들을 여기에 묶어서 멤버변수로 사용하자
<MusicPlayerData.java>

package oop1;

public class MusicPlayerData {
    int volume=0;
    boolean isOn = false;
}

<MusicPalyerMain2.java>

package oop1;

public class MusicPalyerMain2 {
    public static void main(String[] args) {
        MusicPlayerData data = new MusicPlayerData();

        //음악 플레이어 켜기
        data.isOn = true;
        System.out.println("음악 플레이어를 시작합니다");

        //볼륨 증가
        data.volume++;
        System.out.println("음악 플레이어 볼륨:" + data.volume);

        //볼륨 증가
        data.volume++;
        System.out.println("음악 플레이어 볼륨:" + data.volume);

        //볼륨 감소
        data.volume--;
        System.out.println("음악 플레이어 볼륨:" + data.volume);

        //음악 플레이어 상태
        System.out.println("음악 플레이어 상태 확인");
        if (data.isOn) {
            System.out.println("음악 플레이어 ON, 볼륨:" + data.volume);
        } else {
            System.out.println("음악 플레이어 OFF");
        }

        //음악 플레이어 끄기
        data.isOn = false;
        System.out.println("음악 플레이어를 종료합니다");

    }
}

다양한 변수들이 추가되더라도 쉽게 관리 할 수 있다.

메서드 추출

중복되는 부분들을 정리하자 -> 재사용하기 쉬움
<MusicPlayerMain3.java>

package oop1;

public class MusicPalyerMain3 {
    public static void main(String[] args) {
        MusicPlayerData data = new MusicPlayerData();

        //음악 플레이어 켜기
        on(data);

        //볼륨 증가
       volumeUp(data);

        //볼륨 증가
        volumeUp(data);

        //볼륨 감소
        volumeDown(data);

        //음악 플레이어 상태
        showStatus(data);

        //음악 플레이어 끄기
        off(data);
    }

    static void on(MusicPlayerData data){
        data.isOn = true;
        System.out.println("음악 플레이어를 시작합니다");
    }
    static void off(MusicPlayerData data){
        data.isOn = false;
        System.out.println("음악 플레이어를 종료합니다");
    }

    static void volumeUp(MusicPlayerData data){
        data.volume++;
        System.out.println("음악 플레이어 볼륨:" + data.volume);
    }
    static void volumeDown(MusicPlayerData data){
        data.volume--;
        System.out.println("음악 플레이어 볼륨:" + data.volume);
    }

    static void showStatus(MusicPlayerData data){
        System.out.println("음악 플레이어 상태 확인");
        if (data.isOn) {
            System.out.println("음악 플레이어 ON, 볼륨:" + data.volume);
        } else {
            System.out.println("음악 플레이어 OFF");
        }
    }
}

각각의 기능을 메서드로 만들어 모듈화 시켰다.
=> 중복 제거, 변경영향범위(기능 수정 시 해당 메서드만 수정), 메서드이름을 통해 코드를 쉽게 이해할 수 있다.

절차지향프로그램의 한계
데이터(MusicPlayerData)와 기능(MusicPlayerMain3)이 분리되어 있어서 유지보수 관점에서 2곳을 관리하여야한다.
=> 객체지향 프로그램으로 데이터와 기능을 온전히 하나로 묶어서 사용

클래스와 메서드

<ValueData.java>

package oop1;

public class ValueData {
    int value;
}

<ValueDataMain.java>

package oop1;

public class ValueDataMain {
    public static void main(String[] args) {
        ValueData valueData = new ValueData();

        add(valueData);
        add(valueData);
        add(valueData);

        System.out.println("최종 숫자 = "+ valueData.value);
    }
    static void add(ValueData valueData){
        valueData.value++;
        System.out.println("숫자증가 value= " + valueData.value);
    }
}

<결과>

객체지향

클래스 내부에 속성(데이터)과 기능(메서드)을 함께 포함할 수 있다.
<ValueData.java>

package oop1;

public class ValueData {
    int value;

    void add(){
        value++;
        System.out.println("숫자증가 value= " + value);
    }
}

<ValueObjectMain.java>

package oop1;

public class ValueObjectMain {
    public static void main(String[] args) {
        ValueData valueData = new ValueData();

        valueData.add();
        valueData.add();
        valueData.add();

        System.out.println("최종 숫자 = " + valueData.value);
    }
}
  • 클래스는 속성(데이터, 멤버 변수)과 기능(메서드)을 정의할 수 있다.
  • 객체는 자신의 메서드를 통해 자신의 멤버 변수에 접근할 수 있다
  • 객체의 메서드 내부에서 접근하는 멤버 변수는 객체 자신의 멤버 변수이다.

객체지향 프로그래밍

음악 플레이어를 만들어서 제공하는 개발자와 플레이어를 사용하는 개발자가 분리되어있다고 생각하자!

  • 속성 : volume, isOn
  • 기능 : on(), off(), volumeUp(), volumeDown(), showStatus()

<MusicPlayer.java>

package oop1;

public class MusicPlayer {
    int volume =0;
    boolean isOn =false;

    void on(){
        isOn = true;
        System.out.println("음악 플레이어를 시작합니다");
    }
    void off(){
        isOn = false;
        System.out.println("음악 플레이어를 종료합니다");
    }

    void volumeUp(){
        volume++;
        System.out.println("음악 플레이어 볼륨:" + volume);
    }
    void volumeDown(){
        volume--;
        System.out.println("음악 플레이어 볼륨:" + volume);
    }

    void showStatus(){
        System.out.println("음악 플레이어 상태 확인");
        if (isOn) {
            System.out.println("음악 플레이어 ON, 볼륨:" + volume);
        } else {
            System.out.println("음악 플레이어 OFF");
        }
    }
}

=> 음악 플레이어를 사용하는데 필요한 모든 속성과 기능이 하나의 클래스에 포함되어 있다

<MusicPlayerMain4.java>

package oop1;

public class MusicPlayerMain4 {
    public static void main(String[] args) {
        MusicPlayer player = new MusicPlayer();

        //음악 플레이어 켜기
        player.on();
        //볼륨 증가
        player.volumeUp();
        //볼륨 증가
        player.volumeUp();
        //볼륨 감소
        player.volumeDown();
        //음악 플레이어 상태
        player.showStatus();
        //음악 플레이어 끄기
        player.off();

    }
}

MusicPlayer 를 사용하는 입장에서는 이제 MusicPlayer 내부에 어떤 속성(데이터)이 있는지 전혀 몰라도 된다. MusicPlayer 를 사용하는 입장에서는 단순하게 MusicPlayer 가 제공하는 기능 중에 필요한 기능을 호출해서 사용하기만 하면 된다.

캡슐화

MusicPlayer 를 보면 음악 플레이어를 구성하기 위한 속성과 기능이 마치 하나의 캡슐에 쌓여있는 것 같다. 이렇게 속성과 기능을 하나로 묶어서 필요한 기능을 메서드를 통해 외부에 제공하는 것을 캡슐화라 한다.

문제와 풀이

<RectangleProceduralMain.java>

package oop1.ex;

public class RectangleProceduralMain {
    public static void main(String[] args) {
        int width=5;
        int height=8;

        int area = calculateArea(width, height);
        System.out.println("넓이: " + area);
        int perimeter = calculatePerimeter(width, height);
        System.out.println("둘레 길이: " + perimeter);
        boolean square = isSquare(width, height);
        System.out.println("정사각형 여부: " + square);

    }
    static int calculateArea(int width,int height){
        int area = width*height;
        return area;
    }
    static int calculatePerimeter(int width,int height){
        int perimeter = (width+height)*2;
        return perimeter;
    }
    static boolean isSquare(int width,int height){
        return width == height;
    }
}

객체 지향

<RectangleProceduralMain.java>

package oop1.ex;

public class RectangleProceduralMain {
    public static void main(String[] args) {
        Rectangle rectangle = new Rectangle();
        rectangle.width=5;
        rectangle.height=8;

        int area = rectangle.calculateArea();
        System.out.println("넓이: " + area);
        int perimeter = rectangle.calculatePerimeter();
        System.out.println("둘레 길이: " + perimeter);
        boolean square = rectangle.isSquare();
        System.out.println("정사각형 여부: " + square);

    }

}

<Rectangle.java>

package oop1.ex;

public class RectangleProceduralMain {
    public static void main(String[] args) {
        Rectangle rectangle = new Rectangle();
        rectangle.width=5;
        rectangle.height=8;

        int area = rectangle.calculateArea();
        System.out.println("넓이: " + area);
        int perimeter = rectangle.calculatePerimeter();
        System.out.println("둘레 길이: " + perimeter);
        boolean square = rectangle.isSquare();
        System.out.println("정사각형 여부: " + square);

    }

}

문제와 풀이2

<Account.java>

package oop1.ex;

public class Account {
    int balance;

    void deposit(int amount){
        balance+=amount;
    }
    void withdraw(int amount){
        if(balance>=amount){
            balance-=amount;
        }else{
            System.out.println("잔액이 부족합니다.");
        }
    }
}

<AccountMain.java>

package oop1.ex;

public class AccountMain {
    public static void main(String[] args) {
        Account account = new Account();

        account.deposit(10000);
        account.withdraw(9000);
        account.withdraw(2000);

        System.out.println("잔고 : "+ account.balance);

    }
}

생성자

필요한 이유

객체를 생성하는 시점에 어떤 작업을 하고 싶다면 생성자(Constructor)를 이용하면 된다.
<MemberInit.java>

package construct;

public class MemberInit {
    String name;
    int age;
    int grade;

}

<MemberInitMain1.java>

package construct;

public class MethodInitMain1 {
    public static void main(String[] args) {
        MemberInit member1= new MemberInit();

        member1.name = "user1";
        member1.age = 15;
        member1.grade=90;

        MemberInit member2 = new MemberInit();
        member2.name = "user2";
        member2.age = 16;
        member2.grade=80;

        MemberInit[] members = {member1,member2};

        for(MemberInit s: members){
            System.out.println("이름: "+s.name+"  나이:"+s.age+"  성적:"+s.grade);
        }
    }
}

=> 회원의 초기값을 설정하는 부분이 반복 된다. 메서드(initMember)를 사용하여 반복을 제거하자.
<MemberInitMain2.java>

package construct;

public class MethodInitMain2 {
    public static void main(String[] args) {
        MemberInit member1= new MemberInit();
        initMember(member1,"user1",15,90);

        MemberInit member2 = new MemberInit();
        initMember(member2,"user2",16,80);

        MemberInit[] members = {member1,member2};

        for(MemberInit s: members){
            System.out.println("이름:"+ s.name+"  나이:"+s.age+ "  성적: "+ s.grade);
        }
    }
    static void initMember(MemberInit member, String name, int age, int grade){
        member.name = name;
        member.age = age;
        member.grade = grade;
    }
}

this

<MemberInit.java> 에 initMember() 추가

package construct;

public class MemberInit {
    String name;
    int age;
    int grade;

    //추가
    void initMember(String name, int age, int grade){
        this.name = name;
        this.age = age;
        this.grade = grade;
    }
}

<MemberInitMain3.java>

package construct;

public class MethodInitMain3 {
    public static void main(String[] args) {
        MemberInit member1= new MemberInit();
        member1.initMember("user1",15,90);

        MemberInit member2 = new MemberInit();
        member2.initMember("user2",16,80);

        MemberInit[] members = {member1,member2};

        for(MemberInit s: members){
            System.out.println("이름:"+ s.name+"  나이:"+s.age+ "  성적: "+ s.grade);
        }
    }
}

this

  • this.name =name 에서 name 을 구분하는 법?, 멤버변수와 매개변수의 이름이 똑같은 때 구분하는 법?
    => 멤버변수보다 매개변수가 코드블럭의 더 안쪽에 있기 때문에 매개변수가 우선순위를 가진다.
  • 둘의 이름이 같을 땐 this를 사용해서 명확히 구분 해야한다.
  • 멤버변수에 접근하려면 this를 사용
  • this 는 인스턴스 자신을 가리킨다.

this의 생략

this 는 생략할 수 있다. 이 경우 변수를 찾을 때 가까운 지역변수(매개변수도 지역변수다)를 먼저 찾고 없으면 그 다음으로 멤버 변수를 찾는다. 멤버 변수도 없으면 오류가 발생한다.

<MemberThis.java>

package construct;

public class MemberThis {
    String nameField;

    void initMember(String nameParameter){
        nameField = nameParameter;
    }
}

this의 코딩 스타일

항상 this를 사용하는 스타일
<MemberThis.java>

package construct;

public class MemberThis {
    String nameField;

    void initMember(String nameParameter){
        this.nameField = nameParameter;
    }
}

=> 헷갈릴 땐 그냥 추가 해도된다. 이렇게하면 이 코드가 멤버 변수를 사용한다는 것을 눈으로 쉽게 확인할 수 있다. BUT 요즘은 잘 안쓴다. 권장하지 않는다.

도입

프로그래밍을 하다보면 객체 생성후 바로 초기값을 할당해야하는 경우가 생기는데 그럴 때마다 매번 메서드를 만들어야 한다.
그래서 대부분의 객체지향언어는 객체를 생성하자마자 즉시 필요한 기능을 좀 더 편리하게 수행할 수 있도록 생성자라는 기능을 제공한다.

<MemberConstruct.java>

package construct;

public class MemberConstruct {
    String name;
    int age;
    int grade;

//생성자
    MemberConstruct(String name, int age, int grade){
        System.out.println("생성자 호출 name= "+name+", age="+age+", grade="+grade);
        this.name=name;
        this.age=age;
        this.grade = grade;
    }
}
  • 생정자의 이름은 클래스 이름과 동일해야한다. 따라서 첫글자도 대문자로 시작한다.
  • 생정자는 반환타입이 없다.
  • 나머지는 메서드와 동일

<ConstructMain1.java>

package construct;

public class ConstructMain1 {
    public static void main(String[] args) {
        MemberConstruct member1 = new MemberConstruct("user1", 15,90);
        MemberConstruct member2 = new MemberConstruct("user2", 16 ,80);

        MemberConstruct[] members = {member1,member2};

        for (MemberConstruct s : members) {
            System.out.println("이름:" + s.name + " 나이:" + s.age + " 성적:" +
                    s.grade);
        }
    }
}

생성자 장점

  1. 중복 호출 제거
    객체를 생성하면서 동시에 필요한 작업을 한번에 처리할 수 있다.
  2. 객체를 생성할 때 직접 정의한 생성자가 있다면 직접 정의한 생성자를 반드시 호출해야 한다.

기본생성자

<MemberInit.java>, <MethodInitMain3.java> 을 보면 알 수 있다.

  • 매개변수가 없는 생성자를 기본 생성자라고 한다.
  • 클래스에 생성자가 하나도 없으면 자바 컴파일러는 매개변수가 없고, 작동하는 코드가 없는 기본 생성자를 자동으로 만들어준다.
  • 생성자가 하나라도 있으면 자바는 기본 생성자를 만들지 않는다.

<MemberDefault.java>

package construct;

public class MemberDefault {
    String name;

    //java에서 기본생성자 만들어줌
//    MemberDefault(){
//
//    }
    
    //직접 기본생성자 정의
    MemberDefault() {
        System.out.println("생성자 호출");
    }
}

<MemberDefaultMain.java>

package construct;

public class MemberDefaultMain {
    public static void main(String[] args) {
        MemberDefault memberDefault = new MemberDefault();
    }
}

오버로딩과 this()

<MemberConstruct.java>

package construct;

public class MemberConstruct {
    String name;
    int age;
    int grade;

    //오버로딩 추가
    MemberConstruct(String name, int age){
        this.name =name;
        this.age = age;
        this.grade = 50;
    }

    //변경
//    MemberConstruct(String name, int age){
//        this(name, age,50);
//    }

    //생성자
    MemberConstruct(String name, int age, int grade){
        System.out.println("생성자 호출 name= "+name+", age="+age+", grade="+grade);
        this.name=name;
        this.age=age;
        this.grade = grade;
    }
}

기존의 MemberConstruct에 생성자를 하나 추가해서 생성자가 2개가 됨

MemberConstruct(String name, int age)
MemberConstruct(String name, int age, int grade) 

새로 추가한 생성자는 grade를 받지 않고 50점으로 초기화.

<ConstructMain2.java>

package construct;

public class ConstructMain2 {
    public static void main(String[] args) {
        MemberConstruct member1 = new MemberConstruct("user1", 15,90);
        MemberConstruct member2 = new MemberConstruct("user2", 16);

        MemberConstruct[] members = {member1,member2};

        for (MemberConstruct s : members) {
            System.out.println("이름:" + s.name + " 나이:" + s.age + " 성적:" +
                    s.grade);
        }
    }
}

<결과>

=> 오버로딩으로 성적 입력이 필요한 경우에는 grade가 있는 생성자 호출, 없는 경우에는 grade가 없는 생성자 호출!

MemberConstruct.java 의 중복된 코드
this.name ... =>
<MemberConstruct.java>

package construct;

public class MemberConstruct {
    String name;
    int age;
    int grade;

    //오버로딩 추가
//    MemberConstruct(String name, int age){
//        this.name =name;
//        this.age = age;
//        this.grade = 50;
//    }

    //변경
    MemberConstruct(String name, int age){
        this(name, age,50);
    }

    //생성자
    MemberConstruct(String name, int age, int grade){
        System.out.println("생성자 호출 name= "+name+", age="+age+", grade="+grade);
        this.name=name;
        this.age=age;
        this.grade = grade;
    }
}

첫번째 생성자 내부에서 두번째 생성자를 호출한다.

MemberConstruct(String name, int age) -> MemberConstruct(String name, int age,int grade)

=> this()를 사용하면 생성자 내부에서 다른 생성자를 호출 할 수 있다 -> 중복 제거

this 의 규칙

  • this()는 생성자 코드의 첫줄에만 작성가능하다.
    <규칙위반>
public MemberConstruct(String name, int age) {
 System.out.println("go");
 this(name, age, 50);
}

문제와풀이1

<Book.java>

package construct.ex;

public class Book {
    String title;
    String author;
    int page;

    Book(String title, String author){
        this(title, author,0);
    }
    Book(String title, String author, int page){
        this.title = title;
        this.author=author;
        this.page = page;
    }
}

<BookMain.java>

package construct.ex;

public class BookMain {
    public static void main(String[] args) {
        Book book0 = new Book("","");
        Book book1 = new Book("Hello Java", "Seo");
        Book book2 = new Book("JPA 프로그래밍", "kim",700);

        Book[] books = {book0,book1,book2};
        for (Book b:books){
            System.out.println("제목: "+b.title+"  저자:"+b.author+ "  페이지:"+b.page);
        }
    }
}
profile
개발감자
post-custom-banner

0개의 댓글