Programming/Java

혼공자바] 06-5 인스턴스 멤버와 정적 멤버

littlezero48 2022. 11. 28. 10:37
 

06-5 / 인스턴스 멤버와 정적 멤버

클래스에 선언된 필드와 메소드가 모두 객체 내부에 포함되는 것은 아니다. 일부는 객체에 포함되고 일부는 객체에 포함되지 않고 클래스에 남아 있는 것도 있다. 왜 그럴까?

객체마다 모든 필드와 메소드가 동일하다면 경우에 따라 메모리 낭비가 되거나 불필요한 처리가 이루어 질 수도 있다. 자바는 이런 경우를 위해 객체마다 가지고 있는 인스턴스 멤버와 클래스에 위치시키고 객체들이 공유하는 정적 멤버로 구분해 선언할 수 있게 하고 있다. 

 


인스턴스 멤버와 this) 

  • 인스턴스 멤버 정의

객체(인스턴스)를 생성한 후 사용할 수 있는 필드와 메소드를 말하며,  각각 인스턴스 필드, 인스턴스 메소드라 한다.

 

  • 인스턴스 멤버 선언
public class 클래스이름 {
    //필드
    타입 변수명;
    
    //메소드
    타입 메소드명 ([매개값..]){...}
}

 

인스턴스 필드는 객체마다 따로 존재하고, 인스턴스 메소드는 메소드 영역(메모리 영역 중 Method Area 또는 Class Area라고 불리는 영역)에 저장되고 공유된다. 인스턴스는 객체에 소속된 멤버이지만 메소드는 코드 블록이므로 객체마다 동일한 코드 블록을 가질 필요가 없기 때문에 객체와 함께 힙 영역에 저장되는 것이 아니라, 메소드 영역에 저장되고 공유된다. 

그럼 왜 이 메소드에 인스턴스라는 이름이 붙을까?

바로 이 메소드는 인스턴스 필드를 사용하기 때문이다. 인스턴스 필드는 객체(인스턴스)가 없으면 사용 불가능하다. 따라서 이 필드를 사용하기 위해 메소드도 객체가 필요해지기 때문에 그 영향력으로 인스턴스 이름이 붙은 것.

 

  • this

객체 외부에서 인스턴스 멤버에 접근하기 위해 참조변수를 사용하는 것처럼 객체 내부에서는 인스턴스 멤버에 접근하기 위해 this를 사용할 수 있다. this는 주로 매개변수와 필드 이름이 동일할 경우 인스턴스 멤버인 필드를 명시할 때 사용.

public class Car{

    String model;

    Car (String model){
        this.model = model;
    }
    
    void setModel (String model){
    	this.model = model;
    }
}

 

 


정적 멤버와 static) 

정적(static)은 고정된 이라는 뜻이다. 정적 멤버는 클래스에 고정된 멤버로 객체를 생성하지 않고 사용할 수 있는 필드와 메소드이다. 각각 정적 필드, 정적 메소드라고 한다. 

 

  • 정적 멤버 선언

정적 멤버 선언시 static 키워드를 붙이면 된다. 

public class 클래스 {
    // 정적 필드
    static 타입 필드 [= 초기값];
    
    // 정적 메소드
    static 리턴타입 메소드 ([매개값...]) {...}
}

 

정적 멤버는 클래스 로더가 클래스(바이트 코드화된 파일)을 로딩하여 메모리 영역에 적재할 때 클래스 별로 관리한다. 따라서 클래스 로딩이 끝나면 바로 사용 가능하다.

 

 

  • 정적 멤버와 인스턴스 멤버를 선언하는 기준

    * 필드

           -정적 필드 : 객체 마다 가질 필요 없는 변하지 않는 공용 데이터

           -인스턴스 필드 : 객체마다 가지고 있으면 다른 데이터

    * 메소드

           -정적 메소드 : 인스턴스 필드를 포함하고 있지 않다면 정적 메소드로 선언

           -인스턴스 메소드 : 인스턴스 필드를 포함하고 있다면 인스턴스 메소드로 선언

 

 

  • 정적 멤버 사용

클래스 이름과 함께 도트(.)연산자로 접근하여 사용한다. 

클래스.필드;
클래스.메소드([매개값...]);

객체 이름으로 접근하는 것도 가능하지만 원칙적으로는 클래스 이름으로 접근하는 것이 좋다. 

 

 

 

  • 정적 메소드 선언 시 주의할 점

객체가 없이도 실행된다는 특징 때문에 정적 메소드는 내부에 인스턴스 필드나 인스턴스 메소드를 그냥 사용할 수 없다.

또한 객체 자신의 참조인 this 키워드도 사용할 수 없다.

따라서 정적 메소드에서 인스턴스 멤버를 사용하고 싶다면 같은 클래스에 있어도 객체를 먼저 생성하고 참조 변수로 접근해야 한다. 

우리가 실행을 위해 매일 작성하면 main() 메소드도 정적 메소드이기 때문에 인스턴스 멤버를 사용하기 위해 객체를 생성하는 과정을 거친다.

 

 


싱글톤(Singletone)) 

프로그램에서 단 하나의 객체만 만들도록 보장해야 하는 경우가 있는 데 이 경우에서 만들어진 단 하나의 객체를 싱글톤이라고 한다. 

싱글톤을 만들려면 클래스 외부에서 new연산자로 생성자를 호출할 수 없도록 막아야 한다. 생성자 호출 수 만큼 객체가 생성되기 때문인데, 호출을 막기위해 생성자 앞에 private 접근 제한자를 붙여주면 막아줄 수 있다.

그리고 자신의 타입인 정적 필드를 하나 선언하고 자신의 객체를 생성해 초기화한다. (클래스 내부에서도 new 연산자로 생성자 호출이 가능하다. )

정적 필드 역시 외부에서 접근하여 값을 수정하는 걸 막기위해 그 앞에 private 접근 제한자를 붙인다.  

이렇게 필드와 생성자의 접근을 막아 하나의 객체만을 생성하게 했다면 메소드를 통해 정적 필드가 참조하고 있는 자신의 객체를 리턴하여 외부에서 이 싱글톤을 얻게 한다.

public class 클래스 {
    // 정적 필드
	private static 클래스 변수명 = new 클래스 ();  // 예시나 문제에서 변수명을 singleton으로 통일했는데 그렇게 쓰는 게 관례인가?
    
    // 생성자
    private 클래스 () {...}
    
    // 정적 메소드 
    static 클래스 메소드이름 (){
    	return 위의정적필드;
    }
}

//------------------------- 예시

public class Singleton {
    private static Singleton singleton = new Singleton();
    
    private Singleton() {...}
    
    static Singleton getInstance() {
    	return singleton;
    }	
}

 

이렇게 싱클톤으로 만든 객체는 getInstance()를 통해 단 하나의 객체만 리턴하기 때문에 메소드를 여러번 호출해 저장해도 동일한 객체를 참조한다.

클래스 변수1 = 클래스.getInstance();
클래스 변수2 = 클래스.getInstance();

 

+ 예시나 문제에서 변수명을 singleton으로 통일했는데 싱글톤 사용할 때 그렇게 쓰는 게 관례인가?

 


final 필드와 상수) 

  • final 필드

최종적이라는 의미와 같이 초기값이 한번 저장되면 이후 수정될 수 없는 최종값이라는 뜻이다.

final 타입 필드명 [= 초기값];

* final에 초기값을 주는 방법

     - 필드 선언시 함께 지정

     - 필드 선언시 초기값이 지정되지 않았다면 생성자에서 초기값 지정 (만약 생성자 안에서 final 필드가 최종 초기화 되지 않고 남았다면 컴파일 에러 발생)

 

 

  • 상수

불변의 값 (static final) 을 저장하는 변수.

변하지 않는 다는 점에서 final도 상수인가 생각할 수 있지만

final 필드는 "객체"에 저장되고 생성자의 매개값을 통해서 여러값을 가질 수 있기 때문에 상수가 될 수 없다.

 

상수는 객체에 존재하지 않고 "클래스"에만 존재한다. 또한 한번 저장하면 변경될 수 없다. 

static final 타입 상수 = 초기값;

 

상수 이름은 모두 대문자로 작성하는 것이 관례이며 여러단어 혼합시 _(언더바)로 단어를 연결한다. 

static final double PI = 3.14159;
static final double EARTH_RADIUS = 6400;