본격적으로 들어가기전에!
우선, 인터페이스는 하나의 규격(틀)으로, 생성자가 없다. 즉, 인스턴스화할 수도, 실행시킬 수도 없다.
익명 구현 객체란 익명 클래스를 통해 만들어진 객체로 일회성이라는 목적성이 있다.
즉, 메모리 heap영역에 객체를 기록할 뿐, 객체의 이름이 없다.
클래스는 인터페이스와 달리 객체를 찍어내는 생성자()가 반드시 존재해야 한다.
클래스 구성요소는 인스턴스와 정적(static) 구성요소가 있다.
중첩 클래스
----------------------------------------------------------------------------------------
▶중첩 클래스는 내부 클래스라고도 불린다. (inner class)
▶클래스 내부에 클래스를 선언하여 외부 클래스의 필드 접근에 용이하기 위함.
(클래스의 밖에서 접근하게 되면, 객체화를 해줘야하는 불편함이 있다.)
▶내부 클래스의 필드를 사용하기 위해서는 외부클래스를 먼저 객체화하고, 외부클래스에서 내부클래스를 객체화해야만 접근할 수 있다.
▶[형식]
class 클래스명 {
class 내부클래스명 {
...
}
}
▶[객체 형식]
외부클래스명 객체명 = new 외부클래스생성자();
외부클래스명.내부클래스명 객체명 = 외부클래스명.new 내부클래스생성자();
(내부 클래스 타입) (이미 메모리에 올라가있는 외부클래스 객체 안에 내부클래스 객체를 생성)
▶내부클래스를 사용하는 이유?
1. 외부클래스의 필드들을 자유롭게 쓸 수 있다.
2. "캡슐화" (궁극적 목적)
→ A 클래스에서 b라는 작업이 자주 쓰이고, 이 작업은 B클래스를 만들어야 쉽게 관리할 수 있음.(->클래스를 사용하는 이유) 하지만, 다른 클래스에서는 b작업이 필요없거나(A클래스에서만 필요히거나), B클래스를 외부에 노출시키고 싶지 않을때 캡슐처럼 가둬놓기 위해 사용한다.
3. 좋은 가독성과 유지 보수성
▶내부 클래스는 GUI(Graphic User Interface) 개발 시 많이 사용됨.
ex) a라는 버튼과 b라는 버튼이 있다. 두 버튼의 기능이 서로 다르다면, 클래스를 별도로 만드는 것보다 내부 클래스로 만들어 사용하는 것이 적합하다. (클래스 안에 a버튼/b버튼 클래스 생성)
▶내부클래스로는 인스턴스 멤버 클래스와 정적 멤버 클래스, 그리고 로컬클래스가 있다.
선언 위치에 따른 분류 | 선언 위치 | 설명 | |
멤버 클래스 |
인스턴스 멤버 클래스 | class A { class B { ... } } |
A객체를 생성해야만 (B객체도 생성하여) 사용할 수 있는 B 중첩 클래스 |
정적 멤버 클래스 | class A { static class B { ... } } |
A클래스로 바로 접근가능 한 B 중첩 클래스 | |
로컬 클래스 | class A { void method() { class B { ... } } } |
method()가 실행할 때만 사용할 수 있는 B 중첩 클래스 |
(1) 인스턴스 멤버 클래스 (내부 클래스)
----------------------------------------------------------------------------------------
▶밖에 있는 클래스는 내부 클래스를 멤버변수처럼 사용할 수 있다.
▶But, 사용하려면 new로 인스턴스 객체를 만들어야 한다.
▶자신의 밖에 있는 클래스의 자원을 직접 사용할 수 있다.
[형식]
class Outer {
public class Inner {
Inner() {} //생성자
인스턴스 요소;
//정적 요소 (x)
}
}
-> 클래스는 외부클래스의 것을 모두 사용할 수 있다. 반면 내부에서는 static 구성요소를 가질 수 없다.
[객체 생성]
Outer o = new Outer();
Outer.Inner in = Outer.new Inner();
//객체 사용
in.필드 = 2;
in.메서드();
(2) 정적 멤버 클래스
----------------------------------------------------------------------------------------
▶모든 종류의 필드, 메서드 선언 가능하다. (정적, 인스턴스 모두)
▶밖에 있는 클래스의 변수와 메서드 중 (정적)static이 붙은 것들만 사용할 수 있다.
▶외부 클래스의 객체가 없어도 Inner 클래스의 객체 생성이 가능하다. (즉, 인스턴스가 없어도 생성 가능)
[형식]
class Outer {
static class Inner {
인스턴스 요소;
static 요소;
}
}
[객체 생성]
Outer.Inner stin = new Inner();
stin.인스턴스필드 = 3;
stin.인스턴스메서드();
Outer.Inner.정적필드 = 3;
Outer.Inner.정적메서드();
->Outer 클래스의 객체가 없어도 Inner 클래스의 객체 생성이 가능하다.
Q.정적요소에 접근할 때는 클래스로 접근하는데, 왜 그럴까?
A.내 예상은, 정적요소에 접근할 때는 인스턴스가 존재하지 않기 떄문에, 쓸수 없어서 그러한 것이라고 생각한다....
▶클래스가 생성될 때 순서는, static이 먼저 생성되고 인스턴스가 생성된다.
비교해보자면, static은 이미 종이가 만들어져있고 그위에 인스턴스, static을 쓸 수 있다.
반면 인스턴스는 종이자체(static)가 안만들어져있어서 그위에 static을 쓸 수 없다.
▶멤버클래스의 사용제한에 차이점이 있다.
내부 클래스 | (내부) 생성 | (외부) 접근 |
인스턴스 클래스 | 인스턴스요소만 가질 수 있음. | 밖에 있는 인스턴스&static 요소 둘다 쓸수 있음. |
정적(static) 클래스 | 인스턴스&static 요소 둘다 가질 수 있음. | static요소만 쓸 수 있음. |
▶한가지 더 객체를 생성할 때 차이점이 있다.
인스턴스 내부 클래스는 외부 클래스의 객체로 내부 클래스의 객체를 생성하여 접근해야하지만
정적 내부 클래스는 외부 클래스의 객체 없이 내부 클래스의 객체를 생성할 수 있다.
▶static 클래스가 instance는 쓸수 없는 이유? static 구성요소가 만들어지는 그 순간에는 instance가 없기때문에 생성되지않은 instance요소에 당연히 접근할 수 없다.
▶중첩클래스에서 바깥 클래스 참조 얻는 방법
- 내부클래스안에 메서드안에서 내부(자신의) 객체 참조는 this.~
- 내부클래스안에 메서드안에서 바깥 객체 참초는 바깥클래스명.this~
1. 클래스 코드 (로컬클래스 관련)
public class Outter {
String filed = "Outter-field";
void method() {
System.out.println("Outter-method");
}
class Nested {
String field = "Nested-field";
void method() {
System.out.println("Nested-method");
}
void print() {
System.out.println(this.field); //Nested-field 출력
this.method(); //Nested-method 출력
System.out.println(Outter.this.field); //Outter-field 출력
Outter.this.method(); //Outter-method 출력
}
}
}
2. 메인 코드
public class OutterEx {
public static void main(String[] args) {
Outter ot = new Outter();
Outter.Nested nt = ot.new Nested();
nt.print(); //Nested내부 클래스의 print()메서드 실행.
}
}
(3) 로컬 클래스
----------------------------------------------------------------------------------------
▶메소드 내부에 클래스를 정의하는 경우.
▶메소드 내부에서 지역변수처럼 쓰이기 때문에, 메소드 내부에서 new한 뒤 객체로 접근해야한다.
▶메소드 밖에서는 사용할 수 없다.
▶자바8 이후, 매개변수와 로컬변수는 final을 붙이지않아도 자동 final 특성을 가짐.
[형식] 메서드 내부클래스는 메서드 내부에서 [객체 생성]도 함께 해주고 접근까지! 메서드 외부에선 사용 불가.
class Outer {
인스턴스 요소;
메서드1() {}
메서드2() {
class Inner {
인스턴스 요소;
//정적 요소 (x)
}
Inner in = new Inner();
in.필드 = 3;
in.메서드();
}
//여기선 접근할 수 없음. 메서드2() 외부이기 때문.
}
->객체 생성은 메서드 내에서 new로 생성하고 접근해야 한다. 메서드 밖으로 가면 사라지기 때문.
▶메소드 내에서 선언된 클래스. 그렇기 때문에 지역이 끝나면 사라짐.
**보통 실질적인 데이터는 heap영역에, 레퍼런스변수는 stack영역에 저장되고, stack영역에서 heap영역을 가리키는 원리. 그래서 stack영역에서 레퍼런스변수가 빠져나가면 (다시는 heap영역에 있는 객체를 못찾게 되고) 가비지가 된다.
▶그러나, 로컬 변수는 stack영역에 기록된다. 그래서 선언된 후에 지역이 끝나면 stack에서 빠져나가서 없어진다.
예제
----------------------------------------------------------------------------------------
클래스 A 내부에 인스턴스 멤버클래스 B , 정적 멤버클래스 C, 인스턴스 로컬 클래스 D 가 있는 예제를 보자..
1. 인스턴스와 클래스 코드
public class A {
A() { System.out.println("A 객체가 생성됨"); }
/*인스턴스 멤버 클래스*/
public class B {
B() { System.out.println("B 객체가 생성됨"); }
int field1; //인스턴스 필드
void method1() {} //인스턴스 메서드
}
/*정적 멤버 클래스*/
static class C {
C() { System.out.println("C 객체가 생성됨"); }
int field1;
static int field2; //정적 필드
void method1() {};
static void method2() {}; //정적 메서드
}
void method() { //인스턴스 메서드
/*인스턴스 로컬 클래스*/
class D {
D() { System.out.println("D 객체가 생성됨"); }
int field1; //인스턴스 필드
void method1() {} //인스턴스 메서드
}
D d = new D();
d.field = 3;
d.method1();
}
}
2. main 실행 코드
public class Main_Ex {
public static void main(String[] args) {
A a = new A();
A.B b = a.new B();
b.field1 = 3;
b.method1();
A.C c = new A.C(); //정적 클래스기때문에..? 정확히 왜 해주는지 알아봐야겠다.
c.field1 = 3;
c.method1();
A.C.field2 = 3; //정적 필드니까 그냥 클래스로 접근!
A.C.method2();
a.method(); //인스턴스 메서드이니까 클래스 A의 객체 a로 접근. 메서드를 호출하면 로컬 클래스 D도 호출될 예정.
}
}
'언어 > Java' 카테고리의 다른 글
기초 #9. 인터페이스 Soldier (인터페이스 예제편) (0) | 2020.09.01 |
---|---|
기초 #8. 인터페이스 (+익명 구현 객체) (0) | 2020.08.31 |
기초 #6. 인터페이스 예습 (0) | 2020.08.23 |
기초 #5. 추상클래스 (ex. 계산기 프로그램) (0) | 2020.08.22 |
기초 #4. 상속과 다형성 (0) | 2020.08.21 |