본문 바로가기
Backend/Java

Java 기초 문법 : 제어자(modifier)

by 코딩쥐 2024. 6. 13.

Java의 제어자는 클래스나 클래스 변수(멤버 변수, 메서드)의 선언부에 함께 사용하여 부가적인 의미를 부여한다.

분류 항목
접근 제어자 public, protected, (default), private
그외 static, final, abstract, native, trasient, synchronized, volatile, strictfp 

 

접근 제어자

외부로부터 데이터를 보호하기 위해서 사용하는 제어자로, 내부적으로만 사용되는 부분을 감추기 위해서 사용한다. 

접근 범위 제어자 사용될 수 있는 곳 대상
좁음






넓음
private 같은 클래스 내 접근 가능 메서드, 멤버변수
(default) 같은 패키지 내 접근 가능 클래스, 메서드, 멤버변수
protected 같은 패키지 내, 다른 패키지의 자손클래스에서 접근 가능 메서드, 멤버변수
public 접근 제한 없음 클래스, 메서드, 멤버변수
package com.example1;

public class Modifier {
    private int a = 1;
    int b = 2;
    protected int c = 3;
    public int d = 4;

    public static void main(String[] args) {
        Modifier mod1 = new Modifier();
        System.out.println(mod1.a); //1
        System.out.println(mod1.b); //2
        System.out.println(mod1.c); //3
        System.out.println(mod1.d); //4
    }
}
package com.example1;

public class ModifierEx_samePkg {
    public static void main(String[] args) {
        Modifier mod1 = new Modifier();
        // System.out.println(mod1.a);  // 에러
        System.out.println(mod1.b); //2
        System.out.println(mod1.c); //3
        System.out.println(mod1.d); //4
    }
}
package com.example2;

import com.example1.Modifier;

public class ModifierEx_diffPkg {
    public static void main(String[] args) {
        Modifier mod1 = new Modifier();
        // System.out.println(mod1.a); // 에러
        // System.out.println(mod1.b); // 에러
        // System.out.println(mod1.c); // 에러
        System.out.println(mod1.d); //4
    }
}

 

생성자의 접근 제어자

생성자의 접근 제어자를 사용함으로써 인스턴스의 생성을 제한할 수 있다. private 접근 제어자를 사용할 경우, 인스턴스의 생성을 방지할 수 있다. 대신 해당 클래스 내부에서 인스턴스를 생성하고, public 메서드를 제공하여 외부에서 해당 메서드를 통해서만 접근할 수 있도록 제한할 수 있다. * 싱글톤 패턴 : 메모리 절약을 위해 기존의 인스턴스를 가져와 활용할 수 있도록 하는 패턴

package com.example1;

public class Example1 {
    public static void main(String[] args) {
        // Singleton s = new Singleton(); 외부에서 생성자에 접근할 수 없어서 불가능
        // getInstance() 메서드를 통해서 클래스의 인스턴스를 사용한 후에,
        Singleton s = Singleton.getInstance();
        // protected 메서드를 호출해서 사용할 수 있다.
        s.method1(); // method1 호출
    }
}

// 생성자가 private인 경우 다른 클래스의 조상이 될 수 없기 때문에 클래스 앞에 final을 추가하여 알린다.
final class Singleton {
    //private 지정하게 되면 외부에서 생성자에 접근할 수 없음, 클래스 내부에서는 인스턴스 생성가능
    // getInstance()에서 사용될 수 있도록 인스턴스가 미리 생성되어야 하므로 static 이어야 함 (인스턴스 생성하지않아도 사용가능)
    private static Singleton s = new Singleton();

    private Singleton() {
    }

    protected void method1() {
        System.out.println("method1 호출");
    }

    //클래스내부에서 인스턴스를 생성할 수 있기 때문에, 인스턴스를 생성해서 반환해주는 public 메서드 사용
    public static Singleton getInstance() {
        if (s == null)
            s = new Singleton();
        return s;
    }
}

그외

Static

static은 인스턴스가 아닌 클래스에 관계된 키워드로, 인스턴스를 생성하지 않아도 사용이 가능하다. Static는 JVM의 클래스 영역(Method Area)에 적재되어 메모리가 로딩될 때 로드되어 프로그램이 종료할 때까지 유지된다. 따라서 갱신이 없는 데이터, 모든 인스턴스에서 공유해야 하는 공통적인 값을 저장할 때 유용하다. 

 

static 변수

모든 인스턴스에서 공통적으로 사용되는 클래스 변수로, 객체를 생성하지 않고 '클래스이름.변수명'으로 호출이 가능하다.

 

static 메서드

인스턴스를 생성하지 않고도 호출이 가능한 static 메서드로, static메서드 내에서는 인스턴스 멤버들을 직접 사용할 수 없다. 객체를 생성하지 않고 '클래스이름.메서드명()'으로 호출이 가능하다. 

package com.example1;

public class Example1 {
    // static 멤버변수
    public static int a = 100;
    
    // static 메서드
    public static void setA1(int a){
        Example1.a = a;
    }
}
package com.example1;

public class Example1_1 {
    public static void main(String[] args) {
        //따로 인스턴스를 생성하지 않아도 변수와 메서드를 불러올 수 있다. 
        System.out.println(Example1.a); // 100
        Example1.setA1(200);
        System.out.println(Example1.a); // 200 
    }
}

 

Final

클래스, 메서드, 멤버변수 및 지역변수를 바꿀 수 없도록 할 때 쓰인다. 

 

final 클래스

확장될 수 없는 클래스가 되어, 다른 클래스의 조상이 될 수 없다.

 

final 메서드

변경될 수 없는 메서드로, 오버라이딩을 통해 재정의 될 수 없다.

 

final 변수 (멤버변수, 지역변수)

처음 선언한 값을 변경할 수 없는 상수가 된다. 상수의 경우 영어 대문자로 작성한다. 

final class FinalTest {
    final int MAX_SIZE = 10; // 변경할 수 없는 멤버 변수

    final int getMaxSize() { // 오버라이딩할 수 없는 메서드
        final int LV = MAX_SIZE; // 변경할 수 없는 지역 변수
        return MAX_SIZE; // MAX_SIZE 반환
    }
}

 

Abstract 

Abstract의 경우 여러 클래스에서 사용될 수 있는 부분을 공통적인 특성을 클래스로 묶고, 특성 메서드의 구현을 자식 클래스에 맡길 때 사용하는 제어자이다.

 

Abstract 클래스 (추상 클래스)

클래스 내에 하나 이상의 추상 메서드가 선언되었음을 의미한다. 즉,  전체적인 구성을 다 가지지 못한 채 설계만 되어 있는 미완성 클래스이다. Abstract 클래스는 인스턴스 생성이 불가능하며, 상속을 통해서 완전한 클래스를 만든 후에 객체 생성이 가능하다.

  • 접근자 abstract class 클래스명 {}

 

Abstract 메서드

선언부(메서드 명, 매개변수, 반환값)만 작성하고 구현부는 작성하지 않은 메서드로, 클래스 상속 후에 오버라이딩을 통해서 완성되어야 한다. 

  • 접근자 abstract 리턴타입 메서드명();
package com.example1;

// 추상클래스
abstract class Ride {
    // 추상메서드
    abstract void move();
}
package com.example1;

// Ride를 상속받는다.
public class Car extends Ride {
    // Ride의 추상메서드를 오버라이딩을 통해 구현한다.
    @Override
    void move() {
        System.out.println("자동차를 탄다.");
    }

    public static void main(String[] args) {
        Car car1 = new Car();
        car1.move(); // 자동차를 탄다.
    }
}

제어자의 조합

대상 사용가능한 제어자
클래스 public, (default), final, abstract
메서드 모든 접근제어자(private, (default), protected, public), final, static, abstract
멤버변수 모든 접근제어자, final, static
지역변수 final

 

  • 메서드에 static과 abstract을 같이 사용할 수 없다. static메서드는 몸통이 있는 메서드만 가능하다.
  • 클래스에 abstract과 final 동시에 사용할 수 없다. abstract경우 상속을 통해서만 완성되기 때문에, 상속이 불가능한 final 사용이 불가능하다.
  • abstract 메서드의 접근제어자가 private일 수 없다. abstract의 경우 상속을 통해서만 완성되기 때문에, 상속이 불가능한 private와 같이 사용될 수 없다. 
  • 메서드에 private과 final을 같이 사용할 필요가 없다. 둘 중 하나만 작성해도 의미를 전달하는데 충분하다.