본문 바로가기
객체 지향 프로그래밍/Java

chapter 6. 클래스와 객체2

by xangmin 2022. 3. 12.
반응형

6-1. this 예약어

자신의 메모리를 가리키는 예약어

this는 간단히 설명하면 생성된 인스턴스 스스로 가리키는 예약어이다. 이 외에도 다른 역할로 this를 사용할 수 있다.

 

생년월일을 의미하는 BirthDay 클래스를 만들고, this가 실제로 어던 의미인지 알기 위해 this를 출력하는 메서드를 추가해보자.

package thisex;

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


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

출력화면

thisex.BirthDay@4517d9a3
thisex.BirthDay@4517d9a3

* 프로그램 구성이 다른 점에 주의하세요!

이번에는 한 파일에 클래스가 두개 존재하는 것을 알 수 있다. BirthDay 클래스는 객체를 코드로 만든 클래스이고, ThisExample 클래스는 this 출력 값의 의미를 알기 위해 main() 함수를 포함하여 만든 테스트용 클래스이다. 하나의 자바 파일에 하나의 클래스가 있는 경우가 대부분이지만, 이번 예제처럼 하나의 파일에 클래스가 여러 개 존재할 수도 있다. 이때 public 클래스는 하나뿐이며 public 클래스와 자바 파일 이름은 같아야 한다.

 

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

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

 

this로 다른 생성자 호출하기

package thisex;

class Person {
	String name;
    int age;
    
    Person(){
    	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);
        System.out.println(noName.name);
    }
}

Person 클래스에는 Person() 디폴트 생성자와 매개변수를 가지는 Person(String, int) 생성자가 있다. 클래스가 생성될 때 Person(String, int)가 호출되어 이름과 나이를 전달받고, PErson() 디폴트 생성자가 호출되는 경우에는 초깃값으로 "이름없음" 과 1 값을 대입하고자 한다. 물론 디폴트 생성자 코드안에 직접 써도 되지만, 이미 다른 생성자에 이 코드가작성되어 있으므로 8행처럼 this를 활용하여 다른 생성자를 호출할 수 있다.

 

그런데 this로 다른 생성자를 호출할 때는 주의할 점이 있다. this를 사용하여 생성자를 호출하는 코드 이전에 다른 코드를 넣을 수 없다. 만약 다른 코드를 넣으면 다음과 같은 오류 메세지가 나타난다.

Person(){
	this.name = "noname";
    this("이름없음", 1);		// Person(String, int) 생성자 호출
}

 

6-2. 객체 간 협력

학생 클래스 구현하기

학생 클래스는 '이름', '학년', '가진 돈'을 멤버 변수로 가진다. 그리고 '버스를 탄다', '지하철을 탄다', '학생의 현재 정보를 보여준다'를 메서드로 가진다.

package cooperation;

public class Student {
	public String studentName;		//학생이름	
	public int grade;			//학년
	public int money;			//학생이 가지고 있는 돈
	
	public Student(String studentName, int money) {	//학생 이름과 가진 돈을 매개변수로 받는 생성자
		this.studentName = studentName;
		this.money = money;
	}
	
	public void takeBus(Bus bus) {	//학생이 버스를 타면 1,000원을 지불하는 기능을 구현한 메서드
		bus.take(1000);
		this.money -= 1000;
	}
	
	public void  takeSubway(Subwavy subway) { //학생이 지하철을 타면 1,500원을 지불하는 기능을 구현한 메서드
		subway.take(1500);
		this.money -= 1500;
	}
	
	public void showInfo() { // 학생의 현재 정보를 출력하는 메서드
		System.out.println(studentName + "님의 남은 돈은" + money + "입니다.");
	}
}

 

버스 클래스 구현하기

package cooperation;

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 + "입니다.");
	}
}

 

지하철 클래스 구현하기

package cooperation;

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 + "입니다.");
	}
}

 

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

버스와 지하철 타기

package cooperation;

public class TakeTrans {
	
	public static void main(String[] args) {
		Student studentJames = new Student("James", 5000);
		Student studentTomas = new Student("Tomas", 5000);
		
		Bus bus100 = new Bus(100);
		studentJames.takeBus(bus100);
		studentJames.showInfo();
		bus100.showInfo();
		
		Subway subwayGreen = new Subway("2호선");
		studentTomas.takeSubway(subwayGreen);
		studentTomas.showInfo();
		subwayGreen.showInfo();		
	}
}

실행결과

James님의 남은 돈은4000입니다.
버스100번의 승객은1명이고, 수입은 1000입니다.
Tomas님의 남은 돈은3500입니다.
2호선의 승객은 1명이고, 수입은 1500입니다.

 

6-3. static 변수

변수를 여러 클래스에서 공통으로 사용하려면?

public class Student{
	public int studentID;
    public String studentName;
    public int grade;
    public String addrss;
}

학생 클래스를 사용하면 여러 학생의 인스턴스를 만들 수 있습니다. 학생마다 고유한 학번을 가지는데, 학생이 입학하면 학번이 자동으로 생성되도록 만들고자 한다. 생성된 인스턴스는 학번을 순서대로 가져가야한다. 이때 어떻게 학생에게 학번을 부여할 수 있을까? 이 경우에 각 인스턴스마다 따로 생성되는 변수가 아닌, 클래스 전반에서 공통으로 사용할 수 있는 기준 변수가 있어야 한다. 그리고 학생이 생성될 때마다, 기준 변수 값을 하나씩 증가시켜 각 학생 인스턴스의 학번 변수에 대입해 주면 된다. 이 때 클래승서 공통으로 사용하는 변수를 'static 변수'로 선언한다.

 

static 변수의 정의와 사용 방법

static 변수란 다른 용어로 '정적 변수'라고 한다.

static int serialNum;	// [예약어] [자료형] [변수이름]

static 변수는 클래스 내부에 선언하지만 다른 멤버 변수처럼 인스턴스가 생성될 때마다 새로 생성되는 변수가 아니다. static 변수는 프로그램이 실행되어 메모리에 올라갔을 때 딱 한 번 메모리 공간이 할당된다. 그리고 그 값은 모든 인스턴스가 공유한다.

 

static 변수 사용하기

package staticex;

public class Student {
	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;
	}	
}

 

static 변수 테스트하기

package staticex;

public class StudentTest1 {

	public static void main(String[] args) {
		Student studentLee = new Student();
		studentLee.setStudentName("이지원");;
		System.out.println(studentLee.serialNum);
		studentLee.serialNum++;
		
		Student studentSon = new Student();
		studentSon.setStudentName("손수경");
		System.out.println(studentSon.serialNum);
		System.out.println(studentLee.serialNum);
	}
}

실행결과

1000
1001
1001

 

학번 생성하기

package staticex;

public class Student1 {
	public static int serialNum = 1000;
	public int studentID;
	public String studentName;
	public int grade;
	public String address;
	
	public Student1() {
		serialNum++;		//학생이 생성될 때마다 증가
		studentID = serialNum;	//증가된 값을 학번 인스턴스 변수에 부여
	}
	
	public String getStudentName() {
		return studentName;
	}
	
	public void setStudentName(String name) {
		studentName = name;
	}
}

 

학번 확인하기

package staticex;

public class StudentTest2 {

	public static void main(String[] args) {
		Student1 studentLee = new Student1();
		studentLee.setStudentName("이지원");
		System.out.println(studentLee.serialNum);
		System.out.println(studentLee.studentName + " 학번:" + studentLee.studentID);
		
		Student1 studentSon = new Student1();
		studentSon.setStudentName("손수경");
		System.out.println(studentSon.serialNum);
		System.out.println(studentSon.studentName + " 학번:" + studentSon.studentID);		
	}
}

실행결과

1001
이지원 학번:1001
1002
손수경 학번:1002

 

클래스 변수

클래스 이름으로 static 변수 참조하기

package staticex;

public class StudentTest3 {

	public static void main(String[] args) {
		Student1 studentLee = new Student1();
		studentLee.setStudentName("이지원");
		System.out.println(Student1.serialNum);	// serialNum 변수를 직접 클래스 이름으로 참조
		System.out.println(studentLee.studentName + "학번:" + studentLee.studentID);
		
		Student1 studentSon = new Student1();
		studentSon.setStudentName("손수경");
		System.out.println(Student1.serialNum);	// serialNum 변수를 직접 클래스 이름으로 참조
		System.out.println(studentSon.studentName + "학번:" + studentSon.studentID);
	}
}

실행결과

1001
이지원 학번:1001
1002
손수경 학번:1002

*static 변수는 클래스 이름으로 직접 참조한다.

StudenTest2 에서처럼 static 변수를 studentLee.serialNum과 같이 사용하면 serialNum 아래쪽에 노란색 줄이 보인다. 이것은 오류가 아니고 이 변수가 static 변수이므로 인스턴스 참조 변수가 아닌 클래스 이름으로 직접 참조하라는 것이다. StudentTest3 처럼 클래스 이름으로 직접 참조하면 노란색 줄이 사라질 것이다.

 

클래스 메서드

serialNum의 get(), set() 메서드 사용하기

package staticex;

public class Student2 {
	private static int serialNum = 1000;	//private 변수로 변경
	public int studentID;
	public String studentName;
	public int grade;
	public String address;
	
	public Student2() {
		serialNum++;
		studentID = serialNum;
	}
	
	public String getStudentName() {
		return studentName;
	}
	
	public void setStudentName(String name) {
		studentName = name;
	}
	
	public static int getSerialNum() {
		int i = 10;
		return serialNum;
	}
	
	public static void setSerialNum(int serialNum) {
		Student2.serialNum = serialNum;
	}
}

학번 출력하기

package staticex;

public class StudentTest4 {

	public static void main(String[] args) {
		Student2 studentLee = new Student2();
		studentLee.setStudentName("이지원");
		System.out.println(Student2.getSerialNum());
		System.out.println(studentLee.studentName + "학번:" + studentLee.studentID);
		
		Student2 studentSon = new Student2();
		studentSon.setStudentName("손수경");
		System.out.println(Student2.getSerialNum());
		System.out.println(studentSon.studentName + "학번:" + studentSon.studentID);
	}

}

 

6-4. 변수 유효 범위

변수 유효 범위란?

지금까지 세 가지 종류의 변수를 배웠다. 첫 번째는 함수나 메서드 안에서만 사용할 수 있는 지역변수, 클래스 안에서 사용할 수 있는 멤버 변수, 그리고 여러 인스턴스에서 공통으로 사용할 수 있는 static 변수이다.

 

지역 변수의 유효 범위

지역 변수는 함수나 메서드 내부에 선언하기 때문에 함수 밖에서는 사용할 수 없다. 즉 하나의 함수에 선언한 지역 변수는 다른 함수에서 사용할 수 없다.

 

멤버 변수의 유효 범위

멤버 변수는 인스턴스 변수라고도 한다. 클래스가 생성될 때 힙(heap) 메모리에 생성되는 변수이다. 멤버 변수는 클래스의 어느 메서드에서나 사용할 수 있다. 

 

static 변수의 유효 범위

사용자가 프로그램을 실행하면 메모리에 프로그램이 상주한다. 이때 프로그램 영역 중에 데이터 영역이 있다. 이 영역에는 상숨나 문자열, static 변수가 생성된다. 

 

6-5. static 응용 - 싱글톤 패턴

싱글톤 패턴이란?

프로그램을 구현하다가 보면 여러 개의 인스턴스가 필요한 경우도 있고 단 하나의 인스턴스만 필요한 경우도 있다. 객체 지향 프로그램에서 인스턴스를 단 하나만 생성하는 디자인 패턴을 싱글톤 패턴(sigleton pattern)이라고 한다. 싱글톤 패턴은 static을 응용하여 프로그램 전반에서 사용하는 인스턴스를 하나만 구현하는 방식이다. 

 

 

 

 

 

 

반응형

댓글