[Java] 생성자(Constructor)

2021. 1. 23. 14:55Dev.Program/Java & Spring

728x90

 < 생성자(Constructor) >

  • 생성자 메서드 라고도 함
  • 객체가 생성될 때 호출되어 멤버변수 초기화나 객체 생성 시 특정 작업을 수행
  • 메서드 구조와 유사하나, 리턴타입이 없고, 이름을 클래스명과 동일하게 정의
  • 메서드와 마찬가지로 파라미터가 없을 수도 있고, 파라미터가 있을 수도 있다.
  • 생성자를 정의하지 않으면, 컴파일러에 의해 기본 생성자가 자동으로 생성됨
    • ⇒ 하나라도 정의할 경우, 기본 생성자가 자동으로 생성되지 않는다!
    •     (기본 생성자 : 파라미터가 없고, 구현부의 코드가 아무것도 없음)
    • ⇒ 생성자는 반드시 최소한 한 개 이상이 존재해야함(자동 생성 기본 생성자 포함)

< 생성자 정의 기본 문법 >

[제한자] 클래스명([파라미터...]) {
	// 객체 생성 시 수행할 작업들...
}

 

  • 생성자 만들 때 class 명이랑 한 글자라도 다르면 바로 오류남! (일반 메서드로 취급 됨)

  • ctrl + space : 기본으로 생성해줌

  • 밑에 부분(생성자)을 주석처리해도 오류 안남. (자동으로 생성 - p203)

 

  • (콘솔창을 보면) 생성자 호출을 먼저 한 뒤, 호출을 실행함
  • 생성자를 호출하여 "홍길동" 문자열을 전달한 후 종료되면 다음 문장이 실행됨

  • 이제 자동으로 만들어 주지 않음! (비어있는 놈은 없기 때문)

  • 생성자 내에서 멤버변수에 저장될 기본값을 설정할 수 있다!
  • 객체를 만들자마자 이름이 홍길동으로 기본설정됨!

 

  • DefaultPerson()은 아무값을 주지 않았기 때문에 참조형의 기본값인 null 이 나옴

  • DefaultPerson() 과 DefaultPerson2() 둘 의 차이 잘 보기!



> 파라미터로 String, int, boolean 타입 데이터를 전달받아

   멤버변수 name, age, isHungry 초기화하는 생성자 정의

  • String, int, boolean 순서대로 적기
  • 실행창

 

> 생성자도 메서드와 똑같기 때문에 생성자도 생성자오버로딩이 똑같이 적용!

 

> 멤버 변수에 저장할 데이터 3개를 파라미터로 전달받아 초기화하는 생성자 정의

  • 보통 getter / setter 위에 만듭니다!
  • getter / setter 는 Alt + Shift + S → R 로 자동생성하기

 

  • 출력창

 

======== Ex3.java 만들기 (생성자 오버로딩)

< 생성자 오버로딩 >

  • 생성자 호출 시 다양한 형태의 파라미터 전달을 위함
  • 메서드 오버로딩과 방법 동일

 

> 기본 생성자 정의 - 멤버변수 name 을 "홍길동" 으로 초기화

> 파라미터 생성자 정의 - 문자열을 전달받아 멤버변수 name 을 초기화

class Person3 {
    String name;
    public Person3() {
        name = "홍길동";
    }
    public Person3(String s) {
        name = s;
    }
}

  • 호출
  • 출력창
  • Person3 p = new Person3();

                   System.out.println(p.name);

  • Person3 p2 = new Person3("홍길동2");

                   System.out.println(p2.name);

  • 이름이 같은데 오류 안남 ⇒ 오버로딩이 잘 되었다!



======== Test3.java 만들기

문제 )

Account3() 생성자 정의

Account3(String) 생성자 정의 - 계좌번호(accountNo) 초기화

Account3(String, String) 생성자 정의 - 계좌번호, 예금주명 초기화

Account3(String, String, int) 생성자 정의 - 계좌번호, 예금주명, 현재잔고 초기화

Getter/Setter 정의

public class Test3 {

	public static void main(String[] args) {

		Account3 a = new Account3();		
		a.showAccountInfo();
		
		Account3 a2 = new Account3("111-1111-111");		
		a2.showAccountInfo();
		
		Account3 a3 = new Account3("111-1111-111", "유라");		
		a3.showAccountInfo();
		
		Account3 a4 = new Account3("111-1111-111", "유라", 500000);
		a4.showAccountInfo();
	}

}

class Account3 {
	private String accountNo;
	private String ownerName;
	private int balance;
	
	// Account3() 생성자 정의
	public Account3() {
		System.out.println("Account3() 생성자 호출됨!");
	}
	// Account3(String) 생성자 정의 - 계좌번호(accountNo) 초기화
	public Account3(String s) {
		System.out.println("Account3(String) 생성자 호출됨!");
		accountNo = s;
	}
	// Account3(String, String) 생성자 정의 - 계좌번호, 예금주명 초기화
	public Account3(String s, String s2) {
		System.out.println("Account3(String, String) 생성자 호출됨!");
		accountNo = s;
		ownerName = s2;
	}
	// Account3(String, String, int) 생성자 정의 - 계좌번호, 예금주명, 현재잔고 초기화
	public Account3(String s, String s2, int i) {
		System.out.println("Account3(String, String, int) 생성자 호출됨!");
		accountNo = s;
		ownerName = s2;
		balance = i;
	}
	// Getter/Setter 정의
	public String getAccountNo() {
		return accountNo;
	}
	
	public void setAccountNo(String accountNo) {
		this.accountNo = accountNo;
	}
	
	public String getOwnerName() {
		return ownerName;
	}
	
	public void setOwnerName(String ownerName) {
		this.ownerName = ownerName;
	}
	
	public int getBalance() {
		return balance;
	}
	
	public void setBalance(int balance) {
		this.balance = balance;
	}
	
	public void showAccountInfo() {
		System.out.println("계좌번호 : " + accountNo);
		System.out.println("예금주명 : " + ownerName);
		System.out.println("현재잔고 : " + balance + "원");
	}
	
}

 

  • 출력창 (기본값 들어가는거 잘 보기)



  • 기본 생성자 public Account4() {} 를 주석처리하면 오류 : 자동생성X

 

  • Ex3.java 에서 이 부분 주석처리하고 생성자 자동 생성 해보기
  • 단축키 : Alt + Shift + S → O

  • 자동생성! (지금은 모르는 super()지우기)

 

======== Ex4.java 만들기

p.205

< 레퍼런스 this >

  • 자신의 인스턴스 주소를 저장하는 레퍼런스 변수(참조 변수)
  • 자동으로 생성되는 레퍼런스
  • 생성자 또는 메서드 내에서 로컬변수와 멤버변수의 이름이 같을 때 멤버변수를 지정하기 위한 키워드로 사용됨

< 기본 문법 >

this.멤버변수명

 

  • public 속 파라미터 변수(로컬변수, 지역변수) name 과 인스턴스 변수(멤버변수) name 이름이 같아도 오류는 안남.
  • 근데 색을 확인해보면 로컬변수 name 에 로컬변수 name 이 저장됨.

  • (자기한테 자기 값 넣는 거!)
  • 아무 효과 없다고 경고창 뜸!
  • 로컬변수 name 값을 다시 로컬변수 name 에 저장하는 코드 = 효과X

 

> 메서드(생성자) 내의 로컬변수명과 클래스 내의 멤버변수명이 같을 경우 메서드 내에서 두 변수의 이름을 지정하면 로컬변수를 지정하게 됨.

  • 로컬변수 name 값을 다시 로컬변수 name 에 저장하는 코드 = 효과X

 

  • 멤버변수 name(this.name) 에 로컬변수 name(name) 값을 전달
  • 차이점 구분 잘 하기!



======== Test4.java 만들기

  • 오류 고치기! this. 추가

 

  • p.207
  • 같은 기능인데 코드가 계속 중복된다.
  • 생성자 this() 사용하면 코드를 줄일수있다 (코드낭비제거)

 

------------------------------ 오후

======== Ex5.java 만들기 (생성자 this() 오버로딩)

p.207

< 생성자 this() >

  • 생성자 내에서 자신의 또 다른 생성자를 호출하는 키워드
  • 레퍼런스 this 와 동일하게 자신의 인스턴스에 접근하여 다른 생성자를 호출할 때 사용
  • 생성자 오버로딩 시 멤버변수 초기화 코드의 중복을 제거하기 위해 사용
    • ⇒ 여러 생성자에서 멤버변수를 중복으로 초기화하지 않고, 하나의 생성자에서만 초기화하고, 나머지 생성자에서는 해당 생성자를 호출하여 초기화 할 값만 전달 후 대신 초기화 수행
  • 생성자 내의 첫 번째 라인에서 생성자 this() 를 호출해야함
    • ⇒ 생성자 호출 코드보다 다른 코드가 먼저 실행될 수 없다!

< 생성자 this() 호출 기본 문법 >

--------생성자 내의 첫번째 라인에서--------
this(파라미터...);

 

문제 )

MyDate d1 = new MyDate();
// 기본 생성자를 통해 아무것도 전달하지 않으면 1900/1/1 출력
System.out.println(d1.year + "/" + d1.month + "/" + d1.day);

MyDate d2 = new MyDate(2020);
// 생성자 정수 1개를 전달하면 연도를 초기화하여 2020/1/1 출력
System.out.println(d2.year + "/" + d2.month + "/" + d2.day);

MyDate d3 = new MyDate(2020, 2);
// 생성자 정수 2개를 전달하면 연도를 초기화하여 2020/1/1 출력
System.out.println(d3.year + "/" + d3.month + "/" + d3.day);

MyDate d4 = new MyDate(2020, 2, 11);
// 생성자 정수 3개를 전달하면 연도를 초기화하여 2020/1/1 출력
System.out.println(d4.year + "/" + d4.month + "/" + d4.day);
  • 일단은 자동완성 하지 말고, this() 쓰지 말고 만들어보기(원래 하던 대로)
class MyDate {
	int year;
	int month;
	int day;
	
	public MyDate() {
		this.year = 1900;
		this.month = 1;
		this.day = 1;
	}
	
	public MyDate(int year) {
		this.year = year;
		this.month = 1;
		this.day = 1;
	}
	
	public MyDate(int year, int month) {
		this.year = year;
		this.month = month;
		this.day = 1;
	}
	
	public MyDate(int year, int month, int day) {
		this.year = year;
		this.month = month;
		this.day = day;
	}
	
}
// this. 생략가능

답 )

  • 생성자 내에서 다른 코드보다 아래쪽(뒤)에 생성자 this() 가 올 수 없다!
  • ⇒ 반드시 생성자 내의 첫 번째 라인에서 생성자 this() 를 사용해야함!
  • 위로 옮기면 오류창 없어지는 거 확인할 수 있다 (가장 먼저 사용해야함!)

 

  • 실행해보면 이렇게 호출되는데, (1900,1,1)을 찾아가서 제일 위에 문장이 가장 먼저 실행됨. (어떻게 실행되는지 잘 알아두기)



> this() 오버로딩 해보기 )

class MyDate {
	int year;
	int month;
	int day;
	
	public MyDate() {
		this(1900, 1, 1);
		System.out.println("MyDate() 생성자 호출됨!");
	}
	
	public MyDate(int year) {
		this(year, 1, 1);
		System.out.println("MyDate(int) 생성자 호출됨!");
	}
	
	public MyDate(int year, int month) {
		this(year, month, 1);
		System.out.println("MyDate(int, int) 생성자 호출됨!");
	}
	
	// 전체를 초기화하는 생성자는 그대로 두고,
      // 나머지 생성자만 this() 를 사용
	public MyDate(int year, int month, int day) {
         // this(year, month, day); // 많이 하는 실수!!
		System.out.println("MyDate(int, int, int) 생성자 호출됨!");
		this.year = year;
		this.month = month;
		this.day = day;
	}
	
}

- // this(year, month, day); // 많이 하는 실수!!

- 여기서 ctrl c + ctrl v 하면 이렇게 자동 복사

 

======== Test4.java 복사해서 Test5.java 만들기

문제 ) Account5로 바꾸고 전부 this() 로 만들어보기

p.207

public class Test5 {

	public static void main(String[] args) {
		Account5 a = new Account5();	
		a.showAccountInfo();
		
		Account5 a2 = new Account5("111-1111-111");		
		a2.showAccountInfo();
		
		Account5 a3 = new Account5("111-1111-111", "홍길동");		
		a3.showAccountInfo();

		Account5 a4 = new Account5("111-1111-111", "홍길동", 0);
		a4.showAccountInfo();
	}
}

class Account5 {
	private String accountNo; // 기본값 : "111-1111-111"
	private String ownerName; // 기본값 : "홍길동"
	private int balance; // 기본값 : 0
	
	public Account5() {
		this("111-1111-111", "홍길동", 0);
		System.out.println("Account5() 생성자 호출됨!");
	}
	public Account5(String accountNo) {
		this(accountNo, "홍길동", 0);
		System.out.println("Account5(String) 생성자 호출됨!");
	}

	public Account5(String accountNo, String ownerName) {
		this(accountNo, ownerName, 0);
	System.out.println("Account5(String, String) 생성자 호출됨!");

	}

	public Account5(String accountNo, String ownerName, int balance) {
	System.out.println("Account5(String, String, int) 생성자 호출됨!");
		this.accountNo = accountNo;
		this.ownerName = ownerName;
		this.balance = balance;
	}

	public String getAccountNo() {
		return accountNo;
	}
	
	public void setAccountNo(String accountNo) {
		this.accountNo = accountNo;
	}
	
	public String getOwnerName() {
		return ownerName;
	}
	
	public void setOwnerName(String ownerName) {
		this.ownerName = ownerName;
	}
	
	public int getBalance() {
		return balance;
	}
	
	public void setBalance(int balance) {
		this.balance = balance;
	}
	
	public void showAccountInfo() {
		System.out.println("계좌번호 : " + accountNo);
		System.out.println("예금주명 : " + ownerName);
		System.out.println("현재잔고 : " + balance + "원");
	}
}

 

======== Ex6.java 책 p.200-202 코드 따라쳐서 만들기

public class Ex6 {

	public static void main(String[] args) {

		WalkTestBad wtb = new WalkTestBad();
		wtb.walk();
		wtb.walk(100);
		wtb.walk(100, "cm");
		
		WalkTestGood wtg = new WalkTestGood();
		wtg.walk();
		wtg.walk(100);
		wtg.walk(100, "cm");
	}

}

class WalkTestBad {
	void walk() {
		System.out.println("100cm 이동");
	}
	
	void walk(int distance) {
		System.out.println(distance + "cm 이동");
	}
	
	void walk(int distance, String unit) {
		switch(unit) {
		case "cm":
			break;
		case "inch":
			distance *= 2.54;
			break;
		default:
			System.out.println("unknown");
			distance = 0;
		}
		System.out.println(distance + "cm 이동");
	}
}

class WalkTestGood {
	void walk() {
		walk(100, "cm");
	}
	
	void walk(int distance) {
		walk(distance, "cm");
	}
	
	void walk(int distance, String unit) {
		switch(unit) {
		case "cm":
			break;
		case "inch":
			distance *= 2.54;
			break;
		default:
			System.out.println("unknown");
			distance = 0;
		}
		System.out.println(distance + "cm 이동");
	}
}
  • wtb 는 각자 하나하나가 코드를 출력한다면
  • wtg 은 맨 마지막 switch ~ case 문 혼자 일 다함
  • “이동” 이라는 글자를 “이동했습니다!” 라고 수정하려면 wtb 는 3번이나 고쳐야하지만, wtg 는 마지막 문장 하나만 수정하면 된다.
  • 유지보수성이 좋다. 유지보수성이 향상된다. - 라고 할 수 있다.
728x90