[JAVA] 객체지향 프로그래밍(2) - 상속, 오버라이딩, Package와 import, 제어자, 다형성, 추상클래스, 인터페이스

1. 상속(inheritance)


상속

: 기존의 클래스를 재사용해서 새로운 클래스 작성

: 자손은 조상의 모든 멤버(생성자, 초기화블럭 제외)를 상속받음

: 자손의 멤버개수 >= 조상의 멤버개수

: Java는 단일상속만 허용

: '~은 ~이다.'를 가지고 문장을 만들었을 때 말이 되면 상속관계 ex) 원은 도형이다

class 자손클래스 extends 조상클래스 {
    // ...
}
class Parent {}
class Child extends Parent {}
class Child2 extends Parent {}
class GrandChild extends Child {}

 

 

포함(composite)

: 한 클래스의 멤버변수로 다른 클래스 선언

: 비중이 높은 클래스 하나만 상속관계로, 나머지는 포함관계로 함

: '~은 ~을 가지고 있다.'를 가지고 문장을 만들었을 때 말이 되면 포함관계 ex) 원은 점을 가지고 있다

class Point {
	int x;
	int y;
}

class Circle {
	Point c = new Point();
	int r;
}

 

 

Object 클래스

: 모든 클래스의 최고 조상

: 모든 클래스 Object 클래스에 정의된 11개의 메서드 상속받음

 

 

 

2. 오버라이딩(overriding)


오버라이딩

: 조상클래스로부터 상속받은 메서드의 내용을 상속받는 클래스에 맞게 변경

: 선언부 변경 불가. 구현부만 변경 가능

: 반드시 자식 메서드가 실행됨

class MyPoint {
	int x;
	int y;
	
	public MyPoint(int x, int y) {
		this.x = x;
		this.y = y;
	}
	
	// Object 클래스의 toString()을 오버라이딩
	public String toString() {
		return "x:"+x+" y:"+y;
	}
}

class MyPoint3D extends MyPoint {
	int z;
	
	MyPoint3D(int x, int y, int z) {
		super(x, y);
		this.z = z;
	}
	
	// Point 클래스의 toString()을 오버라이딩
	public String toString() {
		return "x:"+x+" y:"+y+" z:"+z;
	}
}

 

 

오버라이딩 조건

  1. 선언부가 조상클래스의 메서드와 일치(이름, 매개변수, 반환타입)
  2. 접근제어자를 좁은 범위로 변경할 수 없음
  3. 조상클래스의 메서드보다 많은 수의 예외 선언 불가

 

 

오버로딩 vs 오버라이딩

- 오버로딩(overloading) : 기존에 없는 새로운(new) 메서드 정의

- 오버라이딩(overriding) : 상속받은 메서드의 내용 변경(change)

class Parent {
	void parentMethod() {}
}

class Child extends Parent {
	void parentMethod() {} // 오버라이딩
	void parentMethod(int i) {} // 오버로딩
	
	void childMethod() {}
	void childMethod(int i) {} // 오버로딩
	void childMethod() {} // 에러. 중복정의
 }

 

 

조상클래스의 생성자 - super()

: 생성자는 무조건 super() 생성자를 호출해야 함

: 사용자가 super() 생성자를 호출하지 않았다면 자동으로 조상클래스의 기본 생성자 호출됨

: 조상클래스가 기본 생성자를 가지고 있지 않다면 사용자는 반드시 직접 super() 생성자를 호출해야 함

public class PointTest {

	public static void main(String[] args) {
		Point3d p = new Point3d(1, 2, 3);
	}

}

class Point3 {
	int x, y;
	
	public Point3() {
	}
	
	public Point3(int x, int y) {
		this.x = x;
		this.y = y;
	}
	
	String getLocation() {
		return "x:"+x+", y:"+y;
	}
}

class Point3d extends Point3 {
	int z;
	
	public Point3d(int x, int y, int z) {
		super(x, y);
		this.z = z;
	}
	
	String getLocation() {
		return super.getLocation()+", z:"+z;
	}
}

 

 

참조변수 - super

: 조상의 멤버와 자신의 멤버를 구별하는 데 사용

: 인스턴스 메서드에서만 사용

public class SuperTest {

	public static void main(String[] args) {
		Child c = new Child();
		c.method();
	}

}

class Parent {
	int x = 10;
}

class Child extends Parent {
	int x = 20;
	void method() {
		System.out.println("x="+x);
		System.out.println("this.x="+this.x);
		System.out.println("super.x="+super.x);
	}
}
x=20
this.x=20
super.x=10

 

 

 

3. package와  import


패키지(package)

: 서로 관련된 클래스와 인터페이스의 묶음

: 하나의 소스파일에는 첫 번째 문장으로 단 한 번의 패키지 선언만을 허용

: 모든 클래스는 반드시 하나의 패키지에 속해야 함(default 패키지 포함)

: 점(.)을 구분자로 하여 계층구조로 구성(ex. com.example.package)

 

package 패키지명;
package com.coding.book;

public class PackageTest {

	public static void main(String[] args) {
		System.out.println("Hello World");
		System.out.println(Math.floor(1.8));
	}

}

 

 

  • 패키지가 정의된 클래스 컴파일
javac -d 경로명 *.java

 

 

import문

: 사용할 클래스가 속한 패키지를 지정하는 데 사용

: 컴파일러에게 소스파일에 사용된 클래스의 패키지에 대한 정보 제공

: import문을 사용하면 소스코드에 사용되는 클래스이름에서 패키지명 생략 가능

: 이클립스에서는 'ctrl+shift+o' 단축키 사용해서 자동으로 import문 생성

: import static을 사용하면 static멤버를 호출할 때 클래스 이름 생략 가능

import 패키지명.클래스명;
또는
import 패키지명.*; // 같은 패키지에서 여러 개의 클래스 사용될 때

- import문에서 클래스 이름 대신 '*' 사용하는 것이 하위 패키지의 클래스까지 포함하는 건 아님

 

 

package com.coding.book;

import static java.lang.System.out;
import static java.lang.Math.*;

public class PackageTest {

	public static void main(String[] args) {
		out.println("Hello World");
		System.out.println(floor(1.8));
	}

}
① package문
② import문
③ 클래스 선언

 

 

 

4. 제어자(modifiers)


제어자(modifiers)

: 클래스, 변수, 메서드의 선언부에 사용되어 부가적인 의미 부여

 

 

static - 클래스의, 공통적인

: static 변수는 인스턴스에 관계없이 공통적인 값 가짐

: static 멤버변수, 메서드, 초기화 블럭은 인스턴스 생성하지 않고도 사용 가능

static이 사용될 수 있는 곳 : 멤버변수, 메서드, 초기화 블럭
class StaticTest {
	static int width = 200; // static 변수
	static int height = 120; // static 변수
	
	static {
		// static 변수의 복잡한 초기화 수행
	}
	
	static int max(int a, int b) { // static 메서드
		return a > b ? a : b;
	}
}

 

 

final - 마지막의, 변경될 수 없는

: 변수에 사용되면 값을 변경할 수 없는 상수가 됨

: 메서드에 사용되면 오버라이딩 불가

: 클래스에 사용되면 자손클래스 정의 불가(상속 불가)

final이 사용될 수 있는 곳 : 클래스, 메서드, 멤버변수, 지역변수
class FinalTest { // 조상이 될 수 없는 클래스
	final int MAX_SIZE = 10; // 상수 멤버변수
	
	final int getMaxSize() { // 오버라이딩할 수 없는 메서드
		final int LV = MAX_SIZE; // 상수 지역변수
		return LV;
	}
}

 

 

abstract - 추상의, 미완성의

: 메서드의 선언부만 작성하고 실제 수행내용은 구현하지 않은 추상 메서드 선언에 사용됨

: 추상클래스에 사용됨

abstract이 사용될 수 있는 곳 : 클래스, 메서드
abstract class AbstractTest {
	abstract void move();
}

 

 

접근 제어자(aceess modifier)

: 멤버 또는 클래스에 사용되어, 외부로부터의 접근 제한

: 접근 제어자는 최대한 좁히고 필요할 때 넓힌다

접근 제어자가 사용될 수 있는 곳 : 클래스, 메서드, 멤버변수, 생성자
public 접근 제한 전혀 없음
protected 같은 패키지 내에서, 다른 패키지의 자손 클래스에서 접근
(default) 같은 패키지 내에서만 접근
private 같은 클래스 내에서만 접근

class Time {
	private int hour; // 0~23 사이의 값으로 제한
	private int minute;
	private int second;
	
	Time() {}
	
	Time(int hour, int minute, int second) {
		this.hour = hour;
		this.minute = minute;
		this.second = second;
	}
	
	public int getHour() {return hour;}
	public void setHour(int hour) {
		if (!isValidHour(hour)) {
			return;
		}
		this.hour = hour;
	}
	private boolean isValidHour(int hour) {
		return hour >= 0 && hour <= 23;
	}
	
	
	public int getMinute() {return minute;}
	public void setMinute(int minute) {
		if(!isValidMinute(minute)) {
			return;
		}
		this.minute = minute;
	}
	private boolean isValidMinute(int minute) {
		return isValidSecond(minute);
	}
	
	public int getSecond() {return second;}
	public void setSecond(int second) {
		if (!isValidSecond(second)) {
			return;
		}
		this.second = second;
	}
	public boolean isValidSecond(int second) {
		return second >= 0 && second <= 59;
	}
	
	public String toString() {
		return hour+":"+minute+":"+second;
	}
}

public class TimeTest {
	public static void main(String[] args) {
		Time t = new Time(12, 35, 30);
		System.out.println(t);
		t.setHour(t.getHour()+1);
		System.out.println(t);
	}
}

 

 

제어자의 조합

  1. 메서드에 static과 abstract 함께 사용 불가
  2. 클래스에 abstract와 final 동시 사용 불가
  3. abstract 메서드의 접근 제어자로 private 불가
  4. 메서드에 private와 final 같이 사용할 필요 없음
대상 사용 가능한 제어
클래스 public, (default), final, abstract
메서드 모든 접근 제어자, final, abstract, static
멤버변수 모든 접근 제어자, final, static
지역변수 final

 

 

 

5. 다형성(polymorphism)


다형성

: 조상클래스 타입의 참조변수로 자손클래스의 인스턴스를 참조할 수 있게 함

: 참조변수의 타입이 참조변수가 참조하고 있는 인스턴스에서 사용할 수 있는 멤버의 개수 결정

: 조상타입의 참조변수로 자손타입의 인스턴스 참조 가능(반대는 불가)

class Tv {
	boolean power;
	int channel;
	
	void power() {power = !power;}
	void channelUp() {++channel;}
	void channelDown() {--channel;}
}

class CaptionTv extends Tv {
	boolean caption;
	
	void displayCaption(String text) {
		if (caption) {
			System.out.println(text);
		}
	}
}

class CaptionTvTest {
	public static void main(String[] args) {
    	CaptionTv ctv = new CaptionTv();
        Tv t = new CaptionTv();
        }
}

둘 다 같은 타입의 인스턴스지만 참조변수의 타입에 따라 사용할 수 있는 멤버 개수 달라짐

 

 

참조변수의 형변환

: 사용할 수 있는 멤버의 개수 조절

: 조상-자손 관계에서만 가능

자손타입 -> 조상타입 : 형변환 생략가능
조상타입 -> 자손타입 : 형변환 생략불가
public class CastingTest1 {
	public static void main(String[] args) {
		Car car = null;
		FireEngine fe = new FireEngine();
		FireEngine fe2 = null;
		
		fe.water();
		car = (FireEngine)fe;
//		car.water(); // 에러
		fe2 = (FireEngine)car;
		fe2.water();
	}
}

class Car {
	String color;
	int door;
	
	void drive() {
		System.out.println("drive");
	}
	
	void stop() {
		System.out.println("stop");
	}
}

class FireEngine extends Car {
	void water() {
		System.out.println("water");
	}
}

 

 

instanceof 연산자

: 참조변수가 참조하는 인스턴스의 실제 타입을 체크하는 데 사용

: 연산결과가 true이면, 해당 타입으로 형변환 가능

public class InstanceofTest {

	public static void main(String[] args) {
		FireEngine fe = new FireEngine();
		
		if (fe instanceof FireEngine) {
			System.out.println("This is FireEngine instance.");
		}
		
		if (fe instanceof Car) {
			System.out.println("This is Car instance.");
		}
		
		if (fe instanceof Object) {
			System.out.println("This is Object instance.");
		}
		
		System.out.println(fe.getClass().getName());
	}
}
This is FireEngine instance.
This is Car instance.
This is Object instance.
FireEngine

 

 

참조변수와 인스턴스변수의 연결

: 메서드가 중복정의된 경우, 참조변수의 타입에 관계없이 항상 실제 인스턴스의 메서드(오버라이딩된 메서드) 호출

: 멤버변수가 중복정의된 경우, 참조변수의 타입에 따라 달라짐

public class BindingTest3 {
	public static void main(String[] args) {
		Parent2 p = new Child4();
		Child4 c = new Child4();
		
		System.out.println("p.x = "+p.x);
		p.method();
		System.out.println();
		System.out.println("c.x = "+c.x);
		c.method();
	}
}

class Child4 extends Parent2 {
	int x = 200;
	
	void method() {
		System.out.println("x="+x);
		System.out.println("super.x="+super.x);
		System.out.println("this.x="+this.x);
	}
}

 

 

매개변수의 다형성

: 참조형 매개변수는 메서드 호출 시, 자신과 같은 타입 또는 자손타입의 인스턴스 넘겨줄 수 있음

public class PolyArgumentTest {
	public static void main(String[] args) {
		Buyer b = new Buyer();
		
		b.buy(new Tv2());
		b.buy(new Computer());
		
		System.out.println("현재 남은 돈은 "+b.money+"만원입니다.");
		System.out.println("현재 보너스점수는 "+b.bonusPoint+"점입니다.");
	}
}

class Product {
	int price;
	int bonusPoint;
	
	Product(int price) {
		this.price = price;
		bonusPoint = (int)(price / 10.0);
	}
	
	Product() {}
}

class Tv2 extends Product {
	Tv2() {
		super(100);
	}
	
	public String toString() {
		return "Tv";
	}
}
Tv을/를 구입하셨습니다.
Computer을/를 구입하셨습니다.
현재 남은 돈은 700만원입니다.
현재 보너스점수는 30점입니다.

 

 

여러 종류의 객체를 배열로 다루기

: 조상타입의 배열에 자손들의 객체를 담을 수 있음

import java.util.Vector;

public class PolyArgumentTest3 {

	public static void main(String[] args) {
		Buyer2 b = new Buyer2();
		Tv2 tv = new Tv2();
		Computer com = new Computer();
		Audio audio = new Audio();
		
		b.buy(tv);
		b.buy(com);
		b.buy(audio);
		b.summary();
		b.refund(com);
		b.summary();
	}

}

class Buyer2 {
	int money = 1000;
	int bonusPoint = 0;
	Vector item = new Vector();
	
	void buy(Product p) {
		if (money < p.price) {
			System.out.println("잔액이 부족하여 물건을 살 수 없습니다.");
			return;
		}
		money -= p.price;
		bonusPoint += p.bonusPoint;
		item.add(p);
		System.out.println(p+"을/를 구입하셨습니다.");
	}
	
	void refund(Product p) {
		if (item.remove(p)) {
			money += p.price;
			bonusPoint -= p.bonusPoint;
			System.out.println(p+"을/를 반품하셨습니다.");
		}
		else {
			System.out.println("구입하신 제품 중 해당 제품이 없습니다.");
		}
	}
	
	void summary() {
		int sum = 0;
		String itemList = "";
		
		if (item.isEmpty()) {
			System.out.println("구입하신 제품이 없습니다.");
			return;
		}
		
		for (int i = 0; i < item.size(); i++) {
			Product p = (Product)item.get(i);
			sum += p.price;
			itemList += (i == 0) ? ""+p : ", "+p;
		}
		
		System.out.println("구입하신 물품의 총 금액은 "+sum+"만원입니다.");
		System.out.println("구입하신 제품은 "+itemList+"입니다.");
	}
}
Tv을/를 구입하셨습니다.
Computer을/를 구입하셨습니다.
Audio을/를 구입하셨습니다.
구입하신 물품의 총 금액은 350만원입니다.
구입하신 제품은 Tv, Computer, Audio입니다.
Computer을/를 반품하셨습니다.
구입하신 물품의 총 금액은 150만원입니다.
구입하신 제품은 Tv, Audio입니다.

 

 

6. 추상클래스(abstract class)


추상메서드

: 선언부만 있고 구현부가 없는 메서드

: 꼭 필요하지만 자손마다 다르게 구현될 것으로 예상되는 경우에 사용

abstract 반환타입 메서드이름();

 

 

추상클래스

: 미완성 메서드(추상메서드)를 포함하고 있는 클래스

: 인스턴스 생성 불가

: 상속을 통해 자손클래스에 의해서만 완성

abstract class 클래스이름 {
    // ...
}
abstract class Player3 { // 추상클래스
	boolean pause;
	int currentPos;
	
	Player3() {
		pause = false;
		currentPos = 0;
	}
	
	abstract void play(int pos); // 추상메서드
	abstract void stop(); // 추상메서드
}

class CDPlayer extends Player3 {
	void play(int pos) {...} // 추상메서드 구현
	void stop() {...} // 추상메서드 구현
}

 

 

 

7. 인터페이스(interface)


인터페이스

추상메서드의 집합

 

: 실제 구현된 것이 전혀 없는 기본 설계도

: 추상메서드와 상수만을 멤버로 가짐 vs 추상클래스 : 추상메서드를 가진 일반 클래스

: 인스턴스 변수 가질 수 없음

: jdk 1.8부터 static 메서드와 디폴트 메서드 가능

]interface 인터페이스이름 {
    public static final 타입 상수이름 = 값;
    public abstract 메서드이름 (매개변수목록);
}

 

 

  • 인터페이스 조건

- 모든 멤버변수는 public static final이어야 하며 생략 가능

- 모든 메서드는 public abstract이어야 하며 생략 가능

- 인터페이스는 인터페이스로부터만 상속받을 수 있음(Object가 최고 조상 아님)

- 다중상속 가능하나 그런 경우는 거의 없음

interface Movable {
	void move(int x, int y);
}

interface Attackable {
	void attack(Unit u);
}

interface Fightable extends Moveable, Attackable {}

 

 

인터페이스의 구현

: 추상클래스의 완성과 동일. but, 사용하는 키워드(implements)만 다름

: 일부만 구현한다면 abstract 붙여서 추상클래스로 선언해야 함

class 클래스이름 implements 인터페이스이름 {
    // 인터페이스에 정의된 추상메서드 구현
}
class UnitState {
	int currentHP;
	int x;
	int y;
}

interface Moveable {
	void move(int x, int y); // public abstract 생략
}

interface Attackable {
	void attack(UnitState u); // public abstract 생략
}

interface Fightable2 extends Moveable, Attackable {}

class Fighter2 extends UnitState implements Fightable2 { // 인터페이스 구현
	public void move(int x, int y) {}
	public void attack(UnitState u) {}

}

public class FighterTest2 {
	public static void main(String[] args) {
		Fighter2 f = new Fighter2();
		
		if (f instanceof UnitState) {
			System.out.println("f는 UnitState의 자손입니다.");
		}
		
		if (f instanceof Fightable2) {
			System.out.println("f는 Fightable 인터페이스를 구현했습니다.");
		}
		
		if (f instanceof Moveable) {
			System.out.println("f는 Moveable 인터페이스를 구현했습니다.");
		}
		
		if (f instanceof Attackable) {
			System.out.println("f는 Attackable 인터페이스를 구현했습니다."); 
		}
		
		if (f instanceof Object) {
			System.out.println("f는 Object의 자손입니다.");
		}
	}
}
f는 UnitState의 자손입니다.
f는 Fightable 인터페이스를 구현했습니다.
f는 Moveable 인터페이스를 구현했습니다.
f는 Attackable 인터페이스를 구현했습니다.
f는 Object의 자손입니다.

 

 

인터페이스를 이용한 다형성

: 인터페이스도 이를 구현한 클래스의 조상이라 할 수 있으므로 다형성 적용 가능

: 인터페이스를 매개변수와 반환타입으로 지정 가능

: 인터페이스 매개변수는 메서드 호출 시 해당 인터페이스 구현한 클래스의 인스턴스를 매개변수로 제공

: 인터페이스 반환타입은 메서드가 해당 인터페이스 구현한 클래스의 인스턴스를 반환

interface Parseable {
	public abstract void parse(String filename);
}

class XMLParser implements Parseable {
	public void parse(String filename) {
		System.out.println(filename+"- XML parsing completed.");
	}
}

class HTMLParser implements Parseable {
	public void parse(String filename) {
		System.out.println(filename + "- HTML parsing completed.");
	}
}

class ParserManager {
	// 리턴타입이 Parseable 인터페이스인 메서드
	public static Parseable getParser(String type) {
		if (type.equals("XML")) {
			//XMLParser x = new XMLParser();
			//return x;
			return new XMLParser();
		}
		else {
			return new HTMLParser();
		}
	}
}


public class ParserTest {
	public static void main(String[] args) {
		Parseable parser = ParserManager.getParser("XML");
		parser.parse("document.xml");
		parser = ParserManager.getParser("HTML");
		parser.parse("document2.html");
	}
}

 

 

인터페이스의 이해

: 두 대상(객체) 간의 중간 역할

: 선언(설계)와 구현을 분리시키는 것을 가능하게 함

class A {
	void autoPlay(I i) {
		i.play();
	}
}

interface I {
	void play();
}

class B implements I {
	public void play() {
		System.out.println("play in B class");
	}
}

class C implements I {
	public void play() {
		System.out.println("play in C class");
	}
}

public class InterfaceTest {
	public static void main(String[] args) {
		A a = new A();
		a.autoPlay(new B());
		a.autoPlay(new C());
	}
}
play in B class
play in C class

 

 

디폴트 메서드(default method) - jdk 1.8부터

: 추상 메서드의 기본적인 구현 제공

: 디폴트 메서드가 추가되어도 해당 인터페이스를 구현한 클래스는 변경 필요 없음

 

 

  • 디폴트 메서드와 기존 메서드 이름 충돌 해결 규칙
  1. 여러 인터페이스의 디폴트 메서드 간의 충돌 - 구현 클래스에서 디폴트 메서드 오버라이딩
  2. 디폴트 메서드와 조상 클래스의 메서드 간의 충돌 - 조상 클래스의 메서드 우선적으로 상속

-> 헷갈리면 직접 오버라이딩할 것!

interface MyInterface {
	default void method1() {
		System.out.println("method1() in MyInterface");
	}
	
	default void method2() {
		System.out.println("method2() in MyInterface");
	}
	
	static void staticMethod() {
		System.out.println("staticMethod() in MyInterface");
	}
}

interface MyInterface2 {
	default void method1() {
		System.out.println("method1() in MyInterface2");
	}
	
	static void staticMethod() {
		System.out.println("staticMethod() in MyInterface2");
	}
}

class Parent5 {
	public void method2() {
		System.out.println("method2() in Parent");
	}
}

class Child5 extends Parent5 implements MyInterface, MyInterface2 {
	public void method1() {
		System.out.println("method1() in Child");
	}
}

public class DefaultMethodTest {
	public static void main(String[] args) {
		Child5 c = new Child5();
		c.method1();
		c.method2();
		MyInterface.staticMethod();
		MyInterface2.staticMethod();
	}
}

 

 

 

8. 내부 클래스(inner class)


내부 클래스

: 클래스 안에 선언된 클래스

: 내부 클래스에서 외부 클래스의 멤버들 쉽게 접근 가능

: 코드의 복잡성 줄임(캡슐화)

class A { // 외부 클래스
    class B { // 내부 클래스
        // 객체 생성 없이도 A의 멤버 접근 가능
    }
}
내부 클래스 특징
인스턴스 클래스 외부 클래스의 멤버변수 선언위치에 선언. 외부 클래스의 인스턴스 멤버처럼 다뤄짐
static 클래스 외부 클래스의 멤버변수 선언위치에 선언. 외부 클래스의 static 멤버처럼 다뤄짐
지역 클래스 외부 클래스의 메서드나 초기화 블럭 안에 선언
익명 클래스 클래스 선언과 객체 생성을 동시에 하는 일회용 클래스

 

 

  • 내부 클래스 특징

- 모든 접근 제어자 사용 가능

- static 내부 클래스에서는 외부 클래스의 인스턴스 멤버 접근 불가

- 외부 클래스의 private 멤버 접근 가능

- 외부 클래스의 지역변수는 상수만 접근 가능(jdk 1.8부터는 변수여도 값이 바뀌지 않으면 상수 취급)

- 인스턴스클래스의 인스턴스 생성하려면 외부 클래스의 인스턴스 먼저 생성해야 함

public class InnerEx3 {
	private int outerIv = 0;
	static int outerCv = 0;
	
	class InstanceInner {
		int iiv = outerIv; // 외부 클래스의 private 멤버도 접근 가능
		int iiv2 = outerCv;
	}
	
	static class StaticInner {
		// static 클래스는 외부 클래스의 인스턴스 멤버에 접근할 수 없다
//		int siv = outerIv;
		static int scv = outerCv;
	}
	
	void myMethod() {
		int lv = 0;
		final int LV = 0;
		
		class LocalInner {
			int liv1 = outerIv;
			int lcv = outerCv;
			// 외부 클래스의 지역변수는 final이 붙은 변수(상수)만 접근 가능
//			int liv2 = lv;
			int liv3 = LV;
		}
	}
}

 

 

익명 클래스(anonymous class)

: 클래스의 선언과 객체 생성을 동시에 하는 일회용 클래스

: 하나의 객체만 생성 가능

new 조상클래스이름() {
    // 멤버 선언
}
또는
new 구현인터페이스이름() {
    // 멤버 선언
}
public class InnerEx6 {
	Object iv = new Object() {void method(){}}; // 익명 클래스
	static Object cv = new Object() {void method(){}}; // 익명 클래스
	
	void myMethod() {
		Object lv = new Object() {void method(){}}; // 익명 클래스
	}
}