자바의정석7(객체지향2)

min seung moon·2021년 3월 2일
0

자바의정석

목록 보기
7/8

객체지향

1. 상속(Inheritance)

  • 기존의 클래스로 새로운 클래스를 작성하는 것(코드의 재사용)
  • 두 클래스를 부모와 자식으로 관계를 맺어주는 것
  • keyword : extends (확장, 부모에게 상속 받으면서 자식이 점차 확장이 된다)
class 자식클래스 extends 부모클래스 {
	// ...
}
class Parent {}
class Child extends Parent {
	// ...
}
  • 자손은 조상의 모든 멤버를 상속 받는다(생성자, 초기화블럭 제외)
  • 자손의 멤버 개수는 조상보다 적을 수 없다(같거나 많다)
// 부모의 멤버는 1개
class Parent{
	int age;
}

// 자식의 멤버는 상속받은 멤버 1개
class child extends Parent {}
  • 자손의 변경은 조상에 영향을 미치지 않는다
class Parent {
	int age
}

// 자신의 멤버 1개 + 상속받은 멤버 1개
class Child extends Preant {
	void play() {
    	System.out.println("놀자~");
	}
}
  • 예시
// 부모는 자식에게 영향을 줄 수 있다(부모가 변경되면 자식에게 영향을 줌)
class Point{
	int x;
    	int y;
}

// 그대로 전부 작성을 해주어야 한다
// 누구에게도 영향을 주지 않는다
class Point3D {
	int x;
    	int y;
        int z;
}

// 부모 Point에게 x, y 변수를 받아서 z만 따로 선언해주면 된다
// 자식이 부모에게 영향을 주지는 못한다(자식이 변경되도 부모에게는 영향이 없다)
class Point3D extends Point {
	int z;
}

2. 포함 관계

  • 포함(composite)이란?

    • 클래스의 멤버로 참조변수를 선언하는 것
    class Point{
    	int x;
      	int y;
    }
    
    // Circle은 무엇하고도 관계가 없음
    class Circle {
    	int x;
      	int y;
          	int z;
    }
    
    // Circle이 Point를 포함하고 있다(포함 관계)
    class Circle {
    	Point c = new Point();
      	int r;
    }

3. 클래스 간의 관계 결정하기

  • 상속관계 : '~은 ~이다.(is-a)'
  • 포함관계 : '~은 ~을 가지고 있다. (has-a)'

4. 단일 상속(Single Inheritance)

  • Java는 단일 상속만 허용된다(C++은 다중상속 허용)
class TvDVD extends Tv, DVD { // 에러 조상은 하나만 허용
	// ...
}
  • 비중이 높은 클래스는 하나만 상속관계로, 나머지는 포함관계로 한다

5. Object클래스 - 모든 클래스의 조상

  • 부모가 없는 클래스는 자동적으로 Object클래스를 상속 받게 된다
  • 모든 클래스는 Object클래스에 정의된 11개의 메서드를 상속받는다
    • toString(), equals(Object obj), hasCode(), ...
// 부모가 없는 클래스는 컴파일러에서 자동으로 Object를 상속시킨다
class Tv extends Object{
	// ...
}

class SmartTv extends Tv {
	// ...
}

6. 오버라이딩(overriding, 덮어쓰다)

  • 상속받은 조상의 메서드를 자신에 맞게 변경하는 것
    • 선언부 변경불가, 내용만 변경 가능({}구현부만 변경 가능)
class Point {
	int x;
    	int y;
        
        String getLocation() {
        	return "x :" + x + ", y :" + y;
        }
}

class Point3D extends Point {
	int z;
    	
        @override
        String getLocation() {
        	return "x :" + x + ", y :" + y + ", z :" + z;
        }
}
  • 오버라이딩의 조건

    • 선언부가 조상 클래스의 메서드와 일치해야 한다(선언부, 반환타입, 메서드이름, 매개변수목록이 모두 일치))
    • 접근 제어자를 조상 클래스의 메서드보다 좁은 범위로 변경할 수 없다
    • 예외는 조상 클래스의 메서드보다 많이 선언할 수 없다(같거나 작아야한다)
    class Parent {
    	void parentMethod() throws IOException, SQLException {
      		...
      	}
    }
    
    class Child extends Parent {
    	void parentMethod() throws IOException {
      		...
      	}
    }
  • 오버로딩 vs 오버라이딩

    • 오버로딩(overloading) : 기존에 없는 새로운 메서드를 정의하는 것(new)
    • 오버라이딩(overriding) : 상속받은 메서드의 내용을 변경하는 것(change{변경}, modify{수정})

7. 참조변수 super

  • 객체 자신을 가리키는 참조변수, 인스턴스 메서드(생성자)내에만 존재
  • 조상의 멤버를 자신의 멤버와 구별할 때 사용
class Parent { int x = 10; // super.x }

class Child extends Parent {
	int x = 20; // this.x
    
    void method() {
    	System.out.println("x=" + x); // 20
        System.out.println("this.x=" + this.x); // 20
        System.out.println("super.x=" + super.x); // 10
    }
}
class Parent2 { int x = 10; // super.x, this.x 둘 다 가능 }

class Child2 extends Parent2 {    
    void method() {
    	System.out.println("x=" + x); // 10
        System.out.println("this.x=" + this.x); // 10
        System.out.println("super.x=" + super.x); // 10
    }
}

8. super() - 조상의 생성자

  • 조상의 생성자를 호출할 때 사용
  • 조상의 멤버는 조상의 생성자를 호출해서 초기화
  • 생성자의 첫 줄에 반드시 생성자를 호출해야 한다 그렇지 않으면 컴파일러가 생성자의 첫줄에 super()를 삽입(super대신에 this()를 사용해도 된다)
class Point {
	int x, y;
    
    Point(int x, int y) {
    	this.x = x;
        this.y = y;
    }
}

class Point3D extends Point {
	int z;
    
    // 자신이 갖고 있는 멤버만 초기화
    Point3D(int x, int y, int z) {
    	super(x, y); // 조상클래스의 생성자Point(int x, int y)를 호출
        this.z = z; // 자신의 멤버를초기화
    }
}

9. 패키지(package)

  • 사로 관련된 클래스의 묶음
  • 클래스는 클래스 파일(*.class), 패키지는 폴더, 하위 패키지는 하위 폴더
  • 클래스의 실제 이름(full name)은 패키지를 포함(java.lang.String)
  • rt.jar는 클래스들을 압축한 파일(JDK설치경로\jre\lib에 위치)
    • 자바9부터는 rt.jar이 없어지고 module 개념이 적용
  • 패키지 소스파일의 첫 번째 문장으로 단 한번 선언
  • 같은 소스 파일의 클래스들은 모두 같은 패키지에 속하게 된다
  • 패키지 선언이 없으면 이름없는(unnamed)패키지에 속하게 된다
package com.codechobo.book

public class PackageTest {
	public static void main(String[] args) {
    	System.out.println("Hello, world!");
    }
}

10. 클래스 패스(classpath)

  • 클래스 파일(*.class)의 위치를 알려주는 경로(path)
  • 환경변수 classpath로 관리하며, 경로간의 구분자는 ';'를 사용
  • classpath(환경변수)에 패키지의 루트를 등록해줘야 함

11. import 문

  • 클래스를 사용할 때 패키지의 이름을 생략할 수 있다
  • 컴파일러에게 클래스가 속한 패키지를 알려준다
  • ctrl + shift + o : 이클립스 명령어
  • java.lang패키지의 클래스는 import하지 않고도 사용할 수 있다
    • String, Object, System, Thread ...
class ImportTest {
	java.util.Date tody = new java.util.Date();
    // ...
}
import java.util.Date;

class ImportTest {
	Date today = new Date();
}

12. import문의 선언

  • import문을선언하는 방법은 다음과 같다
import 패키지명.클래스명;
or
import 패키지명.*;
  • import문은 패키지문과 클래스 선언의 사이에 선언
package com.codechobo.book;

import java.text.SimpleDateFormat;
import java.util.*;

public class PackageTest{
	public static void main(String[] args) {
    	Date today = new Date();
        SimpleDateFormat date = new SimpleDateFormat("yyyy/MM/dd");
    }
}
  • import문은 컴파일 시에 처리되므로 프로그램의 성능에 영향없음
  • 다음의 두 코드는 서로 의미가 다르다
  • 이름이 같은 클래스가 속한 두 패키지를 import할 때는 클래스 앞에 패키지명을 붙여줘야 한다

13. static import문

  • static멤버를 사용할 때 클래스 이름을 생략할 수 있게 해준다
import static java.lang.Interger.*; // Interger클래스의 모든 static메서드
import static java.lang.Math.random; // Math.random()만, 괄호 안붙임
import static java.lang.System,out; // System.out을 out만으로 참조가능

14. 제어자(modifier)

  • 클래스와 클래스의 멤버(멤버 변수, 메서드)에 부가적인 의미 부여
  • 접근 제어자 : public, protected, (default), private
  • 그 외 : static, final, abstract, native, transient, synchronized, volatile, strictfp
  • 하나의 대상에 여러 제어자를 같이 사용가능(접근 제어자는 하나만)
  • 접근제어자는 가능하다면 맨 앞에 선언
public class ModifierTest {
	public static final int WIDTH = 200;
    
    public static void main(String[] args) {
    	System.out.println("WIDTH="+WIDTH)
    }
}

15. static - 클래스의, 공통적인

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

17. abstract - 추상의, 미완성의

18. 접근 제어자(access modifier)

  • private : 같은 클래스 내에서만 접근이 가능
  • (default) : 같은 패키지 내에서만 접근이 가능
  • (protected) : 같은 패키지 내에서, 그리고 다른 패키지의 자손 클래스에서 접근이 가능
  • public : 접근 제한이 전혀 없다

19. 캡슐화와 접근 제어자

  • 접근 제어자를 사용하는 이유
    • 외부로부터 데이터를 보호하기 위해서
// 멤버 변수가 전부 public이여서 외부에서 쉽게 접근이 가능해진다
public class Time{
    public int hour; // 0 ~ 23
    public int minute; // 0 ~ 59
    public int second; // 0 ~ 59
}
Time t = new Time
t.hour = 25; // 멤버변수에 직접 접근하여 잘 못된 값을 입력할 수 있다!
public class Time{
	// 접근 제어자를 private으로 하여 외부에서 직접 접근하지 못하도록 한다
	private int hour; // 0 ~ 23
    	private int minute; // 0 ~ 59
    	private int second; // 0 ~ 59
        
        // 클래스는 public으로 간접접근 허용
        public int getHour() { return hour; }
        // 값을 보호하고 잘못된 값 입력을 방지할 수 있다
        public void setHour(int hour) {
        	if(hour < 0 || hour > 23) return;
            this.hour = hour;
        }
}

20. 다형성(polymorphism)

  • 여러 가지 형태를 가질 수 있는 능력
  • 조상 타입 참조 변수로 자손 타입 객체를 다루는 것
  • 객체와 참조변수의 타입이 일치할 때와 일치하지 않을 때의 차이
  • 자손 타입의 참조변수로 조상 타입의 객체를 가리킬 수 없다
  • Q. 참조변수의 타입은 인스턴스의 타입과 반드시 일치해야 하나요?
  • A. 아닙니다, 일치하는 것이 보통이지만 일치 하지 않을 수도 있습니다!
SmartTv s = new SmartTv(); // 참조변수와 인스턴스의 타입 일치
Tv t = new SmartTv(); // 조상 타입 참조변수로 자손 타입 인스턴스 참조
  • Q. 참조변수가 조상타입일 때와 자손타입일 때의 차이
  • A. 참조변수로 사용할 수 있는 멤버의 개수가 달라진다!
  • Q. 자손 타입의 참조변수로 조상 타입의 객체를 가리킬 수 있나요?
  • A. 아니요 허용되지 않습니다!

21. 참조변수의 형변환

  • 사용할 수 있는 멤버의 갯수를 조절하는 것
  • 조상 자손 관계의 참조변수는 서로 형변환 가능

  • 예제

22. instanceof 연산자

  • 참조변수의 형변환 가능여부 확인에 사용, 가능하면 true 반환
  • 형변환 전에 반드시 instanceof로 확인해야 함
void doWirk(Car c) {
	if (c instance of FireEngine) { // 1. 형변환이 가능한지 확인
     		FireEngine fe = (FireEngine)c; // 2. 형변환
	        fe.water();
            	...
         }
}
// 형변환을 하는 이유는 인스턴스의 원래 기능을 모두 사용하려고
// Car타입의 리모콘인 c로는 water()를 호출할 수 없으니까
// 리모콘을 FireEngine타입으로 바꿔서 water()를 호출
FireEngine fe = new FireEngine();
// 객체의 최상위 조상은 Object이다
System.out.println(fe instanceof Object); // true
System.out.println(fe instanceof Car); // true
System.out.println(fe instanceof FireEngine); // true
  • Q. 참조변수의 형변환은 왜 하나요?
  • A. 참조변수(리모콘)을 변경함으로써 사용할 수 있는 멤버의 갯수를 조절하기 위해서
  • Q. instanceof연산자는 언제 사용하나요?
  • A. 참조변수를 형변환하기 전에 형변환 가능여부를 확인할 때

23. 매개변수의 다형성

  • 참조형 매개변수는 메서드 호출시, 자신과 같은 타입 또는 자손타입의 인스턴스를 넘겨줄 수 있다
  • 불필요한 오버로딩
  • 매개변수의 다형성의 장점

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

  • 조상타입의 배열에 자손들의 객체를 담을 수 있다
  • 조상의 참조변수에 자손 타입의 인스턴스를 할당
Prouct p1 = new Tv();
Prouct p1 = new Computer();
Prouct p1 = new Audio();
  • 조상의 참조배열에 자손 타입의 인스턴스를 할당
Product[] p = new Product[3];
p[0] = new Tv();
p[1] = new Computer();
p[2] = new Audio();

25. 추상 클래스(abstract class)

  • 미완성 설계도, 미완성 메서드를 갖고 있는 클래스
// 추상메서드는 추상클래스를 갖고 있다
abstract class Player { // 추상클래스(미완성 클래스)
	abstract void player(int pos); // 추상메서드(몸통{}이 없는 미완성 메서드)
    abstract void stop();
}
  • 다른 클래스 작성에 도움을 주기 위한것, 인스턴스 생성 불가
Player p = new Player(); // Error, 추상 클래스의 인스턴스 생성불가
  • 상속을 통해 추상 메서드를 완성해야 인스턴스 생성가능
class AudioPlayer extends Player {
	void play(int pos) { /* 내용 생략 */ } // 추상메서드를 구현
    	void stop() { /* 내용 생략 */ } // 추상메서드를 구현
}
AudioPlayer ap = new AudioPlayer(); // 추상클래스를 재정의하여 완성
Player p = new AudioPlayer(); // 추상클래스는 자손인스턴스의 참조변수는 가능하다

26. 추상 메서드(abstract method)

  • 미완성 메서드, 구현부(몸통, {})가 없는 메서드
/*주석을 통해 어떤 기능을 수행할 목적으로 작성하였는지 설명*/
abstract 리턴타입 메서드이름();
  • 꼭 필요하지만 자손마다 다르게 구현될 것으로 예상되는 경우
abstract class Player { // 추상 클래스
	abstract void play(int pos); // 추상메서드
    	abstract void stop(); // 추상메서드
}

class AudioPlayer extends Player {
	void play(int pos) { /*내용생략*/ } // 추상메서드 구현
    	void stop() { /*내용생략*/ } // 추상메서드 구현
}

// 아직 구현되지 않은 추상메서드가 있기 때문에 abstract를 붙여주어야 한다
abstract class AbstractPlayer extends Player {
	void play(int pos) { /*내용생략*/ } // 추상메서드를 구현
}
  • 추상 메서드 호출 가능(호출할 때는 선언부만 필요)
abstract class Player { // 추상 클래스
	boolean pause; // 일시정지 상태를 저장하기 위한 변수
    int currentPos; // 현재 play되고 있는 위치를 저장하기 위한 변수
    
    Player() { // 생성자
    	pause = false;
        currentPos = 0;
    }
    
    /** 지정된 위치(pos)에서 재생을 시작하는 기능이 수행하도록 작성되어야 한다 */
    abstract  void play(int pos); // 추상메서드
    /** 재생을 즉시 멈추는 기능을 수행하도록 작성되어야 한다. */
    abstract  void stop(); // 추상메서드
    
    // 메서드는 선언부만 알면 호출가능하므로 추상메서드도 호출 가능
    // 상속을 통해서 자손이 완성될 수 있기 때문에 선언은 가능
    // 하지만 실제로 사용할려면 객체 생성 후여야 하기 때문에 상속되어 완성되어야 가능
    void play() { // 인스턴스 메서드
    	play(currentPos); // 추상메서드를 사용할 수 있다
    }
}

27. 추상클래스의 작성

  • 여러 클래스에 공톡적으로 사용될 수 있는 추상클래스를 바로 작성하거나 기존클래스의 공통 부분을 뽑아서 추상클래스를 만든다
  • 참조변수나 참조배열로 부모를 선언할 경우 부모가 갖고 있는 메서드만 호출 가능
  • 추상클래스의 장점
    • 설계도를 쉽게 작성
    • 중복 제거
    • 관리가 용이(변경이 쉽다)
  • 추상화 <-> 구체화
  • 추상화된 코드는 구체화된 코드보다 유연하다, 변경에 유리
GreforianCalendar cal = new GregorianCalendar(); // 구체적
Calendar cal = Calendar.getInstance(); // 추상적

28. 인터페이스(interface)

  • 추상 메서드의 집합 (프로그래밍 관점)
    • 부가적 : 상수, static 메서드, default 메서드 JDK 1.8 추가
  • 구현된 것이 전혀 없는 설계도, 껍데기(모든 멤버가 public)
  • 추상클래스 <-> 인터페이스
    • 추상클래스
      • 일반 클래스인데 추상메서드를 갖고 있는 것, 생성자, iv
      • 일부분만 미완성
    • 인터페이스
      • 추상메서드만 갖고 있는 것
      • 전체 미완성
// interface는 모든 멤버가 public이다
interface 인터페이스이름 {
	public static final 타입 상수이름 = 값;
    	public abstract 메서드이름(매개변수목록);
}
interface PlayingCard {
	public static final int SPADE = 4;
    	// public static final은 생략 가능하다
        // 왜냐하면 인터페이스는 항상 public static final이기 때문이다 예외없이
    	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 String getCardNumber();
        // public abstract는 생략가능하다
        // 왜냐하면 인터페이스는 항상 public이고 abstract이기 때문이다 예외없이
        String getCardKind(); // public abstract String getCardKind();
}

29. 인터페이스의 상속

  • 인터페이스의 조상은 인터페이스만 가능(Object가 최고 조상 아님)
  • 다중 상속이 가능(추상메서드는 충돌해도 문제 없음)
    • 일반 클래스는 선언부가 다르면 둘다 상속받으면 그만인데 선언부가 같고 내용 {}이 다르면 어느쪽을 상속받을지 결정할 수 없다
interface Fightable extends Movable, Attackable {}

interface Movable {
	/** 지정된 위치(x, y)로 이동하는 기능의 메서드*/
    	void move(int x, int y);
}

interface Attactable {
	/** 지정된 대상(u)을 공격하는 기능의 메서드*/
    	void attack(Unit u);
}

30. 인터페이스의 구현

  • 인터페이스에 정의된 추상 메서드를 완성하는 것
class 클래스이름 implements 인터페이스이름 {
	// 인터페이스에 정의된 추상메서드를 모두 구현해야 한다
}
interface Fightable {
	// public abstract 생략
	void move(int x, int y);
    	void attack(Unit u);
}

class Fighter implements Fightable {
	public void move(int x, int y) { /* 내용 생략 */ } // 추상메서드 구현
    	public void attack(Unit u) { /* 내용 생략 */ } // 추상메서드 구현
}
  • 일부만 구현하는 경우, 클래스 앞에 abstract를 붙여야 한다
abstract class Fighter implements Fightable {
	public void move(int x, int y) { /* 내용 생략 */ } // 추상메서드 구현
}
  • Q. 인터페이스란?
  • A. 추상 메서드의 집합(상수, static 메서드, default 메서드 JDK 1.8 추가)
  • Q. 인터페이스의 구현
  • A. 인터페이스의 추상메서드 몸통{} 만들기(미완성 설계도 완성하기)
  • Q. 추상클래스와 인터페이스의 공통점은?
  • A. 추상 메서드를 가지고 있다(미완성 설계도)
  • Q. 추상 클래스와 인터페이스의 차이점은?
  • A. 인터페이스는 iv를 가질 수 없다

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

  • 인터페이스도 구현 클래스의 부모? YES!
  • 인터페이스 타입 매개변수는 인터페이스 구현한 클래스의 객체만 가능
  • 인터페이스를 메서드의 리턴타입으로 지정할 수 있다

32. 인터페이스의 장점

  • 두 대상(객체)간의 '연결, 대화, 소통'을 돕는 '중간 역할'을 한다
    • inter(사이) + face(얼굴) => interface(얼굴과 얼굴 사이)
  • 선언(설계)와 구현을 분리시킬 수 있게 한다
  • 인터페이스 덕분에 B가 변경되어도 A는 안바꿀 수 있게 된다.(느슨한 결합)
  • 간접적인 관계 형성 가능
  • 개발 시간을 단축할 수 있다
  • 변경에 유리한 유연한 설계가 가능하다
  • 표준화가 가능하다(JDBC)
  • 서로 관계없는 클래스들을 관계를 맺어줄 수 있다

33. 디폴트 메서드와 static 메서드

  • 인터페이스에 디폴트 메서드, static메서드 추가 가능(JDK 1.8부터)
  • 인터페이스에 새로운 메서드(추상메서드)를 추가하기 어려움
  • 디폴트 메서드는 인스턴스 메서드(인터페이스 원칙 위반)
  • 디폴트 메서드가 기존의 메서드와 충돌할 때의 해결책

34. 내부 클래스(inner class)

  • 클래스 안의 클래스
  • 내부 클래스의 장점
    • 내부 클래스에서 외부 클래스의 멤버들을 쉽게 접근할 수 있다
    • 코드의 복잡성을 줄일 수 있다(캡슐화)

35. 내부 클래스의 종류와 특징

  • 내부 클래스의 종류와 유효범위(scope)는 변수와 동일

36. 내부 클래스의 제어자와 접근성

  • 내부 클래스의 제어자는 변수에 사용 가능한 제어자와 동일
  • 예제1
    • 스태틱멤버는 스태틱 클래스에서만 정의할 수 있다
    • final static은 상수이므로 ic, lc에서도 정의할 수 있다


  • 예제2
    • 스태틱 클래스에서는 인스턴스 클래스를 정의할 수 없다
    • 인스턴스 클래스에서는 스태틱 클래스 정의 가능
    • 지역 내부 클래스는 외부에서 접근할 수 없다
  • 예제3
    • 스태틱 클래스는 외부클래스의 인스턴스 멤버를 접근할 수 없다
    • 지역 클래스는 final이 붙은 상수만 접근 가능
  • 예제4
    • 내부 클래스는 외부 클래스를 먼저 객체 생성하고 나서 객체 생성 할 수 있다
    • 스태틱 클래스는 외부 클래스를 생성하지 않아도 생성할 수 있다
  • 예제5
    • 이름이 동일한 멤버는 this. , 외부클래스.this로 접근이 가능하다

37. 익명 클래스(Anonymous class)

  • 이름이 없는 일회용 클래스, 정의와 생성을 동시에
  • 예제1
  • 예제2(AWT)

profile
아직까지는 코린이!

0개의 댓글