본문 바로가기
프로그래밍언어/Java

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

by 스꼬맹이브로 2021. 2. 17.
728x90
반응형
SMALL

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

정적 멤버 : 클래스에 위치시키고 객체들이 공유하는 멤버

 

<인스턴스 멤버와 this>

인스턴스(instance) 멤버란?

객체(인스턴스)를 생성한 후 사용할 수 있는 필드(인스턴스 필드)와 메소드(인스턴스 메소드)

인스턴스 필드와 메소드는 객체에 소속된 멤버이기 때문에 객체 없이는 사용할 수 없음

 

▶ 인스턴스 멤버 선언

//Car클래스에 인스턴스 필드 gas와 인스턴스 메소드 setSpeed() 선언
public class Car{
  //필드
  int gas;
  
  //메소드
  void setSpeed(int speed){ ... }
}

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

 

Car myCar = new Car();
myCar.gas = 10;
myCar.setSpeed(60);

Car yourCar = new Car();
yourCar.gas = 20;
yourCar.setSpeed(80);

다음 코드를 실행하면 다음과 같이 표현할 수 있다.

 

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

 

메소드는 코드 블록이므로 객체마다 동일한 코드 블록을 가지고 있을 필요가 없기 때문에 객체마다 가지고 있지는 않지만 메모리 블록 내부에 인스턴스 필드 등이 사용되는 경우가 있기 때문에 인스턴스라는 용어가 붙는다.

 

▶ this

객체 외부에서 인스턴스 멤버에 접근하기 위해 참조 변수를 사용하는 것과 마찬가지로 객체 내부에서 인스턴스에 접근하기 위해 this를 사용한다.

this는 주로 생성자와 메소드의 매개 변수 이름이 필드와 동일한 경우, 인스턴스 멤버인 필드임을 명시하고자 할 때 사용한다.

 

public class Car {
	//필드
	String model;
	int speed;
	
	//생성자
	Car(String model) {
		this.model = model; //앞의 model은 필드의 model, 뒤의 model은 매개 변수의 model
	}
	
	//메소드
	void setSpeed(int speed) {
		this.speed = speed; //앞의 speed는 필드의 speed, 뒤의 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 CarExample {
	public static void main(String[] args) {
		Car myCar = new Car("포르쉐");
		Car yourCar = new Car("벤츠");
		
		myCar.run();
		yourCar.run();
	}
}

결과 :

 

<정적 멤버와 static>

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

 

▶ 정적 멤버 선언

정적 필드와 정적 메소드 선언 시 static 키워드를 추가적으로 붙이면 정적 멤버 선언 완료

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

정적 필드와 정적 메소드는 클래스에 고정된 멤버이므로 클래스 로더가 클래스(바이트 코드)를 로딩해서 메소드 메모리 영역에 적재할 때 클래스별로 관리하기 때문에 클래스의 로딩이 끝나면 바로 사용이 가능하다.

 

때문에 필드를 선언할 때는 인스턴스 필드로 할 지, 정적 필드로 할 지에 대해 판단 기준이 필요하다.

객체마다 가지고 있어야 할 데이터라면 인스턴스 필드로 선언하고, 객체마다 가지고 있을 필요가 없는 공용 데이터라면 정적 필드로 선언하는 것이 좋다.

 

메소드 역시 인스턴스 필드를 포함하고 있다면 인스턴스 메소드로 선언하고, 인스턴스 필드를 포함하고 있지 않다면 정적 메소드로 선언한다.

 

▶ 정적 멤버 사용

클래스가 메모리로 로딩되면 정적 멤버를 바로 사용할 수 있는데, 클래스 이름과 함께 도트 연산자로 접근하면 된다.

//사용 방법
클래스.필드;
클래스.메소드(매개값, ...);
//EX: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;
	}
}
//정적 필드와 정적 메소드 사용
public class CalculatorExample {
	public static void main(String[] args) {
		double result1 = 10 * 10 * Calculator.pi;
		int result2 = Calculator.plus(10, 5);
		int result3 = Calculator.minus(10, 5);
		
		System.out.println("result1 : " + result1);
		System.out.println("result2 : " + result2);
		System.out.println("result3 : " + result3);
	}
}

결과:

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

(하지만 클래스 이름으로 접근하는 것이 좋다!)

//객체 참조 변수로 정적 필드와 정적 메소드 접근하기
public class CalculatorExample {
  public static void main(String[] args) {
    Calculator myCalcu = new Calculator();
    double result1 = 10*10*myCalcu.pi;
    int result2 = myCalcu.plus(10,5);
    int result3 = myCalcu.minus(10,5);
  }
}

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

  • 내부에 인스턴스 필드나 인스턴스 메소드를 사용 불가
  • 객체 자신의 참조인 this 키워드도 사용이 불가능\

또한 main()메소드도 정적 메소드이므로 객체 생성 없이 인스턴스 메소드를 main()메소드에서 바로 사용할 수 없다.

public class Car {
	int speed;
	
	void run() {
		System.out.println(speed + "으로 달립니다.");
	}
	
   //잘못된 코드 --> 컴파일 에러
   //public static void main(String[] args) {
	//	speed = 60; 
	//	run();
	//} 
    
	public static void main(String[] args) {
		Car myCar = new Car(); 
		myCar.speed = 60;
		myCar.run();
	}
}

 

<싱글톤>

가끔 전체 프로그램에서 단 하나의 객체만 만들도록 보장해야 하는 경우가 있다.

이런 경우에 단 하나만 생성된다고 해서 이 객체를 싱글톤(Singleton)이라고 한다.

 

싱글톤 만드는 방법

  • 클래스 외부에서 new 연산자로 생성자를 호출할 수 없도록 막아야 함
    → 생성자 앞에 private 접근 제한자 붙이기
  • 자신의 타입인 정적 필드를 선언하고 자신의 객체를 생성해 초기화
    (참고: 클래스 내부에서는 new 연산자로 생성자 호출이 가능)
    → 정적 필드도 private 접근 제한자를 붙여 필드값을 변경할 수 없도록 함
  • 외부에서 호출할 수 있는 정적 메소드인 getInstance()를 선언하고 정적 필드에서 참조하고 있는 자신의 객체 리턴
//싱글톤 만드는 코드
public class 클래스{
  //정적 필드
  private static 클래스 singleton = new 클래스();
  
  //생성자
  private 클래스() {}
  
  //정적 메소드
  static 클래스 getInstance() {
    return singleton;
  }
}

다음으로 인해 외부에서 객체를 얻는 유일한 방법은 getInstance()메소드를 호출하는 방법 뿐이다.

getInstance()메소드는 단 하나의 객체만 리턴하기 때문에 아래 코드에서 변수1과 변수2는 동일한 객체를 참조한다.

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

<final 필드와 상수>

▶final 필드

final 필드란 초기값이 저장되면 이것이 최종적인 값이 되어서 프로그램 실행 도중에 수정할 수 없는 필드이다.

//final 필드 선언 방법
final 타입 필드 [= 초기값];

final 필드의 초기값을 줄 수 있는 방법

  1. 필드 선언 시 초기값 선언 : 초기값이 단순한 값일 때 사용
  2. 생성자에서 선언 : 복잡한 초기화 코드가 필요하거나 객체 생성 시에 외부 데이터로 초기화할 때 사용

만약 초기화되지 않은 final 필드를 그대로 남겨두면 컴파일 에러 발생

// Person 클래스 생성
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(p1.nation);
		System.out.println(p1.ssn);
		System.out.println(p1.name);
		
		//p1.nation = "usa";
		//p1.ssn = "654321-7654321";
		p1.name = "홍삼원";;
	}
}

결과 :

 

<상수>

상수(static final)는 일반적으로 불변의 값을 말하며, 불변읠 값은 수학에서 사용되는 원주율 파이나 지구의 무게 및 둘레 등이 해당된다. 

이런 불변의 값을 저장하는 필드를 자바에서는 상수(constant)라고 한다.

 

그럼 상수와 final 필드의 차이점이 무엇일까?

final필드를 상수라고 부르지 않는 이유는 불변의 값은 객체마다 저장할 필요가 없는 공용성을 띠고 있으며, 여러 가지 값으로 초기화 될 수 없다. 즉, final 필드는 객체마다 저장되고, 생성자의 매개값을 통해서 여러 가지 값을 가질 수 있기 때문에 상수와는 다르다.

이 말은, 상수는 static이면서 final이어야 한다는 것이다. static final 필드는 객체마다 존재하지 않고 클래스에만 존재하며, 한 번 초기값이 저장되면 변경할 수 없다.

//상수 선언 방법
static final 타입 상수 = 초기값;

//상수 선언 예시
static final double PI = 3.141592;
static final double EARTH_RADIUS = 6400;
static final double EARTH_AREA = 4 * Math.PI * EARTH_RADIUS * EARTH_RADIUS;

상수 이름은 모두 대문자로 작성하는 것이 관례이며, 서로 다른 단어가 혼합된 이름이라면 언더바(_)로 단어를 연결한다.

728x90
반응형
LIST

'프로그래밍언어 > Java' 카테고리의 다른 글

07-1 상속  (0) 2021.02.25
06-6 패키지와 접근 제한자  (0) 2021.02.23
06-4 메소드  (0) 2021.02.15
06-3. 생성자  (0) 2021.02.08
06-2. 필드  (0) 2021.01.26