[Java] 인스턴스 멤버와 정적 멤버

🌈 m1naworld ·2022년 11월 29일
0

자바를 잡아! 👻

목록 보기
17/19
post-thumbnail
  • 인스턴스 멤버: 객체마다 가지고 있는 멤버(필드, 메소드)
  • 정적 멤버(클래스 멤버): 클래스에 위치시키고 객체들이 공유하는 멤버

    정적 멤버를 따로 구분하는 이유: 객체 마다 공통의 값을 가지는 필드를 효율적으로 관리하기 위함

예시


위의 예시 사진처럼 객체마다 공통된 값을 공유하는 playerCount 필드를 모든 객체마다 갖고 있다면 메모리의 낭비 뿐만이 아니라, playerCount 값이 바뀔 때 마다 모든 객체의 필드값을 변경해야 하는 추가작업이 필요하여 효율적이지 못하다. 이렇게 공통적인 값을 객체들이 공유하고 쉽게 접근하여 사용할 수 있도록 인스턴스 멤버와 구분하여 정적멤버로 관리한다.


인스턴스 멤버(this)와 정적 멤버(static)

  • 인스턴스 멤버
    객체(인스턴스)를 생성한 후 사용할 수 있는 필드와 메소드를 의미하며 인스턴스 필드와 메소드는 객체에 소속된 맴버이기 때문에 객체 없이는 사용할 수 없다.
    인스턴스 필드는 객체마다 따로 존재하고, 인스턴스 생성 시 힙 영역에 할당되며 인스턴스 소멸시 메모리가 해제된다. 반면에 인스턴스 메소드는 객체마다 존재하지 않고, 정적 메소드처럼 클래스 로딩 시 메소드 영역에서 저장되고 공유된다. 이는 실행해야 할 명령 집합인 메소드는 인스턴스가 달라도 같은 로직을 수행해야 하기 때문이다.

    그렇다면 위와 같은 메소드를 인스턴스 메소드라 칭하는 이유는 무엇일까?
    인스턴스 메소드는 메모리 블록 내부에 인스턴스 필드가 사용되기 때문에 인스턴스 메소드 역시 객체 없이 실행할 수 없기 때문이다.


  • 정적 멤버
    정적 멤버는 객체를 생성하지 않고 사용할 수 있는 필드와 메소드를 말하며, static 키워드롤 통해 선언한다. 객체가 아닌 클래스에 속한 멤버이기 때문에 클래스 멤버라고도 한다.
    위의 그림처럼 JVM에서 클래스 로더가 클래스를 로딩해 메소드 메모리 영역에 적재할 때, 클래스 별로 관리된다. 이때 정적 멤버는 클래스 로딩이 끝나면 바로 사용할 수 있다.

*Method area: JVM이 시작할 때 생성되어 클래스 별로 런타임 상수풀, 필드 데이터, 메소드 데이터, 메소드 코드, 생성자 코드 등을 분류해 저장하는 영역


인스턴스 필드 vs 정적 필드의 선언 기준

객체마다 각기 달리 가지고 있어야 할 데이터라면 인스턴스 필드로, 객체마다 동일한, 공통적인 데이터라면 정적 필드로 선언하는 것이 좋다.

인스턴스 메소드 vs 정적 메소드의 선언 기준

객체마다 다른 값을 가지는 인스턴스 필드를 이용하거나 건드린다면 인스턴스 메소드로, 인스턴스 필드와 무관하게 파라미터 등을 이용하여 실행한다면 정적 메소드로 선언하는 것이 좋다.


코드로 비교해 보기

정적 메소드 선언

public class Calculator{
	String color; // 인스턴스 필드
	void setColor(String color){this.color = color}; // 인스턴스 메소드
	
	static int plus(int x, int y){return x + y}; // 정적 메소드
	static int minus(int x, int y){return x - y}; // 정적 메소드
}

위의 Calculator 클래스처럼 덧셈, 뺄셈 기능은 인스턴스 필드를 이용하기보다는 외부에서 주어진 매개값들을 가지고 덧셈과 뺄셈을 수행하므로 정적 메소드로 선언하는 것이 좋다.


정적 메소드 사용

public class Calculator{
	static double pi = 3.14159; // 정적 필드
	static int plus(int x, int y){return x + y}; // 정적 메소드
	static int minus(int x, int y){return x - y}; // 정적 메소드
}


// 객체 생성 없이 클래스를 통해 정적 멤버 접근 
double result1 = 10 * 10 * Calculator.pi;
int result2 = Calculator.plus(10, 5);
int result3 = Calculator.minus(10, 5);


// 객체 생성 후
Calculator myCalcu = new Calculator();
// 객체 참조 변수를 통해 정적 멤버에 접근 
double result1 = 10 * 10 * myCalcu.pi; // 가능은 하나 별로인 예시

위의 예시처럼 객체를 생성하지 않고 클래스를 통해 정적 멤버를 사용한다. 또한 객체를 생성한 후 객체 참조 변수를 통해서도 정적 멤버에 접근이 가능하나, 클래스로 접근하는 것이 원칙이라고 한다.
정적 멤버의 이러한 객체가 없어도 실행된다는 특징 때문에 정적 메소드를 선언할 때는 이들 내부에서 객체를 생성하지 않는 한 인스턴스 필드나 인스턴스 메소드를 사용할 수 없다.


정적 메소드안 인스턴스 멤버 사용

정적 메소드에 인스턴스 멤버를 사용하고 싶은 경우도 가능하며, 이러한 경우에는 정적 메소드 안에 객체를 먼저 생성한 후 참조 변수로 접근해야 한다.

public class ClassName{

	// 인스턴스 필드와 메소드
	int field1;
	void method1(){...}

	// 정적 필드와 메소드
	static int field2;
	static void method2() {...}

	// 정적 메소드 >> 에러 문법 
	static void method3(){
		this.field1 = 10; // 컴파일 에러 
		this.method1(); // 컴파일 에러 
		field2 = 10;
		method2();
	}
	// 정적 메소드 >> 옳바른 문법 
	static void method3(){
	  ClassName obj = new ClassName();
		obj.field1 = 10;
		obj.method1();
	}
}

❗️이는 main()메소드에서도 동일한 규칙으로 적용된다.
main() 메소드도 정적 메소드이므로 객체 생성 없이 인스턴스 멤버를 main() 메소드에서 바로 사용할 수 없다!

public class Car{

	int speed;
	void run() {...}
	
	// 아래 선언한 변수와 메소드를 찾을 수 없다는 컴파일 에러 
	public static void main(String[] args){
		speed = 60;
		run ();
	
	}
}
// 올바른 예시

public class Car{

	int speed;
	void run() {...}
	
	public static void main(String[] args){
		Car myCar = new Car(); // 객체 생성 후 
		
		myCar.speed = 60;
		myCar.run(); 
	}    
}
profile
개발자로 사는 내 삶은 즐거워 👾

0개의 댓글