Java - 상속

kojam9041·2022년 3월 20일
0

KH정보교육원 - JAVA

목록 보기
10/12

상속

* 매 클래스마다 중복된 필드, 메소드들을 하나의 클래스(부모)로 정의해두고
* 다른 클래스(자식)에서 해당 부모 클래스의 내용물을 가져다 쓰는 개념.
*
* [상속의 장점]
* 중복된 코드를 부모 클래스에서 공통적으로 관리, 새로운 코드를 추가, 수정하기 용이함.
* => 보다 적은 양의 코드로, 새로운 클래스들을 관리 가능
* => 상속 사용 안할 시, 수정할때마다 매번 각각의 클래스의 코드를 수정해주어야 함.
* => 프로그램의 생산성을 높여줄 수 있음
* 
* [상속의 특징]
* 클래스간의 상속은 다중상속이 불가능함. 단일상속만 가능함.
* => 나를 기준으로 부모님이 여러명이면 안됨
* 자바에서 제공하는 또는 내가 만든 모든 클래스는 Object라는 클래스의 후손임.		 
* => extends Object가 명시되어 있지는 않지만, 생략되어 있음.
* Object클래스에 있는 메소드를 가져다 쓸 수 있음.
* Object클래스에 있는 메소드가 맘에 안든다면, 오버라이딩하여 입맛에 맞게 사용 가능.
*  

상속 전


1. Desktop클래스
package com.kh.chap01_beforeVSafter.before.model.vo;

public class Desktop {
//	필드부
	private String brand;
	private String pCode;
	private String pName;
	private int price;
	private boolean allInOne;
//	생성자부
	public Desktop() {}
	public Desktop(String brand, String pCode, String pName,
			       int price, boolean allInOne) {
		this.brand = brand;
		this.pCode = pCode;
		this.pName = pName;
		this.price = price;
		this.allInOne = allInOne;
	}
//	메소드부
//	setter
	public void setBrand(String brand) 	{ this.brand = brand; }
	public void setpCode(String pCode) 	{ this.pCode = pCode; }
	public void setpName(String pName) 	{ this.pName = pName; }
	public void setPrice(int price) 	{ this.price = price; }
	public void setAllInOne(boolean allInOne) { this.allInOne = allInOne; }
//	getter
	public String getBrand() 	{ return brand; }
	public String getpCode() 	{ return pCode; }
	public String getpName() 	{ return pName; }
	public int price() 			{ return price; }
	public boolean getAllInOne() { return allInOne; }
//	information
	public String information() {
		return brand+", "+pCode+", "+pName+", "+price+", "+allInOne;
	}
}

2. SmartPhone클래스
package com.kh.chap01_beforeVSafter.before.model.vo;

public class SmartPhone {
//	필드부
	private String brand;
	private String pCode;
	private String pName;
	private int price;
	private String mobileAgency;
//	생성자부
	public SmartPhone() {}
	public SmartPhone(String brand, String pCode, String pName,
			          int price, String mobileAgency) {
		this.brand = brand;
		this.pCode = pCode;
		this.pName = pName;
		this.price = price;
		this.mobileAgency = mobileAgency;
	}
//	메소드부
//	setter메소드
	public void setBrand(String brand) 	{ this.brand = brand; }
	public void setpCode(String pCode) 	{ this.pCode = pCode; }
	public void setpName(String pName) 	{ this.pName = pName; }
	public void setPrice(int price) 	{ this.price = price; }
	public void setMobileAgency(String mobileAgency) { this.mobileAgency = mobileAgency; }
//	getter메소드
	public String getBrand() 	{ return brand; }
	public String getpCode() 	{ return pCode; }
	public String getpName() 	{ return pName; }
	public int getPrice() 		{ return price; }
	public String getMobileAgency() { return mobileAgency; }
//	information
	public String information() {
		return brand+", "+pCode+", "+pName+", "+price+", "+mobileAgency;
	}
}

3. Tv
package com.kh.chap01_beforeVSafter.before.model.vo;

public class Tv {
//	필드부
	private String brand;
	private String pCode;
	private String pName;
	private int price;
	private int inch;
//	생성자부
	public Tv() {}
	public Tv(String brand, String pCode, String pName, 
			  int price, int inch) {
		this.brand = brand;
		this.pCode = pCode;
		this.pName = pName;
		this.price = price;
		this.inch = inch;
	}
//	메소드부
//	setter부
	public void setBrand(String brand) 	{ this.brand = brand; }
	public void setpCode(String pCode) 	{ this.pCode = pCode; }
	public void setpName(String pName) 	{ this.pName = pName; }
	public void setPrice(int price) 	{ this.price = price; }
	public void setInch(int inch) { this.inch = inch; }
//	getter메소드
	public String getBrand() 	{ return brand; }
	public String getpCode() 	{ return pCode; }
	public String getpName() 	{ return pName; }
	public int getPrice() 		{ return price; }
	public int getInch() { return inch; }
//	information메소드
	public String information() {
		return brand+", "+pCode+", "+pName+", "+price+", "+inch;
	}
}

4. Run클래스
package com.kh.chap01_beforeVSafter.before.run;

import com.kh.chap01_beforeVSafter.before.model.vo.Desktop;
import com.kh.chap01_beforeVSafter.before.model.vo.SmartPhone;
import com.kh.chap01_beforeVSafter.before.model.vo.Tv;

public class BeforeRun {

	public static void main(String[] args) {
//		1. Desktop 객체
//		brand, pCode, pName, price, allInOne(일체형여부)
		Desktop d = new Desktop("삼성","d-01","짱짱데스크탑",2000000,true);
//		2. SmartPhone 객체
//		brand pCode, pName, price, mobileAgency
		SmartPhone s = new SmartPhone("애플","s-01","아이폰",1000000,"SKT");	
//		3. Tv 객체
//		brand, pCode, pName, price, inch
		Tv t = new Tv("엘지","t-01","고오급벽걸이티비",3000000,60);
//		보다시피, 매 클래스마다 중복된 코드들이 존재하는 상황임
//		(brand, pCode,pName,price)
//		=> 코드를 일일이 기술하는 과정에서 실수가 생겨날 수 있음.
//		=> 유지보수하는 과정에서 일일이 찾아가 수정해야 하는 번거로움이 생김.
		
//		출력
//		System.out.println(d);
//		System.out.println(t);
//		System.out.println(s);
//		각각의 주소값만 출력됨.
		
//		1. getter메소드
//		System.out.println(d.getBrand());
//		System.out.println(f.getBrand());
//		System.out.println(s.getBrand());
//		...
		
//		2. information메소드
		System.out.println(d.information());
		System.out.println(t.information());
		System.out.println(s.information());
	}
}

상속 후


1. 부모클래스 
Product 클래스
package com.kh.chap01_beforeVSafter.after.model.vo;

public class Product {
	/*
	 * Tv, SmartPhone, Desktop은 "상품"이라는 공통적인 특징이 있음.
	 * "상품"이라면 당연히 가져야 하는 속성 : brand, pCode, pName, price 
	 * 
	 * 새 클래스 모두 공통적으로 기술했던 요소들만 추출해서 단 한번만 정의
	 * => 이렇게 정의한 클래스를 부모클래스(상위클래스, 슈퍼클래스)라고 함.
	 * 
	 */
//	필드부
	private String brand;
	private String pCode;
	private String pName;
	private int price;
//	생성자부
	public Product() {}
	public Product(String brand, String pCode, String pName, int price) {
		this.brand = brand;
		this.pCode = pCode;
		this.pName = pName;
		this.price = price;
	}
//	메소드부
	public String getBrand() { return brand; }
	public void setBrand(String brand) { this.brand=brand; }
	public String getpCode() { return pCode; }
	public void setpCode(String pCode) { this.pCode = pCode; }
	public String getpName() { return pName; }
	public void setpName(String pName) { this.pName = pName; }
	public int getPrice() { return price; }
	public void setPrice(int price) { this.price = price; }
	public String information() {
		return brand+", "+pCode+", "+pName+", "+price;
	}
}

2. 자식클래스 
Desktop 클래스
package com.kh.chap01_beforeVSafter.after.model.vo;

public class Desktop extends /*부모클래스명*/Product {
//	클래스 선언부 : 어디에서 공통적인 것을 가져올건지 언급해주어야함.
	
//	필드부
//	(brand, pCode, pName, price) 필드부에 쓰는 것이 아닌, 부모클래스의 필드를 참조하는 것임.
	private boolean allInOne;
	
//	생성자부
//	기본생성자
	public Desktop() {}
	
//	매개변수 생성자
//	brand, pCode, pName, price, allInOne이라는 필드에 값을 다 대입해주는 생성자
	public Desktop(String brand, String pCode, String pName, int price,
				   boolean allInOne) {
//		super.은 해당 부모클래스의 객체의 주소를 담고 있음.
//		즉, 부모에 접근이 가능함.
//		ex) super.brand;
//		단, private일 경우에는 직접접근이 불가함!!
//		[오류메세지]
//		The field Product.brand is not visible
//		각 필드의 값이 private이기 때문에, 직접접근이 불가하여 오류가 뜸.
//		[해결]
//		1. private를 protected로 바꿔주기
//		=> 오류는 사라지나, 보안때문에 부적합함.
		/*
		super.brand = brand;
		super.pCode = pCode;
		super.pName = pName;
		super.price = price;
		*/
//		2. 부모클래스에 있는 public접근 제한자인 setter메소드 활용
//		=> 해결은 되나, 코드의 길이가 길어짐.
		/*
		super.setBrand(brand);
		super.setpCode(pCode);
		super.setpName(pName);
		super.setPrice(price);
		this.allInOne = allInOne;
		*/
//		3. 부모클래스의 매개변수생성자를 호출하여 사용하기.
//		=> super()생성자를 이용하여 작성함.
//		=> 매개변수생성자도 일종의 메소드이기 때문에 super(매개변수)로 호출이 가능함.
//		=> 마찬가지로 호출을 한 후, 다시 부모클래스의 해당 매개변수 생성자로 돌아가 값을 초기화할 수 있음.
//		=> this()생성자와 같이 맨위에 작성해줌 
		super(brand,pCode,pName,price);
		this.allInOne = allInOne;
	}
//	메소드부
	public boolean getAllInOne() {
		return allInOne;
	}
	public void setAllInOne(boolean allInOne) {
		this.allInOne = allInOne;
	}
//	오버로딩 : 같은 클래스 내에 같은 이름으로 메소드를 만들 수 있음.
//			   다만, 매개변수의 종류,개수,순서가 달라야 성립할 수 있음.
//	오버라이딩 : 부모 클래스에 있는, 같은 이름의 메소드를 정의해서 새롭게 쓰는 것임.
//	오버로딩은 한 클래스 내에 메소드 이름은 동일하나, 
//	매개변수의 종류, 개수, 순서를 다르게 하여 메소드를 만드는 것이고
//	오버라이딩은 상속관계에서 부모클래스의 매개변수의 종류,개수,순서를 그대로 가져와 사용하는 것임.
//	여기서, 생성자 역시 일종의 메소드이기 때문에 오버로딩, 오버라이딩이 적용됨.
	public String information() {
//		return brand+pCode+pName+price+allInOne;
//		[오류메세지]
//		The field Product.brand is not visible
//		product의 4가지 필드가 desktop에 존재하지 않아서 생김.
		
//		return super.getBrand()+", "+super.getpCode()+", "+super.getpName()+", "
//		+super.getPrice()+", "+allInOne;
//		product의 필드를 protected로 바꾸어주면 오류가 사라짐.
//		(protected : 같은패키지 o, 다른패키지x(상속관계일시에는 가능))
//		다만, 이게 적합한 방식인가? No!
//		같은 패키지 안의, 나랑 상관없는 다른 클래스에서는 자유롭게 접근이 가능한 범위라서
//		private는 "직접접근을 막겠다" => "간접접근을 가능하게 하겠다."
//		=> getter메소드를 호출하여 사용함(public이라 바로 호출 가능함)
//		=> 여기에 부모로부터 메소드를 호출한다는 의미에서 super.을 붙여줌.
		
//		부모로부터 .필드에 직접 접근하고싶다면? super.필드명
//		부모로부터 메소드를 호출하고 싶다면? super.메소드명()
//		부모로부터 생성자(기본, 매개변수)를 호출하고 싶다면? super(), super(매개변수)
		return super.information() + ", "+allInOne;
//		위의 getter메소드를 이용한 방식과 같은 효과임.
	}
}

SmartPhone 클래스
package com.kh.chap01_beforeVSafter.after.model.vo;

public class SmartPhone extends Product{
//	필드부
//	extends로 현재 코드상에는 나타나지않지만, 
//	Product의 brand, pCode, pName, price가 추가된 효과를 부여함.
//	추가로 getter, setter, information메소드드 딸려온 효과를 부여함.
	private String mobileAgency;
	
//	생성자부
	public SmartPhone() {}
	public SmartPhone(String brand, String pCode, String pName, int price
			        , String mobileAgency) {
		super(brand, pCode, pName, price);
//		부모클래스의 매개변수생성자를 가져다 씀.
//		this()와 사용새가 비슷함. 
//		(this()는 같은 클래스의 매개변수생성자를, super()은 상위클래스의 매개변수 생성자를 가져옴)
		this.mobileAgency = mobileAgency;
	}
	
//	메소드부
	public String getMobileAgency() {
		return mobileAgency;
	}
	public void setMobileAgency(String mobileAgency) {
		this.mobileAgency = mobileAgency;
	}
//	오버라이딩 : 부모님의 메소드를 물려받아, 내 입맛대로 정의함.
//	동적바인딩 : 오버라이딩 된 내 메소드가 호출시, 우선순위를 가짐.
	public String information() {
//		내입맛대로, 부모클래스의 info에 내가 원하는 정보도 추가하고 싶음.
		return super.information()+", "+mobileAgency;
	}
}

Tv클래스
package com.kh.chap01_beforeVSafter.after.model.vo;

public class Tv extends Product {

//	필드부
//	코드에는 없지만, Product에서 필드를 가져옴.
	private int inch;
	
//	생성자부
	public Tv() {}
	public Tv(String brand, String pCode, String pName, int price
			, int inch) {
		
		super(brand,pCode,pName,price);
		this.inch = inch;
	}
	
//	메소드부
//	메소드도 마찬가지로 
//	getter,setter메소드가 코드에는 없지만, Product에서 메소드를 가져와서 사용.
	public int getInch() {
		return inch;
	}
	public void setInch(int inch) {
		this.inch = inch;
	}
	//
	//@override : 일종의 주석임. 단순히 오버라이드된 메소드라는 사실을 알려줌.
//				     다만, 오버라이드가 안되는데 이 주석을 달면 오류를 나타내줌.	
	public String information() {
		return super.information()+", "+inch;
	}
	
}

Run 클래스
package com.kh.chap01_beforeVSafter.after.run;

import com.kh.chap01_beforeVSafter.after.model.vo.Desktop;
import com.kh.chap01_beforeVSafter.after.model.vo.SmartPhone;
import com.kh.chap01_beforeVSafter.after.model.vo.Tv;

public class AfterRun {

	public static void main(String[] args) {
//		상속테스트
		Desktop d = new Desktop("삼성","d-01","짱짱데스크탑",2000000,true);
//		Desktop의 매개변수생성자로 이동 => super()생성자로 Product의 매개변수 생성자로 이동 
//		=> Desktop로 다시 이동해서 값 대입 
		System.out.println(d.information());
//		=> Desktop의 매개변수생성자의 값을 호출함.
		
		SmartPhone s = new SmartPhone();
		s.setBrand("애플");
//		SmartPhone에 setBrand라는 메소드를 만들지 않았는데도, 잘 작동됨.
		s.setpCode("s-01");
		s.setpName("아이폰");
		s.setPrice(1000000);
		s.setMobileAgency("SKT");
		System.out.println(s.information());
//		SmartPhone에 information()을 만들어주면 정상작동함.
		
		Tv t = new Tv("엘지","t-01","고오급벽걸이TV",3000000,60);
		System.out.println(t.getBrand());
		System.out.println(t.getpCode());
		System.out.println(t.getpName());
		System.out.println(t.getPrice());
//		Tv에 getterBrand,pCode,pName,Price가 없음에도 잘 실행됨.
//		이는, 자식클래스에는 없지만, 부모클래스에는 있어서 사용할 수 있음.
		System.out.println(t.getInch());
//		System.out.println(t.information());
//		Tv에 information에서 가격까지만 찍힘.
		System.out.println(t.information());
//		Tv에 information을 만들어서 정상적으로 인치까지 찍힘.
//		내가 실행하고자 하는 메소드가 자식 클래스에 없다면,
//		자동으로 부모 클래스에 있는 메소드로 직행함.
//		내가 실행하고자 하는 메소드가 자식 클래스에 있다면,
//		자동으로 자식 클래스에 있는 메소드로 직행함.
//		이를 '동적바인딩'이라고 함.
}

오버라이딩, 동적바인딩

 * 오버라이딩
 * 상속받고 있는 부모클래스의 메소드를 자식 클래스에서 재정의(재작성)하는 것.
 * 즉, 부모가 제공하고 있는 메소드를 자식이 일부 고쳐서 사용하겠다는 의미.
 *
 * 동적바인딩
 * 호출시, 자식메소드가 우선권을 가짐.
 * 
 * [오버라이딩 성립 조건]
 * 1. 부모 메소드명과 동일해야 함.
 * 2. 반환형이 같아야 함.
 * 3. 매개변수의 자료형, 개수, 순서가 동일해야 함.(오버로딩때와는 반대임)
 * => 단, 매개변수명은 무관함.
 * 4. 자식메소드의 접근제한자가 부모메소드의 접근제한자보다 범위가 같거나 또는 공유 범위가 더 커야함. 
 * => 규약의 개념이 들어가있음(재정의 하려면 적어도 이정도의 규칙은 지켜야 함.)
 * 
 * @Override : annotation(주석). 생략이 가능함.(부모메소드와 형태가 같다면)
 * 			       주석이지만, 잘못 기술했을 경우 오류를 알려주기 때문에 검토할 수 있음.  
 * 			       부모메소드가 후에 수정됐을 경우에도 또한 오류로 알려주기 때문에 검토할 수 있음. 

부모클래스
Vehicle 클래스
package com.kh.chap02_inherit.model.vo;

public class Vehicle {
//	필드부
	private String name;
	private double mileage;
	private String kind;
//	생성자부
	public Vehicle() {}
	public Vehicle(String name, double mileage, String kind) {
		this.name = name;
		this.mileage = mileage;
		this.kind = kind;
	}
//	매소드부
//	setter
	public void setName(String name) {
		this.name= name;
	}
	public void setMileage(double mileage) {
		this.mileage = mileage;
	}
	public void setKind(String kind) {
		this.kind = kind;
	}
//	getter
	public String getName() {
		return name;
	}
	public double getMileage() {
		return mileage;
	}
	public String getKind() {
		return kind;
	}
//	information
	public String information() {
		return name+", "+mileage+", "+kind;
	}
	public void howToMove() {
		System.out.println("움직인다.");
	}
}

자식클래스
1. Airplane클래스
package com.kh.chap02_inherit.model.vo;

public class Airplane extends Vehicle {
//	필드부
	private int tire;
	private int wing;
	
//	생성자부
	public Airplane() {}
	public Airplane(String name, double mileage, String kind,
			int tire, int wing) {
		super(name,mileage,kind);
		this.tire = tire;
		this.wing = wing;
	}
	
//	메소드부
	public void setTire(int tire) {
		this.tire = tire;
	}
	public void setWing(int wing) {
		this.wing = wing;
	}
	public int getTire() {
		return tire;
	}
	public int getWing() {
		return wing;
	}
	@Override
	public String information() {
		return super.information()+", "+tire+", "+wing;
	}
	@Override
	public void howToMove() {
		System.out.println("바퀴를 움직이고, 날개를 움직인다.");
	}
}

2. Car클래스 
package com.kh.chap02_inherit.model.vo;

public class Car extends Vehicle{
//	필드부
	private int tire;
	
//	생성자부
	public Car() {}
	public Car(String name, double mileage, String kind, int tire) {
		super(name,mileage,kind);
		this.tire = tire;
	}
//	메소드부
	public void setTire(int tire) {
		this.tire = tire;
	}
	public int tire() {
		return tire;
	}
	@Override
	public String information() {
		return super.information()+", "+tire;
	}
	@Override
	public void howToMove() {
		System.out.println("바퀴를 움직인다.");
	}
}

3. Ship 클래스
package com.kh.chap02_inherit.model.vo;

public class Ship extends Vehicle{
//	필드부
	private int propeller;
	
//	생성자부
	public Ship() {}
	public Ship(String name, double mileage, String kind,int propeller) {
		super(name,mileage,kind);
		this.propeller = propeller;
	}
//	메소드부
	public void setPropeller(int propeller) {
		this.propeller = propeller;
	}
	public int getPropeller() {
		return propeller;
	}
	@Override
	public String information() {
		return super.information()+", "+propeller;
	}
	@Override
	public void howToMove() {
		System.out.println("프로펠러를 움직인다.");
	}
}

Run 클래스
package com.kh.chap02_inherit.run;

import com.kh.chap02_inherit.model.vo.Airplane;
import com.kh.chap02_inherit.model.vo.Car;
import com.kh.chap02_inherit.model.vo.Ship;

public class Run {

	public static void main(String[] args) {
		
//		객체 생성 테스트
		Car c = new Car("벤틀리",12.5,"세단",4);
		Ship s = new Ship("새우잡이배",3,"어선",1);
		Airplane a = new Airplane("보잉774",0.02,"여객기",16,5);
		
		System.out.println(c.information());
		System.out.println(s.information());
		System.out.println(a.information());
        
		/*
        Vehicle vehicle = new Vehicle();
        vehicle.howToMove(); // 움직인다. 
        */
        a.howToMove();	// 바퀴를 움직이고, 날개를 움직인다.
		c.howToMove();	// 바퀴를 움직인다.
		s.howToMove();	// 프로펠러를 움직인다.
//		=> 부모클래스와 자식클래스가 동일한 메소드명을 공유함에도
//		=> 각각, 호출한 자식클래스의 메소드가 우선하여 출력됨.
//		=> 이를, '동적바인딩'이라고 함.
	}
}

Object클래스 동적바인딩


Book클래스
package com.kh.chap03_override.model.vo;

public class Book /*extends Object*/ {
//	최상위 클래스인 Object 상속받아보기
//	만약, 나의 클래스에 부모클래스가 존재하는데, 
//	그럼, Object와 부모클래스에 다중상속을 받는것이 아닌가?
//	아니다. 이떄는 나의 클래스로부터 부모클래스, Object클래스로 거슬러 올라가는 것임.
	
//	필드부
	private String title;
	private String author;
	private int price;
	
//	생성자부
//	메뉴툴바 - source - generate constructor using field
	public Book() {}
	public Book(String title, String author, int price) {
		this.title = title;
		this.author = author;
		this.price = price;
	}
//	메소드부
//	getter , setter
//	메뉴툴바 - source - generate getter and setter
	public String getTitle() {
		return title;
	}
	public String getAuthor() {
		return author;
	}
	public int getPrice() {
		return price;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public void setAuthor(String author) {
		this.author = author;
	}
	public void setPrice(int price) {
		this.price = price;
	}
//	information
//	메뉴툴바 - source - generate toString()
//	toString()은 기본적으로 Object의 상속을 받고 있는 상태임.
//  다만, 기존의 Object 클래스의 toString()은 주소값을 반환하는 메소드임에 반해
// 	이 클래스의 toString()은 오버라이딩을 하여, 문자열을 반환하는 메소드로 바뀜.
	@Override
	public String toString() {
		return "Book [title=" + title + ", author=" + author + ", price=" + price + "]";
	}
}

Run클래스
package com.kh.chap03_override.run;
import com.kh.chap03_override.model.vo.Book;

public class OverrideRun {

	public static void main(String[] args) {
		/*
		 * 모든 클래스는 Object의 후손임.
		 * 즉, 자바에서 최상위클래스는 항상 Object
		 * => Object에 있는 메소드들은 어느 클래스를 만들든지 가져다 쓸 수 있음.
		 * => 마음에 안들면 입맛대로 재정의하여 쓸 수 있음.
		 */
		
		Book bk = new Book("자바의정석","김자바",23000);
		System.out.println(bk.toString()); // 필드의 내용물을 문자열로 연이어서.
		System.out.println(bk /*.toString*/); // 주소값이 아닌 실제값이 찍힘.
		
		/*
		 * Object 클래스에서 제공하는 toString() 메소드의 원래 역할 => 주소값 찍어주기
		 * (해당 참조형 변수의 풀클래스명 @해당 객체의 주소값의 16진수형태 => 10진수는 .hashcode())
		 * => 지금은 주소값을 찍는 대신에, 각 필드의 값을 문자열로 연이어서 리턴하게끔 오버라이딩을 해줬음.
		 * => toString()메소드를 주석처리하면, 오버라이딩이 해제되어, 원래대로 주소값이 찍히는 것을 볼 수 있음.
		 */
	
	}
}

0개의 댓글