Java/Java Study

new 연산자 와 객체생성에 대하여

모모토 2021. 4. 24. 10:53

다형성 공부하던 도중 예제에서 궁금한 점이 발견되었고, 나는 객체의 선언과 생성에 관하여 약간의 오해가 있었다는 걸 알게 되었다.

 

Computer com = new Computer();

 

컴퓨터라는 클래스에서 객체를 하나 생성하였다. 이것을 하나하나 분석해보자

 

1) Computer com;

 

내가 만든 클래스인 컴퓨터라는 참조 타입의 변수를 선언하였다(참조 변수). com은 참조 변수 이므로 초기값이 자동으로 null로 설정된다.(지역변수는 꼭 초기화를 거쳐야 한다.) 이제 com은 주소 값이 담길 그릇인 것이다.

 

2) new Computer();

 

컴퓨터 클래스의 생성자에 new를 붙여주면 연산자 new에 의해서 Computer클래스의 인스턴스가 메모리 상의 빈 공간에 생성과 함께 이 객체의 주소가 생성된다. 이때 멤버 변수는 각 자료형에 해당하는 기본값으로 초기화된다.

(기본값으로 초기화되는 이유는 생성자 Computer() 때문이다. 생성자는 인스턴스가 생성될 때 호출되는 인스턴스 초기화 메서드이다. 나중에 오버 로딩 등을 배우면 기본값 말고 원하는 값으로 초기화도 가능하다는 것만 알아두고 다음에 여유가 된다면 생성자에 관한 게시물도 다뤄보겠다.)

 

3) Computer com = new Computer();

 

대입연산자(=)에 의해서 앞서 생성된 객체의 주소값이 참조변수 com에 저장된다. 이제 com을 이용하여 인스턴스의 멤버 변수에 접근할 수가 있게 되었다.

 

결론적으로 말하자면 new Computer(); 만으로 객체가 생성된 것이다.

 

Computer com = new Computer();

 

위의 형태만이 객체를 만들어서 쓰는 게 아니다.

 

다음 예제를 살펴보자

 

이 예제는 다형성에 관한 예제이고 물건을 사고 환불하는 예제이다. 메인 메서드만 가볍게 보고 결과를 

 

import java.util.Vector;

class Product {
	int price;
	int bonusPoint;

	Product(int price) {
		this.price = price;
		bonusPoint = (int) (price / 10);
	}

	Product() {
		price = 0;
		bonusPoint = 0;
	}
}

class Tv extends Product {
	Tv() {
		super(100);
	}

	public String toString() {
		return "Tv";
	}
}

class Computer extends Product {
	Computer() {
		super(200);
	}

	public String toString() {
		return "Computer";
	}
}

class Audio extends Product {
	Audio() {
		super(50);
	}

	public String toString() {
		return "Audio";
	}
}

class Buyer {
	int money = 1000;
	int bonusPoint = 0;
	Vector item = new Vector();

	void buy(Product p) {
		if (money < p.price) {
			System.out.println("잔액이 부족하여 물건을 살 수 없습니다.");
		}
		money -= p.price;
		bonusPoint += p.bonusPoint;
		item.add(p); // 구입한 제품을 vector에 저장한다
		System.out.println(p + "을/를 구입하였습니다.");
	}

	void refund(Product p) {
		if (item.remove(p)) {
			money += p.price;
			bonusPoint -= p.bonusPoint;
			System.out.println(p + "을/를 반품하셨습니다.");
		} else {
			System.out.println("구입하신 제품중 해당 제품이 없습니다.");
		}
	}

	void summary() {
		int sum = 0;
		String itemList = "";

		if (item.isEmpty()) {
			System.out.println("구입하신 제품이 없습니다.");
			return;
		}

		for (int i = 0; i < item.size(); i++) {
			Product p = (Product) item.get(i);
			sum += p.price;
			itemList += (i == 0) ? "" + p : ", " + p;
		}
		System.out.println("구입하신 물품의 촘 금액은"+sum+ "만원입니다.");
		System.out.println("구입하신 제품은 "+ itemList + "입니다.");
	}
}

public class PolyArgumentTest03 {
	public static void main(String[] args) {
		Buyer b = new Buyer();
		Tv tv = new Tv();
		Computer com = new Computer();
		Audio audio = new Audio();
		
		b.buy(tv);
		b.buy(com);
		b.buy(audio);
		b.summary();
		System.out.println();
		b.refund(com);
		b.summary();
	}
}

 

위의 코드는 Tv, Computer, Audio 객체를 생성 후 Buyer객체를 생성하여 각 제품을 구매하는 코드이다.

 

실행결과

더보기

Tv을/를 구입하였습니다.
Computer을/를 구입하였습니다.
Audio을/를 구입하였습니다.
구입하신 물품의 촘 금액은350만원입니다.
구입하신 제품은 Tv, Computer, Audio입니다.

Computer을/를 반품하셨습니다.
구입하신 물품의 촘 금액은150만원입니다.
구입하신 제품은 Tv, Audio입니다.

 

그리고 메인 메서드의 객체 생성을 달리해보자

 

public class PolyArgumentTest03 {
	public static void main(String[] args) {
		Buyer b = new Buyer();

		b.buy(new Tv());
		b.buy(new Computer());
		b.buy(new Audio());
		b.summary();
		System.out.println();
		b.refund(new Computer());
		b.summary();
	}
}

 

실행결과

더보기

Tv을/를 구입하였습니다.
Computer을/를 구입하였습니다.
Audio을/를 구입하였습니다.
구입하신 물품의 촘 금액은350만원입니다.
구입하신 제품은 Tv, Computer, Audio입니다.

Computer을/를 반품하셨습니다.
구입하신 물품의 촘 금액은350만원입니다.
구입하신 제품은 Tv, Audio입니다.

차이점이 보이는가? 바로 computer가 환불되지 않았다. 그 이유는 바로 computer 객체를 두 번 생성하였기 때문이다. 쉽게 말하면 refund에서 new Computer는 우리가 구매하지 않은 새로 생성한 컴퓨터를 환불한 것이다. 

그렇다 new Computer() 자체로 이미 주소 값이 할당된 것 즉 인스턴스가 생성된 것이다.

 

본인은 헷갈렸던 부분이고 객체 생성에 대한 유연함이 생길 수 있었던 좋은 예제였었다. 물론 이런 기초적인 것도 모르냐 할 수 있지만 반대로 나 같은 비전공자들이 코딩을 시작할 땐 너무나도 당연하게 간과할 수 있는 부분 같아서 이렇게 장황하게 글로 남겨본다.