Chap 06. Java 클래스와 객체 (2) [Do it! 자바 프로그래밍 입문]

doriskim·2022년 11월 25일
0

*본 내용은 [Do it! 자바 프로그래밍 입문] 책과 강의를 보고 공부하면서 요점 정리한 내용입니다.

◼ this 예약어

✔ this가 하는 일

(this == 자신의 메모리의 주소)

  1. 자신의 메모리를 가리킴
  2. 생성자에서 다른 생성자를 호출
  3. 자신의 주소를 반환 함

✔ 1. 자신의 메모리를 가리킴

ThisExample.Java

package thisex;

class Birthday{
	int day;
	int month;
	int year;
	
	public void setYear(int year) {	//8번줄
//		year = year; 	//9번줄
// 코드는 가장 가까운 곳을 참조한다. 위 코드에서 left value는 8줄의 year 참조, right value는 9줄의 left value 참조

		this.year = year; //따라서 다음과 같이 this를 이용해 코딩
	}
	
	public void printThis() { //this 출력
		System.out.println(this);
	}
}

public class ThisExample {
	
	public static void main(String[] args) {
		Birthday b1 = new Birthday();
		Birthday b2 = new Birthday();
		
		System.out.println(b1);
		b1.printThis(); 

		System.out.println(b2);
		b2.printThis(); 
	
	}
	
}

콘솔창 결과
b1을 출력한 값과 b1을 printThis()한 값은 같지만 b1과 b2의 this는 서로 값이 다르다.
같은 this라도 인스턴스마다 매번 가리키는 값이 달라진다.

thisex.Birthday@1d81eb93
thisex.Birthday@1d81eb93
thisex.Birthday@7291c18f
thisex.Birthday@7291c18f

✔ 2. 생성자에서 다른 생성자를 호출

CallAnotherConst.java

package thisex;

class Person{

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

public class CallAnotherConst {
	
	public static void main(String[] args) {
		
		Person p1 = new Person();
		System.out.println(p1.name);
		
	}
	
}

콘솔창 결과

이름없음

※주의
생성자에서 다른 생성자 부르는 this 사용할 때는 앞에 어떤 문장도 올 수 없다.
따라서 다음과 같이 this("이름없음", 1); 앞에 int i = 0;를 작성하면 오류가 발생한다.
생성자에서 다른 생성자를 호출할 때 this를 포함한 문장은 가장 먼저 나오는 문장이어야한다.

	public Person() {
		int i = 0; //오류 발생
		this("이름없음", 1);
	}

✔ 3. 자신의 주소를 반환 함

package thisex;

class Person{
	String name;
	int age;
	
	public Person() {
		this("이름없음", 1);
	}
	
	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}
	
	public Person returnSelf() {
		return this;
	}
	
}

public class CallAnotherConst {
	
	public static void main(String[] args) {
		
		Person p1 = new Person();
		System.out.println(p1.returnSelf()); //this 이용해 자신의 주소 출력
		
	}
	
}

콘솔창 결과

thisex.Person@1d81eb93

◼ 객체 간의 협력

✔ 학생, 버스, 지하철 실습

학생이 버스, 지하철을 이용하면 학생의 돈이 줄어들고 버스, 지하철의 수입은 늘어나게 만들고자 한다.
Student.java

package cooperation;

public class Student {
	String studentName;
	int grade;
	int money;
	
	public Student(String studentName, int money) {
		this.studentName = studentName;
		this.money = money;
	}
	
	public void takeBus(Bus bus) { //객체의 협업 (버스~학생)
		bus.take(1000);
		money -= 1000;
	}
	
	public void takeSubway(Subway subway) { //객체의 협업 (지하철~학생)
		subway.take(1500);
		money -= 1500;
	}
	
	public void showInfo() {
		System.out.println(studentName + "님의 남은 돈은" + money + "입니다.");
	}
}

Bus.java

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

Subway.java

package cooperation;

public class Subway {
	
	int lineNumber;
	int passengerCount;
	int money;
	
	public Subway(int lineNumber) {
		this.lineNumber = lineNumber;
	}
	
	public void take(int money) { //승객을 한 명 태웠을 때
		this.money += money;
		passengerCount++;
	}
	
	public void showInfo() {
		System.out.println("지하철" + lineNumber + "번의 승객은" + passengerCount + 
				"명이고, 수입은" + money + "입니다");
	}
}

TakeTrans.java

package cooperation;

public class TakeTrans {

	public static void main(String[] args) {

		Student james = new Student("James", 5000);
		Student tomas = new Student("Tomas", 10000);
		
		Bus bus100 = new Bus(100);
		james.takeBus(bus100);
		james.showInfo();
		bus100.showInfo();
		
		Subway subwayGreen = new Subway(2);
		tomas.takeSubway(subwayGreen);
		tomas.showInfo();
		subwayGreen.showInfo();
		
	}

}

콘솔창 결과

James님의 남은 돈은4000입니다.
버스100번의 승객은1명이고, 수입은1000입니다
Tomas님의 남은 돈은8500입니다.
지하철2번의 승객은1명이고, 수입은1500입니다

◼ static 변수

✔ static 변수의 정의와 사용 방법

  • 여러 개의 인스턴스가 같은 메모리의 값을 공유하기 위해 static 변수를 사용한다.

  • static 변수 사용 방법
static예약어 자료형 변수이름;

//예시
static int serialNum;

✔ static 변수 (클래스 변수)

static int serialNum = 100; //serailNum이 static 변수
  • static 변수는 클래스 변수라고도 한다.

  • static 변수는 인스턴스가 생성될 때마다 다른 메모리를 가지는 것이 아니라 프로그램이 메모리에 적재(load) 될 때 데이터 영역의 메모리에 생성 된다. 따라서 인스턴스의 생성과 관계없이 클래스 이름으로 직접 참조 한다.

✔ static 메서드 (클래스 메서드)

Student.getSerialNum(); //getSerialNum()이 static 메서드
  • static 메서드는 클래스 메서드라고도 한다.

  • 메서드에 static 키워드를 사용하여 구현한다. 주로 static 변수를 위한 기능을 제공한다.

  • static 메서드도 인스턴스의 생성과 관계 없이 클래스 이름으로 직접 메서드를 호출한다.

  • static 메서드에서 인스턴스 변수를 사용할 수 없다.
    인스턴스의 변수의 경우 꼭 인스턴스가 먼저 생성되어야 하므로 static 메서드에서는 생성이 불확실한 인스턴스 변수를 사용할 수 없다.

✔ 학번 생성하기 실습

학생이 생성될 때마다 학번이 증가해야 하는 경우를 코드로 작성하고자 한다.
Student studentJ = new Student();할 때마다 자동으로 1씩 추가된 studentID값이 들어가게 만들고자 한다.
기준이 되는 값은 static 변수로 생성하여 유지한다.
각 학생이 생성될 때마다 static 변수 값을 복사해 와서 하나 증가 시킨 값을 생성된 인스턴스의 학번 변수에 저장해 준다.

Student.java

package staticex;

public class Student {
	
	static int serialNum = 10000; //static 변수
	
	int studentID;
	String studentName;
	
	public Student() {
		serialNum++;
		studentID = serialNum;
	}

	public static int getSerialNum() { //static 메서드
		return serialNum; 
	}
    
    public static void setSerialNum(int serialNum) { //static 메서드
		Student.serialNum = serialNum;
	}
}
  • 주의) static 메서드에서 인스턴스 변수를 사용할 수 없고 지역변수는 사용 가능하다.
    생성되는 시기가 다르기 때문이다. 지역변수는 메서드가 호출될 때 생성되고 메서드가 끝나면 없어진다.(스택)
    하지만 인스턴스 변수는 new를 이용해 생성 된다.
    System.out.println(Student.getSerialNum()); 인스턴스 생성과 상관 없이 다음 코드는 사용 가능하다. 즉 인스턴스가 만들어지지도 않은 채로 다음과 같이 "홍길동"값을 입력하고자 하면 오류가 발생할 수 있다.
	public static int getSerialNum() {
    
    	//[정상 작동]
		int i = 10;	//지역변수
        i++; 
        System.out.println(i);
		
        //[오류 발생]
		studentName = "홍길동"; //인스턴스 변수, 멤버 변수

		return serialNum; //static 변수, 클래스 변수
	}

StudentTest1.java

  • static 변수와 static 메서드는 인스턴스의 생성과 관계없이 클래스 이름으로 직접 참조하는 것을 지향해야한다. 따라서 studentJ.serialNum studentT.serialNum 보단 Student.serialNum으로 작성해야 하고, studentJ.getSerialNum() studentT.getSerialNum() 보단 Student.getSerialNum()으로 작성해야 한다.
package staticex;

public class StudentTest1 {

	public static void main(String[] args) {
    
		Student studentJ = new Student();
		System.out.println(studentJ.studentID);
		
		Student studentT = new Student();
		System.out.println(studentT.studentID);
		
		System.out.println(Student.serialNum);		//static 변수 출력
		System.out.println(Student.getSerialNum());	//static 메서드 활용해 static 변수 출력
	}

}

콘솔창 결과

10001
10002
10002
10002

+참고) static 변수 강화
static 변수에 private를 이용하고, get/set 함수 중 set 함수를 없애 참조만 할 수 있게 만들면 중요한 static 변수 값을 외부에서 변경하지 못하게 할 수 있다. (인스턴스 만들어질 때만 변경시킬 수 있음)
Student.java

package staticex;

public class Student {
	
	private static int serialNum = 10000; //private 이용
	
	int studentID;
	String studentName;
	
	public Student() {
		serialNum++;
		studentID = serialNum;
	}

	public static int getSerialNum() { 
		return serialNum; 
	}
    
    //가져갈수만 있게 값 변경 못하게
    public static void setSerialNum(int serialNum) {
		Student.serialNum = serialNum;
	}
}

✔ 변수의 유효 범위

변수 유형선언 위치사용 범위메모리생성과 소멸
지역 변수
(로컬 변수)
함수 내부에 선언함수 내부에서만 사용스택함수가 호출될 때 생성되고
함수가 끝나면 소멸함
멤버 변수
(인스턴스 변수)
new 예약어를 사용하여 클래스 멤버 변수로 선언클래스 내부에서 사용하고
private이 아니면 참조 변수로 다른 클래스에서 사용 가능
인스턴스가 생성될 때 힙에 생성되고
가비지 컬렉터가 메모리를 수거할 때 소멸됨
static 변수
(클래스 변수)
static 예약어를 사용하여 클래스 내부에 선언클래스 내부에서 사용하고
private이 아니면 클래스 이름으로 다른 클래스에서 사용 가능
데이터 영역프로그램이 처음 시작할 때 상수와 함께 데이터 영역에 생성되고
프로그램이 끝나고 메모리를 해제할 때 소멸됨

*데이터 영역 - static, literal 등이 생성되는 영역

*static 변수는 프로그램이 사용하는 메모리가 한정되어 있으므로 100크기의 배열과 같이 너무 큰 메모리를 선언하는 것을 지양해야 한다.

◼ static 응용: 싱글톤 패턴

✔ 싱글톤(singleton) 패턴

프로그램을 구현하면서 하나의 인스턴스(객체)만 필요한 경우 싱글톤 패턴을 이용하는데,
객체 지향 프로그램에서 인스턴스를 단 하나만 생성하는 디자인 패턴을 싱글톤 패턴이라고 한다.

하나의 객체만 필요한 경우 C언어는 글로벌 변수를 선언하면 되지만 Java는 클래스 외부에 변수를 선언할 수 없다. 어떻게 해야할까? 회사 클래스를 구현하면서 알아보자

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

단 하나 있어야 하는 회사를 구현해보자

[클래스 다이어그램]

※ 클래스 다이어그램 읽는 방법

  • 클래스 이름
  • 멤버변수
  • 생성자, 메서드

Company.java

package singleton;

public class Company {
	//클래스 내부에서 Company를 생성한다. 전체에서 유일하게 사용 될 인스턴스이다.
	//함부로 값을 바꿀수 없으므로 private, 단 하나이므로 static을 이용한다.
	private static Company instance = new Company(); 
	
    //private로 외부에서 함부로 Company를 생성하지 못하게 한다.
	private Company(){} 

	//생성된 인스턴스는 외부에서 가져다 사용할 수 있게 한다.
    //객체를 생성하지 않고 메서드를 부르기 위해 static 메서드를 이용한다. 
	public static Company getInstance() {
		return instance;
	} 
}

CompanyTest.java

package singleton;

import java.util.Calendar;

public class CompanyTest {

	public static void main(String[] args) {
		
		Company c1 = Company.getInstance();
		Company c2 = Company.getInstance();
		
        //동일한 주소값 출력된다.
		System.out.println(c1);
		System.out.println(c2);

		//Company c3 = new Company(); //error
		//System.out.println(c1 == c2); //true
	}

}

콘솔 창

singleton.Company@1d81eb93 //동일한 주소값
singleton.Company@1d81eb93

※ 안전장치를 위해 getInstance()를 다음과 같이 작성하기도 한다.

	public static Company getInstance() {
		if(instance == null)
			instance = new Company();
		return instance;
	} 

※ 날짜도 싱글톤 패턴으로 구현되어 있어 다음과 같이 사용한다.

		Calendar cal = Calendar.getInstance();

0개의 댓글