Don't Panic
in Java ☕ 5분 읽기

[Java] java 캡슐화, 싱글톤,this

목차

11. 캡슐화 (encapsulation)

정보 은닉을 활용한 캡슐화

  • 꼭 필요한 정보와 기능만 외부에 오픈한다.
  • 대부분의 멤버 변수와 메서드를 감추고, 외부에는 통합된 인터페이스만 제공하여 일관된 기능을 구현하게 한다.
  • 각각의 메서드나 멤버 변수에 접근함으로써 발생하는 오류를 최소화한다.
  • 정보 은닉에서 한 단계 더 추상화하여, 외부에는 꼭 필요한 한두 개만 오픈하고 나머지는 자동으로 처리되도록 하는 것이다.

레포트 만들기 예제

public class MakeReport {

	StringBuffer buffer = new StringBuffer();

	private String line = "===========================================\n";
	private String title = "  이름\t   주소 \t\t  전화번호  \n";
	private void makeHeader()
	{
		buffer.append(line);
		buffer.append(title);
		buffer.append(line);
	}

	private void generateBody()
	{
		buffer.append("James \t");
		buffer.append("Seoul Korea \t");
		buffer.append("010-2222-3333\n");

		buffer.append("Tomas \t");
		buffer.append("NewYork US \t");
		buffer.append("010-7777-0987\n");
	}

	private void makeFooter()
	{

		buffer.append(line);
	}

	public String getReport()
	{
		makeHeader();
		generateBody();
		makeFooter();
		return buffer.toString();
	}
}
public class TestReprt {

	public static void main(String[] args) {

		MakeReport report = new MakeReport();
		String builder = report.getReport();

		System.out.println(builder);
	}

}

레포트 출력 결과


12. 객체 자신을 가리키는 this

this가 하는 일

  • 인스턴스 자신의 메모리를 가리킨다.
  • 생성자에서 또 다른 생성자를 호출할 때 사용한다.
  • 자신의 주소(참조값)를 반환한다.

생성된 인스턴스 메모리의 주소를 가짐

  • 클래스 내에서 참조 변수가 가지는 주소 값과 동일한 주소 값을 가지는 키워드다.

this가 가리키는 인스턴스 주소

public void setYear(int year)
{
    this.year = year;
}

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

  • 클래스에 생성자가 여러 개인 경우, this를 이용하여 한 생성자에서 다른 생성자를 호출할 수 있다.
  • 생성자에서 다른 생성자를 호출하는 경우, 인스턴스의 생성이 완전하지 않은 상태이므로 this() 문장 이전에 다른 문장을 쓸 수 없다.
public class Person {

	String name;
	int age;

	public Person() {
		this("이름없음", 1);
	}

	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}
}

자신의 주소를 반환하는 this

public class Person {

	String name;
	int age;

	public Person() {
		this("이름없음", 1);
	}

	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public Person getPerson() {
		return this;
	}


	public static void main(String[] args)
	{
		Person p = new Person();
		p.name = "James";
		p.age = 37;

		Person p2 = p.getPerson();
		System.out.println(p);
		System.out.println(p2);
	}
}

this 반환 결과 - 두 참조가 같은 주소


13. 객체 간의 협력 (collaboration)

객체 지향 프로그래밍에서의 협력

  • 객체 지향 프로그램에서 객체 간에는 협력이 이루어진다.
  • 협력을 위해서는 필요한 메시지를 전송하고, 이를 처리하는 기능이 구현되어야 한다.
  • 매개 변수로 객체가 전달되는 경우가 발생한다.

객체 협력의 예:

객체 간 협력의 예


14. 버스 타고 학교 가는 학생의 과정을 객체 지향 프로그래밍으로 구현해보기

버스와 지하철을 타는 예제 프로그래밍

James와 Tomas는 각각 버스와 지하철을 타고 학교에 갑니다.
James는 5000원을 가지고 있었고, 100번 버스를 타면서 1000원을 지불합니다.
Tomas는 10000원을 가지고 있었고, 초록색 지하철을 타면서 1200원을 지불합니다.

두 학생이 버스와 지하철을 타는 상황을 구현해 봅시다.

Student.java

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);
		this.money -= 1000;
	}

	public void takeSubway(Subway subway) {
		subway.take(1200);
		this.money -= 1200;
	}

	public void showInfo() {
		System.out.println(studentName +"님의 남은 돈은 " + money + "원 입니다");
	}
}

Bus.java

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 showBusInfo() {
		System.out.println(busNumber + "번 버스의 승객은 " + passengerCount + "명 이고, 수입은 " + money + "원 입니다");
	}
}

Subway.java

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 showSubwayInfo() {
		System.out.println(lineNumber + "번 지하철의 승객은 " + passengerCount + "명 이고, 수입은 " + money + "원 입니다");
	}
}

TakeTransTest.java

public class TakeTransTest {

	public static void main(String[] args) {
		Student studentJ = new Student("James", 5000);
		Student studentT = new Student("Tomas", 10000);

		Bus bus100 = new Bus(100);

		Subway subwayGreen = new Subway(2);


		studentJ.takeBus(bus100);
		studentT.takeSubway(subwayGreen);

		studentJ.showInfo();
		studentT.showInfo();

		bus100.showBusInfo();

		subwayGreen.showSubwayInfo();
	}

}

객체 협력 예제 실행 결과


15. 복습해봅시다 (객체 협력)

다음과 같은 상황을 구현해 봅시다

앞의 예제에서 Edward는 지각을 해서 택시를 타야 했습니다.
20000원을 가지고 있었는데 10000원을 택시비로 사용했습니다.
택시는 '잘나간다 운수' 회사 택시를 탔습니다.

출력 결과:

택시 예제 출력 결과


16. 여러 인스턴스에서 공통으로 사용하는 변수를 선언하자 - static 변수

공통으로 사용하는 변수가 필요한 경우

  • 여러 인스턴스가 공유하는 기준 값이 필요한 경우
  • 학생마다 새로운 학번 생성
  • 카드회사에서 카드를 새로 발급할 때마다 새로운 카드 번호를 부여
  • 회사에 사원이 입사할 때마다 새로운 사번이 필요한 경우

static 변수의 공유 개념

static 변수 선언과 사용하기

static int serialNum;
  • 인스턴스가 생성될 때 만들어지는 변수가 아니라, 프로그램이 처음 메모리에 로딩될 때 메모리를 할당받는다.
  • 클래스 변수, 정적 변수라고도 한다. (vs. 인스턴스 변수)
  • 인스턴스 생성과 상관없이 사용 가능하므로 클래스 이름으로 직접 참조한다.
Student.serialNum = 100;

static 변수 테스트하기

Employee.java

public class Employee {

	public static int serialNum = 1000;

	private int employeeId;
	private String employeeName;
	private String department;

	public int getEmployeeId() {
		return employeeId;
	}
	public void setEmployeeId(int employeeId) {
		this.employeeId = employeeId;
	}
	public String getEmployeeName() {
		return employeeName;
	}
	public void setEmployeeName(String employeeName) {
		this.employeeName = employeeName;
	}
	public String getDepartment() {
		return department;
	}
	public void setDepartment(String department) {
		this.department = department;
	}

}

EmployeeTest.java

public class EmployeeTest {

	public static void main(String[] args) {
		Employee employeeLee = new Employee();
		employeeLee.setEmployeeName("이순신");
		System.out.println(employeeLee.serialNum);


		Employee employeeKim = new Employee();
		employeeKim.setEmployeeName("김유신");
		employeeKim.serialNum++;

		System.out.println(employeeLee.serialNum);
		System.out.println(employeeKim.serialNum);

	}
}

System.out.println(employeeLee.serialNum);에서 employeeLee는 인스턴스이지만 serialNum은 이미 메모리에 올라와 있는 static 변수이므로, Employee.serialNum만 써도 출력 및 사용이 가능하다.

static 변수 출력 결과

  • static 변수는 여러 인스턴스가 공통으로 사용하는 영역임을 알 수 있다.

static 변수의 메모리 영역

회사원이 입사할 때마다 새로운 사번 부여하기

Employee.java 생성자 구현

...

	public Employee()
	{
		serialNum++;
		employeeId = serialNum;
	}

...

EmployeeTest.java

public class EmployeeTest {

	public static void main(String[] args) {
		Employee employeeLee = new Employee();
		employeeLee.setEmployeeName("이순신");

		Employee employeeKim = new Employee();
		employeeKim.setEmployeeName("김유신");

		System.out.println(employeeLee.getEmployeeName() + "," + employeeLee.getEmployeeId());
		System.out.println(employeeKim.getEmployeeName() + "," + employeeKim.getEmployeeId());
	}
}

사번 자동 부여 실행 결과

static 변수와 메서드는 클래스 이름으로 직접 참조

static 변수와 메서드는 인스턴스 변수·메서드가 아니므로 클래스 이름으로 직접 참조한다.

System.out.println(Employee.serialNum);

17. static 메서드의 구현과 활용, 변수의 유효 범위

static 메서드 만들기

serialNum 변수를 private으로 선언하고 getter/setter를 구현한다.

Employee.java

private static int serialNum = 1000;

 ...
public static int getSerialNum() {
	return serialNum;
}

public static void setSerialNum(int serialNum) {
	Employee.serialNum = serialNum;
}

클래스 이름으로 호출 가능하다. (클래스 메서드, 정적 메서드)

System.out.println(Employee.getSerialNum());

static 메서드(클래스 메서드)에서는 인스턴스 변수를 사용할 수 없다

  • static 메서드는 인스턴스 생성과 무관하게 클래스 이름으로 호출될 수 있다.
  • 인스턴스 생성 전에 호출될 수 있으므로, static 메서드 내부에서는 인스턴스 변수를 사용할 수 없다.

Employee.java

public static void setSerialNum(int serialNum) {
		int i = 0;

		employeeName = "Lee";  //오류발생
		Employee.serialNum = serialNum;
	}

EmployeeTest2.java

public class EmployeeTest2 {

	public static void main(String[] args) {

		System.out.println(Employee.getSerialNum());
		Employee.setSerialNum(1003);
		System.out.println(Employee.getSerialNum());
	}
}

변수의 유효 범위와 메모리

  • 변수의 유효 범위(scope)와 생성·소멸(life cycle)은 각 변수의 종류마다 다르다.
  • 지역 변수, 멤버 변수, 클래스 변수는 유효 범위와 life cycle, 사용하는 메모리가 모두 다르다.

변수의 종류별 유효 범위와 메모리

  • static 변수는 프로그램이 메모리에 있는 동안 계속 그 영역을 차지하므로, 너무 큰 메모리를 할당하는 것은 좋지 않다.
  • 클래스 내부의 여러 메서드에서 사용하는 변수는 멤버 변수로 선언하는 것이 좋다.
  • 멤버 변수가 너무 많으면 인스턴스 생성 시 쓸데없는 메모리가 할당된다.
  • 상황에 적절하게 변수를 사용해야 한다.

18. static 응용 - 싱글톤 패턴(singleton pattern)

싱글톤 패턴이란?

  • 프로그램에서 인스턴스가 단 한 개만 생성되어야 하는 경우 사용하는 디자인 패턴이다.
  • static 변수, 메서드를 활용하여 구현할 수 있다.

싱글톤 패턴으로 회사 객체 구현하기

생성자는 private으로 선언한다.

private Company() {}

클래스 내부에 유일한 private 인스턴스를 생성한다.

private static Company instance = new Company();

외부에서 유일한 인스턴스를 참조할 수 있는 public 메서드를 제공한다.

public static Company getInstance() {

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

}

CompanyTest.java

public class CompanyTest {

	public static void main(String[] args) {
		Company company1 = Company.getInstance();

		Company company2 = Company.getInstance();

		System.out.println(company1);
		System.out.println(company2);

		//Calendar calendar = Calendar.getInstance();
	}
}

싱글톤 패턴 실행 결과 - 같은 인스턴스 참조


19. 복습해봅시다 (static과 싱글톤 패턴)

설명에 따른 객체를 구현하여 테스트 코드가 실행되도록 구현하기

자동차 공장이 있습니다. 자동차 공장은 유일한 객체이고, 이 공장에서 생산되는 자동차는 제작될 때마다 고유의 번호가 부여됩니다.
자동차 번호가 10001부터 시작되어 자동차가 생산될 때마다 10002, 10003 이렇게 번호가 붙도록 자동차 공장 클래스, 자동차 클래스를 구현하세요
다음 CarFactoryTest.java 테스트 코드가 수행 되도록 합니다.

CarFactoryTest.java

public class CarFactoryTest {

	public static void main(String[] args) {
		CarFactory factory = CarFactory.getInstance();
		Car mySonata = factory.createCar();
		Car yourSonata = factory.createCar();

		System.out.println(mySonata.getCarNum());     //10001 출력
		System.out.println(yourSonata.getCarNum());   //10002 출력
	}
}