자바/기본

자바 - 클래스 (내부클래스-인스턴스 클래스,static 클래스)

backend dev 2024. 3. 6.

내부 클래스(= 중첩클래스,Inner클래스)

내부 클래스(inner class)란 하나의 클래스 내부에 선언된 또 다른 클래스를 의미한다.
보통 사용자 클래스 자료형이 필요하면, 
메인 클래스 외부에 선언하거나, 따로 독립적인 클래스 파일을 만들어 불러와 사용해 왔다. 

내부 클래스는 대신 클래스 내에 선언되어 사용되며, 
내부에 정의된다는 점을 제외하고는 일반적인 클래스와 다르지 않다. 

우리가 어느 클래스에 변수나 상수가 필요하다면 클래스 멤버로서 클래스 내에서 선언하여 사용해 왔듯이, 
선언 주체를 변수에서 클래스로 바꾼다면 그것이 내부 클래스인 것이다.

이처럼 내부 클래스는 보통 두 클래스가 서로 긴밀한 관계가 있거나
하나의 클래스또는 메소드에서만 사용되는 클래스일 때 이용되는 기법이라고 보면 된다.

 

 

public class Outer { //메인클래스

    class Inner{ //내부클래스

    }

    class Inner2{ //내부클래스

    }

    public void method() { // 메서드안에서 사용가능
        Inner inner = new Inner();
        Inner2 inner2 = new Inner2();
    }

}

 

 

내부 클래스의 장점

 

1. 클래스를 논리적으로 그룹화

 

클래스가 여러 클래스와 관계를 맺지 않고 하나의 특정 클래스와만 관계를 맺는다면
외부에 클래스를 새로 작성하는 것이 아닌 내부 클래스로 작성할 수 있다.

이런 경우 내부 클래스와 외부 클래스를 함께 관리하는 것이 가능해 유지보수 면에서나 코드 이해성 면에서 편리해진다.
또한 내부 클래스로 인해 새로운 클래스를 생성하지 않아도 되므로 패키지를 간소화할 수 있다.


코드로 예시를 들어보자.
평소대로라면 아래처럼 외부 혹은 다른 파일로 클래스를 선언해놓고, 인스턴스화 하여 사용해왔을 것이다.

public class Outer { //메인클래스
    
    int var;

    public void method() {
        Another another = new Another();
    }
}

class Another{
    
}

 

Another 클래스가 오직 Outer클래스안에서만 사용된다면 이렇게 외부에 선언하거나 새로 자바파일을 만들것이 아니라  

public class Outer { //메인클래스

    int var;

    class Another{

    }

    public void method() {
        Another another = new Another();
    }

}

이렇게 내부에 넣고 사용하는것이 코드 이해에 도움이 된다.

 

2. 더욱 타이트한 캡슐화의 적용

내부 클래스에 private 제어자를 적용해줌으로써, 캡슐화를 통해 클래스를 내부로 숨길 수 있다.
즉, 캡슐화를 통해 외부에서의 접근을 차단하면서도, 내부 클래스에서 외부 클래스의 멤버들을 제약 없이 

쉽게 접근할 수 있어 구조적인 프로그래밍이 가능해 진다. 

그리고 클래스 구조를 숨김으로써 코드의 복잡성도 줄일 수 있다.

public class Outer { //메인클래스
    private int outVar = 100;
    class Inner{
        private int InnerVar = 200;

        void innerMethod() {
            System.out.println(outVar); // 내부클래스에서 외부클래스의 private 멤버접근가능
        }

    }
    void outMethod() {
        Inner inner = new Inner();
        System.out.println(inner.InnerVar); // 외부클래스에서 내부클래스의 private 멤버접근가능
        inner.innerMethod();
    }
}

 

3. 가독성이 좋고 유지 관리가 쉬운 코드

결국은 내부 클래스를 작성하는 경우 클래스를 따로 외부에 작성하는 경우보다, 

물리적으로 논리적으로 외부 클래스에 더 가깝게 위치하게 된다. 

 

따라서 시각적으로 읽기가 편해질 뿐 아니라 유지보수에 있어 이점을 가지게 된다.


한 클래스를 다른 클래스의 내부 클래스로 선언하면 두 클래스 멤버들 간에 서로 자유로이 접근할 수 있고, 

그리고 외부에는 불필요한 클래스를 감춰서 

클래스간의 연관 관계 따지는 것과 같은 코드의 복잡성을 줄일 수 있다는 장점이 있기 때문이다.


간단하게 말하자면 
어차피 A 클래스안에서만 사용하기 위한 클래스이니 

괜히 연관관계 생각없이 내부에 선언해 직관적으로 사용하자는 취지인 것이다.

 

내부 클래스 종류

클래스 멤버 변수도 선언되는 위치나 접근제어자에 따라 역할과 이름이 달라지듯이, 

내부 클래스도 선언된 위치, static 키워드의 유무 등에 따라 4가지로 내부 클래스가 구분된다.

 

 

1.인스턴스 클래스(instance class)

외부 클래스의 멤버변수 선언 위치에 선언하며, 외부 클래스의 인스턴스 멤버처럼 다뤄진다.

주로 외부 클래스의 인스턴스 멤버들과 관련된 작업에 사용될 목적으로 선언된다.

- 클래스의 멤버 변수 선언부에 위치하고 static 키워드가 없는 내부 클래스
- 외부 클래스의 멤버로 취급되기 때문에 외부 클래스의 객체 먼저 생성한 후 내부 클래스의 객체를 생성이 가능하다


- 인스턴스 클래스 내부에는 instance 멤버만 선언할 수 있다. (static 멤버는 선언 불가)

-> Static declarations in inner classes are not supported at language level '11'라는 오류가 발생 (자바 16부터 사용가능)


- 주로 외부 클래스의 인스턴스 멤버들과 관련된 작업에 사용될 목적으로 선언된다

public static void main(String[] args) {
    Outer outer = new Outer();
    Inner inner = outer.new Inner(); 
    // 이런식으로 외부클래스의 객체를 생성 후 그 객체를 이용하여 내부클래스의 객체를 생성가능하다.  
}

외부 클래스를 인스턴스화하면 외부 클래스의 코드가 메모리에 올라오게 되고

이 때 내부 클래스의 코드도 메모리에 올라오게 된다.

이렇게 코드를 메모리에 올린 이후에야 내부 클래스의 인스턴스를 생성할 수가 있다.

다만 내부 클래스는 다른 클래스에서 직접 사용하는 것보다 해당(외부) 클래스에서만 사용하는 것이 일반적이므로,

위의 메인 코드와 같이 내부 클래스의 인스턴스를 다른 클래스에서 만드는 경우는 드물다.

 

2. static 클래스 (정적 클래스)

- static 키워드가 붙은 내부 클래스

- static 클래스 내부에는 instance 멤버와 static 멤버 모두 선언 할 수 있다.
- 그러나 일반적인 static 메서드와 동일하게 외부 클래스의 인스턴스 멤버에는 접근이 불가하고, 

  정적(static) 멤버에만 접근할 수 있다.

public class Outer {
    private int outVar = 100;
    static int staticOutVar = 1000;
    static class Inner{ // <----  static 내부 클래스
        private int InnerVar = 200;

        void innerMethod() {
//            System.out.println(outVar); // 외부클래스의 일반 멤버는 접근 불가능
            System.out.println(staticOutVar); // 외부클래스의 static 멤버는 접근 가능
            System.out.println(InnerVar);
        }
        static void innerStaticMet() {
            System.out.println(staticOutVar);
//            System.out.println(InnerVar); // static 메소드이므로 비정적 멤버는 접근 불가능
        }
    }
}


public class Main2 {

    public static void main(String[] args) {
        Outer.Inner inner = new Outer.Inner(); //static 내부 클래스는 외부클래스 객체를 생성하지않아도 된다.
        Outer.Inner.innerStaticMet(); // 다음과 같이 static 내부 메소드도 사용가능
    }

}

 

 

내부클래스로 static 클래스를 사용해야하는 이유

위에서 인스턴스 클래스를 생성했을때를 다시보면

public static void main(String[] args) {
    Outer outer = new Outer();
    Inner inner = outer.new Inner(); 
    // 이런식으로 외부클래스의 객체를 생성 후 그 객체를 이용하여 내부클래스의 객체를 생성가능하다.  
}

외부 클래스의 객체를 생성하고 그 객체를 이용해서 내부 클래스 객체를 생성해줬다.

즉 내부의 인스턴스 클래스 ( == 비 정적 클래스 == static을 사용하지 않은 클래스) 는

외부 클래스에 대한 참조가 필요하다는 것이다. ( ==외부 클래스 객체에 대한 참조가 필요함)

 

Disassembler를 이용해서 비 정적 클래스( 인스턴스 클래스)를 살펴보면

내부에 바깥클래스에 대한 참조를 가지고있는것을 확인 할 수 있다.

 

정적 클래스( static 클래스)는 내부에 바깥클래스에 대한 참조를 가지고 있지않다.

 

즉 이것은

메모리 누수 가능성으로 이어진다.

비정적 내부 클래스의 경우 바깥 클래스에 대한 참조를 가지고 있기 때문에 메모리 누수가 발생할 여지가 있다. 

 

바깥 클래스는 더 이상 사용되지 않지만 내부 클래스의 참조로 인해 GC가 수거하지 못해서 

 

바깥 클래스의 메모리 해제를 하지 못하는 경우가 발생할 수 있다.

 

그래서 컴파일러 또한 아래와 같이 내부 클래스 사용시 static을 권장해준다.

Inner class 'Inner' may be 'static'

 

 

 

'자바 > 기본' 카테고리의 다른 글

try-with-resources  (2) 2024.03.19
디자인 패턴 - 추상 팩토리 패턴  (1) 2024.03.15
자바 - 상속  (0) 2024.03.11
자바 - 배열 , 제어문  (0) 2024.03.04

댓글