제어자

서지우·2023년 7월 11일
0

JAVA

목록 보기
17/28

제어자란?

- 클래스, 변수, 메서드의 선언부에 사용되어 부가적인 의미를 부여함
- 제어자는 크게 접근 제어자와 그 외의 제어자로 나뉨
- 하나의 대상에 여러 개의 제어자를 조합해서 사용할 수 있으나, 접근제어자는 단 하나만 사용할 수 있음    


static

사용될 수 있는 곳

멤버변수, 메서드, 초기화 블럭


final

사용될 수 있는 곳

클래스, 메서드, 멤버변수, 지역변수


생성자를 이용한 final 멤버변수 초기화

- final이 붙는 변수는 상수이므로 보통은 선언과 초기화를 동시에 함
- 하지만 인스턴스마다 고정값을 갖는 인스턴스 변수의 경우 생성자에서 초기화함


abstract

사용될 수 있는 곳

클래스, 메서드


접근제어자

- 멤버 또는 클래스에 사용되어, 외부로부터의 접근을 제한함 


접근제어자를 이용한 캡슐화


생성자의 접근 제어자

- 일반적으로 생성자의 접근제어자는 클래스의 접근제어자와 일치
- 생성자에 접근제어자를 사용함으로써 인스턴스의 생성을 제한할 수 있음


제어자의 조합

1. 메서드에 static과 abstract를 함께 사용할 수 없다
- static메서드는 몸통(구현부)이 있는 메서드만 사용할 수 있기 때문
2. 클래스에 abstract와 final을 동시에 사용할 수 없다
- 클래스에 사용되는 final은 클래스를 확장할 수 없다는 의미
- abstract는 상속을 통해서 완성되어야 한다는 의미로 서로 모순되기 때문
3. abstract메서드의 접근제어자가 private일 수 없다
- abstract메서드는 자손클래스에서 구현해주어야 하는데 접근제어자가 private이면, 자손클래스에서 접근할 수 없기 때문
4. 메서드에 private와 final을 같이 사용할 필요가 없다.
- 접근제어자가 private인 메서드는 오버라이딩될 수 없기 때문
- 둘 중 하나만 사용해도 의미가 충분


다형성이란?

- 여러 가지 형태를 가질 수 있는 능력
- 하나의 참조변수로 여러 타입의 객체를 참조할 수 있는 것
- 즉, 조상타입의 참조변수로 자손타입의 객체를 다룰 수 있는 것을 말함


참조변수의 형변환

- 서로 상속관계에 있는 타입간의 형변환만 가능
- 자손 타입에서 조상타입으로 형변환하는 경우, 형변환 생략가능


instanceof연산자

- 참조변수가 참조하는 인스턴스의 실제 타입을 체크하는데 사용
- 이항연산자이며 피연산자는 참조형 변수와 타입, 연산결과는 true 또는 false
- instanceof의 연산결과가 true이면, 해당 타입으로 형변환 가능


참조변수와 인스턴스변수의 연결

- 멤버변수가 중복정의된 경우, 참조변수의 타입에 따라 연결되는 멤버변수가 달라짐
  (참조변수타입에 영향받음0
- 메서드가 중복정의된 경우, 참조변수의 타입에 관계없이 항상 실제 인스턴스의 타입에 정의된 메서드가 호출됨
  (참조변수타입에 영향받지 않음)


매개변수의 다형성

- 참조형 매개변수는 메서드 호출시, 자신과 같은 타입 또는 자손타입의 인스턴스를 넘겨줄 수 있음


여러 종류의 객체를 하나의 배열로 다루기

- 조상타입의 배열에 자손들의 객체를 담을 수 있음


실습 - ch07 / S04.java

롤을 예시로 들어서 실습해 보았다
주석으로 설명..

아래 공격력 작성할시 참고해서 사용함

package ch07;

// 모든 스킬들의 공통적인 부분을 세팅한다
// MAX_POINT 모든 스킬들이 공통적으로 사용하는 변하지 않는 값을 세팅(static final)
// 스킬마다 바뀔 수 있는 부분 name, point, champ는 필드 (멤버변수)로 작성한다.
class Skill{
    static final int MAX_POINT = 5;
    String name;
    int point;
    Champ champ;

    public Skill(String name, Champ champ) {
        this.name = name;
        this.champ = champ;
        this.point = 0;
    }

    public int getDamage(){
        return 0;
    }

    public void plusPotin(){
        if (point >= MAX_POINT) {
            point = MAX_POINT;
            System.out.println("포인트를 더 이상 올릴 수 없습니다.");
            return;
        }

        this.point++;
    }
}

// 부모가 필요로하는 정보는 넘겨주고
// 내가 부모와 다르게 커스텀 할 부분을 세팅한다
class Volly extends Skill {

    public Volly(Champ champ) {
        super("일제사격", champ);
    }

    @Override
    public int getDamage() {
        // 고정 데미지 20 / 35 / 50 / 65 / 80
        // + 캐릭터 공격력 * 1.0

        // 스킬을 찍지 않았을 때
        if(point < 1){
            System.out.println("스킬을 사용할 수 없습니다.");
            return super.getDamage();
        }

        int damage = (int) (20 + (15 * (point - 1)) + (this.champ.ad * 1.0));

        return damage;
    }
}

// 챔피언의 공통적인 부분들 만들어준다.
class Champ {
    // 물리 공격력
    int ad;
    Skill skill;

    public Champ(int ad, Skill skill) {
        this.ad = ad;
        this.skill = skill;
    }
}

// 챔피언의 공통적인 부분을 이어 받되
// 개별 캐릭터의 특징을 다시 세팅 해준다 
class 애쉬 extends Champ{
    public 애쉬(){
        // 1. 상속받은 객체가 생성됨
        // super를 통해서 객체 안의 필드에 값을 최기화해줌
        super(100, null);
        // 현재 class의 객체가 생성됨
        // 현재 객체 안의 필드에 값을 넣어줌
        this.skill = new Volly(this);
    }
}

// 스킬이름 : 필트오버 피스메이커
// 고정 공격력 : 50 90 130 170 210
// + 캐릭터 공격력 * 1.25 / 1.45 / 1.65 / 1.85 / 2.05  
class Peace extends Skill{
    public Peace(Champ champ){
        super("필트오버 피스메이커", champ);
    }

    @Override
    public int getDamage(){
        if (point < 1) {
            System.out.println("스킬을 사용할 수 없습니다.");
            return super.getDamage();
        }

        // int damage = (int)(20 + (15 * (point - 1)) + (this.champ.ad * 1.0));

        // return damage;

        int damage = (int)(50 + (40 * (point - 1)) + (this.champ.ad * (1.25 + (0.2 * (point - 1)))));

        return damage;
    }
}

class 케이틀린 extends Champ{
    public 케이틀린(){ 
        // 필요한 스킬 Peace
        super(100, null);
        this.skill = new Peace(this);
    }
}

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

        // // 참조형 변수에 주조값을 아직 모르는 상태에서
        // // 초기화를 해주려고 하는 경우 null을 사용한다
        // String str = null;

        // System.out.println(str);

        // str = "홍길동";

        // System.out.println(str);
        // // 더 이상 쓸모 없는 객체에 null을 입력한다
        // str = null;

        // 애쉬 champ1 = new 애쉬();    

        // System.out.println(champ1.skill.getDamage());

        // champ1.skill.plusPotin();

        // System.out.println(champ1.skill.getDamage());

        // champ1.skill.plusPotin();

        // System.out.println(champ1.skill.getDamage());

        // 케이틀린 생성
        // 케이틀린 Peace의 데미지 가져오기

        // 케이틀린 champ2 = new 케이틀린();

        // System.out.println(champ2.skill.getDamage());

        // champ2.skill.plusPotin();

        // System.out.println(champ2.skill.getDamage());

        // 상속을 받으면
        // 부모의 타입으로 공통화 하여 사용할 수 있다
        Champ[] champArray = new Champ[2];

        champArray[0] = new 애쉬();
        champArray[1] = new 케이틀린();

        for (int i = 0; i < champArray.length; i++) {

            champArray[i].skill.plusPotin();

            // 항상 같은 메소드를 사용하기 때문에
            // 메소드 이름에 대한 고민을 할 필요가 없어서
            // 신뢰성이 높아진다
            System.out.println(champArray[i].skill.getDamage());
        }
    }
}

vscode에서 생성자와 오버라이드 만들 때 쓰는 단축키

alt + insert

해당 단축키를 사용해주면 아래의 그림과 같이 창이 뜨고 선택해주면 쉽게 만들 수 있음


실습 - ch07 / S06.java

주석으로 설명..

package ch07;

import ch07.sub.Sub01;

public class S06 {
    public static int first = 1;
    public int second = 2;
    public static final int third = 1;

    public void printSecond(){
        System.out.println(second);
    }

    public static void main(String[] args) {
        Sub01 sub01 = new Sub01();
        System.out.println(sub01.getData());

        System.out.println(first); //S06.first와 같다
        System.out.println(S06.first);

        // System.out.println(second); //가져올 수 없다
        // System.out.println(printSecond()); //실행할 수 없다.

        // static이 아닌 필드는 인스턴스를 생성해서 가져와야 한다.
        // (static 메소드 기준)
        S06 s06 = new S06();
        System.out.println(s06.second);
        s06.printSecond();

        S06.first = 11;
        // final 값은 바꿀 수 없다
        // S06.third = 33;

        final String str = "홍길동";
        // str = "임꺽정";

    }
}

실습 - ch07 > sub / Sub01.java

주석으로 설명..

package ch07.sub;

public class Sub01 {
    // 객체지향에서 데이터는 객체만 볼 수 있는게 기본적이다.
    private String data = "데이터";

    // 객체의 데이터를 외부에 노출 시키는 것은
    // 보통 메소드로 한다
    // 원본 데이터를 넘길 수도 있고
    // 숨겨야 하는 부분은 숨길 수도 있다
    public String getData(){
        return "숨긴 " + data;
    }

}
profile
미래가 기대되는 풀스택개발자 공부 이야기~~

0개의 댓글