카테고리 없음

접근지정자와 Static 그리고 캡슐화에 대해

kingmusung 2024. 2. 20. 19:15

싱글톤 방식에 대해 공부를 하다가 문득 접근지정자, 그리고 static의 개념에 대해 기억이 희미해져 다시 정리를 해보려고 합니다!

 

순서는 접근지정자, static 의 정의 및 사용 예시들을 나열 후 캡슐화의 원칙과 접근지정자의 관계에 대해 설명하려고 합니다.

 

🤚 접근지정자란

접근지정자의 종류에는 4가지가 있습니다.

 

public

->모든 클레스에서 접근이 가능합니다.

default(아무것도 안붙어있는거)

->동일한 패키지 내부의 클레스에서 접근 가능합니다.

protected

->동일한 패키지 내부의 클레스에서 접근 가능합니다.

private

-> 해당 클레스 내부에서만 접근이 가능합니다.

잠시만 그럼 default랑 protected랑 같은거 아니야 ?! 라고 한다면 경기도 오산시..

둘의 큰 차이점은 상속관계에 있습니다.

default상속받은 하위 클레스에서 접근 불가

Protected  상속받은 하위 클레스에서 접근 가능

 

그래도 접근을 하고 싶은데...

직접적인 방법으로는 접근이 불가능하나. Getter, Setter 메서드를 이용하여 접근하는 방법이 있다.

접근지정자 사용 예시 4.5를 참고하자

 

원할한 예시를 위해 프로젝트 구조 투척


🛎️  접근지정자 사용 예시

예시1️⃣ 

동일 패키지내에서 부모를 상속받은 자식 클레스에서 protected, Default로 선언 되어진 부모 인스턴스(변수) 접근

->가능

package mypackage;

public class Parent {
    protected int protectedVar = 10;
    int defaultVar = 20;
}
   public class Child extends Parent {
        public void accessParentVars() {
            System.out.println(protectedVar); // 상속받은 protected 변수에 접근
            System.out.println(defaultVar); // 같은 패키지 내에서 상속받은 default 변수에 접근
            System.out.println(privateVar); // private는 접근이 불가능
        }

    public static void main(String[] args) {
//        System.out.println(protectedVar);
//        System.out.println(defaultVar);
        //static으로 선언이 되어 있기때문에 객체를 만들어서 접근해야함.

        Child child = new Child();
        System.out.println(child.protectedVar); // 상속받은 protected 변수에 접근
        System.out.println(child.defaultVar); // 같은 패키지 내에서 상속받은 default 변수에 접근

        Parent parent = new Parent();
        System.out.println(parent.protectedVar); // 상속받은 protected 변수에 접근
        System.out.println(parent.defaultVar); // 같은 패키지 내에서 상속받은 default 변수에 접근
    }

 

같은 패키지 내부 부모를 상속 받은 자식 클레스가 있다. 당연히 바로 변수에 접근이 가능하다.

 

🌈 단!! 동일 패키지 내에서도 Static으로 선언한 메서드에서는 객체를 만든 후 접근을해야한다(psvm 도 포함.)

 


예시 2️⃣

다른 패키지에 있는 Other클레스에서 Parent와 Child의 Protected, Default로 선언된 인스턴스(변수) 접근

->불가능

package otherpackage;

import mypackage.Child;

public class Other {
    public void accessParentVarsFromOther() {
        Child child = new Child();
        System.out.println(child.protectedVar); // 접근 불가
        System.out.println(child.defaultVar); // 접근 불가
    }

    public static void main(String[] args) {
        Child child = new Child();
        System.out.println(child.protectedVar); // 접근 불가
        System.out.println(child.defaultVar); // 접근 불가
    }
}

 

 

접근이 안되는 이유는 다른패키지에 있기 때문에 접근이 안된다.

 

🌈  다른 패키지에 있기때문에 Static으로 선언된 메서드에서 객체를 생성 후 접근을 시도 해도 접근이 불가능하다.

 

 


예시 3️⃣

동일 패키지내에서 부모를 상속받은 자식 클레스에서 public, private로 선언 되어진 부모 인스턴스(변수) 접근

-> public은 가능하나 private는 불가능, static메서드 내에서 객체 생성 후 접근하더라도 불가능

->예시들을 근거로 유추를해보자. 그렇다면 다른 패키지에서는 ??

package mypackage;

public class Parent {

	private int privateVar = 30;
    public int publicVar = 40;
}
package mypackage;

    public class Child extends Parent {
        public void accessParentVars() {
            System.out.println(publicVar); //접근가능
            
            System.out.println(privateVar) // 접근 불가능
            Parent parent = new Parent();
            System.out.println(parent.privateVar); // private는 접근이 불가능

        }

    public static void main(String[] args) {
    
        Child child = new Child();
        System.out.println(child.publicVar); //접근가능
        System.out.println(child.privateVar); // 접근 불가능
        
        Parent parent = new Parent();
        System.out.println(parent.publicVar); // 접근가능
        System.out.println(parent.privateVar); // 접근 불가능
        
    }

}

예시 4️⃣

private, protect, Default. public 으로 선언한 변수들을 보면 참고를 할 수 있는 범위들이 정해져 있다.

그 범위를 벗어나는 곳에서 변수를 참고하는 방법을 알아보자.

package mypackage;

public class Parent {
    protected int protectedVar = 10;
    int defaultVar = 20;
    private int privateVar = 30;


     public int getDefaultVar(){
        return this.defaultVar;
    }

     public int getProtectedVar(){
        return this.protectedVar;
    }

    public int getPrivateVar(){
         return this.privateVar;
    }
}

 

Getter, Setterpublic으로 지정을 하여 외부에서도 변수에 접근을 할 수 있도록 만들어주었다.

캡슐화의 원칙때문에 통상적으로 변수를 선언 할 시 public으로 선언을 하지 않고 적절한 접근지정자를 붙혀서 선언을 하고, getter, setter 메서드를 정의해 변수에 접근하도록 한다.

package mypackage;

    public class Child extends Parent {
        public void accessParentVars() {
              Parent parent = new Parent();
              System.out.println(parent.getDefaultVar());
			  System.out.println(parent.getProtectedVar());
              System.out.println(parent.getPrivateVar());


        }

    public static void main(String[] args) {
          Child child = new Child();
          System.out.println(child.getDefaultVar());
          System.out.println(child.getProtectedVar);
          System.out.println(child.getPrivateVar());
          Parent parent = new Parent();
          System.out.println(parent.getDefaultVar());
		  System.out.println(parent.getProtectedVar());
          System.out.println(parent.getPrivateVar());
    }

}

 

동일 패키지에 자식클레스에서도 접근지정자와 상관없이 값을 출력 할 수 있다.

 

package otherpackage;

import mypackage.Child;
import mypackage.Parent;

public class Other {
    public void accessParentVarsFromOther() {
        Child child = new Child();
        System.out.println(child.getPrivateVar()); // 접근 가능
        System.out.println(child.getDefaultVar()); // 접근 가능
        System.out.println(child.getProtectedVar()); // 접근 가능
    }

    public static void main(String[] args) {
        Child child = new Child();
        System.out.println(child.getPrivateVar()); // 접근 가능
        System.out.println(child.getDefaultVar()); // 접근 가능
        System.out.println(child.getProtectedVar()); // 접근 가능

    }
}

 

다른 패키지에서도 물론 접근지정자와 상관 없이 값을 출력 할 수 있다.


예시 5️⃣ 

 

그럼 Getter, Setter 메서드의 접근지정자를 Protected로 변경을 하면?

package mypackage;

public class Parent {
    protected int protectedVar = 10;
    int defaultVar = 20;
    private int privateVar = 30;


     protected int getDefaultVar(){
        return this.defaultVar;
    }

    protected int getProtectedVar(){
        return this.protectedVar;
    }

    protected int getPrivateVar(){
         return this.privateVar;
    }
}

 

package mypackage;

    public class Child extends Parent {
        public void accessParentVars() {
              Parent parent = new Parent();
              System.out.println(parent.getDefaultVar());
			  System.out.println(parent.getProtectedVar());
              System.out.println(parent.getPrivateVar());


        }

    public static void main(String[] args) {
          Child child = new Child();
          System.out.println(child.getDefaultVar());
          System.out.println(child.getProtectedVar);
          System.out.println(child.getPrivateVar());
          Parent parent = new Parent();
          System.out.println(parent.getDefaultVar());
		  System.out.println(parent.getProtectedVar());
          System.out.println(parent.getPrivateVar());
    }

}

 

 

 

Protected의 정의에 따라 동일패키지 내에서는 getter,setter로 접근이 가능하다.

 

 

package otherpackage;

import mypackage.Child;
import mypackage.Parent;

public class Other {
    public void accessParentVarsFromOther() {
        Child child = new Child();
        System.out.println(child.getPrivateVar()); // 접근 불가능
        System.out.println(child.getDefaultVar()); // 접근 불가능
        System.out.println(child.getProtectedVar()); // 접근 불가능
    }

    public static void main(String[] args) {
        Child child = new Child();
        System.out.println(child.getPrivateVar()); // 접근 불가능
        System.out.println(child.getDefaultVar()); // 접근 불가능
        System.out.println(child.getProtectedVar()); // 접근 불가능

    }
}

 

Protected의 정의에 따라 다른 패키지에서 접근이 불가능하다.

 


📟  Static 이란

정적 멤버는 해당 클래스의 일부로 간주되어 클래스에 로드될 때 메모리에 할당됩니다. 따라서 클래스의 인스턴스를 생성하지 않아도 클래스 이름을 통해 직접 접근할 수 있습니다.

 

정적 멤버는 특정 인스턴스와는 독립적으로 존재하며, 클래스의 모든 인스턴스에 공유됩니다. 따라서 한 인스턴스에서 정적 멤버를 변경하면 다른 모든 인스턴스에서도 변경된 값을 공유합니다.

 

정적 멤버는 해당 클래스의 인스턴스와 관련이 없기 때문에 한 번만 메모리에 할당되어 공유됩니다. 따라서 여러 인스턴스를 생성하더라도 정적 멤버는 한 번만 메모리에 할당되므로 메모리 효율적입니다.

 

\정적 멤버는 해당 클래스가 처음으로 로드될 때 초기화됩니다. 클래스 로딩 시에 정적 블록(static block)이나 정적 초기화(static initialization)를 사용하여 초기화할 수 있습니다. 정적 멤버는 주로 공통적인 속성이나 동작을 가지는데 사용됩니다. 예를 들어, 상수(constant), 유틸리티 메서드(utility method), 카운터(counter) 등을 정의할 때 자주 활용됩니다.

 

예시들을 통해 살펴보자~!

 


🛎️  Static 사용 예시

예시 1️⃣

StaticTest클레스 내부에 public static으로 생성한 add 메서드가 있다.(간단한 더하기 연산을 도와주는 메서드이다.)

 

StaticTest와 다른 패키지에 있는 클레스에서 add메서드를 사용하고 싶다.

 

package mypackage;

public class StaticTest {

    public static int add(int x, int y){
        return x+y;
    }
}
package otherpackage;

import mypackage.Child;
import mypackage.Parent;
import mypackage.StaticTest;

public class Other {

    public static void main(String[] args) {
        int result = StaticTest.add(10,20); // 실행가능
    }
}

 

public static으로 선언을 하였기때문에 다른 패키지에서 인스턴스를 생성하지 않고 바로 접근이 가능하다.

 

protected static 혹은 static 으로 add 메서드를 선언했다면 접근이 가능했을까 ?? -> 불가능

 

접근지정자의 개념과 같이 생각을 해보면 매우 쉽다.

 


예시 2️⃣

package mypackage;

public class StaticTest {

    public static int Var =10;

}
package otherpackage;

import mypackage.Child;
import mypackage.Parent;
import mypackage.StaticTest;

public class Other {

    public static void main(String[] args) {
        int result = StaticTest.Var;
        System.out.println(result); //10 출력
    }
}

💊 캡슐화의 원칙

 

캡슐화(Encapsulation)는 객체 지향 프로그래밍의 핵심 개념 중 하나로, 데이터와 그 데이터를 다루는 메서드를 하나로 묶어서 외부로부터 접근을 제어하는 것을 말합니다. 캡슐화를 통해 객체의 내부 구현을 외부로부터 숨기고, 객체 간의 상호 작용을 보다 안전하고 효율적으로 관리할 수 있습니다.

 

우리가 접근지정자를 사용하고, getter setter를 사용해서 값을 참조하는 과정들은 캡슐화의 원칙을 준수하기 위해 사용하는 방법들 입니다.