본문 바로가기

JAVA

[Snack Java] 14. 자바 클래스 구성요소(변수, 필드, 메서드, 생성자, 인스턴스, 내부 클래스, 정적 멤버, this)


요약

1. 자바의 변수 종류

  • 클래스 변수
  • 인스턴스 변수
  • 지역 변수

2. 필드(Field): 클래스, 객체의 속성을 나타내는 변수. 객체가 사용하는 내부 데이터

  • 필드의 구성요소: 클래스 변수, 인스턴스 변수
  • 객체가 사용하는 내부 데이터

3. 메서드(Method): 클래스의 기능을 나타내는 함수. 객체 내부의 명령코드뭉치

  • 메서드는 메서드 시그니처와 메서드 바디로 구분
  • 메서드 시그니처(Method Signature): 메서드의 반환타입, 접근권한, 메서드명, 매개변수를 담은 영역
  • 메서드 바디(Method Body): 실제 메서드의 기능을 담은 영역, 지역 변수를 포함한다.
  • 메서드 오버로딩(Method Body): 같은 메서드명의 여러 메서드를 제작하는 것. "같은 메서드명 + 다른 매개변수"의 형태를 지닌다.
  • 메서드 오버로딩의 장점: 여러가지 경우의 수를 하나의 메서드로 해결 가능

4. 생성자(Constructor): 객체를 생성하는 기능을 담당하는 함수(메서드)

  • 인스턴스가 생성될 때 자동으로 호출되는 '인스턴스 초기화 메서드'
  • 생성자의 이름은 반드시 클래스의 이름과 같아야 함
  • 생성자는 리턴 타입이 없음
  • 생성자는 일종의 특수 메서드로 메서드 오버로딩이 가능
  • this 키워드: 메서드의 매개변수와 클래스의 필드 변수의 이름이 같을 때 이 두 변수를 구분하기 위한 용도. this는 인스턴스 자신을 가리키며, this를 통해 인스턴스 자신의 변수에 접근
  • this() 메서드: 생성자도 일반적인 메서드처럼 상호 호출이 가능, this() 메서드는 자신이 속한 클래스에서 다른 생성자를 호출하는 경우에 사용.  this()메서드는 반드시 생성자 내부에서 사용하며 생성자 첫 줄에 위치

5. 내부클래스(Inner Class): 클래스 내부의 클래스

  • 외부 클래스(OuterClase)와 내부 클래스(Inner Class)가 서로 연관되어 있을 때 사용
  • 내부 클래스는 외부 클래스의 멤버들을 쉽게 접근 할 수 있고, 코드의 복잡성을 줄일 수 있음.
  • 인스턴스 내부 클래스(Instance Inner Class): 객체 내부에 멤버의 형태로 선언. 외부 클래스의 접근제어자에 관계 없이 접근 가능
  • 정적 내부 클래스(Static Inner Class) : 내부 클래스가 외부 클래스 생성과 무관하게 정적 변수를 사용하려 할 때 정적 내부 클래스를 선언. 정적 내부 클래스는 멤버 변수 위치에 정의하며 static 예약어를 사용.  외부 클래스의 접근제어자에 관계 없이 접근 가능
  • 지역 내부 클래스(Local Inner Class): 메서드 내에서 정의되는 클래스. 지역 변수와 같이 메서드 내부에서만 사용가능하며 메서드가 호출될 때만 메모리에 로딩되기 때문에 정적 클래스로 지정할 수 없음. 일반적으로 선언 후 바로 객체를 생성

 

 

 

이번에는 자바의 클래스와 객체의 구성요소에 대해 알아보겠습니다.

 

1. 자바의 변수 종류: 클래스 변수(Class Variable), 인스턴스 변수(Instane Variable), 지역 변수(Local Variable)

자바에서는 변수를 크게 3가지로 나눕니다.

  • 클래스 변수(cv, Class variable): 클래스 내에서 static으로 선언된 변수. 힙메모리에 저장된다. 공통된 저장 공간을 공유하여, 한 클래스로부터 생성되는 모든 인스턴스들이 특정한 값을 공유해야하는 경우에 사용한다. 공통값을 가리킨다. 인스턴스 변수와 달리 인스턴스를 생성하지 않고 "클래스명.클래스변수명"(ex. ClassExample.classVar)  형태로 사용 가능하다. 초기화를 실행하지 않더라도 강제로 초기화가 된다.
  • 인스턴스 변수(iv, Instance variable): 클래스 내에서 선언된 변수. 힙메모리에 저장된다. 인스턴스가 가지는 고유한 속성을 저장하기 위한 변수로 new 생성자()를 통해 인스턴스가 생성될 때 만들어진다. 클래스를 통해 만들어진 인스턴스는 힙 메모리(Heap memory)의 독립적인 공간에 저장되며, 객체의 고유한 개별성을 가진다. 즉, 개별값을 가리킨다. 초기화를 실행하지 않더라도 강제로 초기화가 된다.
  • 지역 변수(lv, Local variable): 메서드 내에서 선언된 변수. 스택 메모리에 저장된다. 메서드 내({ }블록 내부)에서만 사용가능한 변수로, 멤버 변수와는 달리 스택 메모리에 저장되어 메서드가 종료되면 동시에 소멸된다. 스택 메모리는 강제로 초기화되지 않으므로 지역 변수는 반드시 초기화를 실행해주어야 한다.

 

예시를 보면 다음과 같습니다.

public class ClassExample { // 클래스 영역
	int instanceVar; // 인스턴스 변수
	static int classVar; // 클래스 변수 (static 변수, 공유변수)
	 methodExmaple() { // => 메서드 영역
		int localVar = 0; // 지역 변수. {}블록 안에서만 유효
	}
}

 

표로 한 번 정리해보겠습니다.

 

  클래스 변수
(cv, Class Variable)
인스턴스 변수
(iv, Instane Variable)
지역 변수
(lv, Local Variable)
메모리 저장 위치 힙 메모리(Heap Memory) 힙 메모리(Heap Memory) 스택 메모리(Heap Memory)
유효 범위(Scope) 클래스 내부/외부 클래스 내부 메서드(method) 실행문 내
영역 클래스 필드(Class Field) 클래스 필드(Class Field) 클래스 메서드(Class Method)
할당 방법 static 키워드 사용하여 클래스 실행문 영역에 선언 클래스 실행문 영역에 선언 메서드 내 실행문에 선언
강제 초기화 유무 강제 초기화  강제 초기화 직접 초기화 필요
사용 방법 "Class명.멤버명"으로 사용
인스턴스(객체) 생성 없이 사용
인스턴스(객체) 생성 후 사용 메서드 실행 시 자동 사용

 

2. 필드(Field): 클래스, 객체의 속성을 나타내는 변수 

필드(Field)의 구성요소로는 '클래스 변수(Class Variable)'와 '인스턴스 변수(Instance Variable)'가 있습니다. 두 속성의 차이는 static 키워드의 유무로 확인 가능합니다. 이 필드는 객체가 사용할 '데이터'에 해당합니다.

public class ClassExample { // 클래스 영역
	int instanceVar; // 인스턴스 변수
	static int classVar; // 클래스 변수 (static 변수, 정적 멤버)
}

 

★ static 키워드란?

static은 클래스의 멤버(필드, 메서드, 이너클래스)에 사용하는 키워드입니다. static 키워드가 붙어있는 멤버를 정적 멤버(static member)라고 부르고 static이 붙어있지 않은 인스턴스 변수와 구분합니다. 인스턴스 멤버는 객체를 생성한 이후에 변수와 메서드에 접근하여 멤버를 사용하지만, 정적 멤버는 인스턴스 생성 없이도 사용 가능합니다. 즉, 클래스 변수인 정적 멤버는 클래스 메모리 공간에 저장되기 때문에 객체를 생성하지 않고 사용 가능하지만, 인스턴스는 독립적인 저장공간이 생겨야 사용 가능하다는 것입니다.

 

3. 메서드(Method): 클래스의 기능을 나타내는 함수

메서드는 객체의 주요 구성요소인 속성과 기능 중 기능에 해당하며, 명령코드 뭉치를 의미합니다. 즉, 특정 작업을 수행하는 일련의 명령문의 집합입니다. 메서드는 메서드의 반환타입, 메서드 명, 매개변수를 나타내는 메서드 시그니처(Method Signature)와 실제 작업을 담은 실행문인 메서드 바디(Method Body)로 구분합니다.

public class ClassExample { 
	int instanceVar; 
	static int classVar; 
   // 접근제어자 정적여부 반환타입 메서드명(매개변수)
	 public static int methodExmaple(int x, int y) { // => 메서드 시그니처

		int result = x + y; // 메서드 바디
        	return result;
	}
}

 

★ 메서드 오버로딩(method overloading)이란?

메서드 오버로딩은 같은 이름의 메서드를 여러개 쓰는 형태를 말합니다. 메서드 시그니처의 형태에 따라 같은 이름을 가진 메서드라도 서로 다른 메서드 바디를 가지고 다른 결과값을 낼 수 있습니다. 자바 가상머신은 메서드 시그니처의 매개변수의 타입이나 매개변수의 수가 달라지면 서로 다른 메서드로 인식합니다.

즉,  메서드 오버로딩이란 '같은 메서드명 + 다르게 정의된 매개변수(개수 or 타입)'로 정의할 수 있습니다.   메서드 오버로딩을 사용하면 여러가지 경우의 수를 한번에 관리 할 수 있습니다.

 

public class Overloading {
    public static void main(String[] args) {
        Shape s = new Shape(); // 객체 생성

        s.area(); // 메서드 호출
        s.area(5);
        s.area(10,10);
        s.area(6.0, 12.0);
    }
}

class Shape {
    public void area() { // 메서드 오버로딩. 같은 이름의 메서드 4개.
        System.out.println("넓이");
    }
    public void area(int r) {
        System.out.println("원 넓이 = " + 3.14 * r * r);
    }

    public void area(int w, int l) {
        System.out.println("직사각형 넓이 = " + w * l);
    }

    public void area(double b, double h) {
        System.out.println("삼각형 넓이 = " + 0.5 * b * h);
    }
}

//출력값
넓이
원 넓이 = 78.5
직사각형 넓이 = 100
삼각형 넓이 = 36.0

 

4. 생성자(Constructor): 객체를 생성하는 기능을 담당하는 함수(메서드)

생성자(Constructor)는 객체를 생성하는 역할을 하는 클래스의 구성 요소로서, 인스턴스가 생성될 때 자동으로 호출되는 '인스턴스 초기화 메서드'입니다. 즉, 인스턴스(객체)를 생성 과정을 보면 우리는 new 키워드를 통해 생성 명령을 내리고 생성자를 이용해 생성하는 인스턴스의 초기화를 실행하는 것입니다.

 

인스턴스 생성 예

MainClass mainObj = new MainClass();
// 반환타입 객체명 = 인스턴스생성연산자 생성자

 

이렇게 인스턴스를 초기화하는 생성자는 메서드와 같은 구조를 가지고 있습니다. 하지만 차이가 있는 부분이 존재합니다.

 

public class ClassExample { 
	public class ClassExample(){} // 생성자

	 public static int methodExmaple(int x, int y) { // 메서드
		int result = x + y; 
        	return result;
	}
}

 

위에서 보면 알 수 있듯이

 

1. 생성자의 이름은 반드시 클래스의 이름과 같아야 합니다.

2. 생성자는 리턴 타입이 없습니다.

 

또한 만약 생성자를 직접 지정해주지 않으면 기본 생성자를 컴파일러가 알아서 생성해주기 때문에 생성자에 사용할 매개변수가 없다면(조작하지 않는 경우에는) 생성자를 생략하는 경우가 많습니다. 

 

매개변수가 있는 생성자는 메서드처럼 매개변수를 통해 호출 시에 해당 값을 받아 인스턴스를 초기화하는 데 사용됩니다. 메서드 오버로딩으로 매개변수를 조작한다면, 고유한 특성을 가진 인스턴스를 계속 만들어야하는 경우 인스턴스마다 각기 다른 값을 가지고 서로 다르게 초기화할 수 있어서 매우 유용합니다. 

 

생성자는 일종의 특수 메서드로 메서드 오버로딩이 가능합니다. 클래스와 객체는 이 메서드 오버로딩을 통해 매개변수별로 여러 생성자를 가질 수 있으며 이는 객체의 활용형태를 결정합니다. 

 

class IPad {
    public static void main(String[] args) {
        IPad iPad1 = new IPad();
        IPad iPad2 = new IPad("아이패드 프로 12.9", 1600000)
    }

    public String model;
    public int price;

    public IPad(){} // 기본생성자. 생성자가 없는 경우 자동 생성

		// 생성자 오버로딩
    public IPad(String model, int price) { // 매개변수가 있는 생성자
        this.model = model;
        this.price = price;
        
        System.out.printf("제 " + model + "의 가격은 " + price + "입니다.");
    }
}

// 출력값: 제 아이패드 프로 12.9의 가격은 1600000입니다.

//기본 생성자로 생성한 인스턴스는 아무런 결과값이 없음

 

위와 같이 인스턴스 생성 또는 사용 시에 매개변수의 형태에 따라 다른 결과값을 줄 수 있습니다.

 

★ this 키워드?

메서드의 매개변수와 클래스의 필드 변수의 이름이 같을 때 이 두 변수를 구분하기 위한 용도로 쓰입니다. 원래 모든 메서드에는 자신이 포함된 클래스의 객체를 가리키는 this 참조변수가 존재합니다.(일반적으로는 컴파일러가 알아서 추가를 해주고 있습니다.) 결론적으로 this는 인스턴스 자신을 가리키며, this를 통해 인스턴스 자신의 변수에 접근할 수 있습니다.

즉, 위의 this.model = model에서 this.model은 인스턴스 변수(필드 멤버)를 가리키고 model은 생성자 내부 매개변수를 가리키는 것이죠. 저 식의 의미를 풀어보면 "인스턴스 변수 (this.)model에 생성자를 이용해 입력받을 매개변수의 model을 할당한다"는 의미입니다.

 

★ this() 메서드?

위 예에서는 등장하진 않지만 this() 메서드가 존재합니다. 생성자도 일반적인 메서드처럼 상호 호출이 가능합니다.이를 위해 사용하는 것이 바로 this() 메서드입니다. 즉, this() 메서드는 자신이 속한 클래스에서 다른 생성자를 호출하는 경우에 사용합니다.

예를 들어 만약 클래스명이 IPad라면, IPad 클래스의 생성자를 호출하는 것은 IPad()가 아니라 this()이고, 그 효과는 IPad() 생성자를 호출하는 것과 동일합니다.  this() 메서드의 특징을 정리하자면

1. this()메서드는 반드시 생성자 내부에서 사용한다.

2. this()메서드는 반드시 생성자 첫 줄에 위치한다.

이 두 가지가 있습니다. 예제를 보겠습니다.

 

class IPad {
    public static void main(String[] args) {
        IPad iPad1 = new IPad("아이패드 에어 2세대");
        IPad iPad2 = new IPad("아이패드 프로 3세대 12.9", 1600000);
    }

    public String model;
    public int price;

    public IPad(String model){
        System.out.println(model + "입니다."); // 1번째 생성자
    }

    public IPad(String model, int price) {
        this(model); // 이전 생성자 호출
        this.model = model;
        this.price = price;

        System.out.printf("제 " + model + "의 가격은 " + price + "입니다.");
    }
}

/*
아이패드 에어 2세대입니다.
아이패드 프로 3세대 12.9입니다.
제 아이패드 프로 3세대 12.9의 가격은 1600000입니다.

-> 1번째 + (1번째 생성자+2번째 생성자)
 */

 

5. 내부클래스(Inner Class): 클래스 내부의 클래스

내부 클래스(Inner Class)는 클래스 내에 선언된 클래스입니다. 외부 클래스(OuterClase)와 내부 클래스(Inner Class)가 서로 연관되어 있을 때 사용합니다. 내부 클래스는 외부 클래스의 멤버들을 쉽게 접근 할 수 있고, 코드의 복잡성을 줄일 수 있습니다.

 

class OuterClass { // 외부 클래스
	
	class InnerClass {
		// 인스턴스 내부 클래스	
	}
	
	static class StaticInnerClass {
		// 정적 내부 클래스
	}

	void method() {
		class LocalInnerClass {
		// 지역 내부 클래스
		}
	}
}

 

클래스의 종류를 표로 정리해보겠습니다.

 

종류 선언 위치 사용 가능한 변수
인스턴스 내부 클래스
(Instance Inner Class)
외부 클래스의 멤버 변수 선언 위치에 선언 외부 인스턴스 변수, 외부 전역 변수
정적 내부 클래스
(Static Inner Class)
외부 클래스의 멤버 변수 선언 위치에 선언 외부 전역 변수
지역 내부 클래스
(Local Inner Class)
외부 클래스의 메서드나 초기화 블럭(생성자) 안에 선언 외부 인스턴스 변수, 외부 전역 변수
익명 내부 클래스
(Anonymous Inner Class)
클래스의 선언과 객체 생성을 동시에 하는 일회용 클래스 외부 인스턴스 변수, 외부 전역 변수

 

변수와 마찬가지로 필드 멤버에 해당하는 내부 클래스에는 인스턴스 내부 클래스정적 내부 클래스가 있습니다.

1. 인스턴스 내부 클래스(Instance Inner Class): 객체 내부에 멤버의 형태로 선언. 외부 클래스의 접근제어자에 관계 없이 접근 가능

2. 정적 내부 클래스(Static Inner Class) : 내부 클래스가 외부 클래스 생성과 무관하게 정적 변수를 사용하려 할 때 정적 내부 클래스를 선언. 정적 내부 클래스는 멤버 변수 위치에 정의하며 static 예약어를 사용.  외부 클래스의 접근제어자에 관계 없이 접근 가능

 

이외에 메서드 내에 존재하는 지역 변수와 같은 위치에 선언 가능한 지역 내부 클래스가 있습니다. 

 

3. 지역 내부 클래스(Local Inner Class): 메서드 내에서 정의되는 클래스. 지역 변수와 같이 메서드 내부에서만 사용가능하며 메서드가 호출될 때만 메모리에 로딩되기 때문에 정적 클래스로 지정할 수 없음. 일반적으로 선언 후 바로 객체를 생성한다.