국비교육(22-23)

14일차(4)/java(10) : Constructor

서리/Seori 2022. 10. 26. 00:09

14일차(4)/java(10) : Constructor(생성자)

 

*자바의정석 3판 291p~ 참조.

 

[ 생성자 ]
1. 클래스명과 동일하다
2. 메소드 모양과 유사하지만 리턴 type이 없다.
3. 객체를 생성할 때(new할때) 호출된다.
4. 객체를 생성하는 시점에 무언가 준비 작업을 할 때 유용하다.
5. 생성자를 명시적으로 정의하지 않아도 기본 생성자는 있다고 간주된다.

- 더해서, 변수의 초기화에 사용되는 메소드이다.

 

 

<Student>

package test.mypac;

/*
 * [ 생성자 ]
 * 
 * 1. 클래스명과 동일하다
 * 2. 메소드 모양과 유사하지만 리턴type이 없다.
 * 3. 객체를 생성할 때(new할때) 호출된다.
 * 4. 객체를 생성하는 시점에 무언가 준비 작업을 할 때 유용하다.
 * 5. 생성자를 명시적으로 정의하지 않아도 기본 생성자는 있다고 간주된다.
 */
public class Student {
	public Student() {
		System.out.println("Student 클래스의 생성자 호출됨");
	}
	public void study() {
		System.out.println("공부해요!");
	}
}

 

<MainClass01>

package test.main;

import test.mypac.Student;

public class MainClass01 {
	public static void main(String[] args) {
		//student 클래스의 기본 생성자를 호출해서 객체를 생성하고 참조값을 s1이라는 이름의 지역변수에 담는다.
		Student s1=new Student();
		s1.study();
	}
}

 

 

 

 

[기본 생성자의 모양]

public class Student {
	public Student() {
		
	}
}

- 정의하지 않아도 있다고 간주되므로, 텅 비어있어도 객체를 생성할 수 있다.

- 리턴 타입(void, int 등)이 없다. (리턴타입이 있다면 메소드이다)

- 객체를 생성할 때(new) 실행 순서가 돌아온다.

  따라서 객체 생성시에 무언가 준비작업을 할 때 유용하다.(컴퓨터 부팅or시동 거는 느낌...)

 

- Student s1 = new Student(); 가 실행되면

 Student 클래스에 실행 순서가 돌아온다. 그 때 실행되면서 콘솔창으로 호출된다.

 

- 모든 객체는 이 9개의 메소드를 가지고 있다.

 

- 모든 클래스에는 하나 이상의 생성자가 정의되어 있어야 한다!

 (따로 정의하지 않을 경우에도 자동으로 생성되어 있다)

 


 

[ 생성자 활용 예제 ]  
- 생성자가 있으면 무엇이 달라지는가?

step03의 Member 복사해옴

 

<Member>

package test.mypac;

public class Member {
	//번호를 저장할 필드	
	public int num;
	//이름을 저장할 필드	
	public String name;
	//주소를 저장할 필드	
	public String addr;
	
	//생성자를 하나라도 정의하면 기본 생성자는 없다고 간주되기 때문에
	//기본 생성자도 필요하면 명시적으로 정의해야 한다.
	public Member() {}
	
	//3개의 인자를 전달받을 준비가 된 생성자
	public Member(int num, String name, String addr) {//생성자에도 매개변수를 선언할 수 있다.
		//생성자의 인자로 전달받은 값을 필드에 저장하기
		this.num=num;
		this.name=name;
		this.addr=addr;
	}
		
	//메소드
	public void showInfo() {
		//필드에 저장된 내용을 활용해서 정보를 출력하기
		System.out.println("번호:"+this.num+", 이름:"+this.name+", 주소:"+this.addr);
	}
}

 

<MainClass02>

package test.main;

import test.mypac.Member;

public class MainClass02 {
	public static void main(String[] args) {
		//기본 생성자를 호출하면서 객체 생성하기
		Member m1=new Member();
		m1.num=2;
		m1.name="딸기";
		m1.addr="부산";
		
		
		//값을 3개 전달하는 생성자를 호출
		Member m2=new Member(1, "바나나", "서울");
		
		m1.showInfo();
		m2.showInfo();
	}
}

- 생성자 구분하는 법 : 이름이 클래스명과 똑같고 리턴 타입이 없다!
 메소드와 비슷한 듯 하지만 다르다.

- 메소드처럼 생성자에도 매개변수를 선언할 수 있다.
   ex) public Member(int num, String name, String addr) {} : 매개변수 3개. 각각 int, string, string 타입

 

- 생성자에서 인자를 받아서 위에 선언한 필드에 대입해주는 것! 보통 전달받은 값은 필드에 넣는다.

 


 

public Member(int num, String name, String addr) {
    this.num=num;
    this.name=name;
    this.addr=addr;
}

- 위와 같이 정의했을 때에는 인자 3개가 필요하기 때문에

 new Member(); 모양(기초적인 모양)으로 사용할 수 없다.

생성자를 하나라도 정의하면 디폴트 생성자는 사라진다.

- 해결방법: 생성자를 2개 정의하기 → 생성자를 2가지로 다중정의(overload)한 상태!
1) public Member() { }
2) public Member(int num, String name, String addr) {}

- 이렇게 두가지로 정의해두면 객체를 생성하는 방법이 아래 2가지가 된다.

1) new Member(); 

2) new Member(숫자, 문자열, 문자열);

 

- 실행해 봤을 때에는 이런 차이가 있다.(콘솔 창 확인)

1)에는 현재 값이 대입되지 않은 상태

2)는 바로 값을 만들어 넣어준 것!

 

- m1에도 값을 출력하고 싶다면 이렇게 참조값을 만들어주면 된다.

 (2가 더 편리한 구조이다)

 

- 이 객체를 어떻게 사용할지, 용도에 따라 어떤 생성자를 선택할것인지 개발자가 선택해서 호출할 수 있다.
 어떤 클래스는 생성자가 10개씩 정의될 수도 있다. 클래스에 따라 더 편한 구조로 만들면 된다.

(즉석에서 만들어서 전달하기 or 참조해서 전달하기)

 


 

[ 생성자 활용 예제 ]  

Cpu, Memory, HardDisk라는 클래스파일을 만들어줌 (main 메소드 외 내용없음)

 

<Cpu> / memory, harddisk도 동일한 파일

package test.mypac;

public class Cpu {

}

 

<MacBook>

package test.mypac;

public class MacBook {
	//외부에서 접근 불가하도록 접근 지정자를 private로 해서 필드 3개 선언하기
	private Cpu cpu;
	private Memory memory;
	private HardDisk hardDisk;
	//cpu, memory, harddisk 타입의 참조값을 담을수있는 필드!	
	
	//생성자
	public MacBook(Cpu cpu, Memory memory, HardDisk harddisk) {
		this.cpu=cpu;
		this.memory=memory;
		this.hardDisk=harddisk;
	}
	
	//메소드
	public void doGame() {
		if(this.cpu==null || memory==null || hardDisk==null) {
			System.out.println("부품이 부족해서 맥북이 동작을 할 수 없습니다.");
			return; //메소드를 여기서 끝내기
		}
		System.out.println("신나게 게임을 해요!");
	}
}

- 가상의 맥북 객체의 작동에 대한 메소드

- cpu, memory, harddisk 세 필드의 값이 모두 null이 아니어야만 "신나게 게임을 해요!" 가 가능

 

<MainClass03>

package test.main;

import test.mypac.Cpu;
import test.mypac.HardDisk;
import test.mypac.MacBook;
import test.mypac.Memory;

public class MainClass03 {
	public static void main(String[] args) {	
		/*
		 * 1. MacBook 객체를 생성해서 참조값을 mac1이라는 지역변수에 담아 보세요.
		 * 단, MacBook 클래스를 절대 수정하지 마세요.
		 */		
		MacBook mac1=new MacBook(new Cpu(), new Memory(), new HardDisk());
		
		//2. mac1지역변수에 들어있는 참조값을 이용해서 .doGame()이라는 메소드를 호출해 보세요.
		mac1.doGame();
		
		
		//MacBook 클래스의 생성자의 인자로 전달할 값이 지역변수에 준비가 되어있다고 가정하면
		Cpu c1=new Cpu();
		Memory m1=new Memory();
		HardDisk h1=new HardDisk();		
		
		//아래와 같이 MacBook 객체를 생성할수도 있다.
		MacBook mac2=new MacBook(c1, m1, h1);
		mac2.doGame();
	}
}

- 생성자 : public MacBook(Cpu cpu, Memory memory, HardDisk harddisk) {}

→ 3종류의 인자를 가진다. 직접 대입해주는 방법과 지역변수에 담에 참조값을 전달하는 방법이 있다.

 

 

private Cpu cpu;
private Memory memory;
private HardDisk hardDisk;
→ 필드를 선언하는 것. 앞의 Cpu는 데이터타입, 뒤의 cpu는 필드명

 

- 필드는 private으로 지정되었기 때문에 외부, 즉 다른 class파일에서 MacBook.cpu 로 접근할 수 없다.
- 생성자는 public으로 되어있으면 외부에서 new로 호출할 수 있다.(인자와는 관계없음)

 


this.cpu = cpu;

this.memory = memory;
this.hardDisk = harddisk;
- 앞의 cpu는 필드이고, 뒤의 cpu는 생성자에 들어가는 인자
→ 인자에 들어가는 값을 필드에 대입하는 것.