숑숑이의 개발일기

본글은 'Do it 자바 완전 정복' 책을 통한 공부내용을 정리한 글입니다.

 

 

이번에는 대표적인 자바의 제어자 중 접근 지정자와 static에 대해서 알아보자

 

접근 지정자(modifier)

자바 제어자는 클래스, 필드, 메서드, 생성자 등에게 어떠한 특징을 부여하는 문법 요소다. '빨간색 사과', '맛있는 사과'와 같이 클래스나 멤버 등을 꾸며주는 형용사라고 생각하면 된다. 접근 지정자는 자바 제어자의 한 종류로 클래스, 멤버, 생성자 앞에 위치할 수 있고, 사용 범위를 정의하는 역할을 란다. 접근 지정자는 멤버와 생성자 앞에 붙어 있을 때와 클래스 자체에 붙어 있을 때를 나눠 생각해야 한다.

 

멤버 및 생성자의 접근 지정자

멤버 및 생성자에는 public, protected, default, private 4가지 종류의 접근 지정자를 사용할 수 있다. 필드나 메서드, 생성자 앞에 위치하고 아무것도 작성하지 않으면 자동으로 default 접근 지정자가 설정된다.

지시자 같은클래스 동일 패키지 상속 클래스 기타 영역
private o x x x
default o o x x
protected o o o x
public o o o o
  • private : 동일 클래스에서 사용가능
  • default : 동일 패키지의 모든 클래스에서 사용 가능
  • protected : 동일 패키지의 모든 클래스 + 다른 패키지의 자식 클래스에서 사용 가능
  • public : 동일 패키지의 모든 클래스 + 다른 패키지의 모든 클래스에서 사용 가능

 

클래스의 접근 지정자

클래스에서는 public, default 접근 지정자만 사용할 수 있다.

  • default : 같은 패키지 내에서만 사용 가능(다른 패키지에서 임포트도 안됨)
  • public : 다른 패키지에서도 사용 가능

 

클래스 접근 지정자와 생성자 접근 지정자의 연관성

자동으로 추가되는 생성자의 접근 지정자는 클래스의 접근 지정자에 따라 결정된다. 직접 생성자를 정의할때 다르게 지정할 수도 있다. 클래스가 public 이라는 것은 다른 패키지에서 임포트 할 수 있는 것이고, 생성자가 public인 것은 생성자를 호출해 객체를 생성할 수 있다는 것이다. 주의해야 할 점은 클래스가 임포트 되지 않으면 생성자는 접근 지정자와 상관 없이 호출 할 수 없다는 것이다. (클래스 내부에 생성자가 존재하기 때문에)

package abc.bcd;
public class A{	// public 클래스
	A() {}	// default 생성자
}
package abc.bcd;
class B {
	// A 클래스 사용(선언/생성) 가능
}
package bcd.cde;
import abc.bcd.A;	// A클래스의 접근 지정자가 public 이므로 임포트 가능
class C {
	public stati void main(String[] ar) {
    	A a = new A();	// A클래스 생성자의 접근 지정자가 default이므로 호출 불가능
    }
}

반대로 클래스가 default, 생성자가 public으로 선언되어 있을 경우 앞에서 설명한 것처럼 클래스 자체를 임포트 할 수 없으므로 생성자가 public이더라도 다른 패키지에서는 객체를 생성할 수 없다

 

static 제어자

static은 클래스의 멤버(필드, 메서드, 이너 클래스)에 사용하는 제어자다. 다른 클래스에서 어떠한 클래스의 멤버를 사용해야 하는 경우 해당 클래스의 객체를 생성한다고 했다. 이렇게 객체 생성 후에 사용할 수 있는 상태가 된 멤버를 인스턴스 멤버(instance member)라고 한다. 다시 말해, 인스턴스 멤버는 멤버 앞에 static이 붙어 있지 않은 것을 말한다. 반면 앞에 static이 붙어 있다면 해당 멤버를 정적 멤버(static member)라고 부른다.

정적 멤버의 가장 큰 특징은 객체의 생성 없이 바로 사용할 수 있다는 것이다. (클래스명.멤버명) 정적 멤버 사용시에는 호출된 모양만으로 정적 멤버라는 것을 알 수 있도록 해당 형태로 사용하는 것을 권장한다.

 

인스턴스 필드와 정적 필드

인스턴스 필드와 정적 필드의는 각 필드의 메모리 저장 위치때문에 사용하는 방법에 차이가 있다.

 

인스턴스 필드의 경우 먼저 객체를 생성한 후 '참초 변수명.인스턴스 필드명'과 같이 사용한다. 저장 공간이 힙 메모리에 위치하므로 반드시 해당 저장 공간에 값을 읽거나 쓰기 위해서 참조 변수명이 필요하다.

class A {
	int m = 3;
    static int n = 5;
}

public class Ex1 {
	public static void main(String[] args) {
        A a = new A();
        System.out.print(a.m);	// 3 
	}	
}

 

반면 정적 필드(static field)는 '클래스명.정적 필드명'으로 사용한다.

public class Ex1 {
	public static void main(String[] args) {
        System.out.print(A.n);	// 5
	}	
}

특이한 점은, 객체 내부에 정적 필드인 n도 존재한다. 다만 n의 실제 저장 공간은 정적 영역 내부에 있으며, 객체 내부의 n은 실제 정적 필드의 저장 공간의 참좃값만을 포함하고 있다. 따라서 참조 변수명으로도 사용할 수 있지만 정적 필드라는 것을 확실하게 나타내기 위해 권장하지 않는 사용법이다.

 

정적 필드는 객체 간 공유 변수의 성질이 있다는 특징을 가진다.

A a1 = new A();
A a2 = new A();

a1.m = 5;
a1.m = 6;
System.out.println(a1.m);	// 5
System.out.println(a2.m);	// 6

a1.n = 7;	// 정적 멤버
a2.n = 8;	// 정적 멤버
System.out.println(a1.n);	// 8
System.out.println(a2.n);	// 8

A.n = 9;
System.out.println(a1.n);	// 9
System.out.println(a2.n);	// 9

 

인스턴스 메서드와 정적 메서드

이번에는 인스턴스 메서드와 정적 메서드를 비교해 보자. 앞의 필드와 마찬가지로, 인스턴스 메서드는 반드시 객체를 생성한 후에 사용할 수 있다. 정적 메서드는 클래스 명으로 바로 접근할 수 있고, 객체로도 호출할 수 있다. (권장안함)

필드와 다른 점은 인스턴스 메서드와 정적 메서드는 모두 메모리의 첫 번째 영역(메서드 영역)에 위치한다는 것이다.

 

정적 메서드 안에서 사용할 수 있는 필드와 메서드

정적 메서드 내에서는 정적 필드 또는 정적 메서드만 사용할 수 있다. 즉, 인스턴스 필드나 인스턴스 메서드는 사용할 수 없다는 것. 앞에서 계속 말했다 싶이 인스턴스 멤버는 반드시 객체를 생성한 후에 사용할 수 있기 때문이다. 이는 정적 메서드 내부에서는 this 키워드를 사용할 수 없다는 것을 의미하기도 한다.

 

정적 초기화 블록

정적 필드는 객체의 생성 이전에도 사용할 수 있어야 하므로 생성자가 호출되지 않은 상태에서도 초기화할 수 있어야 한다. 정적 필드는 초기화 하기 위한것이 바로 정적 초기화 블록(static {})이다.

정적 초기화 블록은 클래스가 메모리에 로딩될 때 가장 먼저 실행되므로 정적 필드의 초기화 코드를 넣어두면 클래스가 로딩되는 시점에 바로 초기화 할 수 있다.

class A {
	int a;
    static int b;
    
    static {
    	b = 5;
      	// static 초기화 블록
    }
    A() {
    	a = 3;
        // 인스턴스 필드의 초기화 위치
    }
}
profile

숑숑이의 개발일기

@숑숑-

풀스택 개발자 준비중입니다