본문 바로가기

학원/복기

0406 - 클래스, 접근 제한자(private, public),캡슐화,메소드 오버로딩, 다향성, VO클래스, 필드/생성자/메소드 선언

클래스(Class) : 객체(Object)를 생성하기 위한 자료형  (참조형) 

 ▶ Java에서는 객체(Object) 대신 인스턴스(Instance)라는 용어 사용 
 ▶  현실에 존재하는 사물 또는 관념을 클래스라는 자료형으로 표현하여 프로그램에 사용할 수 있도록 객체로 생성 

클래스는 설계도로 존재하고, 실체가 없는 것이다. 실체화 시키기 위한 것이 객체이다. 
 
 
형식)

 [public] class 클래스명 {  //[] -> 생략가능
		필드(Field) //>> 없어도 되지만, 필요한 경우 써줘야 한다.
		...
		생성자(Constructor) 
		...
		메소드(Method) 
		...  
		}

 
-필드: 표현 대상의 속성을 저장하기 위한 변수 - 멤버변수 
-생성자: 클래스로 객체를 생성하기 위한 특별한 형태의 메소드 (new 연산자로 호출되는 메소드로 객체 생성시 필드의 초기화를 담당) 
-메소드: 표현 대상의 행위를 명령으로 제공하기 위한 함수 - 멤버함수 (객체의 동작에 해당하며 클래스의 다양한 동작을 구성)
 
▶ 메소드는 필드를 사용한 명령으로 필요한 기능을 제공되도록 작성해야한다. 
▶ 클래스 내부에 선언된 모든 메소드는 필드에 접근이 가능하다.
 
 
자바에서는 클래스를 만들고 객체를 이용해 필드에 직접적인 접근을 제한하고, 메소드를 통해서만 프로그램을 작성하는 것이 좋다.
>>> 객체를 사용하여 필드에 직접적인 접근을 허용하면 필드에 비정상적인 값이 저장될 수 있기 때문.
 
클래스를 작성할 때 필드와 메소드에는 접근 제한자를 사용하여 접근 허용 유무 설정이 가능하다.
 
 
접근 제한자(Access Modifier)(접근지정자)  : private, package(default), protected , public
 
접근제한자는 클래스, 필드, 생성자, 메소드를 선언할 때 접근 허용을 설정하기 위한 키워드이다. 
 
- private : 클래스 내부에서만 접근 가능하도록 허용하는 접근 제한자 
▶ 필드, 생성자, 메소드를 은닉화 처리하기 위해 사용 - 클래스 외부에서 접근할 경우 에러 발생 
▶ 일반적으로 '필드'에 사용하는 접근 제한자 - 객체로 필드에 직접적인 접근을 제한하기 위해 사용 
     => 이를 데이터 은닉화 라고 한다.
      데이터 은닉화(Data Hiding) : 값을 숨겨 보호하기 위한 기능 

- public : 모든 패키지의 클래스에서 접근가능하도록 설정하기 위한 키워드 (외부 클래스가 자유롭게 사용할 수 있도록)
일반적으로 메소드에 사용하는 접근 제한자 - 클래스를 사용하는 모든 클래스에서 접근할 수 있도록 허용 
 


예시) 자동차를 객체 모델링하여 클래스로 작성

속성 : 모델명, 엔진상태, 현재속도 - 필드
행위 : 시동 On, 시동 Off, 속도 증가, 속도 감소, 이동 중지 - 메소드

 
필드 생성 (필드 은닉화 처리해 생성) 

public class Car {
    //필드(Field) : 클래스 내부에 선언된 모든 메소드는 필드 접근 가능
    private String modelName; //모델명
    private boolean engineStatus; //엔진상태 - false : EngineOff, true : EngineOn
    private int currentSpeed; //현재속도
}

 
생성자 미생성

생성자를 선언하지 않으면 매개변수가 없는 기본 생성자 (Default Constructor)가 자동으로 생성되어 제공

 
메소드 생성 - 필드를 사용하여 메소드의 명령으로 필요한 기능을 제공되도록 작성
 

public class Car {
	public void startEngine() { //시동 On
        engineStatus = true;
        System.out.println(modelName + "의 시동을 켰습니다."); //원래 출력하지 않지만 눈으로 보기 위함
    }

    public void stopEngine() {//시동 Off
        if(currentSpeed != 0) { //자동차가 멈춰있지 않은 상태인 경우
            //클래스 내부에 선언된 메소드는 서로 호출 가능
            // => 코드의 중복성을 최소화 하여 프로그램의 생산성 및 유지보수의 효율성 증가
            speedZero();
        }
        engineStatus = false;
        System.out.println(modelName + "의 시동을 껐습니다.");
    }

    public void speedUp(int speed){//속도 증가
        if(!engineStatus) { //엔진이 꺼져있는 경우
            System.out.println(modelName + "의 자동차 시동이 꺼져있습니다.");
            return; //메소드 종료
        }
        if (currentSpeed + speed > 150) {
            speed = 150 - currentSpeed;
        }
        currentSpeed += speed;
        System.out.println(modelName + "의 속도가 " + speed + "Km/h 증가 되었습니다. 현재 속도는 "
                + currentSpeed + "Km/h 입니다.");
    }

    public void speedDown(int speed) {//속도 감소
        if(!engineStatus) { //엔진이 꺼져있는 경우
            System.out.println(modelName + "의 자동차 시동이 꺼져있습니다.");
            return; //메소드 종료
        }
        if (currentSpeed < speed) {
            speed = currentSpeed;
        }
        currentSpeed -= speed;
        System.out.println(modelName + "의 속도가 " + speed + "Km/h 감소 되었습니다. 현재 속도는 "
                + currentSpeed + "Km/h 입니다.");
    }

    public void speedZero() {//이동 중지
        currentSpeed = 0;
        System.out.println(modelName + "의 자동차가 멈췄습니다.");
    }
}

 
 
은닉화 처리된 필드를 위해 필드값을 반환하는 Getter 메소드와 필드값을 변경하는 Setter 메소드를  선언해야 한다. =>  캡슐화
 

캡슐화(Encapsulation)

: 표현대상의 속성(필드)과 행위(메소드)를 하나의 자료형(클래스)로 작성 (넓은 의미에서의 캡슐화)
 → 보다 정확히 말하자면, 필드를 은닉화 처리하여 보호하고 메소드를 이용하여 처리되도록 설정 (좁은 의미에서의 캡슐화) 
 
 
 
-Getter 메소드 : 클래스에 외부에서 필드값을 사용할 수 있도록 필드값을 반환하는 메소드
 

public 반환형 get필드명() { return 필드명; }

필드의 자료형이 [boolean]인 경우 메소드의 이름을 [is필드명]으로 작성 
 
예시)

public String getModelName() { //카멜표기법 : 변수명, 메소드명
    //=> 첫번째 단어 제외한 나머지 단어의 첫문자를 대문자로 표현
    return modelName;
}

 
-Setter 메소드 : 매개변수로 값을 전달받아 매개변수에 저장된 값으로 필드값을 변경하는 메소드
 

public void set필드명(자료형 변수명) { 필드명 = 변수명; }

매개변수에 전달되어 저장된 값에 대한 검증 가능
 
예시)

public void setModelName(String modelName) {
    //this : 메소드 내부에서 클래스의 객체를 표현하는 키워드
    // => this 키워드를 사용하여 필드 표현
    this.modelName = modelName; //매개변수의 이름과 필드의 이름을 다르게 만들어도 되는데
    //일부러 이런식으로 똑같이 만드는 경우가 많다
    //필드와 매개변수를 구분하기 위해 this 사용

 
이클립스에서는 은닉화 처리된 필드에 대한 Getter 메소드와 Setter 메소드를 생성하는 기능 제공
 
방법1) Source -> generate getters and setters 

방법2) 단축키 : [Alt] + [Shift] + [S] : 팝업메뉴 >> [R] >> 필드 선택(은닉화 되어있는 필드 목록이 뜬다) >> Generate  
 
Getter 메소드와 Setter 메소드를 직접 만들 필요 없이 해당 기능 이용하면 된다
 
이클립스 기능 이용해 메소드 생성

//Getter 메소드와 Setter 메소드 자동 생성
public boolean isEngineStatus() { //필드의 자료형이 boolean이기 때문에 is로 만들어진 것임
    return engineStatus;
}

public void setEngineStatus(boolean engineStatus) {
    this.engineStatus = engineStatus;
}

public int getCurrentSpeed() {
    return currentSpeed;
}

public void setCurrentSpeed(int currentSpeed) {
    this.currentSpeed = currentSpeed;
}

 
 
메소드는 if문을 통해 값에 대한 검증을 할 수 있다는 것이 장점이다. 따라서 Setter 메소드가 있으면 좋은 점은, 값에 대한 검증을 할 수 있다는 것이다. 검증을 통해 잘못된 값을 받더라도 정상적으로 메소드를 동작시킬 수 있다.

즉, 은닉화의 목적 >> 잘못된 값이 저장되는 것을 방지하는 목적
무조건 모든 필드를 은닉화 시켜야 한다는 것이 아니다.. 클래스를 어떻게 설계하느냐에 따라 다르다.
은닉화된 필드를 가져다 쓰기 위해 Setter, Getter을 만드는 것임! (필요시)
 


클래스로 객체를 생성하는 방법

형식) 

클래스명 참조변수 = new 클래스명();


new 연산자로 클래스의 기본 생성자를 호출하여 객체를 생성하고 생성된 객체의 메모리 주소를 참조변수에 저장
참조변수에저장된 객체는 . 연산자를 사용하여 필드 또는 메소드를 이용해 프로그램 작성

 
 

예시) Car 클래스를 객체로 생성하여 작성된 프로그램 만들기 

 

Car 클래스로 객체를 생성하여 참조변수에 저장

public static void main(String[] args) {
    Car carOne = new Car(); //객체 생성
    Car carTwo = new Car(); //객체 생성
    Car carThree = new Car(); //객체 생성
}

 
▶ 하나의 클래스로 서로 다른 객체를 여러개 생성 가능
▶ 클래스는 객체를 생성하기 위한 틀(Template)
▶ 객체를 생성하면 객체의 필드에서는 기본값을 초기값(숫자형 : 0, 논리형 : false, 참조형 : null) 자동 저장된다.
 
carOne, carTwo, carThree 각각의 참조변수에 저장된 객체의 메모리 주소는 모두 다르다. 즉, 세개 모두 다른 객체이다.
 
참조변수.필드명 : 참조변수에 저장된 객체가 . 연산자를 사용하여 필드에 접근
 객체의 필드에는 기본값이 초기값으로 자동 저장되어 있다.

carOne.modelName = "싼타페"; //객체의 필드값 변경 - 필드가 은닉화 처리되어 에러 발생

 
은닉화 선언된 필드에 접근할 경우 에러 발생.
 
-> Setter 메소드, Getter 메소드 호출해 필드값 변경 

carOne.setModelName("싼타페"); //필드값을 변경하는 Setter 메소드 호출

System.out.println("첫번째 자동차 모델명 = " + carOne.getModelName());
System.out.println("첫번째 자동차 엔진상태 = " + carOne.isEngineStatus());
System.out.println("첫번째 자동차 현재속도 = " + carOne.getCurrentSpeed());

/*
 첫번째 자동차 모델명 = 싼타페
 첫번째 자동차 엔진상태 = false
    첫번째 자동차 현재속도 = 0
 */

 
은닉화 예시)
은닉화 전 
 

carTwo.modelName = "쏘나타";
carTwo.engineStatus = true; //원래는 메소드를 호출하는 방식이 정석
carTwo.currentSpeed = 80; //원래는 메소드를 호출하는 방식이 정석


System.out.println("두번째 자동차 모델명 = " + carTwo.modelName);
System.out.println("두번째 자동차 엔진상태 = " + carTwo.engineStatus);
System.out.println("두번째 자동차 현재속도 = " + carTwo.currentSpeed);

 
은닉화 후

carTwo.setModelName("쏘나타");
carTwo.setEngineStatus(true);
carTwo.setCurrentSpeed(80);

System.out.println("두번째 자동차 모델명 = " + carTwo.getModelName());
System.out.println("두번째 자동차 엔진상태 = " + carTwo.isEngineStatus());
System.out.println("두번째 자동차 현재속도 = " + carTwo.getCurrentSpeed());

 
참조변수.메소드명(값, 값 ...) : 참조변수에 저장된 객체가 . 연산자를 사용하여 메소드 호출
 
메소드 호출해 필드값 변경
 

carOne.startEngine();
carOne.speedUp(50); //싼타페의 속도가 50Km/h 증가 되었습니다. 현재 속도는 50Km/h 입니다.
carOne.speedUp(30); //싼타페의 속도가 30Km/h 증가 되었습니다. 현재 속도는 80Km/h 입니다.
carOne.speedDown(40); //싼타페의 속도가 40Km/h 감소 되었습니다. 현재 속도는 40Km/h 입니다.
carOne.speedZero(); //싼타페의 자동차가 멈췄습니다.
carOne.stopEngine(); //싼타페의 시동을 껐습니다.
//엔진을 켜지 않으면
//carOne.startEngine();
carOne.speedUp(50); //싼타페의 자동차 시동이 꺼져있습니다.
carOne.speedUp(30); //싼타페의 자동차 시동이 꺼져있습니다.
carOne.speedDown(40); //싼타페의 자동차 시동이 꺼져있습니다.
carOne.startEngine();
carOne.speedUp(50);
carOne.speedUp(300); //싼타페의 속도가 100Km/h 증가 되었습니다. 현재 속도는 150Km/h 입니다.
carOne.speedDown(40);
//carOne.speedZero();
carOne.stopEngine(); //싼타페의 자동차가 멈췄습니다.
//싼타페의 시동을 껐습니다.

메소드명이 같아도, 매개변수의 자료형이 다르면 서로 다른 메소드로 인식 된다. 

 

메소드 오버로딩(Method Overloading) 

: 클래스에서 동일한 기능을 제공하는 메소드가 매개변수에 의해 여러개 선언해야할 경우 메소드의 이름을 같도록 선언하는 기능 

 단, 매개변수의 자료형 또는 갯수가 같지 않도록 선언해야한다. 
 접근제한자와 반환형은 오버로드 선언과 무관하다. 
 

-오버로드 선언된 메소드는 매개변수에 전달되는 값에 따라 메소드를 선택하여 호출한다.
 >> 메소드 오버로드에 의한 다형성 
 

다형성(Polymorphism) 

: 같은 이름의 메소드를 호출할 경우 상태에 메소드를 선택 호출 => 메소드 오버로드, 메소드 오버라이드
 메소드 오버로드에서 의해 만들어지는 다향성, 메소드 오버라이드에서 만들어지는 다향성 2가지가 존재한다.
                                                                            
 
오버로딩 예시)
 

//Overload 클래스에 메소드 생성 

public class Overload {
    public void display(int param) {
        System.out.println("정수값 = " + param);
    }

    public void display (boolean param) {
        System.out.println("논리값 = " + param);
    }

    public void display(String param) {
        System.out.println("문자열 = " + param);
    }
}

// 실행
public static void main(String[] args) {
    Overload overload = new Overload(); //객체생성 후 overload 참조변수에 저장


    overload.display(200); //정수값 = 200
    overload.display(true); //논리값 = true
    overload.display("임꺽정"); //문자열 = 임꺽정

    //전달받은 값에 대한 자료형의 매개변수가 없는 경우 에러 발생
    //overload.display(12.34);
}

 


VO(Value Object) 클래스 : 특정 대상의 값을 저장할 목적의 객체를 생성하기 위한 클래스 

예시) 회원정보(아이디, 이름, 이메일)를 저장하기 위한 클래스 만들기

 

필드선언

public class Member {
    private String id;
    private String name;
    private String email;
    }

 

생성자 선언

생성자 : 객체를 생성하기 위한 특별한 형태의 메소드
▶ 생성자를 선언하지 않으면 매개변수가 없는 기본 생성자가 제공된다.
▶ 생성자를 선언하면 매개변수가 없는 기본 생성자 미제공
▶ 객체 필드에 원하는 초기값이 저장된 객체를 생성하기 위해 생성자를 선언

형식)

접근제한자 클래스명(자료형 변수명, 자료형 변수명, ... ) { 명령; 명령; ...}

▶ 반환형을 작성하지 않고 생성자의 이름은 반드시 클래스 이름과 동일하게 작성
▶ 메소드 오버로드를 사용하여 생성자를 여러개 선언 가능
▶ 일반적으로 생성자에서는 필드에 필요한 초기값을 저장하기 위한 명령 작성 - 초기화 작업

매개변수가 없는 생성자 선언 - 기본 생성자(Default Constructor)
▶ 초기화 작업 미구현 - 객체 필드에 기본값이 초기값으로 저장
▶ 기본 생성자를 선언하지 않으면 상속시 문제가 발생할 수 있으므로 기본 생성자를 선언하는 것을 권장
이클립스를 사용하여 기본 생성자 선언 가능
▶ [Ctrl] + [Space]  >> 나열된 목록 중 Constructor 선택

////이클립스 사용해 기본 생성자 선언
public Member() {

}

 
 
매개변수가 있는 생성자
▶ 매개변수에 전달되어 저장된 값을 필드의 초기값으로 저장
이클립스를 사용하여 매개변수가 있는 생성자 선언 가능
▶ [Alt] + [Shift] + [S] >> [O] >> 필드 선택 >> Generate
 

//이클립스 사용해 매개변수가 있는 생성자 선언
public Member(String id) {
    super(); //super은 추후에 배울 예정
    this.id = id;
}

public Member(String id, String name) {
    super();
    this.id = id;
    this.name = name;
}


public Member(String id, String name, String email) {
    super();
    this.id = id;
    this.name = name;
    this.email = email;
}

 

메소드 선언

 

//메소드 선언
//VO 클래스에서는 주로 속성에 대한 Setter 메소드, Getter 메소드 생성한다.

public String getId() {
    return id;
}


public void setId(String id) {
    this.id = id; //this는 추후에 배운다
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}
public String getEmail() {
    return email;
}

public void setEmail(String email) {
    this.email = email;
}

 
필드값을 확인하기 위해 필드값을 출력하는 메소드 (원래는 만들지 X 테스트 목적으로 만든거)

public void display() {
    System.out.println("아이디 = " + id);
    System.out.println("이름 =  " + name);
    System.out.println("이메일 = " + email);
}

 
Member 객체 실행하는 클래스 생성
 
 
new 연산자로 Member 클래스의 매개변수가 없는 기본 생산자를 호출하여 객체 생성
=> 생성된 객체의 필드에는 기본값이 초기값으로 자동 저장
=> new 연산자로 호출한 생성자가 없는 경우 에러 발생 - 객체 생성 불가능
 

public static void main(String[] args) {
    Member member1 = new Member();
}

 
Getter 메소드 호출하여 Member 객체의 필드값을 반환받아 출력
 

System.out.println("아이디 = " + member1.getId()); //아이디 = null
System.out.println("이름 =  " + member1.getName()); //이름 =  null
System.out.println("이메일 = " + member1.getEmail()); //이메일 = null

//참고) null은 원래 출력되지 않지만 문자열로 결합되어 출력된 것임.

 
Setter 메소드를 호출하여 Member 객체의 필드값 변경
 

member1.setId("abc123");
member1.setName("홍길동");
member1.setEmail("abc@itwill.xyz");

member1.display();

		/*
		아이디 = abc123
		이름 =  홍길동
		이메일 = abc@itwill.xyz
		*/

 
new 연산자로 매개변수가 있는 생성자를 호출하여 객체 생성
 

 Member member2 = new Member("def456");
        member2.display();
		/*
		아이디 = def456
		이름 =  null
		이메일 = null
		*/
        
 Member member3 = new Member("ghj789","임꺽정");
        member3.display();

		/*
		아이디 = ghj789
		이름 =  임꺽정
		이메일 = null
		*/
        
 Member member4 = new Member("xyz258", "전우치", "xyz@itwill.xyz");
        member4.display();

		/*
		아이디 = xyz258
		이름 =  전우치
		이메일 = xyz@itwill.xyz
		*/