본문 바로가기
👨‍💻 2. 웹개발_Back end/2-1 Java

[JAVA] 06-5 인스턴스 멤버와 정적 멤버

by 달님🌙 2021. 8. 3.
반응형

 

인스턴스 멤버 vs 정적멤버

 

- 메모리 낭비를 줄이기위해 필드를 각 객체마다 갖게 하는 것이 아니라 한 곳에 모아놓고 객체들이 공유하도록 한다. 

- 인스턴스 멤버 : 객체마다 가지고 있는 멤버

- 정적 멤버 : 클래스에 위치하며 객체들이 공유하는 멤버

 

인스턴스 멤버

 

- 인스턴스 멤버란, 객체(인스턴스)를 생성하고 나서 사용할 수 있는 필드와 메소드를 말함

- 각각 인스턴스 필드와 인스턴스 메소드라고 불림 (이전까지 배운 것들이 모두 인스턴스 멤버임)

- 인스턴스 필드와 인스턴스 메소드는 모두 객체에 소속된 멤버이므로 객체가 있어야만 사용이 가능함

 

인스턴스 멤버 선언 및 호출

 

public class Car4 {
	// 인스턴스 필드 선언
	int gas;

	// 인스턴스 메소드 선언
	void setSpeed(int speed) {
		System.out.println("speed : " + speed);
	}
}

 

public class Car4Example {
	public static void main(String[] args) {
		Car4 myCar = new Car4(); 
        // 인스턴스 멤버인 gas필드와 setSpeed() 메소드를 사용하려면 반드시 객체(인스턴스)를 먼저 생성해주어야한다.
		myCar.gas = 10; // 그 다음에 참조 변수 myCar로 접근해야한다.
		myCar.setSpeed(60);
	
		Car4 yourCar = new Car4();
		yourCar.gas = 20;
		yourCar.setSpeed(80);
	}
}

 

- 인스턴스 필드 gas는 객체마다 따로 존재하지만 인스턴스 메소드 setSpeed()는 메소드 영역에 저장되어 사용할 때마다 공유된다. 

- 인스턴스 메소드는 인스턴스라는 이름이 붙었긴 하지만 메소드 자체는 객체마다 동일한 메소드 코드 블록이 실행되므로 굳이 객체마다 하나씩 가지고 있을 필요가 없다.

- 그럼에도 인스턴스라는 단어가 붙은 이유는 인스턴스 필드가 블록 내부에서 사용되기 때문이다.

- 인스턴스 메소드 내에 인스턴스 필드가 사용될 경우 객체 없이는 메소드가 실행되지 않는다.

 

this

 

- 생성자와 메소드의 매개변수 이름이 필드 이름과 동일한 경우 필드 이름임을 명시하고자 필드명 앞에 this를 붙인다.

 

this 예제

 

public class Car5 {
	// 필드
	String model;
	int speed;
	
	// 생성자
	Car5(String model) {
		this.model = model;
	}
	
	// 메소드
	void setSpeed(int speed) {
		this.speed = speed;
	}
	void run() {
		for(int i=10; i<=50; i+=10) {
			this.setSpeed(i);
			System.out.println(this.model + "가 달립니다.(시속 : " + this.speed + "km/h)");
		}
	}
}

 

public class Car5Example {
	public static void main(String[] args) {
		Car5 myCar = new Car5("포르쉐");
		Car5 yourCar = new Car5("벤츠");
		
		myCar.run();
		yourCar.run();
	}
}

 

정적(static) 멤버

 

- '고정된' 이라는 의미로 정적 멤버는 클래스 안에서 고정된 멤버로 객체를 생성하지 않아도 사용이 가능한 필드와 메소드를 말함

- 각각 정적 필드와 정적 메소드라고 부름

 

정적 멤버 선언

 

- 정적 멤버를 선언할 때는 인스턴스 멤버와 다똑같지만 맨앞에 static을 붙여준다는 특징이 있다.

public class Car {
	// 정적 필드
    static int speed;
    // 정적 메소드
    static void setSpeed(int speed) { }
}

 

정적 멤버의 메모리 구조

 

- 클래스 로더가 클래스의 바이트 코드를 읽는다.

- 바이트 코드를 로딩해서 메소드 메모리 영역에서 클래스별로 관리한다.

- 클래스 로딩이 끝나면 바로 사용 가능하다.

 

인스턴스 필드와 정적 필드의 판단 기준

 

public class Calculator {
	String color; // 객체별로 서로 다른 색깔을 지정해줄거라면 인스턴스 필드로 선언
	static double pi = 3.14; // 파이값은 객체마다 변하지 않는 공용 데이터라서 정적 필드
}

 

인스턴스 메소드와 정적 메소드의 판단 기준

 

public class Calculator {
	String color;	// 인스턴스 필드
	void setColor() { // 인스턴스 필드의 값(색깔)을 변경하는 메소드는 인스턴스 메소드로 선언
		this.color = color;
	}
	static int plus(int x, int y) { // 외부에서 매개값을 받아서 수행하는 메소드는 정적 메소드로 선언
		int result = x + y;
		return result;
	}
}

 

정적 멤버 사용

 

- 클래스이름.필드 형태로 정적 필드를 사용할 수 있다.

- 클래스이름.메소드 형태로 정적 메소드를 사용할 수 있다.

- 원칙적으로는 클래스 이름으로 접근해야하지만 객체 참조 변수로도 접근이 가능하다. 

- 그 경우에는 이클립스에서 경고 표시가 뜬다.

 

정적 멤버 사용 예제

 

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;
	}
}

 

public class CalculatorExample {
	public static void main(String[] args) {
		// static 메소드를 실행시킬때 원칙적으로 클래스 이름으로 접근해야한다.
		double result1 = 10*10*Calculator.pi;
		int result2 = Calculator.plus(10,5);
		int result3 = Calculator.minus(10,5);
		
		// 하지만 객체 참조 변수로도 접근은 가능하다. 경고 표시가 뜨긴 함
		Calculator myCalc = new Calculator();
		double result4 = 10*10*myCalc.pi;
		int result5 = myCalc.plus(10, 5);
		int result6 = myCalc.minus(10,5);
		
		System.out.println("result1 : " + result1); // result1 : 314.159
		System.out.println("result2 : " + result2); // result2 : 15
		System.out.println("result3 : " + result3); // result3 : 5
		System.out.println("result4 : " + result4); // result4 : 314.159
		System.out.println("result5 : " + result5); // result5 : 15
		System.out.println("result6 : " + result6); // result6 : 5
	}
}

 

-> static 멤버의 사용시 클래스이름으로 접근하는게 원칙인데 객체 참조 변수로 접근해서 경고가 뜨는 것이다.

 

 

정적 메소드 내에서 인스턴스 멤버 사용하고 싶을 때

 

- 객체가 없어도 실행이 되는 정적 메소드의 특징 때문에 정적 메소드 내에서는 인스턴스 멤버를 사용할 수 없다.

- 우선 정적 메소드 안에서 정적 필드나 정적 메소드를 사용하는건 그냥 이름만 적어도 사용이 가능하다.

- 정적 메소드 안에서 인스턴스 멤버를 사용하기위해 this 접근 연산자를 사용하면 컴파일 에러가 난다.

- 그럼 정적 메소드 안에서 인스턴스 멤버를 사용하고 싶다면?? 

- 먼저 객체를 생성하고 참조 변수를 통해 접근하면 된다.

 

public class ClassName {
	
	// 인스턴스 멤버 선언
	int field1;
	void method1() {};
	
	// 정적 멤버 선언
	static int field2;
	static void method2() {};
	
	// 정적 메소드 안에서 필드와 메소드 사용
	static void method3() {
//		this.field1 = 10; //인스턴스 필드를 this 연산자로 접근시 컴파일에러
//		this.method1(); //인스턴스 메소드를 this 연산자로 접근시 컴파일에러
		field2 = 10; //정적 필드는 그냥 사용할 수 있음
		method2(); //정적 메소드도 그냥 사용할 수 있음
		
		ClassName obj = new ClassName(); // 인스턴스 멤버 사용을 위해 먼저 객체를 생성
		obj.field1 = 10; // 인스턴스 필드를 사용하기 위해 참조 변수로 접근
		obj.method1(); // 인스턴스 메소드를 사용하기 위해 참조 변수로 접근
	}
}

 

main 메소드 안에서 인스턴스 메소드 사용

 

- main 메소드 형태 : public static void main(String[] args) {

- main 메소드도 정적 메소드 이므로 객체 생성을 해야 인스턴스 필드와 인스턴스 메소드를 사용할 수 있다.

 

public class Car4 {
	// 필드
	int speed;
	
	// 메소드
	void run() {
		System.out.println(speed + "km/hour로 달립니다.");
	}
	
	// main 메소드
	public static void main(String[] args) {
		Car4 myCar = new Car4(); // 객체 생성
		myCar.speed = 60; // 참조 변수로 접근 가능
		myCar.run(); // 60km/hour로 달립니다.
	}
}

 

싱글톤

 

- 싱글톤은 항상 private을 떠올리도록 하자

- 객체를 딱 하나만 생성해야하는 경우가 있는데 이 경우에 유일하게 생성된 그 객체를 바로 싱글톤이라고 부른다.

- 싱글톤을 만들기 위해서는 객체를 생성하지 못하게 막아야한다.

- 우리는 new 연산자를 통해 생성자를 호출하여 객체를 생성하는 방식을 사용한다.

- 그렇다면 생성자 호출을 막으면 객체는 더이상 생성될 수가 없게 된다.

- 생성자 호출을 막기위해서는 클래스 이름 앞에 private 접근 제한자를 붙여주면 된다.

 

싱글톤 만드는 코드

 

public class Singleton {
	// 정적 필드
	// 타입이 자기자신(클래스)인 정적 필드 선언하고 객체 생성하여 초기화 
	private static Singleton singleton = new Singleton();
	
	// 생성자
	// 다른 클래스에서 객체를 추가로 생성하지 못하도록 private을 붙임
	private Singleton() {}
	
	// 정적 메소드
	// 타입이 클래스 자기 자신이고 return값으로는 참조하고 있는 자신의 객체를 리턴해준다.
	static Singleton getInstance() {
		return singleton;
	}
 }

 

public class SingletonExample {
	public static void main(String[] args) {
		// Singleton obj1 = new Singleton(); // 컴파일에러
		// Singleton obj2 = new Singleton(); // 컴파일에러
		
		Singleton obj1 = Singleton.getInstance(); // 외부에서 객체를 얻는 유일한 방법은
		Singleton obj2 = Singleton.getInstance(); // getInstance()메소드를 호출하는 것 뿐이다.
		
		if(obj1==obj2) { // obj1과 obj2는 동일한 객체를 참조하므로 같다.
			System.out.println("같은 Singleton 객체입니다.");
		} else {
			System.out.println("다른 Singleton 객체입니다.");
		}
	}
}

 

final 필드 선언

 

- final 필드는 최종적인 필드를 의미하며 초기값이 한 번 저장되면 이게 최종 값이 되어 수정이 불가능한 것을 말한다.

- 필드 선언 형태 : final 타입 필드; 

- 필드 선언 형태 (초기화까지) : final 타입 필드 = 초기값; 

 

final 필드 초기화하는 방법 2가지

 

1. 필드 선언 시 초기화하기 : 단순 값이거나 항상 고정된 값일때 이용

2. 생성자에서 초기값 주기 : 초기화 코드가 복잡하거나 외부 데이터로 초기화해야 하는 경우 이용

   ->  final 필드로 선언시 최종 초기화를 마쳐야하며 초기화 되지 않은 채로 final 필드 선언만 있다면 컴파일 에러 발생

 

final 필드 선언과 초기화 예시

 

public class Person {
	// 필드
	final String nation = "korea";
	final String ssn;
	String name;
	
	// 생성자
	public Person(String ssn, String name) {
		this.ssn = ssn;
		this.name = name;
	}
}

 

public class PersonExample {
	public static void main(String[] args) {
		Person p1 = new Person("123456-1234567", "홍길동");
		System.out.println("nation: " + p1.nation);
		System.out.println("ssn: " + p1.ssn);
		System.out.println("name: " + p1.name);
		
//		p1.nation = "usa"; // final 필드 값 변경 시 에러 발생
//		p1.ssn = "654321-7654321"; // final 필드 값 변경 시 에러 발생
		p1.name = "홍길동";
	}
}

 

final 필드 값 변경 시 발생된 에러 내용

 

상수

 

- 상수란, 원주율 파이, 지구의 무게 등 변하지 않는 값을 말한다. constant 라고 불린다.

 

상수와 final 필드의 차이 

 

- 우선 final 필드는 한 번 초기화되면 수정할 수 없는 필드이다.

- 상수는 객체마다 저장할 필요가 없는 공용의 것(=static)이지만 final 필드는 객체마다 저장이 된다.

- 상수는 여러 가지 값으로 초기화될 수 없지만 final 필드는 생성자의 매개값을 통해서 여러 가지 값을 가질 수 있음

- 따라서 상수는 final 이면서 static 이기도 해야함

- static final 필드는 객체마다 존재하지 않고 클래스에만 존재한다. 또한 초기값 저장 후에는 변경할 수 없다.

 

상수 선언 및 사용 예제

 

public class Earth {
	static final double EARTH_RADIUS = 6400;
	static final double EARTH_AREA = 4 * Math.PI * Math.pow(EARTH_RADIUS, 2);	
}

 

public class EarthExample {
	public static void main(String[] args) {
		System.out.println("지구의 반지름: " + + Earth.EARTH_RADIUS + "km");
		System.out.println("지구의 표면적: " + Earth.EARTH_AREA + "km^2");
	}
}

 

지구의 반지름: 6400.0km
지구의 표면적: 5.147185403641517E8km^2

 

반응형

'👨‍💻 2. 웹개발_Back end > 2-1 Java' 카테고리의 다른 글

[JAVA] 07-1 상속  (0) 2021.08.04
[JAVA] 06-6 패키지와 접근 제한자  (0) 2021.08.04
[JAVA] 06-4 메소드  (0) 2021.08.02
[JAVA] 06-3 생성자  (0) 2021.08.02
[JAVA] 06-2 필드  (0) 2021.08.02

댓글