제어자

자바에서 제어자는 아주 중요한 개념입니다.

 

자바에서 제어자는 클래스, 메서드, 변수, 생성자 등의 동작 방식을 정의하거나 제한하는 키워드입니다.

이 중 접근 제어자는 클래스, 메서드, 변수 등에 접근할 수 있는 범위를 제어합니다.

 

제어자의 종류

제어자는 크게 두 가지로 나눌 수 있습니다.

접근 제어자

클래스, 메서드, 변수의 접근 범위를 정의합니다.

public, protected, private 등이 있습니다.

 

기타 제어자

동작 방식을 추가적으로 정의해 줍니다.

static, abstract, final  등이 있습니다.

 

접근 제어자 설명

Java 에서 public, private, protected, default 는 접근 제어자(Access Modifiers) 라고 하며,

클래스, 메서드, 변수, 생성자 등의 접근 범위를 설정하는 데 사용됩니다. 이를 통해 코드의 캡슐화를 유지하고, 클래스 간의 상호작용을 제어할 수 있습니다.

 

 

public

모든 클래스에서 접근이 가능합니다.

프로젝트 내 어디서나 접근할 수 있는 가장 개방적인 접근 수준입니다.

// MyClass.java
public class MyClass {
    public String message = "Hello, World!";

    public void displayMessage() {
        System.out.println("Message: " + message);
    }
}
// Main.java

public class Main {
    public static void main(String[] args) {
        // MyClass 객체 생성
        MyClass myClass = new MyClass();

        // public 변수에 접근
        System.out.println("Accessing public variable: " + myClass.message);

        // public 메서드 호출
        myClass.displayMessage();
    }
}

 

private

같은 클래스 내에서만 접근이 가능합니다.

외부 클래스는 물론, 같은 패키지 내의 다른 클래스에서도 접근할 수 없습니다.

데이터를 외부에 노출하지 않고 캡슐화를 유지할 때 사용합니다.

public class MyClass {
    private int secretNumber;

    public int getSecretNumber() {
        return secretNumber; // getter를 통해 간접적으로 접근
    }

    public void setSecretNumber(int number) {
        this.secretNumber = number; // setter로 값 설정
    }
}

 

secretNumber 변수는 직접 접근이 불가능하며, getter , setter 메서드를 통해서만 간접적으로 접근이 가능합니다.

 

protected

같은 패키지 내에서 접근이 가능합니다.

다른 패키지의 클래스라도 상속받은 클래스는 접근 가능합니다.

주로 상속관계에서만 외부 접근을 허용하는 경우 사용합니다.
public class Parent {
    protected String familyName = "Smith";
}

public class Child extends Parent {
    public void showFamilyName() {
        System.out.println("Family Name: " + familyName); // 부모의 protected 필드 접근
    }
}

 

familyName 은 같은 패키지의 클래스 또는 Parent 를 상속받은 클래스에서 접근이 가능합니다.

 

default (package-private)

접근 제어자를 명시하지 않을 경우, default 접근 제어자가 적용됩니다.

같은 패키지 내에서만 접근 가능하고, 다른 패키지에서는 접근 불가합니다.

class MyClass { // 접근 제어자 없음 → default
    int packageLevelVariable; // 접근 제어자 없음 → default

    void display() { // 접근 제어자 없음 → default
        System.out.println("Default access level!");
    }
}

 

MyClass 와 그 내부의 packageLevelVariable 및 display 메서드는 같은 패키지 내에서만 접근이 가능합니다.

 

기타 제어자 설명

접근 제어자와 달리 접근 범위가 아닌 특정 동작 또는 속성을 설정합니다.

자주 사용하는 static, final, abstract, super, this, interface 에 대해서 설명하겠습니다.

 

Static

static 으로 선언된 변수나 메서드는 클래스에 속하며, 객체가 아닌 클래스 자체에서 접근할 수 있습니다.

또한 모든 객체가 static 멤버를 공유합니다. 즉, 클래스의 모든 인스턴스에서 동일한 메모리영역을 사용합니다.

public class MyClass {
    // static 변수: 클래스에 속하며, 모든 객체가 공유
    public static int staticCounter = 0;

    // static 메서드: 클래스 자체에서 호출 가능, 객체 생성 불필요
    public static void incrementCounter() {
        staticCounter++;
    }

    // 비-static 변수: 객체마다 독립적으로 존재
    public int instanceCounter = 0;

    // 비-static 메서드: 객체 생성 후 호출 가능
    public void incrementInstanceCounter() {
        instanceCounter++;
    }
}

public class Main {
    public static void main(String[] args) {
        // 1. 객체를 생성하지 않고 static 멤버 접근
        System.out.println("Initial staticCounter: " + MyClass.staticCounter);
        MyClass.incrementCounter(); // static 메서드 호출
        System.out.println("After incrementing staticCounter: " + MyClass.staticCounter);

        // 2. 객체 생성 후 static 멤버와 비-static 멤버 비교
        MyClass obj1 = new MyClass();
        MyClass obj2 = new MyClass();

        // Static 멤버 공유
        obj1.incrementCounter();
        System.out.println("Static counter after obj1.incrementCounter(): " + MyClass.staticCounter);

        // 비-static 멤버는 객체마다 독립적
        obj1.incrementInstanceCounter();
        obj2.incrementInstanceCounter();
        System.out.println("obj1 instanceCounter: " + obj1.instanceCounter);
        System.out.println("obj2 instanceCounter: " + obj2.instanceCounter);
    }
}
Initial staticCounter: 0
After incrementing staticCounter: 1
Static counter after obj1.incrementCounter(): 2
obj1 instanceCounter: 1
obj2 instanceCounter: 1

 

final

한번 초기화된 final 변수는 값을 변경할 수 없습니다.

상수처럼 사용하며 선언 시 초기화하거나, 생성자에서 반드시 초기화해야 합니다.

public class FinalVariableExample {
    public final int CONSTANT = 100; // 선언 시 초기화
    public final int anotherConstant;

    // 생성자를 통해 초기화
    public FinalVariableExample(int value) {
        this.anotherConstant = value;
    }

    public void showValues() {
        System.out.println("CONSTANT: " + CONSTANT);
        System.out.println("anotherConstant: " + anotherConstant);
    }
}

public class Main {
    public static void main(String[] args) {
        FinalVariableExample example = new FinalVariableExample(50);
        example.showValues();

        // example.CONSTANT = 200; // 에러: final 변수는 수정할 수 없음
    }
}
CONSTANT: 100
anotherConstant: 50

 

또한 final 을 메서드에 적용한다면 하위 클래스에서 오버라이드 할 수 없습니다.

특정 동작을 변경하지 않도록 보호하는 이유 때문입니다.

class Parent {
    public final void displayMessage() {
        System.out.println("This is a final method in Parent class.");
    }
}

class Child extends Parent {
    // @Override
    // public void displayMessage() {
    //     System.out.println("Cannot override final method!"); // 에러 발생
    // }
}

public class Main {
    public static void main(String[] args) {
        Child child = new Child();
        child.displayMessage();
    }
}
This is a final method in Parent class.

 

이 밖에도 final 클래스로 선언을 한다면 final 클래스는 상속할 수 없습니다.

클래스의 구조를 그대로 유지하도록 제한하며 보안이나 특정 동작을 확실히 고정해야 하는 경우 사용합니다.

final class FinalClass {
    public void showMessage() {
        System.out.println("This is a final class. No inheritance allowed.");
    }
}

// class SubClass extends FinalClass {
//     // 에러: final 클래스는 상속할 수 없음
// }

public class Main {
    public static void main(String[] args) {
        FinalClass obj = new FinalClass();
        obj.showMessage();
    }
}

 

abstract

구현이 없는 메서드 또는 클래스를 정의합니다.

클래스에 사용하면 해당 클래스가 추상 클래스가 됩니다. 추상 클래스는 직접 인스턴스화 할 수 없고, 반드시 상속을 통해 구현해야 합니다. 메서드에 사용하면 해당 메서드는 구현부가 없고, 하위 클래스가 반드시 오버라이드하여 구현해야 합니다.

public abstract class Shape {
    public abstract void draw(); // 추상 메서드

    public void show() {
        System.out.println("This is a concrete method");
    }
}

public class Circle extends Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a circle");
    }
}

 

this

현재 인스턴스를 참조합니다.

클래스의 멤버 변수와 매개변수의 이름이 같을 때 이를 구분하기 위해 사용합니다.

현재 객체를 메서드 또는 생성자에 전달할 때도 사용됩니다.

public class Person {
    private String name;

    public Person(String name) {
        this.name = name; // this로 인스턴스 변수와 매개변수 구분
    }
}

 

super

부모 클래스를 참조합니다.

부모 클래스의 생성자나 메서드, 변수에 접근할 때 사용됩니다.

public class Parent {
    public void show() {
        System.out.println("Parent class");
    }
}

public class Child extends Parent {
    public void show() {
        super.show(); // 부모 클래스의 메서드 호출
        System.out.println("Child class");
    }
}

 

interface

추상적인 설계도로, 메서드 선언만 포함하고 구현은 포함하지 않습니다.

다중 상속을 구현하거나, 클래스들 간의 공통 행위를 정의할 때 사용됩니다.

public interface Animal {
    void sound();
}

public class Dog implements Animal {
    @Override
    public void sound() {
        System.out.println("Woof");
    }
}

'Java' 카테고리의 다른 글

인터페이스  (0) 2024.12.10
List, ArrayList, LinkedList  (1) 2024.12.10
로깅(Logging)  (1) 2024.11.30
해시맵(HashMap)  (1) 2024.11.29
MyBatis  (0) 2024.11.21