728x90
내부 클래스
- 클래스 내부에 선언된 또 다른 클래스
- 외부 클래스와 밀접한 관련이 있고 다른 클래스와는 협력할 일이 없는 경우에 내부 클래스로 선언해서 사용
- 내부 클래스는 중첩 클래스라고도 함
- 내부 클래스에서 외부 클래스의 멤버에 손쉽게 접근할 수 있음
- 서로 관련 있는 클래스를 논리적으로 묶어서 표현함으로써, 코드의 캡슐화를 증가시킴
- 외부에서는 내부 클래스를 접근할 수 없으므로 코드의 복잡서을 줄일 수 있음
내부 클래스 종류
- 인스턴스 내부 클래스
static 키워드를 가지지 않는 클래스
외부 클래스의 인스턴스 변수나 인스턴스 메소드에 사용될 목적으로 선언 - 정적 내부 클래스
static 키워드를 가지는 클래스
외부 클래스의 클래스(정적) 변수나 인스턴스 메소드에 사용될 목적으로 선언 - 지역 내부 클래스
외부 클래스의 메소드나 초기화 블록에 선언된 클래스를 의미 - 익명 내부 클래스
이름을 가지지 않는 클래스
생성자를 선언할 수 없고 하나의 클래스나 인터페이스를 상속받거나 구현할 수 있음
클래스 선언과 동시에 객체를 생성하고 하나의 객체만 생성하는 클래스
인스턴스 내부 클래스
package test16;
class OutClass{ //외부 클래스
private int num = 10; //외부 클래스 private 변수
private static int sNum = 20; //외부 클래스 정적 변수
private InClass inClass; //내부 클래스 자료형 변수를 먼저 생성
//외부 클래스 디폴트 생성자. 외부 클래스가 생성된 후에 내부 클래스 생성 가능
public OutClass() {
inClass = new InClass();
}
class InClass{ //인스턴스 내부 클래스
int inNum = 100; //내부 클래스의 인스턴스 변수
//static int sInNum = 200; //인스턴스 내부 클래스에 정적 변수 선언 불가능
void inTest() {
System.out.println("OutClass num = " + num + "(외부 클래스의 인스턴스 변수)");
System.out.println("OutClass sNum = " + sNum + "(외부 클래스의 정적 변수)");
System.out.println("InClass innum = " + inNum + "(내부 클래스의 인스턴스 변수");
}
}
//static void sTest(){} //정적 메소드 정의 불가능
public void usingClass() { //외부 일반 메소드 : 외부 클래스에서 내부 클래스의 메소드를 호출해서 사용
inClass.inTest(); //내부 클래스 메소드 호출
}
}
public class InnerTest2 {
public static void main(String[] args) {
//외부 클래스 생성 후 내부 클래스를 생성
//외부 클래스를 먼저 생성하지 않고 인스턴스 내부 클래스를 사용할 수 없음
//외부 클래스의 객체 생성 시 내부 클래스도 동시에 객체 생성
OutClass outClass = new OutClass();
outClass.usingClass(); //내부 클래스 기능 호출
}
}
package test16;
class OutClass{ //외부 클래스
private int num = 10; //외부 클래스 private 변수
private static int sNum = 20; //외부 클래스 정적 변수
private InClass inClass; //내부 클래스 자료형 변수를 먼저 생성
//외부 클래스 디폴트 생성자. 외부 클래스가 생성된 후에 내부 클래스 생성 가능
public OutClass() {
inClass = new InClass();
}
//내부 클래스 작성 : private 접근 제어자를 지정하게 되면 다른 클래스에서 접근 할 수 없음
//private class InClass{
class InClass{ //인스턴스 내부 클래스
int inNum = 100; //내부 클래스의 인스턴스 변수
//static int sInNum = 200; //인스턴스 내부 클래스에 정적 변수 선언 불가능
void inTest() {
System.out.println("OutClass num = " + num + "(외부 클래스의 인스턴스 변수)");
System.out.println("OutClass sNum = " + sNum + "(외부 클래스의 정적 변수)");
System.out.println("InClass innum = " + inNum + "(내부 클래스의 인스턴스 변수");
}
}
//static void sTest(){} //정적 메소드 정의 불가능
public void usingClass() { //외부 일반 메소드 : 외부 클래스에서 내부 클래스의 메소드를 호출해서 사용
inClass.inTest(); //내부 클래스 메소드 호출
}
}
public class InnerTest2 {
public static void main(String[] args) {
//외부 클래스 생성 후 내부 클래스를 생성
//외부 클래스를 먼저 생성하지 않고 인스턴스 내부 클래스를 사용할 수 없음
//외부 클래스의 객체 생성 시 내부 클래스도 동시에 객체 생성
OutClass outClass = new OutClass();
outClass.usingClass(); //내부 클래스 기능 호출
System.out.println("================");
//private으로 지정하지 않은 내부 클래스는 다른 클래스에서 인스턴스 내부 클래스 생성이 가능
//외부 클래스 생성 후 내부 클래스 생성
OutClass outClass1 = new OutClass();
outClass1.usingClass();
//내부 클래스의 멤버에 접근
//외부클래스.내부클래스명 객체명 = 외부클래스객체명.new 내부클래스명
OutClass.InClass inClass = outClass1.new InClass();
inClass.inTest();
}
}
정적 내부 클래스
package test16;
class OutClass1{
private int num = 10;
private static int sNum = 20;
private InStaticClass inClass;
public OutClass1() {
inClass = new InStaticClass();
}
static class InStaticClass{ //정적 내부 클래스
int inNum = 100; //정적 내부 클래스의 인스턴스 변수
static int sInNum = 200; //정적 내부 클래스의 정적 변수
//정적 내부 클래스의 일반 메소드, 내부 인스턴스 메소드
void inTest() {
System.out.println("InStaticClass inNum = " + inNum + "(내부 클래스의 인스턴스 변수 사용)");
System.out.println("InstaticClass sInNum = " + sInNum + "(내부 클래스의 정적 변수 사용)");
//System.out.println("OutClass num = " + num +"(외부 클래스의 변수 사용)"); //외부 클래스의 객체가 생성되어 멤버 변수를 사용할 수 있어야 접근이 가능
System.out.println("OutClass sNum = " + sNum + "(외부 클래스의 정적 변수 사용)");
}
//정적 내부 클래스의 정적 메소드, 내부 정적 메소드. static 변수만 사용 가능
static void sTest() {
//num += 10; //외부 클래스와 외부 클래스의 인스턴스 변수는 사용할 수 없음
//InNum += 10;
//System.out.println("InStaticClass inNum = " + inNum + "(내부 클래스의 인스턴스 변수 사용");
System.out.println("InstaticClass sInNum = " + sInNum + "(내부 클래스의 정적 변수 사용)");
//System.out.println("OutClass num = " + num +"(외부 클래스의 변수 사용)");
System.out.println("OutClass sNum = " + sNum + "(외부 클래스의 정적 변수 사용)");
}
}
}
public class InnerTest3 {
public static void main(String[] args) {
//정적 내부 클래스는 외부 클래스를 따로 생성하지 않고 정적 내부 클래스 생성 가능
//외부클래스명.내부클래스명 객체명 = new 외부클래스명.내부클래스명();
OutClass1.InStaticClass sInClass = new OutClass1.InStaticClass();
sInClass.inTest();
//정적 내부 클래스 내의 정적 메소드를 호출해서 사용
OutClass1.InStaticClass.sTest();
}
}
지역 내부 클래스
package test16;
class OutClass2{
private int num = 10;
private static int sNum = 20;
//지역 내부 클래스(local inner class)
public void outClassMethod(){
class LocalClass{
int inNum = 100;
static int sInNum = 200;
//내부 인스턴스 메소드
void inTest() {
System.out.println("LocalClass inNum = " + inNum + "(내부 클래스의 인스턴스 변수 사용)");
System.out.println("LocalClass sInNum = " + sInNum + "(내부 클래스의 정적 변수 사용)");
System.out.println("OutClass2 num = " + num +"(외부 클래스의 변수 사용)"); //외부 클래스의 객체가 생성되어 멤버 변수를 사용할 수 있어야 접근이 가능
System.out.println("OutClass2 sNum = " + sNum + "(외부 클래스의 정적 변수 사용)");
}
static void sTest() {
//System.out.println("InStaticClass inNum = " + inNum + "(내부 클래스의 인스턴스 변수 사용");
System.out.println("LocalClass sInNum = " + sInNum + "(내부 클래스의 정적 변수 사용)");
//System.out.println("OutClass2 num = " + num +"(외부 클래스의 변수 사용)");
System.out.println("OutClass2 sNum = " + sNum + "(외부 클래스의 정적 변수 사용)");
}
}
//메소드 내부에서 객체 생성
LocalClass lc = new LocalClass();
lc.inTest();
}
}
public class InnerTest4 {
public static void main(String[] args) {
//외부 클래스 객체 생성
OutClass2 outClass = new OutClass2();
outClass.outClassMethod();
}
}
익명 내부 클래스
- 다른 클래스와는 달리 이름을 가지지 않는 클래스
- 클래스의 선언과 동시에 객체를 생성하므로 하나의 객체만을 생성하는 일회용 클래스
- 생성자를 선언할 수 없고 하나의 클래스나 인터페이스를 상속받거나 구현할 수 있음
- 제한적인 용도에 사용되고 구현해야 하는 메소드가 매우 적은 클래스를 구현할 때 사용
package test16;
class Some{
private int a = 3;
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
}
public class InnerTest5 {
public static void main(String[] args) {
Some s1 = new Some();
System.out.println("s1.getA() = " + s1.getA());
s1.setA(10);
System.out.println("s1.getA() = " + s1.getA());
System.out.println("====================");
//익명 객체 구현
//기존의 클래스를 이용하여 멤버 변수와 멤버 메소드의 기능을 새로 구현
//Some 클래스가 아닌 익명 클래스
//블록의 맨 마지막에 ';'을 반드시 작성 요
Some s2 = new Some() {
//오버라이드로 기능 새로 구현
private int a = 100;
@Override
public int getA() {
return a;
}
@Override
public void setA(int a) {
this.a = a;
}
};//익명 객체 구현 시 마지막에 ';'을 반드시 작성
System.out.println("s2.getA() = " + s2.getA());
s2.setA(200);
System.out.println("s2.setA() = " + s2.getA());
}
}
package test16;
//인터페이스 구현
interface Animal{
//인터페이스 내에 구현할 추상 메소드만 정의
abstract void what();
}
//인터페이스는 구현 클래스가 반드시 만들어져 있어야함
class Ani implements Animal{
@Override
public void what() {
System.out.println("Animal 인터페이스의 what() 메소드를 구현해서 호출");
}
}
public class InnerTest6 {
public static void main(String[] args) {
/*
//인터페이스를 구현한 클래스 객체로 생성해서 메소드 오버라이딩 사용
Ani a = new Ani();
a.what();
//인터페이스 변수 = 구현 클래스의 객체를 생성해서 대입
//인터페이스에 정의된 추상 메소드와 동일한 메소드를 구현한 클래스로 객체를 생성해서 오보라이드 (구현)된 메소드 호출해서 사용
Animal a1 = new Ani();
a1.what();
*/
}
}
package test16;
//인터페이스 구현
interface Animal{
//인터페이스 내에 구현할 추상 메소드만 정의
abstract void what();
}
/*
//인터페이스는 구현 클래스가 반드시 만들어져 있어야함
class Ani implements Animal{
@Override
public void what() {
System.out.println("Animal 인터페이스의 what() 메소드를 구현해서 호출");
}
}
*/
public class InnerTest6 {
public static void main(String[] args) {
/*
//인터페이스를 구현한 클래스 객체로 생성해서 메소드 오버라이딩 사용
Ani a = new Ani();
a.what();
//인터페이스 변수 = 구현 클래스의 객체를 생성해서 대입
//인터페이스에 정의된 추상 메소드와 동일한 메소드를 구현한 클래스로 객체를 생성해서 오보라이드 (구현)된 메소드 호출해서 사용
Animal a1 = new Ani();
a1.what();
*/
//인터페이스와 추상 클래스는 객체를 생성할 수 없음
//인터페이스로 객체를 생성
//익명 클래스 => 익명 구현 객체
Animal animal = new Animal() {
@Override
public void what() {
System.out.println("Animal 인터페이스 what() 메소드를 구현하여 호출");
System.out.println("Animal 인터페이스 구현");
System.out.println("dog");
}
};
animal.what();
}
}
package test16;
//인터페이스 구현
interface Animal{
//인터페이스 내에 구현할 추상 메소드만 정의
abstract void what();
}
/*
//인터페이스는 구현 클래스가 반드시 만들어져 있어야함
class Ani implements Animal{
@Override
public void what() {
System.out.println("Animal 인터페이스의 what() 메소드를 구현해서 호출");
}
}
*/
//추상 클래스 구현
abstract class AnimalClass{
//추상 메소드를 한 개라도 가지고 있는 클래스는 추상 클래스가 됨
abstract void what1();
}
public class InnerTest6 {
public static void main(String[] args) {
/*
//인터페이스를 구현한 클래스 객체로 생성해서 메소드 오버라이딩 사용
Ani a = new Ani();
a.what();
//인터페이스 변수 = 구현 클래스의 객체를 생성해서 대입
//인터페이스에 정의된 추상 메소드와 동일한 메소드를 구현한 클래스로 객체를 생성해서 오보라이드 (구현)된 메소드 호출해서 사용
Animal a1 = new Ani();
a1.what();
*/
//인터페이스와 추상 클래스는 객체를 생성할 수 없음
//인터페이스로 객체를 생성
//익명 클래스 => 익명 구현 객체
Animal animal = new Animal() {
@Override
public void what() {
System.out.println("Animal 인터페이스 what() 메소드를 구현하여 호출");
System.out.println("Animal 인터페이스 구현");
System.out.println("dog");
}
};
animal.what();
System.out.println("============================");
//추상 클래스는 객체를 생성할 수 없음
//익명 클래스는 익명 구현 객체
//추상 클래스로 객체를 생성
AnimalClass ac = new AnimalClass() {
@Override
void what1() {
System.out.println("AnimalClass 추상 클래스 what1() 메소드를 구현하여 호출");
System.out.println("AnimalClass 추상 클래스 구현");
System.out.println("cat");
}
};
ac.what1();
}
}
내부클래스 정리
내부 클래스의 종류 => 선언된 위치에 따라 구분
1. 인스턴스 내부 클래스(Instance inner class) => InnerTest2.java
- 내부 클래스명을 지정할 때 아무 예약어(지시어)도 붙어 있지 않은 클래스
- 외부 클래스 영역에 선언한 클래스 중 static 키워드를 가지지 않은 클래스
- 주로 외부 클래스의 인스턴스 변수나 인스턴스 메소드에 사용될 목적으로 선언
- 외부 클래스 내부에서만 생성하여 사용하는 객체를 선언할 때 사용
- 어떤 클래스 내에 여러 변수가 있고 이를 변수 중 일부를 모아 클래스로 표현할 수 있음
- 내부 클래스를 감싸고 있는 외부 클래스에서만 사용하기 위해서 사용
- 다른 클래스에서 private이 아닌 내부 클래스를 생성할 수 있음
- 외부 클래스의 내부에서만 사용할 목적이라면 내부 클래스를 private으로 선언
- 외부 클래스 생성해야만 내부 클래스를 생성할 수 있음
- 외부 클래스를 먼저 생성하지 않고 인스턴스 내부 클래스를 사용할 수 없음
2. 정적 내부 클래스(Static inner class) => InnerTest3.java
- 외부 클래스 영역에 선언된 내부 클래스 중에서 static 키워드를 가진 클래스
- 주로 외부 클래스(outer class)의 클래스(정적) 변수나 인스턴스 메소드에서 사용될 목적으로 선언
- 내부 클래스가 외부 클래스 생성과 무관하게 사용할 수 있어야하고 정적 변수도 사용할 수 있도록 하는 것
- 외부 클래스를 먼저 생성하지 않고 인스턴스 내부 클래스를 생성하고 사용할 수 있음
3. 지역 내부 클래스(Local inner class) => InnerTest4.java
- 외부 클래스의 메소드나 초기화 블록에 선언된 클래스를 의미
- 지역 내부 클래스는 선언된 메소드 블록 내에서만 사용할 수 있음
- 지역 변수처럼 메소드 내부에 클래스를 정의하여 사용하는 것
- 메소드나 초기화 블록의 내부에서 지역변수처럼 다루어지고 선언된 영역 내부에서 사용할 수 있는 클래스
- 객체를 메소드 블록 내에서 생성
4. 익명 내부 클래스(Anonymous inner class) => InnerTest5.java, InnerTest6.java
- 다른 내부 클래스와 달리 이름을 가지지 않는 클래스를 의미
- 클래스 서언과 동시에 객체를 생성하므로 하나의 객체만으로 생성하는 일회용 클래스
- 생성자를 선언할 수 없고 하나의 클래스나 인터페이스를 상속받거나 구현할 수 있음
- 매우 제한적인 용도에 사용되고 구현해야 하는 메소드가 매우 적은 클래스를 구현 할 때 사용
728x90
'KDT > Java' 카테고리의 다른 글
240314 Java - 람다식 2, 스트림 1 (0) | 2024.03.14 |
---|---|
240313 Java - 람다식 (0) | 2024.03.13 |
240307 Java 입출력 스트림 3, 내부 클래스 1 (0) | 2024.03.07 |
240306 Java 입출력과 스트림 2 (0) | 2024.03.06 |
240304 Java - 입출력과 스트림 1 (0) | 2024.03.04 |