-클래스가 여러 클래스와 관계를 맺는 경우 독립적으로 선언하는 것이 좋으나, 특정 클래스와 관계를 맺을 경우에는 관계클래스를 클래스 내부에 선언하는 것도 좋다.
-클래스 내부에 선언한 클래스
-코드의 복잡성을 줄일 수 있음
-두 클래스의 멤버들을 서로 쉽게 접근할 수 있다는 장점
-외부에는 내부를 감춤으로써 정보은닉, 캡슐화, 암호화하여 사용할 수 있다.
class ClassName {
// 클래스 안에 클래스
class NestedClassName {
}
}
-static키워드 없이 선언된 클래스를 뜻함
-인스턴스 필드와 메소드만 선언 가능하고, 정적 필드와 메소드는 선언할 수 없다
/* 인스턴스 멤버 클래스 */
// static 불가
class B {
int field1;
//static int field2;
void method1() { }
// static void method2() { }
B() {
System.out.println("B 객체가 생성됨");
}
}
-static 키워드로 선언된 클래스
-static 이라 모든 종류의 필드와 메소드 선언 가능하다
/* 정적 멤버 클래스 */
static class C {
int field1;
static int field2;
void method1() { }
static void method2() { }
C() {
System.out.println("C 객체가 생성됨");
}
}
-중첩 클래스는 메소드 내에서도 선언 가능 = 로컬클래스
-접근제한자(public, private) 및 static을 붙일 수 없다
-메소드가 실행될 때 메소드 내에서 객체를 생성하고 사용해야한다.
void method() {
/* 로컬 클래스
메소드가 끝나면 D클래스는 힙메모리에서 소멸된다
*/
class D {
int field1;
// static int field2;
void method1() {
System.out.println("이곳은 로컬 클래스의 메소드");
System.out.println("field1 : " + field1);
}
// static void method2() { }
D() {
System.out.println("D 객체가 생성됨");
}
}
System.out.println("로컬클래스에서 넣은 구분자----------");
D d = new D();
d.field1 = 30;
d.method1();
}
중첩 클래스는 new로 직접적 객체 생성이 불가하고, 2단계로 진행된다.
1. 외부 클래스를 객체화한 후
2. 내부 클래스를 객체화한다
class Class {
class Nested {
}
}
public class AMain {
public static void main(String[] args) {
// 1단계
Class cn = new Class();
// 2단계
Class.Nested nested = cn.new Nested();
}
}
class A1 {
A1() {
System.out.println("A 객체가 생성죔");
}
/* 인스턴스 멤버 클래스 */
class B {
int field1;
//static int field2;
void method1() { }
// static void method2() { }
B() {
System.out.println("B 객체가 생성됨");
}
}
/* 정적 멤버 클래스 */
static class C {
int field1;
static int field2;
void method1() { }
static void method2() { }
C() {
System.out.println("C 객체가 생성됨");
}
}
void method() {
/* 로컬 클래스
메소드가 끝나면 D클래스는 힙메모리에서 소멸된다
*/
class D {
int field1;
// static int field2;
void method1() {
System.out.println("이곳은 로컬 클래스의 메소드");
System.out.println("field1 : " + field1);
}
// static void method2() { }
D() {
System.out.println("D 객체가 생성됨");
}
}
System.out.println("로컬클래스에서 넣은 구분자----------");
D d = new D();
d.field1 = 30;
d.method1();
}
}
public class Book395 {
public static void main(String[] args) {
A1 a = new A1();
// 인스턴스 멤버 클래스 객체 생성
A1.B b = a.new B();
b.field1 = 3;
b.method1();
// 정적 멤버 클래스 객체 생성
// 외부 클래스가 객체화되지 않아도 객체화 가능
// static이니까
A1.C c = new A1.C();
c.field1 = 3;
c.method1();
// static 형이기 때문에 표기가
// 클래스명.변수(메소드)명
// 메소드영역에 컴파일할때 할당됨
A1.C.field2 = 3;
A1.C.method2();
System.out.println("-----------------------");
// 로컬 클래스 객체 생성을 위한 메소드 호출
a.method();
}
}
실행화면
내부 클래스는 외부 클래스의 모든 멤버를 사용 가능
단, 생성자는 사용 불가
외부 클래스는 내부 클래스의 멤버를 사용할 수 없다
단, statc 필드값은 사용 가능
// 클래스 (외부 클래스)
class A{
// 필드
int a;
static double b = 3.14;
static final int c = 0;
// 일반 메소드
void aM() { }
// 일반 클래스에 추상 메소드 작성은 불가
// abstract void aaM();
// 정적메소드
static void stM() { }
// 생성자
A(){
// 내부에 있는 생성자 호출 불가
// AA();
System.out.println("외부 클래스의 기본 생성자 부분");
}
// 일반 메소드 오버로딩하기
void iM(int a) {
// 내부 클래스에 있는 필드값은 외부에서 사용 불가
// aa = 200;
// 내부에 있는 정적필드값은 사용 가능
int b = AA.SAA + a;
System.out.println("b 값 : " + b);
System.out.println(" 외부 클래스의 일반 메소드 부분");
// 내부 클래스에 잇는 일반 메소드 호출 불가
// aaM();
}
// 일반메소드 오버로딩하기
void aM(int a) { }
void aM(int a, String b) { }
boolean aM(boolean c) {
return false;
}
// 생성자 오버로딩
A(int a) { }
A(double b) { }
A(String c) { }
// 중첩 클래스 (내부 클래스)
class AA{
int aa;
static final int SAA = 100;
void aaM1() {
System.out.println("내부클래스의 일반 메소드 aaM1() 부분");
}
// 내부 클래스에서 static 메소드를 생성하니 에러남
// static void aasM() { }
void aaM() {
System.out.println("이곳은 내부 클래스의 aaM() 부분");
// 외부 클래스의 인스턴스 호출 사용 가능
a = 10;
// 외부 클래스의 상수 호출 가능
double pi = b;
// 외부 클래스의 메소드 호출 가능
aM();
// 외부 클래스의 정적메소드 호출 가능
// 정적메소드의 호출은
// 클래스명.정적 메소드명
A.stM();
}
// 외부 클래스의 생성자 호출
// 생성자는 생성자에서 호출한다
AA() {
// 에러남
// A();
// this.A();
// 슈퍼의 경우는 에러는 안나지만 외부 클래스의 기본생성자를 가리키는게 아니라
// Object의 기본 생성자를 가리킴
// super()
}
}
}
public class AMain {
public static void main(String[] args) {
// 객체화시켜서 실제로 사용하기
// 외부 클래스 사용하기
A a = new A();
a.iM(100);
// 내부 클래스의 메소드는 외부 클래스의 참조 변수로 호출불가
// a.aaM();
// a.aaM1();
// 내부 클래스 객체 (new)로 생성 불가
// 암호화, 정보 은닉을 하기 위해
//AA aa = new AA();
// 내부 클래스를 객체화 할 수 있는 방법
// 1단계. 외부 클래스를 객체화한다
A a1 = new A();
// 2단계. 내부 클래스를 객체화한다
// = A클래스의 내부에 있는 AA를 객체화하겠다
A.AA aa1 = a1.new AA();
aa1.aaM1();
aa1.aaM();
}
}
실행 모습
바깥클래스.this.필드
바깥클래스.this.메소드();
클래스 내부에서 this는 객체 자신의 참조이다. 중첩 클래스 내부에서 바깥 클래스의 객체 참조를 얻으려면 바깥 클래스의 이름을 this 앞에 붙여주면 된다.
예제 1)
public class ClassMain {
public static void main(String[] args) {
// I 클래스 객체화하기
// 1 외부 객체생성 -> 2. 내부 객체 생성
O o = new O();
O.I oi = o.new I();
// static은 단계없이 그냥 해도 가능
O.SI si = new O.SI();
// 외부 메소드 안에 있는 클래스
// =로컬 클래스의 객체생성은 메소드를 실행하면 객체 생성이 된다
o.oM();
oi.printA();
}
}
class O{
int a = 10;
class I{
int a = 20;
void printA() {
System.out.println("내부 클래스의 a 값 출력하기 : " + this.a);
System.out.println("외부 클래스의 a 값 출력하기 : " + O.this.a);
}
}
static class SI{ }
void oM() {
class D{ }
D d = new D();
}
}
실행 모습
예제 2)
(1)
class Outter {
String field = "Outter-field";
void method() {
System.out.println("Outter-method");
}
class Nested {
String field = "Nested-field";
void method() {
System.out.println("Nested-method");
}
void print() {
// 중첩 객체 참조
// 그니까 여기의 this는 Nested에 있는 값을 참조
System.out.println(this.field);
this.method();
// 외부 클래스 참조
// 외부의 Outter에 있는 값을 참조
System.out.println(Outter.this.field);
Outter.this.method();
}
}
}
(2)
public class OutterEx {
public static void main(String[] args) {
Outter outter = new Outter();
Outter.Nested nested = outter.new Nested();
nested.method();
System.out.println("-----");
nested.print();
}
}
실행 모습