기초단계/JAVA

2022.11.18-1 JAVA 클래스 4

춘핑이 2022. 11. 18. 16:25

6. 클래스

6.13 접근제한자

객체외부에서 사용하려고 할때 공개된 것만 사용가능
왜 공개? 외부에서 잘못사용하면 무결성이 깨지기때문
예를들어 나이 음수를 넣을수 없는데 음수값을 넣으면 안됨

캡슐화 시켜서 노출필드와 노출메소드가 있고 숨긴필드와 숨긴메소드가 있다.
접근제한자를 사용한다.
public protected default(안건거) private가 있다. -> 갈수록 접근제한이 강화
public 클래스 필드 생성자 메소드 제한범위 없음.
protected 필드 생성자 메소드 같은 패키지이거나 자식 객체만 사용가능(상속에서 자세히)
default 클래스 필드 생성자 매소드 같은 패키지
private 필드 생성자 메소드 객체 내부에서만 사용가능하게

6.13.1 클래스의 접근제한

[public] class 클래스 {...}
같은 패키지안에서는 사용가능한데 다른데선 사용불가능함.
public붙은 클래스는 어떤 패키지에서든 사용가능.

package ch06.sec13.exam1.package2;

import ch06.sec13.exam1.package1.B;

public class C {
    A a;
    B b;

}
//다른패키지 클래스 C에서 public이걸린 B는 참조가능한데 A는 불가능함.

6.13.2 생성자의 접근제한

public, 생략, private
public 모든패키지에서 생성자 호출가능 = 모든 패키지에서 객체 생성가능
생략 같은 패키지 안에서만 생성자 호출 가능 == 같은 패키지에서만 객체 생성가능
private 클래스 내부에서만 생성자 호출가능 = 클래스 내부에서만 객체 생성 가능

package ch06.sec13.exam2.package1;
public class A {
//필드선언
A a1 = new A(true);
A a2 = new A(1);
A a3 = new A("문자열");

//생성자 선언 같은 클래스안에서는 어떤 패키지든 사용가능
public A(boolean b){
}

A(int i){        
}

private A(String s){
    }

   package ch06.sec13.exam2.package1;

public class B {

A a1 = new A(true);
A a2 = new A(1);
A a3 = new A("문자열"); //이부분에러 private붙어잇기때문에 그 클래스에서만 사요가

   package ch06.sec13.exam2.package2;

import ch06.sec13.exam2.package1.A;
public class C {

A a1 = new A(true);
A a2 = new A(1); //이부분에러 public붙어있지 않아서 같은 패키지에서만 사용가능하기때문
A a3 = new A("문자열"); //이부분에러 private붙어잇기때문에 그 클래스에서만 사용가능
}

6.13.3 필드와 메소드의 접근제한

필드와 메소드를 선언할때 접근제한
[public | private] 타입 필드;
[public | private] 리턴 타입 메소드(...) {...};
public 모든패키지에서 필드 메소드 호출가능
생략 같은 패키지 안에서만 필드 메소드 호출 가능
private 클래스 내부에서만 필드 메소드 호출가능

package ch06.sec13.exam3.package1;
public class A {
    //필드선언
    public int field1;
    int field2;
    private int field3;

    //메소드 선언
    public void method1() {}
    void method2() {}
    private void method3() {}

    //생성자 선언
    public A()
    {
        field1 = 1;
        field2 = 1;
        field3 = 1;

        method1();
        method2();
        method3();    
    }    
    }

package ch06.sec13.exam3.package1;

public class B {
    public void method1 () {
        A a = new A();
        a.field1 = 1;
        a.field2 = 2;
        // a.field3 = 3; 사용불가

        a.method1(); //a.하고 자동완성보면 초록점은 public 파란세모는 디폴트임.
        a.method2();
        // a.method3(); 호출불가
    }
}

package ch06.sec13.exam3.package2;

import ch06.sec13.exam3.package1.A;

public class C {
    public C() {
        A a = new A();

        a.field1 = 1;
        // a.field2 = 1; 디폴트 다른 패키지 사용불가
        // a.field3 = 1; 프라이베이트 다른 패키지 사용불가

        a.method1();
        //a.method2(); 디폴트 다른 패키지 사용불가
        //a.method3(); 프라이베이트 다른 패키지 사용불가
    }
}

무작정 public을 붙여도안되고 무작정 디폴트 무작정 private를 붙여도 안된다. 설계시에 판단을 해서 붙여야한다.

6.14 Getter와 Setter

게터 값을 가져오는자 , 세터 값을 세팅하는 자
둘다 메소드임. 값가져오는 메소드는 게터 값바꾸는 메소드는 세터
Car myCar = new Car(); mycar.speed = -100; -> 차속도는 음수가 될수 없기때문에 무결성이 깨짐
이런 문제점때문에 객체 지행 프로그래밍에서는 직접적인 외부에서의 필드 접근을 막고 대신 메소드를 통해 필드에 접근하는 것을 선호한다.
필드를 private를 걸고 메소드로 접근해서 필드값을 변경하게 만듬.
// 외부의 값을 검사해서 올바른 값만 받게 만들기 = setter임 set필드이름(대문자시작) 메소드

private double speed;
public void setSpeed(double speed)
{
    if (speed < 0) //외부에서 음수를 받으면 0으로 만들어버림.
    {
        this.Speed = 0;
        return;
    }
    else
    {
        this.speed = speed;
    }
}

필드가 프라이빗이라 외부에서 스피드 값 읽지 못함.
외부에서 이를 읽고싶게 하기
get필드이름 메소드 필드값을 읽고 리턴해주는 코드 or 필드값을 가공해서 리턴하고 싶다면 가공된값 넣기

  private double speed;
public double getSpeed()
{
    double km = speed *1.6;
    return km;
} 

private 타입 fieldName;
public 타입 getFieldName(){return fieldName;}
public void setFiledName(타입 filedName){this.fieldName = fieldName;}
필드이름이 boolean일경우
private boolean stop;
public boolean isStop(){return stop;} // getter를 is로 시작하는 것이관례


이클립스에는 하도 많이써서 내장되어잇음. 상단 source generate Getters and Setters

package ch06.sec14;

public class Car {
    //필드선언
    private int speed;
    private boolean stop;

    //getter와 setter
    public int getSpeed() {

        return speed;
    }
    public void setSpeed(int speed) {
        if (speed < 0)
        {
            this.speed = 0;
            return;
        }
        else {
        this.speed = speed;
        }
    }
    public boolean isStop() {
        return stop;
    }
    public void setStop(boolean stop) {
        this.stop = stop;
        if (stop)
        {
            this.speed = 0;
        }
    }
}

Car myCar = new Car();
// myCar.speed = 1; private라 불가능
myCar.setSpeed(-50);
System.out.println("현재 속도: " + myCar.getSpeed()); //0 음수일경우 0으로 해달라했음.

myCar.setSpeed(60);
System.out.println("현재 속도: " + myCar.getSpeed());

if(!myCar.isStop())
{
    myCar.setStop(true);
    System.out.println("현재 속도: " + myCar.getSpeed());

6.15 싱글톤 패턴

new 연산자로 객체를 만든다.
보통 한클래스에 한 객체만 만들어서 사용함. 애플리케이션 전체에서 단 한개의 객체만 생성해서 사용하고 싶다면?
싱글톤 패턴을 적용할수잇다.
인스턴스를 여러개x 한클래스에 한객체만
싱글톤이란 객체를 하나만 만드는 것.
생성자를 private로만들면 다른데서 못만듬.

private 클래스() {}
public class 클래스{
public 접근 권한을 갖는 정적필드 선언과 초기화
private static 클래스 singleton = new 클래스(); -----------1
private접근 권한을 갖는 생성자 선언
private 클래스() {}
public 접근 권한을 갖는 정적 메소드 선언
private static 클래스 getInstance() --------------2
{
return singleton;
}

1에서는 자신의 타입으로 정적 필드를 ㅇ선언하고 미리 객체를 생성해서 초기화시킨다.
그리고 private 접근 제한자를 붙여 정적필드값을 변경하지 못하도록 막는다.

2.정적필드값을 리턴하는 get이름() 정적 메소드를 public으로 선언한다.
클래스 변수 1 = 클래스.getInstance();
클래스 변수 2 = 클래스.getInstance(); //리턴하는 객체가 singleton을 계속 불러오는거라 번지는 같음
외부에서는 private를 붙여서 사용불가능하나 메소드를 호출해서 리턴값을 받고자함.

package ch06.sec15;

public class Singleton {
    //private 접근 권한을 갖는 정적 필드 선언과 초기화
    private static Singleton singleton = new Singleton();

    //private 접근 권한을 갖는 생성자 선언
    private Singleton() {        
    }

    //public 접근 권한을 갖는 메소드 선언
    public static Singleton getInstance()
    {
        return singleton;
    }

    // Singleton obj1 = new Singleton(); 컴파일에러
    // Singleton obj2 = new Singleton(); 컴파일에러
    Singleton obj1 = Singleton.getInstance();
    Singleton obj2 = Singleton.getInstance();

    //동일 한 객체를 참조하는 지 확인
    if (obj1 == obj2)
    {
        System.out.println("같은 Singleton 객체입니다.");
    }
    else
    {
        System.out.println("다른 Singleton 객체입니다.");

    }

확인문제

18번

싱글톤 패턴을 사용해서 ShopService클래스 만들기

package ch06.sec16;

public class ShopService {
private static ShopService shopservice = new ShopService();

private ShopService()
{

}

public static ShopService getInstence() {
    return shopservice;

}

}

19번

setter와 getter를 사용하여 잔고(balance)필드는 음수값이 될수 없고 최대 백만원까지만 저장가능한 필드 만들기

package ch06.sec16;

public class Account {
    private int balance;
    private static final int MIN_BALANCE = 0;
    private static final int MAX_BALANCE = 100000;

    public int getBalance() {
        return balance;
    }

    public void setBalance(int balance) {
        if (balance < Account.MIN_BALANCE || balance > Account.MAX_BALANCE )
        {
            return;
        }
        else {
            this.balance = balance;
        }
    }
}

20번

키보드로 부터 계좌 정보를 입력받아 계좌를 관리하는 프로그램 계좌는 Account 객체로 생성되고 BankApplication에서 길이 100인 Account[]배열로 관리됨.

public class Account {
    //계좌번호 계좌주 초기입금액
    private String ano;
    private String owner;
    private int balance;

    public Account(String ano, String owner,int balance)
    {
        this.ano = ano;
        this.owner = owner;
        this.balance = balance;    
    }

    public String getAno() {
        return ano;
    }

    public void setAno(String ano) {
        this.ano = ano;
    }

    public String getOwner() {
        return owner;
    }

    public void setOwner(String owner) {
        this.owner = owner;
    }

    public int getBalance() {
        return balance;
    }

    public void setBalance(int balance) {
        this.balance = balance;
    }

}

public class BankApplication {
private static Scanner sc = new Scanner(System.in);
private static Account[] accountArray = new Account[100];
//배열로 account 객체 가져옴
public static void main(String[] args) {

    while (true)
    {
        System.out.println("---------------------------------");
        System.out.println("1.계좌생성|2.계좌목록|3.예금|4.출금|5.종료");
        System.out.println("---------------------------------");
        System.out.print("선택>");

        int selcetNum = sc.nextInt();

        if (selcetNum == 1)
        {
            createAccout();
        }
        else if (selcetNum == 2)
        {
            accountList();
        }
        else if (selcetNum == 3)
        {
            deposit();
        }
        else if (selcetNum == 4)
        {
            withdraw();
        }
        else if (selcetNum == 5)
        {
            System.out.println("프로그램 종료");
            break;
        }
    }
}

    //계좌생성하기
    private static void createAccout()
    {
        System.out.println("-------");
        System.out.println("계좌생성");
        System.out.println("-------");

        System.out.println("계좌번호: ");
        String ano = sc.next();

        System.out.println("계좌주: ");
        String owner = sc.next();

        System.out.println("초기입금액: ");
        int balance = sc.nextInt();

        Account newAccount = new Account(ano, owner, balance);
        for (int i = 0; i < accountArray.length; i++)
        {
            if (accountArray[i] == null)
            {
                accountArray[i] = newAccount;
                System.out.println("결과: 계좌가 생성되었습니다.");
                break;
            }
        }
    }
    //계좌목록
    private static void accountList()
    {
        System.out.println("-------");
        System.out.println("계좌목록");
        System.out.println("-------");


        for (int i = 0; i < accountArray.length; i++)
        {
            Account account = accountArray[i];
            if (account != null) {
                System.out.print(account.getAno());
                System.out.print("   ");
                System.out.print(account.getOwner());
                System.out.print("   ");
                System.out.print(account.getBalance());
                System.out.println();
            }
        }
    }

    //입금하기
    private static void deposit()
    {
        System.out.println("-------");
        System.out.println("예금");
        System.out.println("-------");

        System.out.println("계좌번호: ");
        String ano = sc.next();

        System.out.println("예금액: ");
        int money = sc.nextInt();
        Account account = findAccount(ano);
        if (account == null)
        {
            System.out.println("결과: 계좌가 없습니다.");
            return;
        }
        account.setBalance(account.getBalance() + money);
        System.out.println("결과: 예금이 성공되었습니다.");

    }

    private static void withdraw()
    {
        System.out.println("-------");
        System.out.println("출금");
        System.out.println("-------");

        System.out.println("계좌번호: ");
        String ano = sc.next();

        System.out.println("출금액: ");
        int money = sc.nextInt();
        Account account = findAccount(ano);
        if (account == null)
        {
            System.out.println("결과: 계좌가 없습니다.");
            return;
        }
        account.setBalance(account.getBalance() - money);
        System.out.println("결과: 출금이 성공되었습니다.");

    }

    //account배열에서 ano 동일한 account객체 찾기
    private static Account findAccount(String ano)
    {
        Account account = null;
        for(int i = 0; i < accountArray.length; i++)
        {
            if (accountArray[i] != null)
            {
                String dbAno = accountArray[i].getAno();
                if(dbAno.equals(ano))
                {
                    account = accountArray[i];
                    break;
                }
            }

        }
        return account;
    }

2022.11.18 리뷰

클래스 남은 편을 마무리하고 휴식을 취했다.
뭐 한게 있다고 쉬나 싶지만 내용 복습을 위해 조절이 필요해보였다.
기본 문법 자체는 이해가 가지만 20번 확인문제처럼 응용하는 부분에서는 매우매우매우 어려움이 있다.
내일부터 열심히 달려나가기로 하고 또 다시 돌아와 복습해야 할 것이다.
오늘도 화이팅