Java Category/Java

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

ReBugs 2023. 1. 3.

혼자 공부하는 자바 (저자 : 신용권)의 내용을 개인적으로 정리하는 글임을 알립니다.


객체마다 필드값이 달라야 한다면 해당 필드는 객체마다 가지고 있는 것이 맞다.

하지만 객체의 필드값이 모두 같아야 한다면 이 필드를 객체마다 가지고 있을 필요가 있을지 의문이 든다.

만약 객체마다 갖고 있다면 메모리가 낭비되며, 모든 객체의 필드값을 같게 맞추는 추가적인 작업이 필요할 수도 있다. 오히려 이런 필드는 한 곳에 위치시키고 객체들이 공유하는 것이 좋을 수 있다.

 

자바는 이런 경우를 위해 클래스 멤버를 인스턴스 멤버와 정적 멤버로 구분해서 선언할 수 있도록 하고 있다.

인스턴스 멤버는 객체마다 가지고 있는 멤버를 말하고, 정적 멤버는 클래스에 위치시키고 객체들이 공유하는 멤버를 말한다.

 

인스턴스 멤버와 this

인스턴스 멤버는 객체마다 가지고 있는 멤버이다.

인스턴스 멤버란 객체(인스턴스)를 생성한 후 사용할 수 있는 필드와 메소드를 말하는데, 이들을 각각 인스턴스 필드, 인스턴스 메소드라고 부른다. 인스턴스 필드와 인스턴스 메소드는 객체에 소속된 멤버이기 때문에 객체 없이는 사용할 수 없다.

인스턴스 멤버는 필드와 메소드만 가지고 있다.
클래스는 필드, 생성자, 메소드 이렇게 3가지를 가지고 있지만, 인스턴스 멤버는 필드와 메소드만 가지고 있다.
이유는 인스턴스 멤버는 생성자에 의해 생성된 객체이기 때문에 한번 생성되면 같은 객체가 다시 생성될 일이 없기 때문 
인스턴스 필드와 인스턴스 메소드
인스턴스 필드는 객체에 포함되어 있지만, 인스턴스 메소드는 객체에 소속되어있다.
즉, 인스턴스 필드는 객체 내부에 있지만, 인스턴스 메소드는 객체 내부에 있다.
하지만 인스턴스 메소드는 객체의 생성 없이 접근할 수 없다.
인스턴스 메소드는 메소드 영역에 바이트 코드 형태로 존재한다.
package TestPakage;
public class Car {
	int oil; //인스턴스 필드
	int speed; //인스턴스 필드
	void setSpeed(int speed)//인스턴스 메소드
	{
		this.speed = speed;
	}
}
this 키워드
this 는 객체 내부에 인스턴스 멤버에 접근하기 위해 사용한다.
우리가 자신을 '나'라고 표현하듯, 객체 자신을 this라고 한다.

setSpeed 메소드의 매개변수 이름과 인스턴스 필드 speed 와 이름이 같다.
이 경우 this를 사용하지 않으면 매개 변수 speed에 매개 변수 speed를 대입하라는 이상한 코드가 된다.
따라서 this를 사용해서 객체가 가지고 있는 필드 speed에 매개 변수 speed를 대입하라고 해야 한다.

oil, speed 필드와 setSpeed() 메소드는 인스턴스 멤버이기 때문에 외부 클래스에서 사용할 때는 객체(인스턴스)를 생성하고 참조 변수로 접근해야 한다.

package TestPakage;
public class Test {
	public static void main(String[] args) {	
		Car myCar = new Car();
		myCar.oil = 10;
		myCar.setSpeed(60);
		
		Car yourCar = new Car();
		yourCar.oil = 20;
		yourCar.setSpeed(80);
	}
}

 

위 코드가 실행된 후 메모리 상태를 그림으로 나타내면 아래와 같다.

 

인스턴스 필드 oil과 speed는 객체마다 따로 존재하고, 인스턴스 메소드 setSpeed()는 메소드 영역에 저장되고 공유된다.

인스턴스 메소드는 객체에 소속된 멤버인데, 왜 객체 내부에 존재하지 않고 메소드 영역에 저장될까?
메소드는 코드 블록이므로 객체마다 동일한 코드 블록을 가지고 있을 필요가 없기 때문

그렇다면 왜 인스턴스라는 용어를 붙였을까?
메모리 블록 내부에 인스턴스 필드 등이 사용되는 경우가 있기 때문이다. 또한 인스턴스 필드가 사용되면 메소드 역시 객체 없이는 실행할 수 없기 때문이다.

 

정적 멤버와 static

정적 멤버는 클래스에 위치시키고 객체들이 공유하는 멤버를 말한다.

정적 멤버는 클래스에 고정된 멤버로서 객체를 생성하지 않고 사용할 수 있는 필드와 메소드를 말한다.

이들을 각각 정적 필드, 정적 메소드라고 한다.

 

정적 필드와 정적 메소드를 선언하려면 필드와 메소드 선언 시 static 키워드를 추가적으로 붙이면 된다.

package TestPakage;
public class Calculator {
	String color; //인스턴스 필드
	static double pi = 3.141592; //정적 필드
	void setColor(String color) { //인스턴스 메소드
		this.color = color;
	}
	static void setPi(double pi) { //정적 메소드
		Calculator.pi = pi;
	}
}
인스턴스 VS 정적
필드는 객체마다 데이터가 달라야 한다면 인스턴스 필드로 선언하고, 객체마다 다르지 않아야 할 데이터가 있다면 정적 필드로 선언하는 것이 좋다.
메소드는 인스턴스 필드를 포함하고 있다면 인스턴스 메소드로 선언하고, 인스턴스 필드를 포함하고 있지 않다면 정적 메소드를 선언하는 것이 좋다.
정적 멤버 사용
위의 코드에서 setPi메소드가 정적 필드 pi에 접근하기 위해 Calculator.pi 를 쓴 것을 볼 수 있다.
정적 멤버를 사용하려면 클래스 이름과 도트(.)연산자를 사용하면 된다.

정적 멤버는 원칙적으로 클래스 이름으로 접근해야 하지만 아래와 같이 객체 참조 변수로도 접근이 가능하다.
package TestPakage;
public class Test {
	public static void main(String[] args) {	
		Calculator cal = new Calculator();
		double result = 10 * 10 * cal.pi;
		cal.setPi(3.14);
		System.out.println(result);
		System.out.println(cal.pi);
	}
}
/*
314.1592
3.14
*/

 

정적 멤버는 객체 생성 없이 사용할 수 있다.

 

package TestPakage;
public class Test {
	public static void main(String[] args) {	
		Calculator.setPi(3.14);
		System.out.println(Calculator.pi);
	}
}
/*
3.14
*/

 

객체가 없어도 실행된다는 특징 때문에 정적 메소드를 선언할 때는 이들 내부에 인스턴스 필드나 인스턴스 메소드를 사용할 수 없다. 또한 객체 자신의 참조인 this 키워드도 사용이 불가능하다.

package TestPakage;
public class Calculator {
	String color; //인스턴스 필드
	static double pi = 3.141592; //정적 필드
	void setColor(String color) {
		this.color = color;
	}
	static void setPi(double pi) {
		Calculator.pi = pi;
	}
	static void method() {
		this.color = "검정"; //컴파일 오류
		this.setColor("검정");//컴파일 오류
		pi = 3.14;//가능
		setPi(3.1415);//가능
	}
}

 

정적 메소드에서 인스턴스 멤버를 사용하고 싶다면 아래와 같이 객체를 먼저 생성하고 참조 변수로 접근해야 한다.

package TestPakage;
public class Calculator {
	String color; //인스턴스 필드
	static double pi = 3.141592; //정적 필드
	void setColor(String color) {
		this.color = color;
	}
	static void setPi(double pi) {
		Calculator.pi = pi;
	}
	static void method() {
		Calculator obj = new Calculator();
		obj.pi = 3.14;
		obj.setPi(3.1415);
	}
}

 

정적 블록

정적 필드는 아래와 같이 필드 선언과 동시에 초기값을 주는 것이 일반적이다.

static double pi = 3.14159;

하지만 복잡한 초기화 작업이 필요하다면 정적 블록을 이용해야 한다.

정적 블록은 클래스가 메모리로 로딩될 때 자동으로 실행된다.

정적 블록이 클래스 내부에 여러 개가 선언되어 있을 경우에는 순서대로 실행된다.

생성자에서 초기화를 하지 않는 정적 필드
정적 필드는 객체 생성 없이도 사용할수 있기 때문에 생성자에서 초기화 작업을 하지 않는다.
생성자는 객체 생성 후 실행되기 때문이다.
public class Television 
{
    static String company = "MyCompany";
    static String model = "LCD";
    static String info;

    static 
    {
        info = company + "-" + model;
    }
}

 

public class TelevisionExample 
{
    public static void main(String[] args)
    {
        System.out.println(Television.info);
    }	
}
//MyCompany-LCD

'Java Category > Java' 카테고리의 다른 글

[JAVA] 패키지(Package)  (0) 2023.01.05
[JAVA] 싱글톤(Singleton), final 필드와 상수  (0) 2023.01.04
[JAVA] 메소드(Method)  (0) 2023.01.02
[JAVA] 생성자(Constructor)  (0) 2023.01.01
[JAVA] 필드(Field)  (0) 2022.12.31

댓글