[3/8] Java 기본 Summary (w. 인프런 김영한)

차재현·2024년 12월 24일
0

[ Backend Study - Java ]

목록 보기
4/11
post-thumbnail

Section 6. 접근 제어자

Chapter 1. 접근 제어자 이해1

  • 다음 예시 코드를 보자
    package access;
    
    public class Speaker {
        int volume;
    
        Speaker(int volume) {
            this.volume = volume;
        }
    
        void volumeUp() {
            if (volume >= 100) {
                System.out.println("음량을 증가할 수 없습니다. 최대 음량입니다.");
            } else {
                volume += 10;
                System.out.println("음량을 10 증가합니다.");
            }
        }
    
        void volumeDown() {
            volume -= 10;
            System.out.println("volumeDown 호출");
        }
    
        void showVolume() {
            System.out.println("현재 음량: " + volume);
        }
    }
    
    • 위 처럼 Speaker 클래스의 volume은 100을 넘어서는 안되는 상황이다.

      package access;
      
      public class SpeakerMain {
          public static void main(String[] args) {
              Speaker speaker = new Speaker(90);
              speaker.showVolume();
      
              speaker.volumeUp();
              speaker.showVolume();
      
              speaker.volumeUp();
              speaker.showVolume();
      
              //필드에 직접 접근
              System.out.println("volume 필드 직접 접근 수정");
              speaker.volume = 200;
              speaker.showVolume();
          }
      }
    • 하지만 위 코드처럼 Speaker 클래스를 사용할 때, volume에 직접 접근해서 값을 바꿔버리면 volumeUp 메서드를 만들어도 의미가 없다.

    • 그래서 상황에 따라 특정 필드나 메서드에 접근하는 것을 제어해야한다.
      → 그래서 접근제어자가 필요한 것이다.

Chapter 2. 접근 제어자 이해 2

  • 그렇다면 Speaker 클래스의 volume에 접근하는 것을 어떻게 제어할 수 있을까? 이때는 private을 사용하면 된다.
  • 특정 필드나 메서드의 타입 앞에 접근 제어자를 적으면 된다.
    package access;
    
    public class Speaker {
        private int volume;
    
        Speaker(int volume) {
            this.volume = volume;
        }
    
        // ...
    }
    
    package access;
    
    public class SpeakerMain {
        public static void main(String[] args) {
            Speaker speaker = new Speaker(90);
            speaker.showVolume();
    
            speaker.volumeUp();
            speaker.showVolume();
    
            speaker.volumeUp();
            speaker.showVolume();
    
            //필드에 직접 접근
            System.out.println("volume 필드 직접 접근 수정");
            speaker.volume = 200; // -> 컴파일 에러 발생!
            speaker.showVolume();
        }
    }
    • 이제 다시 SpeakrMain 클래스를 실행하면 speakr.volume=200; 코드에서 컴파일에러가 발생하며 직접 접근하지 못하게 된다.

Chapter 3. 접근 제어자 종류

  • 접근제어자 종류
    • private : 모든 외부 호출을 막는다.
    • default (package-private): 같은 패키지안에서 호출은 허용한다.
    • protected : 같은 패키지안에서 호출은 허용한다. 패키지가 달라도 상속 관계의 호출은 허용한다.
    • public : 모든 외부 호출을 허용한다.
  • default (package-private)
    • 접근제어자를 명시하지 않는다면 기본적으로 default가 붙는다.
  • 접근 제어자 사용 위치
    • 클래스
    • 필드
    • 생성자
    • 메서드
      ❌ 메서드 내부의 지역, 전역 변수의 경우 접근제어자를 사용할 수 없다! ❌

Chapter 4. 접근 제어자 사용 - 필드, 메서드

  • 예시 코드를 통해 실습해보자
    package access.a;
    
    public class AccessData {
    
        public int publicField;
        int defaultField;
        private int privateField;
    
        public void publicMethod() {
            System.out.println("publicMethod 호출 " + publicField);
        }
    
        void defaultMethod() {
            System.out.println("defaultMethod 호출 " + defaultField);
        }
    
        private void privateMethod() {
            System.out.println("privateMethod 호출 " + privateField);
        }
    
        public void innerAccess() {
            System.out.println("내부 호출");
            publicField = 100;
            defaultField = 200;
            privateField = 300;
            publicMethod();
            defaultMethod();
            privateMethod();
        }
    
    }
    
    package access.a;
    
    public class AccessInnerMain {
        public static void main(String[] args) {
            AccessData data = new AccessData();
            //public 호출 가능
            data.publicField = 1;
            data.publicMethod();
    
            //같은 패키지 default 호출 가능
            data.defaultField = 2;
            data.defaultMethod();
    
            //private 호출 불가
            //data.privateField = 3;
            //data.privateMethod();
    
            data.innerAccess();
        }
    }
    • innerAccess 메서드의 경우 public 이기때문에 외부 호출이 가능하다.
    • innerAccess 메서드가 호출되어 실행되는 동안에는 AccessData 내부에서 작동하기에 AccessData의 public, default, private 모두 접근 가능하다.

Chapter 5. 접근 제어자 사용 - 클래스 레벨

  • 클래스 레벨에서는 publicdefault 접근제어자만 사용할 수 있다
  • 하나의 java파일에서는
    • 오직 하나의 public 클래스만 존재 가능
      → public 클래스의 이름은 java파일의 이름과 같아야 한다!
    • 무한대의 default 클래스 존재 가능
  • 예시 코드를 통해 실습해보자
    package access.a;
    
    public class PublicClass {
        public static void main(String[] args) {
            PublicClass publicClass = new PublicClass();
            DefaultClass1 class1 = new DefaultClass1();
            DefaultClass2 class2 = new DefaultClass2();
        }
    }
    
    class DefaultClass1 {
    
    }
    
    class DefaultClass2 {
    
    }
    • PublicClass 클래스는 public이기에 내/외부 패키지에서 모두 호출 가능
    • DefaultClass1, DefaultClass2default 이기에 내부 패키지에서만 호출 가능

강의를 들으며 생긴 의혹

  • 위 코드에서 main 메서드 안에서 바로 PublicClass를 생성하는 모습을 볼 수 있다. 평소에 저런 모습의 코드는 보지 못했었기에 테스트를 해보았다.
           package practice;
                   public class Practice {
           	int a;
           	Practice(int a){
           		this.a = a;
           	}
           	public static void main(String[] args) {
           		Practice practice = new Practice(10);
           		System.out.println(practice.a); // 출력값 : 10
           	}
           }
  • main 메서드도 결국 클래스에서 생성된 하나의 메서드라는 관점을 다시금 알게 되었다. 단지 java 실행 시, 작동의 시작점인 메서드라는 것일 뿐!

Chapter 6. 캡슐화

[ TIP 유용한 단축키 ]

  • command + N

    • 현재 클래스의 생성자를 만들어준다!
    • Generator 화면이 뜰 텐데, 생성자를 만들 현재 클래스의 필드들을 선택할 수 있다.
  • 캡슐화는 쉽게 이야기해서 “속성”과 “기능”을 하나로 묶고, 외부에 꼭 필요한 기능만 노출하고 나머지는 모두 내부로 숨기는 것이다.

    • 데이터를 숨겨라!
      • 모든 데이터를 private으로 숨겨라
    • 기능을 숨겨라!
      • 객체의 메서드 중에서 외부에서 사용하지 않고 내부에서만 사용하는 기능들이 있다. 이런 기능들 모두 private으로 숨겨라!
  • 예시 코드

    package access;
    
    public class BankAccount {
    
        private int balance;
    
        public BankAccount() {
            balance = 0;
        }
    
        // public 메서드: deposit
        public void deposit(int amount) {
            if (isAmountValid(amount)) {
                balance += amount;
            } else {
                System.out.println("유효하지 않은 금액입니다.");
            }
        }
    
        // public 메서드: withdraw
        public void withdraw(int amount) {
            if (isAmountValid(amount) && balance - amount >= 0) {
                balance -= amount;
            } else {
                System.out.println("유효하지 않음 금액이거나 잔액이 부족합니다.");
            }
        }
    
        // public 메서드: getBalance
        public int getBalance() {
            return balance;
        }
    
        private boolean isAmountValid(int amount) {
            // 금액이 0보다 커야함
            return amount > 0;
        }
    }
    • balance (잔액) 데이터를 private으로 잘 숨겼다.
    • 입금 및 출금 시에 입력받을 amount(금액)을 검증하는 isAmountValid 메서드는 BackAcount 클래스 내부에서만 사용되기에 private으로 잘 숨겼다.
  • 만약 balance를 노출하면 어떻게 될까?

    • BankAccount 를 사용하는 개발자 입장에서는 이 필드를 직접 사용해도 된다고 생각할 수 있다. 왜냐하면 외부에 공개하는 것은 그것을 외부에서 사용해도 된다는 뜻이기 때문이다. 결국 모든 검증과 캡슐화가 깨지고 잔고를 무한정 늘리고 출금하는 심각한 문제가 발생할 수 있다.
  • 만약 isAmouontValid를 노출하면 어떻게 될까?

    • BankAccount 를 사용하는 개발자 입장에서는 사용할 수 있는 메서드가 하나 더 늘었다. 여러분이 BankAccount 를 사용하는 개발자라면 어떤 생각을 할까? 아마도 입금과 출금 전에 본인이 먼저 isAmountValid() 를 사용해서 검증을 해야 하나? 라고 의문을 가질 것이다.

Chapter 7. 캡슐화&접근제어자 문제 - 쇼핑 카트

  • 요구사항
    • 접근 제어자를 사용해서 데이터를 캡슐화 하세요.
    • 해당 클래스는 다른 패키지에서도 사용할 수 있어야 합니다.
    • 장바구니에는 상품을 최대 10개만 담을 수 있다.
      • 10개 초과 등록시: "장바구니가 가득 찼습니다." 출력
  • 제공되는 코드
    package access.ex;
    
    public class ShoppingCartMain {
    
        public static void main(String[] args) {
            ShoppingCart cart = new ShoppingCart();
    
            Item item1 = new Item("마늘", 2000, 2);
            Item item2 = new Item("상추", 3000, 4);
    
            cart.addItem(item1);
            cart.addItem(item2);
    
            cart.displayItems();
        }
    }
    package access.ex;
    
    public class Item {
        private String name;
        private int price;
        private int quantity;
    
    		// 나머지 코드를 완성하시오
    }
    package access.ex;
    
    public class ShoppingCart {
        private Item[] items = new Item[10];
        private int itemCount;
    
    		// 나머지 코드를 완성하시오
    }
  • 실행 결과
    장바구니 상품 출력
    상품명:마늘, 합계:4000
    상품명:상추, 합계:12000
    전체 가격 합:16000
  • 정답
    • Item 클래스
      package access.ex;
      
      public class Item {
          private String name;
          private int price;
          private int quantity;
      
          public Item(String name, int price, int quantity) {
              this.name = name;
              this.price = price;
              this.quantity = quantity;
          }
      
          public String getName() {
              return name;
          }
      
          public int getTotalPrice() {
              return price * quantity;
          }
      
      }
      • 설명 각각의 Item 의 가격과 수량을 곱하면 각 상품별 합계를 구할 수 있다. pricequantity 를 외부에 반환한 다음에 외부에서 곱해서 상품별 합계를 구해도 되지만, getTotalPrice() 메서드를 제공하면 외부에서는 단순히 이 메서드를 호출하면 된다.
        이 메서드의 핵심은 자신이 가진 데이터를 사용한다는 점이다.
    • ShoppingCart 클래스
      package access.ex;
      
      public class ShoppingCart {
          private Item[] items = new Item[10];
          private int itemCount;
      
          public void addItem(Item item) {
              if (itemCount >= items.length) {
                  System.out.println("장바구니가 가득 찼습니다.");
                  return;
              }
      
              items[itemCount] = item;
              itemCount++;
          }
      
          public void displayItems() {
              System.out.println("장바구니 상품 출력");
              for (int i = 0; i < itemCount; i++) {
                  Item item = items[i];
                  System.out.println("상품명:" + item.getName() + ", 합계:" + item.getTotalPrice());
              }
      
              System.out.println("전체 가격 합:" + calculateTotalPrice());
          }
      
          private int calculateTotalPrice() {
              int totalPrice = 0;
              for (int i = 0; i < itemCount; i++) {
                  Item item = items[i];
                  totalPrice += item.getTotalPrice();
              }
              return totalPrice;
          }
      }
      • 설명 calculateTotalPrice() : 이 메서드 내부에서만 사용되므로 private 접근 제어자를 사용한다.
profile
Develop what? and why?

0개의 댓글