Link Search Menu Expand Document

자바와 객체지향

객체 지향은 인간 지향이다

  • 구조적 프로그래밍 언어의 핵심은 “함수”, 함수는 코드를 논리적인 단위로 분할하여 정복
  • 객체지향의 핵심은 “객체”, 객체의 속성(필드)와 메서드(함수)를 하나의 단위에서 관리

    객체 지향의 4대 특성

  • 캡슐화(Encapsulation)
  • 상속(Extends)
  • 추상화(Abstraction)
  • 다형성(Polymorphism)

    클래스 vs. 객체

  • 클래스: 분류의 개념, 객체: 실체

    추상화: 모델링

    추상화는 모델링이다

  • 객체: 세상에 존재하는 유일무이한 사물
  • 클래스: 분류, 집합, 같은 속성과 기능을 가진 객체를 총칭하는 개념
  • 구체적인 것을 분해해서 관심 영역(애플리케이션 경계, Application Boundary)에 있는 특성만 가지고 재조합 = 모델링

    추상화와 T 메모리

package abstraction01;

public class Mouse {
	public String name;
  public int age;
  public int countOfTail;

  public void sing() {
  	System.out.println(name + " 찍찍!!!");
  }
}
package abstraction01;

public class MouseDriver {
	public static void main(String[] args) {
  	Mouse mickey = new Mouse();

    mickey.name = "미키";
    mickey.age = 85;
    mickey.countOfTail = 1;

    mickey.sing();

    mickey = null;

    Mouse jerry = new Mouse();

    jerry.name = "제리";
    jerry.age = 73;
    jerry.countOfTail = 1;

    jerry.sing();
  }
}
  • java.lang 패키지와 모든 클래스들 (Mouse, MouseDriver)이 T 메모리의 스태틱 영역에 배치됨
    • Mouse 클래스의 속성은 스태틱 영역에서 이름만 존재
  • new Mouse()에 의해 생성된 객체는 T 메모리의 힙 영역에 배치됨
  • Mouse mickey에 의해 객체 참조 변수 mickey가 T 메모리의 스택 영역 - main() 메서드 스택 프레임 안에 배치됨
  • mickey.name = "미키"에서 객체 참조 연산자(.)를 이용해 힙 상의 객체에 접근, name 속성에 "미키"라는 문자열을 할당
  • mickey.sing()에서 mickey가 참조하는 Mouse 객체의 sing() 메서드가 코드 실행 영역에서 실행
  • mickey = null이 실행된 후 Garbage Collector가 참조하지 않는 객체를 제거함
  • jerry가 참조하는 Mouse 객체는 mickey가 참조한 객체와 다름

    클래스 멤버 vs. 객체 멤버 = static 멤버 vs. 인스턴스 멤버

package abstraction02;

public class Mouse {
	public String name;
  public int age;
  public static int countOfTail = 1;

  public void sing() {
  	System.out.println(name + " 찍찍!!!");
  }
}
  • 객체는 유일무이하게 존재하는 실체이기 때문에 속성에 값을 가짐
  • 클래스는 개념이면서 분류이므로 속성에 값을 가질 수 없음
  • 그러나 모든 객체가 같은 값을 가지는 정적 속성은 static 키워드를 붙여 클래스에 위치시킴
    • 해당 클래스의 모든 객체가 같은 값을 가질 때 사용
    • 정적 메서드: 클래스의 인스턴스를 만들지 않고 사용하는 유틸리티성 메서드에 사용
    • 정적 속성은 T 메모리의 스태틱 영역에 클래스가 배치될 때 공간이 할당됨
  • 세 가지 변수 유형 |이름|다른 이름|사는 곳(T 메모리)| |–|–|–| | static 변수 | 클래스 [멤버] 속성, 정적 변수, 정적 속성 | 스태틱 영역 | | 인스턴스 변수 | 객체 [멤버] 속성, 객체 변수 | 힙 영역 | | local 변수 | 지역 변수 | 스택 영역(스택 프레임 내부) |

    상속: 재사용 + 확장

  • 객체 지향의 상속에 대한 잘못된 예 - 계층도 / 조직도

객체 지향의 상속에 대한 잘못된 예 - 계층도 / 조직도

  • 객체 지향의 상속에 대한 올바른 예 - 분류도

객체 지향의 상속에 대한 올바른 예 - 분류도

  • 객체 지향에서의 상속
    • 상위 클래스의 특성을 하위 클래스에서 상속(특성 상속)
    • 필요한 특성을 추가, 즉 확장해서 사용
    • 부모-자식 관계가 아니라 상위 분류-하위 분류 또는 슈퍼 클래스-서브 클래스
    • 상위 클래스 쪽으로 갈수록 추상화(Abstraction), 일반화(Generalization)
    • 하위 클래스 쪽으로 갈수록 구체화(Concrete), 특수화(Specialization)

      상속의 강력함

package inheritance01;

public class 동물 {
	String myClass;

  동물() {
  	myClass = "동물";
  }

  void showMe() {
  	System.out.println(myClass);
  }
}
package inheritance01;

public class 포유류 extends 동물 {
  포유류() {
  	myClass = "포유류";
  }
}
package inheritance01;

public class 조류 extends 동물 {
  조류() {
  	myClass = "조류";
  }
}
package inheritance01;

public class 고래 extends 포유류 {
  고래() {
  	myClass = "고래";
  }
}
package inheritance01;

public class 박쥐 extends 포유류 {
  박쥐() {
  	myClass = "박쥐";
  }
}
package inheritance01;

public class 참새 extends 조류 {
  참새() {
  	myClass = "참새";
  }
}
package inheritance01;

public class 펭귄 extends 조류 {
  펭귄() {
  	myClass = "펭귄";
  }
}
package inheritance01;

public class Driver01 {
	public static void main(String[] args) {
  	동물 animal = new 동물();
    포유류 mammalia = new 포유류();
    조류 bird = new 조류();
    고래 whale = new 고래();
    박쥐 bat = new 박쥐();
    참새 sparrow = new 참새();
    펭귄 penguin = new 펭귄();

    animal.showMe();
    mammalia.showMe();
    bird.showMe();
    whale.showMe();
    bat.showMe();
    sparrow.showMe();
    penguin.showMe();
  }
}
  • 상위 클래스에서만 showMe() 메서드를 구현했지만 모든 하위 클래스 객체에서 showMe() 메서드를 사용할 수 있음
package inheritance01;

public class Driver02 {
	public static void main(String[] args) {
  	동물 animal = new 동물();
    동물 mammalia = new 포유류();
    동물 bird = new 조류();
    동물 whale = new 고래();
    동물 bat = new 박쥐();
    동물 sparrow = new 참새();
    동물 penguin = new 펭귄();

    animal.showMe();
    mammalia.showMe();
    bird.showMe();
    whale.showMe();
    bat.showMe();
    sparrow.showMe();
    penguin.showMe();
  }
}
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();
    }
  }
}
  • 클래스 상속 구조에서 최상위 클래스는 Object이므로 모든 클래스에서 toString() 메서드를 사용할 수 있음

    상속은 is a 관계를 만족해야 한다?

  • 객체 지향의 상속은
    • 상위 클래스의 특성을 재사용하는 것
    • 상위 클래스의 특성을 확장하는 것
    • is a kind of 관계를 만족하는 것

      다중 상속과 자바

다중 상속의 문제점

  • 다중 상속의 다이아몬드 문제
    • 위의 그림에서 인어가 ‘수영’을 할 경우 ‘사람’의 수영을 사용할 지, ‘물고기’의 ‘수영’을 사용할 지 알 수 없음
    • 자바와 C#은 클래스의 다중 상속을 지원하지 않음

      상속과 인터페이스

  • 인터페이스: 구현 클래스 is able to 인터페이스
  • 해석: 구현 클래스는 인터페이스할 수 있다
  • 예제: 고래는 헤엄칠 수 있다

상속과 인터페이스

  • Serializable 인터페이스: 직렬화할 수 있는
  • Cloneable 인터페이스: 복제할 수 있는
  • Comparable 인터페이스: 비교할 수 있는
  • Runnable 인터페이스: 실행할 수 있는
  • 상위 클래스는 물려줄 특성이 풍성할수록 좋고, 인터페이스는 구현을 강제할 메서드의 개수가 적을수록 좋음

    상속과 T 메모리

    클래스 다이어그램

package inheritance03;

public class Animal {
	public String name;

  public void showName() {
  	System.out.printf("안녕 나는 %s야. 반가워\n", name);
  }
}
package inheritance03;

public class Penguin extends Animal {
	public String habitat;

  public void showHabitat() {
  	System.out.printf("%s는 %s에 살아\n", name, habitat);
  }
}
package inheritance03;

public class Driver {
	public static void main(String[] args) {
  	Penguin pororo = new Penguin();

    pororo.name = "뽀로로";
    pororo.habitat = "남극";

    pororo.showName();
    pororo.showHabitat();

    Animal pingu = new Penguin();

    pingu.name = "핑구";
    // pingu.habitat = "EBS";

    pingu.showName();
    // pingu.showHabitat();

    // Penguin happyfeet = new Animal();
  }

  public void showHabitat() {
  	System.out.printf("%s는 %s에 살아\n", name, habitat);
  }
}
  • 5번째 줄까지 실행 시 Penguin 클래스의 인스턴스 뿐만 아니라 Animal 클래스의 인스턴스도 힙 영역에 생성됨
  • 13번째 줄까지 실행 시 pingu 객체 참조 변수는 Penguin이 아니라 Animal을 가리킴
    • 따라서, pingushowHabitat() 메서드를 사용할 수 없음

      다형성: 사용편의성

      오버라이딩? 오버로딩?

  • 오버라이딩(overriding)
    • 올라타기 → 맨 위에 올라탄 존재만 보임
    • 같은 메서드 이름, 같은 인자 목록으로 상위 클래드의 메서드를 재정의
  • 오버로딩(overloading)
    • 적재하기 → 옆으로 적재된 모든 적재물이 다 보임
    • 같은 메서드 이름, 다른 인자 목록으로 다수의 메서드를 중복 정의

      다형성과 T 메모리

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();
  }
}
  • Penguin 클래스가 상위 클래스인 Animal
    • showName() 메서드를 오버라이딩(재정의)
    • showName(yourName: String) 메서드를 오버로딩(중복 정의)
  • 10번째 줄의 pororo.showName() 부분을 실행하면 Penguin 객체에서 재의한 showName() 메서드가 호출됨
  • 11번째 줄의 pororo.showName("초보람보") 부분을 실행하면 문자열 하나를 인자로 받는 중복 정의된 showName(yourName) 매서드를 호출
  • 17번째 줄의 pingu.showName() 메서드를 실행하면 Animal 객체에 정의된 showName() 메서드가 아니라 Penguin 객체에 정의된 showName() 메서드가 실행됨
    • 상위 클래스 타입의 객체 참조 변수를 사용하더라도 하위 클래스에서 오버라이딩(재정의)한 메서드가 호출됨

      캡슐화: 정보 은닉

      객체 멤버의 접근 제어자

  • 상속을 받지 않았다면 객체 멤버는 객체를 생성한 후 객체 참조 변수를 이용해 접근해야 함
  • 정적 멤버는 클래스명.정적멤버 형식으로 접근하는 것을 권장
    • 일관된 형식으로 접근하기 위함
    • 객체참조변수명.정적멤버 형식으로 접근할 경우 메소드 스택 프레임 → 힙 영역 → 스태틱 영역
    • 클래스명.정적멤버 형식으로 접근할 경우 스태틱 영역에 바로 접근

      참조 변수의 복사

  • Call By Value
package reference;

public class CallByValue {
	public static void main(String[] args) {
  	int a = 10;
    int b = a;

    b = 20;

    System.out.println(a); // 10
    System.out.println(b); // 20
  }
}
  • 기본 자료형 변수를 복사하는 경우 Call By Value에 의해 그 값이 복사되며 두 개의 변수는 서로에 영향을 주지 않음
  • Call By Reference
package reference;

public class CallByReference {
	public static void main(String[] args) {
  	Animal ref_a = new Animal();
    Animal ref_b = ref_a;

    ref_a.age = 10;
    ref_b.age = 20;

    System.out.println(ref_a.age); // 20
    System.out.println(ref_b.age); // 20

    System.out.println(ref_a); // reference.Animal@15db9742
    System.out.println(ref_b); // reference.Animal@15db9742
  }
}

class Animal {
	public int age;
}
  • 객체를 저장하고 있는 객체 참조 변수를 복사할 경우에는 참조 변수의 주소값을 복사
  • ref_anull이나 다른 객체의 참조를 할당하면 ref_b에는 아무런 영향도 없음

  • 기본 자료형 변수는 값을 값 자체로 판단
  • 참조 자료형 변수는 값을 주소, 즉 포인터로 판단
  • 기본 자료형 변수를 복사할 때, 참조 자료형 변수를 복사할 때 일어나는 일은 없음
    • 가지고 있는 값을 그대로 복사해서 넘겨줌