객체 배열 (Array of Objects)

 

프로그래밍의 세계에서 빠질 수 없는 것이 바로 배열(Array)이다.

 

배열은 무엇인가 저장하는 것인데 같은 자료형을 연속적으로 저장하는 것이 특징이다.

 

4byte int형을 100개 저장한다면? 400 byte이다. 기본 자료형의 배열은 논리적으로 물리적으로 연결되어 위치한다.

 

4칸 짜리 구역을 100개 만든다는 것은 알기 쉽다. 그런데 객체를 한개 단위로 100개 나열하면 어떻게 될까?

 

아래처럼 String 이름; int 나이; 이렇게 다른 자료형들이 모여있는 것을 나열해야 할 것이다. String은 사람의 이름에 따라 크기도 달라질텐데 4개짜리 칸을 100개로 늘리는 것과는 모양이 다를 것을 예상할 수 있다. 당연히 사용법도 다를 것을 예상할 수 있다.

 

객체 배열

직원 객체를 만들어 보자. 예제는 private 이름과 나이 정도면 충분하다. 생성자 하나에 게터 세터를 만들어 준다. (이클립스의 코드 생성기능으로 쉽게 만들 수 있다)

public class ArrayObject {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Employee[] tekcom = new Employee[5];
		for (int i = 0 ; i < tekcom.length; i++) {
			System.out.println(tekcom[i]);
		}		
	}
}
class Employee{
	private String name;
	private int age;
	
	public Employee() {}
	
	public Employee(String name, int age) {
		this.setName(name);
		this.setAge(age);
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}
}

메인 함수에서 Employee[] tekcom = new Employee[5] 인것을 주목하자. 원래 new 키워드를 쓸때는 항상 생성자를 사용했다. 그런데 여기에는 ( ) 이렇게 생긴 생성자가 없다. 결과는

 

null
null
null
null
null 

 

이렇게 null 만 출력한다. 이유가 중요하다. new 키워드로 실제 생성자가 실행된 것이 아니다. 대신 new 키워드로 앞으로 만들어질 인스턴스의 주소를 담을 공간을 5개 생성한 것이다. 5개의 인스턴스는 앞으로 만들어질 것이다. 그러니까 5개의 주소공간을 미리 만들어 놓는 다는 것이다. 당연히 실제 값도 여기없다. 

 

그럼 인스턴스를 생성해보자. new Employee[5] 하고 new Employee( ) 은 전혀 다른 기능이다. 객체배열 5개가 있으니까 5개 만큼(0~4)의 객체 배열에 각각 직원 객체를 생성하고 주소값을 넘겨준다. null 이었던 곳에 참조 값을 주는 것이다.

 

출력을 보면 패키지.클래스와 고유의 아이디(인스턴스를 저장한 참조 값 주소)를 가지고 있다 이제 객체 배열은 실제 인스턴스과 연결할 주소를 가진다.

 

 

*배열 복사하기

배열을 복사하려면 크기에 대한 확인을 잘 해야한다. 복사한 양과 복사할 대상의 크기가 맞는지 봐야한다. 

System 클래스의 arraycopy()를 사용해서 배열 ar1을 ar2에 복사한다.

 

public class ArrayObject2 {

	public static void main(String[] args) {
		int[] ar1 = {1,2,3,4,5,6,7};
		int[] ar2 = {10,20,30,40,50,60,70,80,90,100,110,};
		
		System.arraycopy(ar1, 0, ar2,3,7);
		for(int i =0; i<ar2.length; i++) {
			System.out.println(ar2[i]);
		}		
	}
}

 

ar2에 복사가 완료되었다.

* 객체 배열 복사하기

참조만 복사

 

객체 배열도 복사할 수 있다. 배열처럼 System.arraycopy를 해보자.

 

그런데 문제가 있다. 복사는 잘 되었는데 객체 배열의 참조값(인스턴스의 주소)를 확인하니 동일하다. 이 말인 즉슨 어느 한 쪽의 데이터를 변경하면 다른 한쪽도 바뀐다는 것이다. 이 둘은 실상 하나인 것이다. 생각해보니 new 키워드로 생성자도 호출하지 않은 것 같다. 새로운 인스턴스가 생성되어 거기에 복사를 한개 아니다. 가리키는 손가락만 하나 더 늘었을 뿐이다.

 

그러면 진짜 복사하는 코드를 보자. 앞부분만 보면 된다. main 메서드의 첫번째 for문에서 객체를 new 키워드로 생성하고 getter 와 setter 를 이용하여 하나씩 복사해준다. new로 생성되었기 때문에 별도의 독립된 공간에 인스턴스가 만들어진다.

public class ArrayObject {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Employee[] tekcom = new Employee[4];
		Employee[] newcom = new Employee[4];	
		
		tekcom[0] = new Employee("왕가네",38);
		tekcom[1] = new Employee("김팀장",35);
		tekcom[2] = new Employee("아무래",28);
		tekcom[3] = new Employee("최고참",46);
		
		for(int i=0; i<tekcom.length; i++) {
			newcom[i] = new Employee();
			newcom[i].setAge(tekcom[i].getAge());
			newcom[i].setName(tekcom[i].getName());
		}
		
		newcom[0].setName("홍로인");
		newcom[1].setName("고리나");
		
		System.out.println("======= 처음 객체 배열 ======= ");
		for (int i = 0 ; i < tekcom.length; i++) {
			System.out.println(tekcom[i].getName());
		}

		System.out.println("======= 복사한 객체 배열 ======= ");
	    for (int i = 0 ; i < tekcom.length; i++) {
			System.out.println(newcom[i].getName());
		}		
	}
}
class Employee{
	private String name;
	private int age;
	
	public Employee() {}
	
	public Employee(String name, int age) {
		this.setName(name);
		this.setAge(age);
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}
}

실행결과는 다음과 같다. 두번째 객체 배열의 0과 1 인덱스에 있는 이름을 변경했다. 두 객체 배열은 이제 다른 독립적인 저장 공간을 가지고 있다.

 

자바는 배열에서도 인스턴스의 개념을 확실히 알고 있어야 헷갈리지 않는다. 여러번 실습하다보면 자연스럽게 익숙해질 것이다. 필요하면 그림을 그려보는 것도 도움이 된다.

공유하기

facebook twitter kakaoTalk kakaostory naver band