본문 바로가기
카테고리 없음

클래스2 (클래스 구조, 필드)

by 낭만 코딩 2024. 8. 18.

1. 클래스 구조

2. 필드

3. 변수의 범위

 

 

1. 클래스 구조

클래스는 크게 속성과 기능으로 나눌 수 있습니다. 속성은 클래스가 가지고 있는 특징, 변수로 나타낼 수 있고, 기능은 메서드라고 부릅니다. 클래스의 구조를 조금 더 세분화하면 필드(Field), 메서드(Method) , 생성자(Constructor) 로 이루어져 있습니다.

 

필드

필드(Field)는 객체의 특징이나, 특성 값을 저장하는 곳입니다. 클래스안에 있는 변수라고 생각하면 쉽게 이해될 수 있습니다. 하지만 생성자나 메서드 안에 있는 변수와는 다른데요, 생성자와 메서드 안의 변수는 그 안에서만 사용할 수 있지만 필드는 클래스 내부의 전체에서 사용됩니다.

 

생성자

생성자(Constructor)는 말 그대로 객체를 생성할 때 사용되는 부분으로 메서드와 비슷하게 생겼지만, 클래스명과 이름이 같고, 리턴값이 없습니다.

 

메서드

메서드(Method)는 객체의 기능에 해당하는 영역으로 특정 기능 단위로 묶어 하나의 메서드로 정의되어 있습니다. 프로그램이 실행되는 곳에서 이 메서드로 값을 전달하고 메서드는 전달받은 값을 처리하거나 기능을 실행한 후 처리 결과를 실행한 곳으로 돌려주기도 합니다.

 

자료구조적인 개념의 클래스

클래스를 자료구조적인 개념으로 생각해보면 데이터와 기능이 합쳐진 것이라고 볼 수 있습니다. 앞에서 배운 변수와 배열, 그리고 자바에는 존재하지 않지만 C의 구조체와 비교해서 살펴보겠습니다.

변수 하나의 공간에 하나의 값만 저장할 수 있는 자료구조
배열 하나의 변수에 같은 자료형의 여러 값을 저장할 수 있는 자료구조
구조체 다양한 데이터 타입의 값을 여러개 저장할 수 있는 자료구조
클래스 데이터와 기능을 함께 저장할 수 있는 자료구조

 

대표적인 예로 자바에서 문자열을 정의할 때 사용했던 String 이라는 클래스가 있습니다. String 클래스 자료형은 단순히 여러 문자들을 배열처럼 나열한 자료구조가 아니라 클래스로 정의되어 있는 이유는 문자열 데이터 값과 문자열을 다양하게 처리할 수 있는 기능도 포함되어 있기 때문입니다.

 

자료형적 개념의 클래스

자료형(데이터 타입) 개념으로서 클래스는 이미 만들어진 자료형이 아니라 사용자가 직접 정의한 자료형으로 표현할 수 있습니다. 그래서 사용자 정의 자료형이라고도 부릅니다. 자동차라는 클래스를 정의하고 자동차의 속성(색상, 제조사, 종류 등...)과 기능(전진하다. 후진하다 등...)으로 설계한 후 각 자동차 클래스를 자료형으로 지정해서 객체를 생성하게 됩니다. Car라는 클래스를 정의해보겠습니다.

public class Car {

	// 필드
	String color;
	String company;
	String type;
	
	// 메서드
	public void go() {
		System.out.println("전진하다.");
	}
	public void back() {
		System.out.println("후진하다.");
	}
}

 

Car 클래스는 main() 메서드가 없습니다. 이 클래스는 실행하기 위한 클래스가 아니라 다른 클래스에서 실행에 필요한 기능을 제공하는 클래스입니다. Car 클래스를 이용해 객체를 생성하는 예제를 살펴보겠습니다.

 

public class CarMain {

	public static void main(String[] args) {
		
		Car tico = new Car();
		Car pride = new Car();
		
		tico.color = "화이트";
		tico.company = "대우";
		tico.type = "경차";
		
		pride.color = "블랙";
		pride.company = "기아";
		pride.type = "소형";
		
		tico.go();
		pride.go();
		System.out.println(tico.color);
		System.out.println(tico.company);
		System.out.println(tico.type);
		System.out.println(pride.color);
		System.out.println(pride.company);
		System.out.println(pride.type);
		
	}
}

 

실행 결과

전진하다.
전진하다.
화이트
대우
경차
블랙
기아
소형

 

CarMain이라는 클래스의 main() 메서드에서 Car 타입의 tico라는 객체 변수에 new 연산자를 통해 Car 객체를 생성했고, 그 밑에 Car 타입의 pride라는 객체 변수를 생성했습니다. tico 객체의 color 필드에는 화이트pride 객체의 color 필드에는 블랙을 대입했습니다. 앞의 예제 Car 클래스는 설계도 작성하듯 자동차의 특징과 속성을 필드로 정의했고, 전진과 후진을 기능으로 정의하여 설계하였습니다.

이 설계도를 가지고 ticopride라는 데이터 타입은 같지만 전혀 다른 독립적인 객체를 생성한 것입니다. 실행결과를 보면 ticopride 객체의 go() 메서드는 전진하다.” 문자열을 출력했고, color, type, company는 각각 다르게 출력된 것을 알 수 있습니다.

다음 예제는 객체를 배열에 저장하는 예제입니다. 배열은 같은 자료형의 값을 담을 수 있다고 했기 때문에 Car 타입의 배열을 선언하면 여러 개의 Car 객체를 저장할 수 있습니다. 또한 객체 역시 참조 자료형이기 때문에 메모리 주소값을 참조한다. 예제 코드와 출력결과를 비교해서 확인해 보겠습니다.

public class CarMain2 {

	public static void main(String[] args) {
		
		// Car 타입의 배열객체 생성
		Car[] cars = new Car[3];
		
		// car 객체 생성
		Car tico = new Car();
		
		tico.color = "화이트";
		tico.company = "대우";
		tico.type = "경차";

		
		// 모든 인덱스에 tico 객체 저장
		for (int i=0; i<cars.length; i++) {
			cars[i] = tico;
		}
		
		System.out.println("2번 인덱스 color : "+cars[2].color);
		
		cars[0].color = "블랙"; // 0번 인덱스 객체의 color 필드에 "블랙" 대입
		
		System.out.println("2번 인덱스 color : "+cars[2].color);
		
	}
}

 

실행 결과

2번 인덱스 color : 화이트
2번 인덱스 color : 블랙

 

cars는 배열변수를 선언한 것인데, 앞에서 배웠던 배열 선언과 동일합니다. 차이점은 기본 자료형이 아니라 사용자 정의 자료형 Car가 들어간것 뿐입니다. 길이가 3인 배열 객체이고 그 아래 Car 타입의 tico 객체를 생성해 각 필드에 문자열 값을 대입했고, for문은 이 tico 객체를 cars 배열에 0부터 2번 인덱스에 반복하면서 차례대로 대입했습니다. 출력문은 2번 인덱스의 color 필드를 출력했는데 화이트가 출력되었습니다. 0,1,2 인덱스 모두 동일하게 화이트로 저장되어 있습니다. 그런데 0번 인덱스의 color 필드에 블랙을 대입하고 다시 2번 인덱스의 color 필드를 출력했더니 블랙이 출력되었습니다. 어떻게 된것일까요?

이유는 Car 역시 참조자료형이기 때문입니다. cars[0], cars[1], cars[2] 모두 같은 주소값을 갖고 있기 때문에, 하나의 객체에 필드의 값을 변경해도 모두 변경이 되는 것입니다. 정확히 얘기하면 모두 바뀌는 것이 아니라, 값이 저장된 곳은 하나고 이 값을 바꾸면 이 값을 참조하고 있는 모든 변수도 바뀐 값으로 참조하게 됩니다

 

정리하면 클래스는 사용자 정의 자료형이고, 사용자 정의 자료형은 모두 참조 자료형입다. 참조 자료형은 값을 직접 저장하는게 아니라, 저장된 위치의 주소값을 저장하기 때문에 독립된 별도의 객체를 생성하려면 new 연산자를 통해 다른 객체로 생성해야 합니다.

 

 

2. 필드

필드는 객체의 고유한 속성이나 상태값을 저장하는 곳입니다. 위 예제에서 봤던 Car라는 클래스의 색상이나, 종류, 제조사는 Car가 갖는 고유한 속성으로 필드로 선언됩니다. 필드는 클래스 중괄호 블록안에 어디서든 선언될 수 있으며, 필드를 선언하는 방법은 변수의 선언 방법과 동일합니다. 초기값 역시 선언할때 넣어줄 수도 있고, 생략할 수도 있습니다. 필드는 멤버 변수라고도 부르며, 멤버 변수는 클래스 변수와 인스턴스 변수로 나눌 수 있습니다.

변수는 크게 세가지로 분류할 수 있는데, 클래스 변수, 인스턴스 변수, 지역변수로 나눠집니다.

종류 위치 선언 방법 생성 시기
클래스 변수 클래스 블록 내 static 타입 변수명; 클래스가 메모리에 로드되는 시점
인스턴스 변수 타입 변수명; 객체로 생성되는 시점
지역 변수 메서드(생성자) 블록 내 타입 변수명 해당 실행문이 실행되는 시점

 

클래스 변수(class variable)는 클래스 블록 내의 영역에 선언되며, 선언 방법은 변수의 자료형 앞에 static이라는 키워드를 붙이면 됩니다. 이 클래스 변수는 모든 객체에서 공유되는 변수입니다. 객체를 생성하지 않아도 바로 사용할 수 있고, 사용 방법은 클래스명.클래스변수명 형태로 사용합니다.

클래스명.변수명

 

인스턴스 변수(instance variable)도 클래스 변수와 같이 클래스 블록 내의 영역에 선언되지만, static 키워드는 붙지 않습니다. 일반적인 변수 선언 방법으로 선언하면 됩니다. 인스턴스 변수는 객체를 생성할 때 생성되며, 객체끼리는 서로 독립적이기 때문에 이 인스턴스 변수 역시 객체마다 독립적인 값을 갖습니다. 사용 방법은 객체명.인스턴스변수명 형태로 사용됩니다.

객체명.변수명

 

클래스 변수와 인스턴스 변수를 예제를 통해 확인해 보겠습니다.

public class VarSample {

	public static void main(String[] args) {
		
		// 클래스 변수 사용
		System.out.println("Avante 제조사 : "+Avante.company);
		
		Avante a1 = new Avante();
		Avante a2 = new Avante();
		
		// 인스턴스 변수의 값 변경
		a1.color = "화이트";
		a2.color = "블랙";
		
		// 인스턴스 변수 출력
		System.out.println("a1 색상 : "+a1.color);
		System.out.println("a2 색상 : "+a2.color);
		
		// 클래스 변수를 인스턴스 객체로 출력
		System.out.println("a1 제조사 : "+a1.company);
		System.out.println("a2 제조사 : "+a2.company);
		
		// 클래스 변수의 값 변경
		a1.company = "기아";
		
		// 클래스 변수의 값 변경 후 클래스변수와 인스턴스변수 출력
		System.out.println("Avante 제조사 : "+Avante.company);
		System.out.println("a1 제조사 : "+a1.company);
		System.out.println("a2 제조사 : "+a2.company);
		
	
	}
	
	
}

class Avante {
	
	static String company = "현대"; // 클래스 변수
	String color;	// 인스턴스 변수
	
}

 

실행 결과

Avante 제조사 : 현대
a1 색상 : 화이트
a2 색상 : 블랙
a1 제조사 : 현대
a2 제조사 : 현대
Avante 제조사 : 기아
a1 제조사 : 기아
a2 제조사 : 기아

 

이 예제는 하나의 파일에 클래스가 두개 있는 형태로 구성했습니다. 파일명은 VarSample이고, 밑에 Avante라는 클래스가 하나 더 있는 것입니다. Avante라는 클래스는 두 개의 필드로 이루어져 있는데, color는 인스턴스 변수, company는 클래스 변수입니다. 프로그램이 실행되는 main() 메서드가 있는 클래스는 VarSample이며 main 메서드 안에서 Avante.company (클래스명.변수명)으로 사용했습니다. 두 개의 객체 (a1, a2) 를 생성해 각 객체의 color 인스턴스 변수에 화이트와 블랙 문자열을 대입했습니다. 이 인스턴스 변수를 출력하는데, 객체마다 다르게 출력되었고, 클래스변수도 인스턴스변수처럼 사용할 수 있습니다.

a1.company

 

이번엔 a1.company(클래스변수)를 기아라고 변경했는데, Avante.company(클래스변수)를 출력하고, a1, a2객체를 이용해서도 a1.company / a2.company(클래스변수)를 출력했는데, 실행결과를 보면 모두 기아로 바뀐 것을 알 수 있습니다.

 

다시 정리하면, 인스턴스 변수(static이 없는)는 객체마다 독립적인 값을 갖고, 클래스 변수(static이 있는)는 모든 객체들이 공유되는 변수입니다. 또한 클래스 변수는 객체 생성 없이도 클래스명 만으로 사용할 수 있지만, 인스턴스 변수는 반드시 객체를 먼저 생성하고 객체를 통해 사용해야만 한다. 그렇지 않으면 에러가 나게 됩니다.

 

3. 변수의 범위 (scope)

이번엔 지역변수와 함께 변수의 범위(scope)에 대해 알아보겠습니다. 변수는 사용할 수 있는 범위가 정해져 있는데, 먼저 예제부터 보겠습니다.

 

public class LocalValSample {

	public static void main(String[] args) {
	
		Local local = new Local();
		
		System.out.println(local.name); // null
		
		local.process();
		System.out.println(local.name); // 홍길동
		
		local.printAge1();
		local.printAge2();
		
		// for문 블록 내에서의 변수 선언
		for (int i=0; i<10; i++) {
			int temp = 0;
			temp += i;
		}
		
		//System.out.println(temp); // 에러

	}

}

class Local {
	
	String name;
	
	void process() {
		name = "홍길동";
	}
	
	void printAge1() {
		int age = 20; // 지역변수
		System.out.println(age);
	}
	
	void printAge2() {
		int age = 30; // 지역변수
		System.out.println(age);
	}
	
}

 

실행 결과

null
홍길동
20
30

 

이 예제도 파일명은 LocalValSample 이고 내부에 Local 이라는 클래스가 하나 더 있는 구조입니다. main 메서드 내에서 Local 객체를 생성하고 name 인스턴스 변수를 출력했는데, 이 변수는 아직 초기화 되지 않았기 때문에 null로 출력이 됩니다.

그 아래에서는 local.process()  process 메서드가 실행되는데, Local 클래스의 process 메서드를 보면 name 변수에 홍길동 문자열을 대입하고 있습니다. 이 인스턴스 변수는 클래스 블록에 있기 때문에 사용 범위(scope)는 클래스 전체가 됩니다. 그래서 이 process 메서드가 실행되고 나서 다시 name 인스턴스 변수를 출력하면 이제 홍길동이라고 출력이 됩니다.

그 아래에서는 printAge1()printAge2() 메서드를 실행하고 있습니다.

local.printAge1();
local.printAge2();

 

Local 클래스의 해당 메서드 영역을 살펴보면 printAge1() 메서드 블록 안에는 int age = 20;이, printAge2() 메서드 블록 안에는 int age = 30; 이 정의되어 있습니다. age라는 변수는 지역변수로 각 메서드 안에서만 사용되는 변수입니다. 그래서 두 변수는 서로 전혀 관계없이 별개의 변수가 됩니다. 그리고 for문 블록 안에는 temp라는 변수가 정의되어 있는데, 이 변수 역시 for문 안에서만 사용되는 변수이며 지역변수입니다. 그래서 for문 밖에서 temp변수를 출력하려고 하면 에러가 나게 된다. 또, for문의 i 변수 역시 지역변수입니다. 이 지역변수들의 공통점은 변수가 선언된 곳 (메서드 블록 내부나 for문 블록 내부)에서 선언되면 해당 블록안에서만 사용가능하다는 것입니다.

 

지역변수의 사용가능한 범위를 쉽게 구분 지을 수 있는 방법은 범위(scope)는 중괄호 블록으로 구분 지으면 쉽게 이해할 수 있습니다. 선언된 중괄호 안에서는 어디서든 사용 될 수 있습니다. Local 클래스의 인스턴스 변수는 지역변수가 아니라, 전역변수입니다. 즉 메서드안에 선언된 변수는 아니지만, 클래스 블록(중괄호) 안에 선언 되었기 때문에 클래스 중괄호 안에서는 모두 사용 될 수 있습니다.

printAge1() 메서드의 age 변수도 마찬가지로 printAge1() 메서드의 중괄호가 곧 사용범위(scope)가 되는 것입니다. for문도 마찬가지로 for문의 영역을 나타내는 중괄호가 사용범위가 됩니다.