
- 기존의 클래스를 재사용해서 새로운 클래스를 작성하는 것
- 두 클래스를 조상과 자손으로 관계를 맺어주는 것
- 자손은 조상의 모든 멤버를 상속받는다 (생성자, 초기화블럭 제외)
- 자손의 멤버개수는 조상보다 적을 수 없다 (같거나 많다)
class 자손클래스 extends 조상클래스 {
//내용
}
class Point{
int x;
int y;
}
//상속을 사용하지 않을때
class Point3D{
int x;
int y;
int z;
}
//상속 사용
class Point3D extends Point{
int z;
}
//두 클래스 모두 x,y,z를 변수로가지고 있음.
- 공통부분은 조상에서 관리, 개별부분은 자손에서 관리
- 조상의 변경은 자손에 영향, 자손의 변경은 조상에 영향이 없다
class Parent {} // 조상
class Child extends Parent {} //자손
class Child extends Parent {} //자손
class GrandChild extends Child {} //자손의 자손
- 한 클래스의 멤버변수로 다른 클래스를 선언하는 것
- 작은 단위의 클래스를 먼저 만들고, 이 둘을 조합해서 하나의 커다란 클래스를 만든다.
//포함관계
class Car {
// 객체가 클래스에 포함!
Engine e = new Engine();
Door[] d = new Door[4];
}
- 상속관계는 is-a 이다.
ex) 원은 점이다.(Circle is a Point.)
- 포함관계는 has-a 이다.
ex) 원은 점을 가지고 있다.(Circle has a Point.)
//상속관계 - is-a
class Circle extends Point {
int r;
}
//포함관계 has-a
class Circle {
Point c = new Point();
int r;
}
- Java는 단일 상속만을 허용한다. (C++은 다중 상속 허용)
class Unit {
void move() {};
}
class Machine {
void move() {};
}
class Tank extends Unit, Machine { //다중상속을 허용하지 않는다.
//메소드간 충돌 우려 - move()호출시 어떤 클래스의 move()가 호출될지 모름
}
- 비중이 높은 클래스 하나만 상속관계, 나머지는 포함관계로 한다.
class TV {
boolean power;
int channel;
void power() { power = !power; }
void channelUp() { ++channel; }
void channelDown() { --channel; }
}
class VCR {
boolean power;
int counter = 0;
void power() { power = !power }
void play() { //... }
void stop() { //... }
}
//비중이 큰 TV클래스를 상속함 - TV와 상속관계를 가짐.
class TVCR extends TV {
VCR vcr = new VCR(); //VCR과 포함관계를 가짐.
int counter = vcr.counter;
void play() {
vcr.play();
}
void stop() {
vcr.stop();
}
}
- 조상이 없는 클래스는 자동적으로 Object클래스를 상속받음.
- 상속계층도의 최상위에는 Object클래스가 위치한다.
- 모든 클래스는 Object클래스에 정의된 11개의 메소드를 상속받는다.
- toString(), equals(Object obj), hashCode(), ...
class TV 'extends Object' { //생략되어있음
//...
System.out.println(t);
System.out.println(t.toString());
//두 출력의 결과는 같다! t는 t.toString()과 같다.
}
class CaptionTV extends TV {
//...
}
- 클래스, 변수, 메소드의 선언부에 사용. 부가적인 의미 부여.
- 크게 접근 제어자와 그 외의 제어자로 구분.
- 하나의 대상에 여러 개의 제어자를 조합해서 사용가능.
하지만 접근 제어자는 단 하나만 사용 가능.
| 접근 제어자 | 그 외의 제어자 |
|---|---|
| public, protected, default, private | static, final, abstract, native, synchronized, translent, volatile, strictfp |
멤버 또는 클래스에 사용되어, 외부로부터의 접근을 제한한다.
클래스, 멤버변수, 메소드, 생성자
- private : 같은 클래스 내에서만 접근이 가능
- default : 같은 패키지 내에서만 접근이 가능
- protected : 같은 패키지 내, 다른 패키지의 자손클래스에서 접근이 가능
- public : 접근 제한이 없음
- 외부로부터 데이터를 보호
- 외부에는 불필요하거나 내부적으로 사용되는 부분을 감춤
class Time {
private int hour;
private int minute;
private int second;
Time(int hour, int minute, int second){
setHour(hour);
setMinute(minute);
setSecond(second);
}
public int getHour() { return hour; }
public void setHour(int hour) {
if(hour < 0 || hour > 23) return;
this.hour = hour;
}
public String toString() {
return hour + ":" + minute + ":" + second;
}
}
public static void main(String[] args) {
Time t = new Time(12, 35, 30);
System.out.println(t); // t는 t.toString()과 같다!
//t.hour = 13; //에러!! private는 직접접근불가
t.sethour(t.getHour() + 1); //set메소드로 간접접근만 가능
System.out.println(t);
}
// 12:35:30
// 13:35:30 출력
- 일반적으로 생성자의 접근제어자는 클래스의 접근제어자와 일치
- 생성자에 접근제어자를 사용함으로써 인스턴스 생성 제한
final class Singleton {
private static Singleton s = new Singleton();
private Singleton() { // 생성자
//...
}
public static Singleton getInstance() {
if(s==null){
s = new Singleton();
}
return s;
}
}
class SingletonTest {
public static void main(String[] args) {
// Singleton s = new Singleton(); //에러!! 생성자에 접근불가
Singleton s1 = Singleton.getInstance(); //생성자에 간접접근
}
}
클래스의, 공통적인
멤버변수, 메소드, 초기화 블럭

class StaticTest {
static int width = 200;
static int height = 100;
static { // 클래스 초기화 블럭
// static변수의 복잡한 초기화 수행.
}
static int max(int a, int b){
return a> b ? a : b;
}
}
마지막의, 변경될 수 없는
클래스, 메소드, 멤버변수, 지역변수
final class FinalTest {
final int MAX_SIZE = 10; //멤버변수
final void getMaxSize() {
final LV = MAX_SIZE; //지역변수
return MAX_SIZE;
}
}
class Child extends FinalTest {
void getMaxSize() {} // 오버라이딩으로 재정의 불가
}
final이 붙은 변수는 상수이므로 보통은 선언과 초기화를 동시에 하지만, 인스턴스변수의 경우 생성자에서 초기화할 수 있다.
class Card {
final int NUMBER; // 상수지만 선언과 함께 초기화 하지 않고
final String KIND; // 생성자에서 단 한번만 초기화할 수 있다.
static int width = 100;
static int height = 250;
Card(String kind, int num) {
KIND = kind;
NUMBER = num;
}
Card() {
this("HEART", 1);
}
public String toString() {
return "" + KIND + " " + NUMBER;
}
}
추상의, 미완성의
클래스, 메소드
abstract class AbstractTest { // 추상클래스
abstract void move(); // 추상메소드
}
- 메소드에 static과 abstract를 함께 사용할 수 없다.
- static메소드는 구현부가 있는 메소드에만 사용가능
- 클래스에 abstract와 final을 동시에 사용할 수 없다.
- final은 확장불가, abstract는 확장하여 완성시켜야함. 서로 상반됨.
- abstract메소드의 접근제어자가 private일 수 없다.
- abstract메소드는 자손클래스에서 구현해야함. private는 접근불가.
- 메소드에 private와 final을 같이 사용할 필요는 없다.
- private메소드도 오버라이딩이 불가. 둘중 하나만 써도 충분.
- 조상클래스로부터 상속받은 메소드의 내용을 상속받는 클래스에 맞게 변경하는 것
class Point{
int x;
int y;
String getLocation() {
return "x :" + x + "y :" + y;
}
}
class Point3D extends Point{
int z;
String getLocation() { //오버라이딩!
return "x :" + x + "y :" + y + "z :" + z;
}
}
- 선언부가 같아야 한다. (이름, 매개변수, 리턴타입)
- 접근제어자를 좁은 범위로 변경할 수 없다.
- 조상의 메소드가 protected라면 범위가 같거나 넓은 protected, public만 사용 가능.
- 조상클래스의 메소드보다 많은 수의 예외를 선언할 수 없다.
- 오버로딩(overloading) - 기존에 없는 새로운 메소드를 정의하는 것
- 오버라이딩(overriding) - 상속받은 메소드의 내용을 변경하는 것
class Parent {
void parentMethod() {}
}
class Child extends Parent {
void parentMethod() {} //오버라이딩
void parentMethod(int i) {} //오버로딩
void childMethod() {} //자식이 메소드 선언
void childMethod(int i) {} //오버로딩
void childMethod() {} //에러!! 중복정의된 메소드
}
📜 this : 인스턴스 자신을 가리키는 참조변수. 인스턴스의 주소가 저장되어있음. 모든 인스턴스 메소드에 지역변수로 숨겨진 채 존재.
- super : this와 같음. 조상의 멤버와 자신의 멤버를 구별하는데 사용.
class Point{
int x;
int y;
String getLocation() {
return "x :" + x + "y :" + y;
}
}
class Point3D extends Point{
int z;
String getLocation() { //오버라이딩!
//return "x :" + x + "y :" + y + "z :" + z;
return super.getlocation() + "z :" + z; //조상 메소드 호출
}
}
- 자손클래스의 인스턴스 생성시 조상의 멤버들도 초기화가 필요.
따라서 첫 문장에서 조상의 생성자를 호출해야한다.
Object클래스를 제외한 모든 클래스의 생성자 첫 줄에는 생성자를 호출!
그렇지 않으면 컴파일러가 자동적으로 'super();'를 생성자의 첫 줄에 삽입!
class Point {
int x;
int y;
Point() {
this(0,0);
}
Point(int x, int y){
'super();' // Object();와 같음. 컴파일러가 자동으로 삽입
this.x = x;
this.y = y;
}
}
class Circle extends Point{
int z;
Circle(int x, int y, int z){
'super();' // Point();와 같음. 컴파일러가 자동으로 삽입
this.x = x;
this.y = y;
this.z = z;
}
}
❗ 주의해야할 오류!
class Point {
int x;
int y;
Point(int x, int y){
'super();' // Object();와 같음. 컴파일러가 자동으로 삽입
this.x = x;
this.y = y;
}
}
class Circle extends Point{
int z;
Circle(int x, int y, int z){
'super();' // Point();와 같음. 컴파일러가 자동으로 삽입
this.x = x;
this.y = y;
this.z = z;
}
}
- 에러!! Point()가 없으므로 조상멤버를 초기화할 수 없다고 판단해버림!!
조상타입의 참조변수로 자손타입의 객체를 다룰 수 있는 것
class TV 'extends Object' { //생략되어있음
boolean power;
int channel;
void power() {}
void channelUP { ++channel; }
void channelDown { --channel; }
}
class CaptionTV extends TV {
String text;
void caption() {}
}
TV t = new TV;
CaptionTV = new CaptionTV();
//다음과 같이 사용가능
TV t = new CaptionTV();
// CaptionTV가 물려받은 변수와 메소드는 사용가능!
// CaptionTV가 가진 변수와 메소드는 사용불가!
- 버튼이 5개인 리모컨으로 기능이 7개인 TV를 조작해도 조작에 에러가 발생하진 않음
반대로 자손타입의 참조변수로 조상타입의 인스턴스를 참조할 수는 없다!!
CaptionTV ct = new TV(); //에러!! 자손타입으로 조상타입을 참조할 수 없음
- 버튼이 7개인 리모컨으로 기능이 5개인 TV를 조작시 두개의 버튼은 에러발생
- 사용할 수 있는 멤버의 갯수를 조절하는 것
- 서로 상속관계에 있는 타입간의 형변환만 가능
- 자손타입에서 조상타입으로 형변환하는 경우, 형변환 생략가능
- 자손타입 → 조상타입 (Up-casting) : 형변환 생략가능
- 자손타입 ← 조상타입 (Down-casting) : 형변환 생략불가
class Car { //조상
String color;
int door;
void drive() {}
void stop() {}
}
class FireEngine extends Car { //자식1
void water() {}
}
class Ambulance extends Car { //자식2
void siren() {}
}
public static void main(String[] args){
Car car = null;
FireEngine fe = new FireEngine();
FireEngine fe2 = null;
fe.water();
car = fe; // car = (Car)fe; 조상 <- 자손. 형변환 생략가능
//car.water(); //에러!! Car타입은 water()메소드를 사용하지못함
fe2 = (FireEngine)car; // 조상 -> 자손. 형변환 생략불가
fe2.water(); //FireEngine 타입이므로 사용가능!
//Ambulance a = (Ambulance)f;//에러! 상속관계가 아닌 클래스간 형변환 불가
}
❗ 객체 값과 변수의 주소값은 모두 그대로! 단지 타입이 바뀔뿐임!!!
(TV의 상태가 바뀌는게 아닌 리모컨이 바뀌는 것)
참조변수 형변환 가능여부 확인에 사용. 가능하면 true 반환
FireEngine fe = new FireEngine();
System.out.println(fe instanceof Object); //Object는 최고조상이므로 true
System.out.println(fe instanceof Car); //Car은 조상이므로 true
System.out.println(fe instanceof FireEngine); //자기자신이므로 true
- 조상으로 형변환이 가능!
참조형 매개변수는 메소드 호출시, 자신과 같은 타입 또는 자손타입의 인스턴스를 넘겨줄 수 있다.
class Product{
int price;
int bonusPoint;
}
class TV extends Product {}
class Computer extends Product {}
class Audio extends Product {}
class Buyer {
int money = 1000;
int bonusPoint = 0;
void buy(TV t) { //TV만 살수 있음. 타입이 다르기 때문
money = t.price;
bonusPoint += t.bonusPoint;
}
- 다른물건도 사려면 같은 꼴의 메소드를 물건 갯수만큼 만들어야함!
//다형성사용. 다음과같이 메인함수에 객체생성
Product p1 = new TV();
Product p2 = new Computer();
Product p3 = new Audio();
//
void buy(Product p) { //모두 Product타입이므로 사용가능!
money = p.price;
bonusPoint += p.bonusPoint;
}
-이 메소드 하나만으로 모든 물건을 살수 있게됨!
}
조상타입의 배열에 자손들의 객체를 담을 수 있다.
Product p1 = new TV();
Product p2 = new Computer();
Product p3 = new Audio();
//배열 사용
Product[] p = new Product[3];
p[0] = new Tv();
p[1] = new Computer();
p[2] = new Audio();
미완성 설계도. 미완성 메소드를 갖고 있는 클래스
abstract class Player {
abstract void play(int pos); //{}이 없는 미완성메소드(추상메소드)
abstract void stop(); //추상메소드
}
- 추상클래스의 인스턴스는 생성불가
- 상속을 통해 추상메소드를 완성해야 인스턴스 생성가능
Player p = new Player(); // 에러!
class AudioPlayer extends Player {
void play(int pos) {}
void stop() {}
}
AudioPlayer ap = new AudioPlayer(); //생성가능!
Player p = new AudioPlayer(); //다형성도 가능!!
미완성 메소드. 구현부({ })가 없는 메소드
abstract 리턴타입 메소드이름(); // 추상메소드 작성
◼ 추상메소드가 있으면 추상클래스이다!
공통적으로 사용될 수 있는, 사용되는 부분을 추상클래스로 만든다.
class Marine {
int x,y; // 공통부분
void move(int x, int y) {} // 공통부분
void stop() {}; // 공통부분
void stimpack() {};
}
class Tank {
int x,y; // 공통부분
void move(int x, int y) {} // 공통부분
void stop() {}; // 공통부분
void changeMode() {};
}
class Dropship {
int x,y; // 공통부분
void move(int x, int y) {} // 공통부분
void stop() {}; // 공통부분
void load() {};
void unLoad() {};
}
//추상클래스 사용
abstract class Unit {
int x,y; // 공통부분
abstract void move(int x, int y); // 공통부분-각각작성
void stop() {}; // 공통부분
}
class Marine {
void move(int x, int y) {}
void stimpack() {};
}
class Tank {
int x,y; // 공통부분
void move(int x, int y) {}
void changeMode() {};
}
class Dropship {
void move(int x, int y) {} // 공통부분
void load() {};
void unLoad() {};
}
-코드가 간결해지고, 다형성도 사용이 가능해짐!
Unit[] group = new Unit[3];
group[0] = new Marine();
group[1] = new Tank();
group[2] = new Dropship();
//Unit[] group = {new Marine(), new Tank(), new Dropship()}; 과 같음
//다중 이동 가능!
for(int i = 0; i<group.length; i++){
group[i].move(100,200); //각각의 move()메소드가 호출된다
}
코드의 관리가 용이. - 중복제거, 변경에 유리한 코드가 만들어짐
추상메소드의 집합 - 프로그래밍 관점
추상클래스 : 일반클래스(멤버변수, 메소드) + 추상메소드
인터페이스 : only 추상메소드 - iv를 가질 수 없다.
//모든멤버가 public
interface 인터페이스이름 {
public static final 타입 상수이름 = 값;
public abstract 메소드이름(매개변수);
}
//예제
interface PlayingCard {
//모든 변수는 상수 - public static final 생략가능
public static final int SPADE = 4;
final int DIAMOND = 3; //public static final int DIAMOND = 3;
static int HEART = 2; //public static final int HEART = 2;
int CLOVER = 1; //public static final int CLOVER = 1;
//모든 메소드는 추상메소드 - public abstract 생략가능
public abstract String getCardNumber();
String getKind(); // public abstract String getKind();
}
- 인터페이스의 조상은 인터페이스만 가능(Object가 최고 조상이 아님)
- 다중 상속이 가능.(추상메소드는 충돌문제가 없음)
interface Movable {
void move(int x, int y);
}
interface Attackable {
void attack(Unit u);
}
interface Fightable extends Movable, Attackable {}
인터페이스에 정의된 모든 추상메소드를 완성하는 것
class 클래스이름 implements 인터페이스이름 {
//인터페이스에 정의된 '모든 추상메소드'를 구현
}
//예제
class Fighter implements Fightable {
void move(int x, int y) { //이동 }
void attack(Unit u) { //공격 }
}
일부만 구현하는 경우, 클래스앞에 abstract를 붙여야한다
abstract class Fighter implements Fightable {
void move(int x, int y) { //이동 }
}
인터페이스도 구현클래스의 조상으로 취급한다.
class Fighter extends Unit implements Fightable {
public void move(int x, int y) {}
public void attack(Fightable f) {}
}
Unit u = new Fighter(); //조상이 자손을 참조
Fightable f = new Fighter(); //인터페이스도 가능
인터페이스 타입 매개변수는 인터페이스를 구현한 클래스의 객체만 가능
interface Fightable {
void move(int x, int y);
//Fightable 인터페이스를 구현한 클래스의 인스턴스만 가능
void attack(Fightable f);
}
인터페이스를 메소드의 리턴타입으로 지정할 수 있다.
//인터페이스를 구현한 클래스의 인스턴스를 반환
Fightable method() {
Fighter f = new Fighter(); //Fighter는 Fightable을 구현한 클래스
return f;
// 위 문장들을 return new Fighter(); 로 줄일 수 있음
}
- 두 대상(객체)간의 '연결, 대화, 소통'을 돕는 '중간역할'을 한다
- 표준화가 가능하다. - JDBC를 통한 DB관련 메소드들을 표준화함.
선언(설계)과 구현을 분리시킬 수 있게 한다.
class B {
public void method() {
System.out.println("B");
}
}
//분리
interface I {
void method();
}
class B implements I {
public void method() {
System.out.println("B");
}
}
- 결합을 느슨하게하여 변경에 유리한 코드 작성이 가능
//직접적인 관계의 A와 B
class A {
public void methodA(B b) { //B를 C로 변경시 변경이 필요하다
b.method();
}
}
class B {
public void method() {
System.out.println("B");
}
}
class C {
public void method() {
System.out.println("C");
}
}
//간접적인 관계의 A와 B
class A {
public void method(I i) { //B를 C로 바꾸어도 변경 불필요
i.method();
}
}
class B implements I {
public void method() {
System.out.println("B");
}
}
class C implements I {
public void method() {
System.out.println("C");
}
}
서로 관계없는 클래스들의 관계를 맺어줄 수 있다.
class Tank extends GroundUnit{
int x,y;
void move(int x, int y) {}
void changeMode() {};
}
class Dropship extends AirUnit{
void move(int x, int y) {}
void load() {};
void unLoad() {};
}
//두 클래스는 관계가 없다!
Tank와 Dropship은 repairable 가능하다.
void repair(Tank t) {}
void repair(Dropship d) {}
- 함수의 오버로딩은 코드의 중복을 발생시킴.
//인터페이스 사용
interface Repairable {}
class Tank extends GroundUnit implements Repairable{}
class Dropship extends AirUnit implements Repairable{}
void repair(Repairable r) {}
- GroundUnit과 AirUnit을 Repairable로 관계를 맺어줌.
인터페이스에 디폴트 메소드, static메소드 추가 가능 (JDK1.8부터)
인터페이스에 새로운 메소드(추상메소드)를 추가하기 어려움.
- 해결책 => 디폴트 메소드(default method)
interface Repairable{
//모든 자손이 똑같이 구현하는 추상메소드
//이 메소드를 모든 인터페이스의 자손이 구현해야함. - 코드의 중복
void EqualMethod();
}
class Tank extends GroundUnit implements Repairable{}
class Dropship extends AirUnit implements Repairable{}
-새로운 공통 메소드를 추가해야할때마다 모든 자손들이 구현을 해야함. - 번거로움
//
interface Repairable{
//모든 자손이 똑같이 사용하는 인스턴스 메소드
//자손이 구현하지 않고 인터페이스 자체에서 구현.
default void EqualMethod() {}; //구현부가 존제
}
디폴트메소드는 기존의 다른 메소드와 충돌할 수 있음
- 여러 인터페이스의 디폴트 메소드 간의 충돌
- 인터페이스를 구현한 클래스에서 디폴트 메소드를 오버라이딩!
interface Unit{
default void move() {};
}
interface GroundUnit{
default void move() {};
}
class Tank implements Unit,GroundUnit {}
Tank t = new Tank();
t.move(); //에러! move()가 충돌됨!
class Tank implements Unit,GroundUnit {
void move() {} //오버라이딩
}
Tank t = new Tank();
t.move(); //OK
- 디폴트 메소드와 조상클래스의 메소드 간의 충돌
- 조상클래스의 메소드가 상속되고, 디폴트 메소드는 무시
class Unit{
void move() {};
}
interface GroundUnit{
default void move() {};
}
class Tank extends Unit implements GroundUnit {}
Tank t = new Tank();
t.move(); //조상클래스의 move()가 실행됨!
클래스 안의 클래스
class A { //외부 클래스
//...
class B { //내부 클래스
//...
}
}
- 내부 클래스에서 외부 클래스의 멤버들을 쉽게 접근할 수 있다.
- 코드의 복잡성을 줄일 수 있다.(캡슐화)
class A { //외부 클래스
int i;
int plus(int i) {};
class B { //내부 클래스
int sum;
sum = plus(i); //객체 생성없이 외부클래스 접근가능
}
}
내부 클래스의 종류와 범위는 변수와 동일
class Outer {
class Inner {} // iv(인스턴스 변수)와 같음
static class StaticInner {} // cv(클래스 변수)와 같음
void outerMethod() {
class LocalInner {} // lv(지역 변수)와 같음
}
}
| 내부 클래스 | 특징 |
|---|---|
| 인스턴스 클래스 (instance class) | 외부 클래스의 멤버변수 선언위치에 선언하며, 외부 클래스의 인스턴스멤버처럼 다루어진다. 주로 외부클래스의 인스턴스맴버들과 관련된 작업에 사용될 목적으로 선언 |
| 스태틱 클래스 (static class) | 외부 클래스의 멤버변수 선언위치에 선언하며, 외부 클래스의 static멤버처럼 다루어진다. 주로 외부 클래스의 static멤버, 특히 static메소드에서 사용될 목적으로 선언 |
| 지역 클래스 (local class) | 외부 클래스의 메소드나 초기화블럭 안에 선언하며, 선언된 영역 내부에서만 사용 가능 |
| 익명 클래스 (anonymous class) | 클래스의 선언과 객체의 생성을 동시에 하는 이름없는 클래스(일회용) |
내부 클래스의 제어자는 변수에 사용 가능한 제어자와 동일
class Outer {
private class Inner {} //private iv(인스턴스 변수)와 같음
protected static class StaticInner{}//protected cv(클래스 변수)와 같음
void outerMethod() {
class LocalInner {} // lv(지역 변수)와 같음
}
}
static 내부클래스만 static 멤버를 가질 수 있음
class Outer{
class InstanceInner {
int iv = 100;
static int cv = 100; //에러! 선언불가
final static int CONST = 100; //상수는 허용
}
static class StaticInner { // 외부 클래스의 인스턴스 멤버에 접근 불가
int iv = 200;
static int cv = 200; //static클래스만 static메소드를 가질 수 있다.
}
void method() {
class LocalInner{
int iv = 200;
static int cv = 200;//에러! 선언불가
final static int CONST = 100; //상수는 허용
}
}
}
외부 클래스의 private멤버를 내부클래스에서 접근가능하다.
class Outer {
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; //static에는 접근 가능
}
void method() {
int lv = 0;
final int Lv = 0;
class LocalInner{
int liv = outerIv;
int liv2 = outerCv;
//외부 클래스의 지역변수는 final이 붙은 변수(상수)만 접근가능
//내부클래스의 객체가 지역변수보다 더 오래 생존가능해서
int liv3 = lv; //에러!
//(JDK1.8부터 에러아님-상수로 간주) lv가 값이 바뀐다면 에러!!
int liv4 = Lv; //상수는 ok
}
}
}
외부 클래스의 인스턴스를 먼저 만든 뒤, 내부 클래스 인스턴스 생성
public static void main(String[] args) {
Outer oc = new Outer(); //외부 클래스 인스턴스 생성
Outer.InstanceInner li = oc.new InstanceInner(); //내부 클래스 인스턴스
//static내부 클래스의 인스턴스는 외부클래스를 먼저 생성하지 않아도 됨.
Outer.StaticInner si = new Outer.StaticInner();
}
외부 클래스와 내부 클래스의 변수 이름이 같을때는 this 사용
class Outer{
int value = 30; //Outer.this.value
class Inner {
int value = 20; //this.value
void method() {
int value = 10; //value
System.out.println(value + this.value + Outer.this.value);
}
}
}
이름이 없는 일회용 클래스. 정의와 생성을 동시에
new 조상클래스이름() {
//선언
}
new 인터페이스이름() {
//선언
}
//Ex)
Object iv = new Object() {
void method() {};
}
static Object cv = new Object() {
void method() {};
}
void method() {
Object lv = new Object() {
void lvMethod() {};
}
}