객체 지향의 4대 특성 : "캡! 상추다"
클래스는 분류에 대한 개념이지 실체가 아니다. 객체는 실체다.
추상의 사전적 의미 : 여러 가지 사물이나 개념에서 공통되는 특성이나 속성 따위를 추출하여 파악하는 작용
"객체 지향의 추상화는 곧 모델이다" 라는 관점
추상화란 구체적인 것을 분해해서 관찰자가 관심 있는 특성(관심 영역, 애플리케이션 경계)만 가지고 재조합하는 것 = 모델링
여기서 모델은 실제 사물을 정확히 복제하는 게 아니라 목적에 맞게 관심 있는 특성만을 추출해서 표현하는 것이다. 즉 추상화를 통해 실제 사물을 단순하게 묘사하는 것이다. 이런 모델링(추상화)은 객체 지향에서 클래스를 설계할 때 필요한 기법이고 또한 데이터베이스의 테이블을 설계할 때 필요한 기법이다.
객체 : 세상에 존재하는 유일무이한 사물
클래스 : 분류, 집합. 같은 속성과 기능을 가진 객체를 총칭하는 개념
추상화 = 모델링 = 자바의 class 키워드
static 변수
인스턴스 변수
local 변수
객체 참조 변수명은 객체스럽게, 클래스명은 클래스명답게 정하는 습관을 들이기
package inheritance01;
public class Driver03 {
public static void main(String[] args) {
동물[] animals = new 동물[7];
animals[0] = new 동물();
animals[1] = new 포유류();
animals[2] = new 조류();
animals[3] = new 고래();
animals[4] = new 박쥐();
animals[5] = new 참새();
animals[6] = new 펭귄();
for (int index = 0; index < animals.length; index++) {
animals[index].showMe();
}
}
}
상속
인터페이스
상위 클래스는 하위 클래스에게 특성(속성과 메서드)을 상속해 주고, 인터페이스는 클래스가 '무엇을 할 수 있다'라고 하는 기능을 구현하도록 강제한다.
하위 클래스의 인스턴스가 생성될 때 상위 클래스의 인스턴스도 함께 생성된다.
오버라이딩(재정의): 상위 클래스의 메서드와 같은 메서드 이름, 같은 인자 리스트
오버로딩(중복정의): 같은 메서드 이름, 다른 인자 리스트
package polymorphism01;
public class Animal {
public String name;
public void showName() {
System.out.printf("안녕 나는 %s야. 반가워\n", name);
}
}
package polymorphism01;
public class Penguin extends Animal {
public String habitat;
public void showHabitat() {
System.out.printf("%s는 %s에 살아\n", name, habitat);
}
//오버라이딩 - 재정의: 상위클래스의 메서드와 같은 메서드 이름, 같은 인자 리스트
public void showName() {
System.out.println("어머 내 이름은 알아서 뭐하게요?");
}
// 오버로딩 - 중복정의: 같은 메서드 이름, 다른 인자 리스트
public void showName(String yourName) {
System.out.printf("%s 안녕, 나는 %s라고 해\n", yourName, name);
}
}
package polymorphism01;
public class Driver {
public static void main(String[] args) {
Penguin pororo = new Penguin();
pororo.name = "뽀로로";
pororo.habitat = "남극";
pororo.showName();
pororo.showName("초보람보");
pororo.showHabitat();
Animal pingu = new Penguin();
pingu.name = "핑구";
pingu.showName(); // 어머 내 이름은 알아서 뭐하게요?
Animal pingu2 = new Animal();
pingu2.name = "핑구2"
pingu2.showName(); // 안녕 나는 핑구2야. 반가워
}
}
→ 상위 클래스 타입의 객체 참조 변수를 사용하더라도 하위 클래스에서 오버라이딩(재정의)한 메서드가 호출된다.
하위 클래스가 재정의한 메서드를 알아서 호출해 줌으로써 형변환이나 instanceof 연산자를 써서 하위 클래스가 무엇인지 신경 쓰지 않아도 된다.
오버라이딩을 통한 메서드 재정의, 오버로딩을 통한 메서드 중복 정의를 통해 다형성을 제공함으로써 사용편의성을 준다.
class Driver {
public static void main(String[] args) {
동물[] 동물들 = new 동물[5];
동물들[0] = new 쥐();
동물들[1] = new 고양이();
동물들[2] = new 강아지();
동물들[3] = new 송아지();
for(int i = 0; i < 동물들.length; i++) {
동물들[i].울어보세요();
}
}
}
//출력
나는 쥐!찍!찍!
나는 고양이!야옹!야옹!
나는 강아지!멍!멍!
나는 송아지!음메!음메!
접근 제어자는 절대 단순하지 않다.
package encapsulation01.packageOne;
public class ClassA {
private int pri;
int def;
protected int pro;
public int pub;
static private int priStatic;
static int defStatic;
static protected int proStatic;
static public int pubStatic;
void runSomething() {
pri = 1;
this.pri = 1;
def = 1;
this.def = 1;
pro = 1;
this.pro = 1;
pub = 1;
this.pub = 1;
// 정적 멤버는 클래스명.정적멤버 형태의 접근을 권장
ClassA.priStatic = 1;
priStatic = 1;
this.priStatic = 1;
ClassA.defStatic = 1;
defStatic = 1;
this.defStatic = 1;
ClassA.proStatic = 1;
proStatic = 1;
this.proStatic = 1;
ClassA.pubStatic = 1;
pubStatic = 1;
this.pubStatic = 1;
}
static void runStaticThing() {
// 객체를 생성하지 않고는 객체 멤버 접근 불가
// pri = 1; this.pri = 1;
// def = 1; this.def = 1;
// pro = 1; this.pro = 1;
// pub = 1; this.pub = 1;
// 정적 멤버는 클래스명.정적멤버 형태의 접근을 권장
ClassA.priStatic = 1;
priStatic = 1; // this.priStatic = 1;
ClassA.defStatic = 1;
defStatic = 1; // this.defStatic = 1;
ClassA.proStatic = 1;
proStatic = 1; // this.proStatic = 1;
ClassA.pubStatic = 1;
pubStatic = 1; // this.pubStatic = 1;
// 객체 멤버를 객체 생성 후에 객체 참조 변수를 통해 접근 가능
ClassA ca = new ClassA();
ca.pri = 1;
ca.def = 1;
ca.pro = 1;
ca.pub = 1;
// 객체 참조 변수를 통해 정적 멤버도 접근 가능, 권장하지는 않음
ca.priStatic = 1;
ca.defStatic = 1;
ca.proStatic = 1;
ca.pubStatic = 1;
}
}
package encapsulation01.packageOne;
public class ClassAA extends ClassA {
void runSomething() {
//pri = 1; this.pri = 1;
def = 1; this.def = 1;
pro = 1; this.pro = 1;
pub = 1; this.pub = 1;
// 정적 멤버는 클래스명.정적멤버 형태의 접근을 권장
//ClassA.priStatic = 1; priStatic = 1; this.priStatic = 1;
ClassA.defStatic = 1; defStatic = 1; this.defStatic = 1;
ClassA.proStatic = 1; proStatic = 1; this.proStatic = 1;
ClassA.pubStatic = 1; pubStatic = 1; this.pubStatic = 1;
}
static void runStaticThing() {
// 객체를 생성하지 않고는 객체 멤버 접근 불가
//pri = 1; this.pri = 1;
//def = 1; this.def = 1;
//pro = 1; this.pro = 1;
//pub = 1; this.pub = 1;
// 정적 멤버는 클래스명.정적멤버 형태의 접근을 권장
//ClassA.priStatic = 1; priStatic = 1; //this.priStatic = 1;
ClassA.defStatic = 1; defStatic = 1; //this.defStatic = 1;
ClassA.proStatic = 1; proStatic = 1; //this.proStatic = 1;
ClassA.pubStatic = 1; pubStatic = 1; //this.pubStatic = 1;
// 객체 멤버를 객체 생성 후에 객체 참조 변수를 통해 접근 가능
ClassAA caa = new ClassAA();
//ca.pri = 1;
caa.def = 1;
caa.pro = 1;
caa.pub = 1;
// 객체 참조 변수를 통해 정적 멤버도 접근 가능, 권장하지는 않음
//ca.priStatic = 1;
caa.defStatic = 1;
caa.proStatic = 1;
caa.pubStatic = 1;
}
}
package encapsulation01.packageOne;
public class ClassB {
void runSomething() {
// 상속을 받지 않았기에 ClassA 의 객체 멤버는 객체 생성 후에 접근 가능
//pri = 1; this.pri = 1;
//def = 1; this.def = 1;
//pro = 1; this.pro = 1;
//pub = 1; this.pub = 1;
// 정적 멤버는 클래스명.정적멤버 형태의 접근을 권장
//ClassA.priStatic = 1; //priStatic = 1; this.priStatic = 1;
ClassA.defStatic = 1; //defStatic = 1; this.defStatic = 1;
ClassA.proStatic = 1; //proStatic = 1; this.proStatic = 1;
ClassA.pubStatic = 1; //pubStatic = 1; this.pubStatic = 1;
// 객체 멤버를 객체 생성 후에 객체 참조 변수를 통해 접근 가능
ClassA ca = new ClassA();
//ca.pri = 1;
ca.def = 1;
ca.pro = 1;
ca.pub = 1;
}
static void runStaticThing() {
// 객체를 생성하지 않고는 객체 멤버 접근 불가
//pri = 1; this.pri = 1;
//def = 1; this.def = 1;
//pro = 1; this.pro = 1;
//pub = 1; this.pub = 1;
// 정적 멤버는 클래스명.정적멤버 형태의 접근을 권장
//ClassA.priStatic = 1; //priStatic = 1; //this.priStatic = 1;
ClassA.defStatic = 1; //defStatic = 1; //this.defStatic = 1;
ClassA.proStatic = 1; //proStatic = 1; //this.proStatic = 1;
ClassA.pubStatic = 1; //pubStatic = 1; //this.pubStatic = 1;
// 객체 멤버를 객체 생성 후에 객체 참조 변수를 통해 접근 가능
ClassA ca = new ClassA();
//ca.pri = 1;
ca.def = 1;
ca.pro = 1;
ca.pub = 1;
// 객체 참조 변수를 통해 정적 멤버도 접근 가능, 권장하지는 않음
//ca.priStatic = 1;
ca.defStatic = 1;
ca.proStatic = 1;
ca.pubStatic = 1;
}
}
package encapsulation01.packageTwo;
import encapsulation01.packageOne.ClassA;
public class ClassAB extends ClassA {
void runSomething() {
//pri = 1; this.pri = 1;
//def = 1; this.def = 1;
pro = 1; this.pro = 1;
pub = 1; this.pub = 1;
// 정적 멤버는 클래스명.정적멤버 형태의 접근을 권장
//ClassA.priStatic = 1; priStatic = 1; this.priStatic = 1;
//ClassA.defStatic = 1; defStatic = 1; this.defStatic = 1;
ClassA.proStatic = 1; proStatic = 1; this.proStatic = 1;
ClassA.pubStatic = 1; pubStatic = 1; this.pubStatic = 1;
}
static void runStaticThing() {
// 객체를 생성하지 않고는 객체 멤버 접근 불가
//pri = 1; this.pri = 1;
//def = 1; this.def = 1;
//pro = 1; this.pro = 1;
//pub = 1; this.pub = 1;
// 정적 멤버는 클래스명.정적멤버 형태의 접근을 권장
//ClassA.priStatic = 1; priStatic = 1; //this.priStatic = 1;
//ClassA.defStatic = 1; defStatic = 1; //this.defStatic = 1;
ClassA.proStatic = 1; proStatic = 1; //this.proStatic = 1;
ClassA.pubStatic = 1; pubStatic = 1; //this.pubStatic = 1;
// 객체 멤버를 객체 생성 후에 객체 참조 변수를 통해 접근 가능
ClassAB cab = new ClassAB();
//ca.pri = 1;
//ca.def = 1;
cab.pro = 1;
cab.pub = 1;
// 객체 참조 변수를 통해 정적 멤버도 접근 가능, 권장하지는 않음
//ca.priStatic = 1;
//cab.defStatic = 1;
cab.proStatic = 1;
cab.pubStatic = 1;
}
}
package encapsulation01.packageTwo;
import encapsulation01.packageOne.ClassA;
public class ClassC {
void runSomething() {
// 상속을 받지 않았기에 ClassA 의 객체 멤버는 객체 생성 후에 접근 가능
//pri = 1; this.pri = 1;
//def = 1; this.def = 1;
//pro = 1; this.pro = 1;
//pub = 1; this.pub = 1;
// 정적 멤버는 클래스명.정적멤버 형태의 접근을 권장
//ClassA.priStatic = 1; //priStatic = 1; this.priStatic = 1;
//ClassA.defStatic = 1; //defStatic = 1; this.defStatic = 1;
//ClassA.proStatic = 1; //proStatic = 1; this.proStatic = 1;
ClassA.pubStatic = 1; //pubStatic = 1; this.pubStatic = 1;
// 객체 멤버를 객체 생성 후에 객체 참조 변수를 통해 접근 가능
ClassA ca = new ClassA();
//ca.pri = 1;
//ca.def = 1;
//ca.pro = 1;
ca.pub = 1;
}
static void runStaticThing() {
// 객체를 생성하지 않고는 객체 멤버 접근 불가
//pri = 1; this.pri = 1;
//def = 1; this.def = 1;
//pro = 1; this.pro = 1;
//pub = 1; this.pub = 1;
// 정적 멤버는 클래스명.정적멤버 형태의 접근을 권장
//ClassA.priStatic = 1; //priStatic = 1; //this.priStatic = 1;
//ClassA.defStatic = 1; //defStatic = 1; //this.defStatic = 1;
//ClassA.proStatic = 1; //proStatic = 1; //this.proStatic = 1;
ClassA.pubStatic = 1; //pubStatic = 1; //this.pubStatic = 1;
// 객체 멤버를 객체 생성 후에 객체 참조 변수를 통해 접근 가능
ClassA ca = new ClassA();
//ca.pri = 1;
//ca.def = 1;
//ca.pro = 1;
ca.pub = 1;
// 객체 참조 변수를 통해 정적 멤버도 접근 가능, 권장하지는 않음
//ca.priStatic = 1;
//ca.defStatic = 1;
//ca.proStatic = 1;
ca.pubStatic = 1;
}
}
기본 자료형 변수는 저장하고 있는 값을 그 값 자체로 해석하는 반면, 객체 참조 변수는 저장하고 있는 값을 주소로 해석