Chapter 6 클래스와 객체(2)

Ruinak·2021년 5월 19일
0

Java

목록 보기
6/15
post-thumbnail

1. this 예약어

1-1 자신의 메모리를 가리키는 this

  • this는 간단히 설명하면 생성된 인스턴스 스스로를 가리키는 예약어입니다.

예제 6-1 this 출력하기

class BirthDay {
	int day;
	int month;
	int year;
	
	// 태어난 연도를 지정하는 메서드
	public void setYear(int year) {
		// bDay.year = year;과 같음
		this.year = year;
	}
	
	// this 출력 메서드
	public void printThis() {
		// System.out.println(bDay);와 같음
		System.out.println("this : " + this);
	}
}

public class ThisExample {
	public static void main(String[] args) {
		BirthDay bDay = new BirthDay();
		// 태어난 연도를 2021로 지정
		bDay.setYear(2021);
		// 참조 변수출력
		System.out.println("bDay : " + bDay);
		// this 출력 메서드 호출
		bDay.printThis();
	}
}

  • 인스턴스를 가리키는 변수가 참조 변수이며, 참조 변수를 출력하면 '클래스 이름@메모리 주소' 문자열 값이 나옵니다.
  • 출력 결과를 보면 bDay.printThis( ) 메서드를 호출하여 출력한 this 값이 변수 bDay를 출력한 값과 같습니다.
  • 클래스 코드에서 사용하는 this는 생성된 인스턴스 자신을 가리키는 역할을 합니다.

1-2 생성자에서 다른 생성자를 호출하는 this

  • 클래스에서 생성자가 여러 개 있을 때 어떤 생성자에서 다른 생성자를 호출하는 경우가 종종 있습니다.
  • this를 사용해 클래스의 생성자에서 다른 생성자를 호출할 수 있습니다.

예제 6-2 this로 다른 생성자 호출하기

class Person {
	String name;
	int age;
	
	Person(){
		// this를 사용해 Person(String, int) 생성자 호출
		this("이름 없음", 1);
	}
	
	Person(String name, int age){
		this.name = name;
		this.age = age;
	}
}

public class CallAnotherConst {
	public static void main(String[] args) {
		Person noName = new Person();
		System.out.println("noName.name : " + noName.name);
		System.out.println("noName.age : " + noName.age);
	}
}

  • Person 클래스에서는 Person( ) 디폴트 생성자와 매개변수를 가지는 Person(String, int) 생성자가 있습니다.
  • 클래스가 생성될 때 Person(String, int)가 호출되어 이름과 나이를 전달받고, Person( ) 디폴트 생성자가 호출되는 경우에는 초깃값으로 "이름 없음"과 1 값을 대입하고자 합니다.
  • 다른 생성자 안에 코드가 작성되어 있으면 9행 처럼 this를 활용하여 디폴트 생성자 안에 다른 생성자를 호출할 수 있음
  • this를 사용하여 생성자를 호출하는 코드 이전에 다른 코드를 넣을 수 없음(오류 발생)
  • 디폴트 생성자에서 생성이 완료되는 것이 아니라 this를 사용해 다른 생성자를 호출하므로, 이 때는 this를 활용한 문장이 가장 먼저 와야합니다.

1-3 자신의 주소를 반환하는 this

  • this를 사용하여 생성된 클래스 자신의 주소 값을 반환할 수 있습니다.
  • 인스턴스 주소 값을 반환할 때는 this를 사용하고 반환형은 클래스 자료형을 사용합니다.

예제 6-3 this를 사용하여 주소 값 반환하기

class Person {
	String name;
	int age;
	
	Person(){
		// this를 사용해 Person(String, int) 생성자 호출
		this("이름 없음", 1);
	}
	
	Person(String name, int age){
		this.name = name;
		this.age = age;
	}
	
	// 반환형은 클래스형
	Person returnItSelf() {
		// this 반환
		return this;
	}
}

public class CallAnotherConst {
	public static void main(String[] args) {
		Person noName = new Person();
		System.out.println("noName.name : " + noName.name);
		System.out.println("noName.age : " + noName.age);
		
		System.out.println("==============================");
		
		// this 값을 클래스 변수에 대입
		Person p = noName.returnItSelf();
		// noName.returnItSelf()의 반환 값 출력
		System.out.println("p : " + p);
		// 참조 변수 출력
		System.out.println("noName : " + noName);
	}
}

  • this를 반환하는 메서드를 사용할 일이 흔하지는 않지만, 클래스 자료형과 상관없이 클래스 내에서 this를 사용하면 자신의 주소 값을 반환할 수 있습니다.

2. 객체 간 협력

  • 학생, 버스, 지하철의 객체를 만들고 이들 사이에 어떻게 협력이 이루어지는지 보겠습니다.

2-1 학생 클래스 구현하기

  • 학생 클래스는 '이름', '학년', '가진 돈'을 멤버 변수(속성)로 가집니다.
  • '버스를 탄다', '지하철을 탄다', '학생의 정보를 보여준다'를 메서드(멤버 함수)로 가집니다.

예제 6-4 학생 클래스 구현하기

public class Student {
	// 학생 이름
	public String studentName;
	// 학년
	public int grade;
	// 학생이 가지고 있는 돈
	public int money;
	
	// 학생이름과 가진 돈을 매개변수로 받는 생성자
	public Student(String sutendtName, int money) {
		this.studentName = studentName;
		this.money = money;
	}
	
	// 학생이 버스를 타면 1,000원을 지불하는 기능을 구현한 메서드
	public void takeBus(Bus bus) {
		bus.take(1000);
		this.money -= 1000;
	}
	
	// 학생이 지하철을 타면 1,500원을 지불하는 기능을 구현한 메서드
	public void takeSubway(Subway subway) {
		subway.take(1500);
		this.money -= 1500;
	}
	
	// 학생의 현재 정보를 출력하는 메서드
	public void showInfo() {
		System.out.println(studentName + "님의 남은 돈은 " + money + "원 입니다.");
	}	
}
  • 준비한 생성자는 학생 이름과 학생이 가진 돈을 매개변수로 받습니다.
  • 학생 클래스를 하나 생성하면 학생 이름과 학생이 가진 돈을 초기화합니다.
  • 디폴트 생성자를 제공하지 않으므로 학생 클래스를 생성하려면 매개변수가 있는 Student(String studentName, int money) 생성자를 호출해야 합니다.
  • takeBus( ) 메서드는 학생이 버스를 선택해서 탄 경우를 구현한 코드입니다.
  • 버스를 타면 요금(1,000원)을 내기 때문에 this.money -= 1000; 문장이 수행되면 학생이 가진 돈이 1,000원만큼 줄어듭니다.
  • 지하철도 마찬가지입니다.
  • showInfo( ) 함수는 학생 정보를 출력해주며, 학생의 이름과 남은 돈 정보가 문자열로 연결되어 출력됩니다.

2-2 버스 클래스 구현하기

  • 버스 객체는 학생 한 명이 승차하면 버스 요금을 받고 승객 수가 증가할 것입니다.
  • 버스 클래스의 멤버 변수로는 버스 번호, 승객 수, 버스가 받은 요금 총액이 있습니다.

예제 6-5 버스 클래스 구현하기

public class Bus {
	// 버스 번호
	int busNumber;
	// 승객 수
	int passengerCount;
	// 버스 수입
	int money;
	
	// 버스 번호를 매개변수로 받는 생성자
	public Bus(int busNumber) {
		this.busNumber = busNumber;
	}
	
	// 승객이 버스에 탄 경우를 구현한 메서드
	public void take(int money) {
		// 버스 수입 증가
		this.money += money;
		// 승객 수 증가
		passengerCount++;
	}
	
	// 버스 정보를 출력하는 메서드
	public void showInfo() {
		System.out.println("버스 " + busNumber + "번의 승객은 " + passengerCount + "명이고, 수입은" + money + "원 입니다.");
	}
}
  • Bus 생성자에서는 버스 번호를 매개변수로 받아 버스가 생성될 때 버스 번호를 초기화합니다.
  • take( ) 메서드에서 승객 한 명이 버스를 탄 경우를 구현합니다.
  • 승객이 요금을 지불하므로, 요금을 매개변수로 받고 요금이 들어오면 버스 수입이 증가하고 승객 수도 증가합니다.
  • showInfo( ) 메서드에서 버스 번호와 버스를 탄 승객 수 그리고 버스 수입을 문자열로 연결하여 출력합니다.

2-3 지하철 클래스 구현하기

  • 지하철 객체는 학생 한 명이 승차하면 지하철 요금을 받고 승객 수가 증가할 것입니다.
  • 지하철 클래스의 멤버 변수로는 지하철 노선, 승객 수, 지하철이 받은 요금 총액이 있습니다.

예제 6-6 지하철 클래스 구현하기

public class Subway {
	// 지하철 노선
	String lineNumber;
	// 승객 수
	int passengerCount;	
	// 수입액
	int money;	
	
	// 지하철 노선 번호를 매개변수로 받는 생성자
	public Subway(String lineNumber) {
		this.lineNumber = lineNumber;
	}
	
	// 승객이 지하철에 탄 경우를 구현한 메서드
	public void take(int money) {
		// 지하철 수입 증가
		this.money += money;
		// 승객 수 증가
		passengerCount++;
	}
	
	// 지하철 정보를 출력하는 메서드
	public void showInfo() {
		System.out.println(lineNumber + "번 승객은 " + passengerCount + "명이고, 수입은" + money + "원 입니다.");
	}
}
  • Subway(String lineNumber) 생성자가 지하철 몇 호선인지를 매개변수로 받아 Subway 클래스를 생성합니다.
  • take( ) 메서드에서 승객 한 명이 지하철을 탄 경우를 구현합니다.
  • 승객이 요금을 지불하므로, 요금을 매개변수로 받고 요금이 들어오면 지하철 수입이 증가하고 승객 수도 증가합니다.
  • showInfo( ) 메서드에서 지하철 노선과 지하철을 탄 승객 수 그리고 지하철 수입을 문자열로 연결하여 출력합니다.

2-4 학생, 버스, 지하철 객체 협력하기

예제 6-7 버스와 지하철 타기

public class TakeTrans {
	public static void main(String[] args) { 
    
		// 학생 두 명 생성
		Student studentRuin = new Student("Ruin", 5000);
		Student studentArk = new Student("Ark", 10000);
		
		// 노선 버스가 8번인 버스 생성
		Bus bus8 = new Bus(8);
		// Ruin이 8번 버스를 탐
		studentRuin.takeBus(bus8);
		// Ruin 정보 출력
		studentRuin.showInfo();
		// 버스 정보 출력
		bus8.showInfo();
		
		System.out.println("===============================================");
		
		// 노선 번호가 3호선인 지하철 생성
		Subway subwayBlue = new Subway("3호선");
		// Ark가 3호선을 탐
		studentArk.takeSubway(subwayBlue);
		// Ark 정보 출력
		studentArk.showInfo();
		// 지하철 정보 출력
		subwayBlue.showInfo();		
	}
}

  • 두 개의 학생 인스턴스(Ruin, Ark)가 생성됩니다.
  • 학생, 버스, 지하철 사이에서 객체 간 협력이 이루어집니다.
  • 객체 사이에서는 서로 어떤 값을 주고받고 메서드를 호출하는 일이 발생합니다.
  • 구현 과정 정리
    1) 필요한 객체를 정의합니다.
    2) 객체에 필요한 멤버 변수를 선언하고, 생성자를 정의했습니다.
    3) 객체 사이의 협력 기능을 구현(메서드)했습니다.

3. static 변수

3-1 static 변수의 정의와 사용 방법

  • 클래스에서 공통으로 사용하는 변수를 'static 변수'로 선언합니다.
  • static 변수란 다른 용어로 '정적 변수'라고 합니다.
  • static 변수는 자바뿐만 아니라 다른 언어에서도 비슷한 개념으로 사용하고 있는 변수로서 자바에서는 다른 멤버 변수처럼 클래스 내부에 선언합니다.
  • static 변수는 클래스 내부에 선언하지만, 다른 멤버 변수처럼 인스턴스가 생서될 때마다 새로 생성되는 변수는 아닙니다.
  • static 변수는 프로그램이 실행되어 메모리에 올라갔을 때 딱 한 번 메모리 공간이 할당되고, 이 값은 모든 인스턴스가 공유함
  • 일반 멤버 변수는 인스턴스가 생성될 때 마다 새로 생성되어 각자 다른 studentName을 가지게 되지만, static으로 선언한 변수는 인스턴스 생성과 상관없이 먼저 생성되고 그 값을 모든 인스턴스가 공유하게 되는 것
  • 이런 이유로 static 변수를 클래스에 기반한 변수라고 해서 '클래스 변수(class variable)'라고도 함

예제 6-8 static 변수 사용하기

public class Student {
	// static 변수는 인스턴스 생성과 상관없이 먼저 생성됨
	public static int serialNum = 1000;
    
	public int studentID;
	public String studentName;
	public int grade;
	public String address;
	
	public String getStudentName() {
		return studentName;
	}
	
	public void setStudentName(String name) {
		 studentName = name;
	}
}

예제 6-9 static 변수 테스트하기

public class StudentTest1 {
	public static void main(String[] args) {
		Student studentYou = new Student();
		studentYou.setStudentName("유중혁");
		// serialNum 변수의 초깃값 출력
		System.out.println("studentYou.serialNum : " + studentYou.serialNum);
		// static 변수 값 증가
		studentYou.serialNum++;
		
		Student studentLee = new Student();
		studentLee.setStudentName("이지혜");
		// 증가된 값 출력
		System.out.println("studentLee.serialNum : " + studentLee.serialNum);
		System.out.println("studentYou.serialNum : " + studentYou.serialNum);
	}
}

  • static으로 선언한 serialNum 변수는 모든 인스턴스가 공유합니다.
  • 두 개의 참조 변수가 동일한 변수의 메모리를 가리키고 있습니다.

학번 생성하기

예제 6-10 학번 자동으로 부여하기

public class Student {
	public static int serialNum = 1000;
	public int studentID;
	public String studentName;
	public int grade;
	public String address;
	
	// 생성자
	public Student() {
		// 학생이 생성될 때마다 serialNum 증가
		serialNum++;
		// 증가된 값을 학번 인스턴스 변수에 부여
		studentID = serialNum;
	}
	
	public String getStudentName() {
		return studentName;
	}
	
	public void setStudentName(String name) {
		 studentName = name;
	}
}
  • static 변수(serialNum)는 학생이 생성될 때마다 이 변수 값이 증가합니다.
  • 주의할 점은 static 변수를 그냥 바로 학번으로 사용하면 안됩니다.
  • static 변수는 모든 인스턴스가 공유하는 변수이므로 이 변수를 바로 학번으로 사용하면 모든 학생이 동일한 학번 값을 가지게 되기 때문입니다.
  • 학번은 학생의 고유 번호이므로 학생의 멤버 변수로 선언해 주고, 학생이 한 명 생성될 때마다 증가한 serialNum 값을 studentID에 대입해주면 됩니다.
  • 생성자를 추가하고 생성자에서 serialNum 값을 증가시키고 증가 값을 studentID 변수에 대입합니다.

예제 6-11 학번 확인하기

public class StudentTest2 {
	public static void main(String[] args) {
		Student studentKim = new Student();
		studentKim.setStudentName("김독자");
		System.out.println("studentYou.serialNum : " + studentKim.serialNum);
		System.out.println(studentKim.studentName + " 학번 :" + studentKim.studentID);
		
		System.out.println("============================");
		
		Student studentLee = new Student();
		studentLee.setStudentName("이상아");
		System.out.println("studentYou.serialNum : " + studentLee.serialNum);
		System.out.println(studentLee.studentName + " 학번 :" + studentLee.studentID);
	}
}

  • 학생 인스턴스를 생성할 때마다 serialNum 변수의 값은 증가합니다.
  • 새로 생성되는 학생마다 가지는 studentID 변수에 증가한 serialNum을 복사해주었으므로, 두 학생의 학번은 다릅니다.
  • static 변수는 같은 클래스에서 생성된 인스턴스들이 같은 값을 공유할 수 있으므로, 인스턴스 간에 공통으로 사용할 값이 필요한 경우 유용하게 사용할 수 있습니다.

3-2 클래스 변수

  • 앞 예제에서 살펴본 것처럼 static 변수는 인스턴스를 생성할 때마다 만들어 지는 것이 아니고 클래스를 선언할 때 특정 메모리에 저장되어 모든 인스턴스가 공유하는 변수입니다.
  • 변수는 인스턴스 생성과는 별개이므로 인스턴스보다 먼저 생성되므로, 인스턴스가 아닌 클래스 이름으로도 참조하여 사용할 수 있음

예제 6-12 클래스 이름으로 static 변수 참조하기

public class StudentTest1 {
	public static void main(String[] args) {
		Student studentKim = new Student();
		studentKim.setStudentName("김독자");
		// serialNum 변수를 직접 클래스 이름으로 참조
		System.out.println("Student.serialNum : " + Student.serialNum);
		System.out.println(studentKim.studentName + " 학번 :" + studentKim.studentID);
		
		System.out.println("============================");
		
		Student studentLee = new Student();
		studentLee.setStudentName("이상아");
		// serialNum 변수를 직접 클래스 이름으로 참조
		System.out.println("Student.serialNum : " + Student.serialNum);
		System.out.println(studentLee.studentName + " 학번 :" + studentLee.studentID);
	}
}

  • static 변수는 인스턴스가 생성되지 않아도 사용할 수 있기 때문에 보통은 클래스 이름과 같이 사용합니다.
  • staitc 변수, 정적 변수, 클래스 변수라는 세 가지 용어 모두 자바에서 static 변수를 의미를 합니다.
  • 자바에서 static 변수를 클래스 변수라고 하는 이유는 인스턴스마다 생성되는 변수가 아니라 클래스에 속해 한 번만 생성되는 변수이고 이를 여러 인스턴스가 공유하기 때문입니다.

3-3 클래스 메서드

  • 일반 멤버 변수를 위한 메서드가 존재하듯이 static 변수를 위한 메서드도 있습니다.
  • 이런 메서드를 'static 메서드' 또는 '클래스 메서드(class method)'라고 합니다.

예제 6-13 serialNum의 get( ), set( ) 메서드 사용하기

public class Student {
	// private 변수로 변경
	private static int serialNum = 1000;
	public int studentID;
	public String studentName;
	public int grade;
	public String address;
	
	// 생성자
	public Student() {
		// 학생이 생성될 때마다 serialNum 증가
		serialNum++;
		// 증가된 값을 학번 인스턴스 변수에 부여
		studentID = serialNum;
	}
	
	// serialNum의 get( ) 메서드 	
	public static int getSerialNum() {
		int i = 10;
		return serialNum;
	}
	
	// serialNum의 set( ) 메서드 
	public static void setSerialNum() {
		Student.serialNum = serialNum;
	}
	
	public String getStudentName() {
		return studentName;
	}
	
	public void setStudentName(String name) {
		 studentName = name;
	}
}

  • 외부 클래스에서 serialNum 변수에 직접 참조하지 못하도록 일단 private로 선언하고 이 변수에 대한 get( ) 메서드와 set( ) 메서드를 생성합니다..
  • 외부 클래스에서 serialNum을 사용하려면 get( ) 메서드를 호출하고, serialNum 변수 값을 변경하려면 set( ) 메서드를 사용해야 합니다.

예제 6-14 학번 출력하기

public class StudentTest2 {
	public static void main(String[] args) {
		Student studentKim = new Student();
		studentKim.setStudentName("김독자");
		// serialNum 값을 가져오기 위해 get( ) 메서드를 클래스 이름으로 직접 호출
		System.out.println("Student.getSerialNum : " + Student.getSerialNum());
		System.out.println(studentKim.studentName + " 학번 :" + studentKim.studentID);
		
		System.out.println("============================");
		
		Student studentLee = new Student();
		studentLee.setStudentName("이상아");
		// serialNum 값을 가져오기 위해 get( ) 메서드를 클래스 이름으로 직접 호출
		System.out.println("Student.getSerialNum : " + Student.getSerialNum());
		System.out.println(studentLee.studentName + " 학번 :" + studentLee.studentID);
	}
}

  • serialNum을 직접 참조하지 않고 getSerialNum( ) 메서드를 호출하여 참조합니다.
  • static 메서드 또한 static 변수처럼 인스턴스 참조 변수가 아닌 클래스 이름으로 직접 호출할 수 있습니다.

3-4 클래스 메서드와 인스턴스 변수

  • 클래스 메서드 내부에서는 인스턴스 변수를 사용할 수 없습니다.
  • getSerialNum( ) 메서드는 static 예약어를 붙인 클래스 메서드입니다.
  • 위 메서드는 세 종류의 변수를 사용하고 있습니다.

1) int i

  • 메서드 내부에서 선언합니다.
  • 메서드 내부에서 선언한 변수를 그 지역에서만 사용한다고 해서 지역 변수(local variable)라고 합니다.
  • 지역 변수는 메서드가 호출될 때 메모리에 생성되어 메서드가 끝나면 사라지는 변수입니다.
  • 따라서 이 변수는 getSerialNum( ) 메서드 내부에서만 사용할 수 있습니다.

2) studentNmae 변수

  • 오류가 발생합니다.
  • Student 클래스의 멤버 변수로, 인스턴스가 생성될 때 만들어지는 인스턴스 변수이기 때문에 오류가 발생합니다.

3) return SerialNum;

  • serialNum 변수는 static 변수입니다.
  • 클래스 메서드인 getSerialNum( ) 메서드 내부에서도 사용 가능합니다.
  • 클래스 메서드와 클래스 변수는 인스턴스가 생성되지 않아도 사용할 수 있음

예제 6-15 studentName 변수 살펴보기

public class StudentTest1 {
	public static void main(String[] args) {
		System.out.println("Student.getSerialNum : " + Student.getSerialNum());
	}
}

  • 클래스 메서드는 student.getSerialNum( )과 같이 인스턴스가 생성되지 않아도 언제든 호출할 수 있습니다.
  • 인스턴스가 생성되어야 메모리가 할당되는 인스턴스 변수는 클래스 메서드에서 사용할 수 없습니다.

정리

  • 클래스 메서드 내부에서 지역 변수와 클래스 변수는 사용할 수 있지만, 인스턴스 변수는 사용할 수 없습니다.
  • 클래스 메서드에서 인스턴스 변수를 사용할 수는 없지만, 반대로 일반 메서드에서 클래스 변수를 사용하는 것은 전혀 문제가 되지 않습니다.
  • 일반 메서드는 인스턴스가 생성될 때 호출되는 메서드이고, 클래스 변수는 이미 만들어진 변수이기 때문에 일반 메서드에서도 클래스 변수를 호출할 수 있습니다.

4. 변수 유효 범위

4-1 변수 유효 범위란?

  • 변수는 어디에 어떻게 선언되느냐에 따라 유효 범위(scope)가 달라집니다.

변수의 종류

1) 지역 변수(로컬 변수, local variable) : 함수나 메서드 안에서만 사용
2) 멤버 변수(인스턴스 변수, insatance variable) : 클래스 안에서 사용
3) static 변수(클래스 변수, class variable) : 여러 인스턴스에서 공통으로 사용

지역 변수의 유효 범위

  • 지역 변수는 함수나 메서드 내부에 선언하기 때문에 함수 밖에서는 사용할 수 없습니다.
  • 하나의 함수에 선언한 지역 변수는 다른 함수에서 사용할 수 없습니다.
  • 지역 변수가 생성되는 메모리를 스택(stack)이라고 합니다.
  • 스택에 생성되는 지역 변수는 함수가 호출될 때 생성되었다가 함수가 반환되면 할당되었던 메모리 공간이 해제되면서 함께 없어집니다.

멤버 변수의 유효 범위

  • 멤버 변수는 인스턴스 변수라고도 합니다.
  • 멤버변수는 클래스가 생성될 때 힙(heap) 메모리에 생성되는 변수입니다.
  • 멤버 변수는 클래스의 어느 메서드에서나 사용할 수 있습니다.
  • 힙에 생성된 인스턴스가 가비지 컬렉터(garbage collector)에 의해 수거되면 메모리에서 사라집니다.
  • 클래스 내부의 여러 메서드에서 사용할 변수는 멤버 변수로 선언하는 것이 좋음

staiac 변수의 유효 범위

  • 사용자가 프로그램을 실행하면 메모리에 프로그램이 상주합니다.
  • 프로그램 영역 중에 데이터 영역이 있는데, 이 영역에는 상수나 문자열, static 변수가 생성됩니다.
  • 인스턴스 변수는 객체가 생성되는 문장 즉 new가 생성되지만, static 변수는 클래스 생성과 상관 없이 처음부터 데이터 영역 메모리에 생성됩니다.
  • 인스턴스 변수와 static 변수는 사용하는 메모리가 다릅니다.
  • 생성된 static 변수는 private이 아니라면 클래스 외부에서도 객체 생성과 무관하게 사용할 수 있습니다.
  • 프로그램 실행이 끝난 뒤 메모리에서 내려가면 static 변수도 소멸됩니다.
  • static 변수는 프로그램이 시작할 때 부터 끝날 때까지 메모리에 상주하므로 크기가 너무 큰 변수를 static으로 선언하는 것은 좋지 않습니다.

4-2 변수 유형에 따른 용도

  • 아래 표는 세 가지 변수를 정리한 것입니다.
  • 변수는 특성에 맞게 선언해서 사용하는 것이 중요합니다.
  • 용도에 따라 변수 유형을 명확히 정해서 효율적으로 프로그래밍 하는 것이 좋습니다.

5. static 응용 - 싱글톤 패턴

5-1 싱글톤 패턴이란?

  • 프로그램을 구현하다 보면 여러 개의 인스턴스가 필요한 경우도 있고 단 하나의 인스턴스만 필요한 경우도 있습니다.
  • 객체 지향 프로그램에서 인스턴스를 단 하나만 생성하는 디자인 패턴을 싱글톤 패턴(singleton pattern)이라고 합니다.
  • 싱글톤 패턴은 static을 응용하여 프로그램 전반에서 사용하는 인스턴스를 하나만 구현하는 방식입니다.
  • 실무나 프레임워크에서 많이 사용하는 패턴으로 내용을 잘 익혀두면 도움이 될 것입니다.

5-2 싱글톤 패턴으로 회사 클래스 구현하기

단계 1 : 생성자를 private로 만들기

  • 생성자가 하나도 없는 클래스는 컴파일러가 자동으로 디폴트 생성자 코드를 넣어주는데, 컴파일러가 만들어 주는 디폴트 생성자는 항상 public입니다.
  • 생성자가 public이라면 외부 클래스에서 인스턴스를 여러 개 생성할 수 있습니다.
  • 싱글톤 패턴에서는 생성자를 반드시 명시적으로 만들고 그 접근 제어자를 private로 지정해야 합니다.
  • 명시적으로 만들고 지정을 하면 컴파일러가 디폴트 생성자를 만들지 않고, 접근 제어자가 private이므로 외부 클래스에서 마음대로 Company 인스턴스를 생성할 수 없게 됩니다.
  • Company 클래스 내부에서만 이 클래스의 생성을 제어할 수 있습니다.

예제 6-16 private 생성자 만들기

public class Company {
	// 단계 1 : private 생성자 생성
	private Company() { }
}

단계 2 : 클래스 내부에 static으로 유일한 인스턴스 생성하기

  • 프로그램에서 사용할 인스턴스 하나는 필요합니다.
  • Company 클래스 내부에서 하나의 인스턴스를 생성하는데, 이 인스턴스가 프로그램 전체에서 사용할 유일한 인스턴스가 됩니다.
  • private로 선언하여 외부에서 이 인스턴스에 접근하지 못하도록 제한해야 인스턴스 오류를 방지할 수 있습니다.

예제 6-17 인스턴스 생성하기

public class Company {
	// 단계 2 : 유일하게 생성한 인스턴스
	private static Company instance = new Company();
	// 단계 1 : private 생성자 생성
	private Company() { }
}

단계 3 : 외부에서 참조할 수 있는 public 메서드 만들기

  • private로 선언한 유일한 인스턴스를 외부에서도 사용할 수 있도록 설정해야 합니다.
  • public 메서드를 생성한 후, 유일하게 생성한 인스턴스를 반환해 줍니다.
  • 인스턴스를 반환하는 메서드는 반드시 static으로 선언해야합니다.
  • getInstance( ) 메서드는 인스턴스 생성과 상관없이 호출할 수 있어야 하기 때문입니다.

예제 6-18 public 메서드 만들기

public class Company {
	// 단계 2 : 유일하게 생성한 인스턴스
	private static Company instance = new Company();
	// 단계 1 : private 생성자 생성
	private Company() { }
	
	// 단계 3 : 인스턴스를 외부에서 참조할 수 있도록 public get( ) 메서드 구현
	public static Company getInstance() {
		if(instance == null) {
			instance = new Company();
		}
		// 유일하게 생성한 인스턴스 반환
		return instance;
	}
}

단계 4 : 실제로 사용하는 코드 만들기

  • 외부 클래스에서는 Company를 생성할 수 없으므로 static으로 제공되는 getInstance( ) 메서드를 호출합니다. Company.getInstance( );와 같이 호출하면 반환 값으로 유일한 인스턴스를 받아 옵니다.

예제 6-19 변수의 주소 값 비교하기

public class CompanyTest {
	public static void main(String[] args) {
		// 클래스 이름으로 getInstance( ) 호출하여 참조 변수에 대입
		Company myCompany1 = Company.getInstance();
		Company myCompany2 = Company.getInstance();
		
		// 두 변수가 같은 주소인지 확인
		System.out.println(myCompany1 == myCompany2);
	}
}

  • myCompany1과 myCompany2를 비교해 보면 같은 참조 값을 가지는 동일한 인스턴스 임을 알 수 있습니다.
  • Company 클래스는 내부에 생성된 유일한 인스턴스 외에는 더 이상 인스턴스를 생성할 수 없습니다.
  • static을 사용하여 유일한 객체를 생성하는 싱글톤 패턴을 구현할 수 있습니다.
profile
Nil Desperandum <절대 절망하지 마라>

0개의 댓글