2. 객체지향언어ⅱ
A. 상속
- 형식: 자손클래스 extends 조상클래스{ ... } | class children extends class parents { ... }
- 기존 클래스로 새로운 클래스를 작성하는 것(코드의 재사용)
- 두 클래스를 부모와 자식으로 관계를 맺어준다.
- 자손은 조상의 모든 멤버를 상속 받는다(단, 생성자와 초기화 블럭은 제외)
- 자손의 멤버수는 조상의 멤버수보다 작을 수 없다(같거나 많아야 한다)
a. 단일 상속
Java는 단일상속(하나의 조상=부모만 상속한다)
비중이 높은 클래스만 상속하고 나머지는 포함관계로 한다.
B. 포함관계
- 클래스의 멤버로 다른 클래스 타입의 참조변수를 선언하는 것.
- 작은단위의 클래스를 만들고 이들을 조합하여 클래스를 만든다.
- 클래스간 관계 결정하기.
① 상속관계 : ~는 ~이다(~is a ~)
class Parents{
int x;
int y;
}
class Child extends Parents{
int x; //부모클래스를 상속 받았기 때문에 부모 멤버를 자동 추가(int x, y)
int y;
int z;
}
② 포함관계 : ~는 ~를 ~가지고 있다(~has a~)
아래의 Point c = new Point(); 와 같은 경우는 대부분 포함관계에 해당한다.
class Circle{
Point c = new Point();
int r;
}
class Point{
int x;
int y;
}
C. Object 클래스
모든 클래스의 조상, 부모가 없는 클래스는 자동으로 Object클래스를 상속 받는다(extends를 선언하지 않아도 자동 상속)
모든 클래스는 Object클래스에 정의된 11개의 메소드를 상속받는다(주요 메소드 목록 추가 예정)
D. Super
참조변수 super
객체 자신을 가르키는 참조변수. 인스턴스 메소드(생성자) 내에서만 존재하며 조상(부모)의 멤버를 자신의 멤버와 구분할 때 사용한다.
조상(부모)의 생성자 super()
- 조상의 생성자를 호출할 때 사용한다(생성자와 초기화 블럭은 상속되지 않기 때문에)
- 조상의 멤버는 조상의 생성자를 호출하여 초기화 해야 한다.
- 자손의 생성자는 자신이 선언한 변수만 초기화 할 수 있다.
- 생성자의 첫 줄에는 반드시 생성자를 호출해야 한다. 하지 않을 경우 컴파일 생성자의 첫 줄에 super()를 삽입한다.
E. 제어자
- 클래스와 클래스멤버(멤버 변수, 메소드)에 부가적인 의미를 부여한다. 그 대표적인 제어자들은 다음과 같다.
①접근제어자 : public, (default), protected, private
②그 외 : static, final abstract
- 하나의 대상에 여러 제어자를 같이 사용 가능하다(단 접근제어자는 하나만 사용 가능) 이때 접근제어자의 위치는 선언부의 가장 왼쪽에 배치한다 → ex) public static void main(){}
1. static : 클래스의, 공통적인
- static은 멤버변수, 메소드, 초기화 블럭에서 사용한다(객체생성 없이도 사용)
- static 메소드 내에서는 인스턴스 멤버들을 직접 사용할 수 없다.
2. final : 마지막의, 변경될 수 없는(상수)
- final은 어디든 붙일 수 있다. final이 어디 붙느냐에 따라 기능이 달라진다.
- 클래스 : 변경과 확장이 불가능한 클래스. 상속계층도의 제일 마지막으로 자손(자식)없는 클래스.
- 변수(iv, lv) : 상수
- 메소드 : 오버라이딩이 불가능한 메소드(변경될 수 없다)
3. abstract : 추상의, 미완성의
- 클래스 : 클래스 안에 추상메소드가 있다(미완성 설계도)
- 메소드 : 선언부만 있고 {구현부}가 없는 메소드로 상속 받는 메소드에서 정의해줘야 한다.
- abstract 클래스는 객체를 생성할 수 없고, 추상메소드를 갖고 있으니 상속 받아서 완성해야 한다.
4. 접근제어자
제어자(허용 범위) | 동일 클래스 | 동일 패키지 | 자손 클래스 | 전체 |
public | O | O | O | O |
protected | O | O | O | |
(default) | O | O | ||
private | O |
*접근범위 : public(제한없음) > protected(같은 패키지+다른 패키지의 자손) > default(같은 패키지) > private(같은 클래스)
5. 캡슐화와 접근제어자
캡슐화에서 접근제어자를 사용하는 이유
① 외부로부터 내부 데이터의 직접적인 접근을 방지하여 데이터를 보호하기 위해(외부 메소드를 통해 간접접근 허용)
② 외부에는 불필요한, 내부적으로만 사용되는 부분을 감추기 위해
F. 다형성(★)
- 다형성은 조상(부모)타입의 참조변수로 자식 객체를 다루는 것으로 배열로도 가능하며, 하나의 배열로 여러 종류의 객체를 저장할 수 있다(단, 자손타입의 참조변수로 조상타입의 인스턴스를 참조할 수는 없다)
- 참조변수가 사용할 수 있는 멤버의 개수는 인스턴스의 멤버개수보다 같거나 작아야 한다. 즉 참조하는 조상의 멤버수만큼만 사용 가능하다는 의미다.
- 참조변수 타입과 인스턴스 타입은 보통 일치하지만 일치하지 않을 수도 있다.
1. 참조변수의 형변환
- 참조변수의 형변환이란 사용할 수 있는 멤버의 개수를 조절하는 것이다.
- 형변환을 하기 위해서는 가능 여부를 instance of로 형변환의 가능/불가능 여부를 확인해야 한다.
- 형변환이 가능한지 확인하기 위해서는 intanceof를 사용하는데 주로 if문과 같은 조건문에서 사용한다 → ex) if(참조변수 instanceof 타입(클래스명))
자손타입 → 조상타입(Up-casting) : 형변환 생략가능
조상타입 → 자손타입(Down-casting) : 형변환 생략 불가능
2. 매개변수의 다형성
- 참조형 매개변수는 메소드 호출 시 자신과 같은 타입 또는 자손타입의 인스턴스를 넘겨줄 수 있다.
3. 여러종류의 객체를 배열로 다루기
- 조상타입의 배열에 자손타입 객체를 담을 수 있다.
- 다루고 싶은 객체들의 상속관계를 따져 가장 가까운 공통조상 클래스타입의 참조변수 배열을 생성하여 객체들을 저장한다.
//1. 평소에 하던 방식
Product p1 = new Tv();
Product p1 = new Computer();
Product p1 = new Audio();
//2. 배열형태의 인스턴스화
Product p[] = new Product[3];
p[0] = new Tv();
p[1] = new Computer();
p[2] = new Audio();
G. 추상클래스(Abstract Class)
- 미완성 설계도로 미완성의 메소드를 지닌 클래스.
- 다른 클래스 작성에 도움을 주기 위한 클래스로 객체 생성이 불가능하다.
- 상속(extends)을 통해 추상메소드를 완성해야 인스턴스를 생성할 수 있다.
//추상 클래스의 작성은 abstract를 붙이면 된다. 이는 클래스, 메소드 둘 다 마찬가지.
public abstract class Animal{
public String kind;
public void attack(){
system.out.println("공격한다");
}
public abstract void sound(); //구체적인 구현부는 상속 받는 클래스에서 해주어야 함(오버라이딩)
}
public class Dog extend Animal{
public Dog(){
this.kind="포유류";
}
//상속을 통해 Animal 추상 클래스에 선언돼있던 sound()메소드의 구현부를 정의했다.
public void sound("멍멍!");
}
추상 클래스의 장점
- 설계도를 쉽게 작성할 수 있다
- 코드 중복 제거가 가능하다
- 코드관리가 용이하다 why? 구체화 하지 않았기 때문에 변경에 유리하다
추상메소드
abstract 리턴타입 메소드명();
- 미완성 메소드는 구현부가 없다. 즉, 선언부만 있고 구현부는 없는데 이 구현부는 상속을 통해 추상메소드를 완성해야 인스턴스를 생성할 수 있다.
- 공통적으로 필요하지만 자손마다 다르게 구현될 것으로 예상되는 경우 사용한다(구현부가 자손마다 다를 경우)
- 추상메소드는 하나라도 구현되지 않았다면 여전히 미완성 상태로 클래스에 abstract를 무조건 명시해야 한다.
- 필수적인 기능이기 때문에 강제성이 부여된다.
- 여려 클래스에 공통적으로 사용할 수 있는 추상클래스를 바로 작성하거나 기존 클래스의 공통된 부분을 추출하여 추상클래스를 작성한다.
H. 인터페이스
//인터페이스 선언 방법
interface 인터페이스명{
public static final 타입 상수명 = 값; → 상수
public abstract 메소드명(매개변수 목록); → 추상메소드
}
- 인터페이스는 추상메소드의 집합이다.
- 구현된 것이 전혀 없는 설계도와 껍데기(모든 멤버가 public)다.
- 다중상속이 가능하다(추상메소드는 충돌해도 문제가 되지 않는다)
- 인터페이스의 조상은 인터페이스만 가능하다(Interface는 Object가 최고조상이 아니다)
- public, static, final, abstract 모두 생략이 가능하다.
추상클래스와 인터페이스의 차이점
추상클래스는 '추상메소드 외' 일반적 클래스의 구조를 지니고 있지만 인터페이스는 '추상메소드만' 구현돼 있다.
//인터페이스 구현방법
class 클래스명 implements 인터페이스명{
//인터페이스에 정의된 추상메소드를 모두 구현해야 한다.
}
인터페이스를 implements를 통해 상속 받으면 인터페이스에 선언된 메소드 모두 구현해야 하며, 일부만 구현하는 경우 클래스 앞에 abstract를 붙여야 한다. ex) abstract class 클래스명 implements 인터페이스명{ ... }
인터페이스를 이용한 다형성
인터페이스도 구현클래스의 부모다. 그렇기 때문에 자손타입의 객체를 조상타입의 참조변수로 가르키는 게 가능하다.
(★)인터페이스 타입의 매개변수는 인터페이스를 구현한 클래스의 객체만 가능하다.
인터페이스를 메소드의 리턴타입으로 지정할 수 있다(해당 인터페이스를 구현한 녀석을 반환하겠다는 의미)
인터페이스의 장점
- 두 대상(객체)간의 '연결, 대화, 소통'을 돕는 중간역할을 한다. 때문에 변경에 유리한 설계가 가능하다.
- 아래 그림1을 보면 사람의 언어를 컴퓨터에게 전달한다고 해서 이해할 수 없는 것처럼, 그림2에서는 컴퓨터에게 사람의 언어를 윈도우를 통해 전달하여 명령하고 있는 것을 볼 수 있다. 그렇기 때문에 컴퓨터(알멩이)가 바뀐다고 하더라도 윈도우(껍데기)가 바뀌지 않는다면 사용자에게는 의도를 전달하려는 목적에 아무런 영향이 없는 것이다.
- 선언과 구현을 분리할 수 있다.
- 만약 A클래스가 B클래스를 참조하여 사용하고 있었는데 갑자기 C를 사용해야 한다면 어떨까? 아마 A 클래스의 코드를 변경해야 할 것이다. 그렌데 인터페이스를 이용하면 어떻게 될까? 그럴 필요가 없어진다는 것이다. 그림으로 이해하면 쉽게 이해할 수 있다.
- [강한 결합]을 통해 묶여 있는 기존의 방식은 사용할 때마다 코드를 변경해야 하는 수고스러움이 발생할 수 있다. 만약 그림처럼 서로 엮여있는 상태라면 A클래스가 B를 사용하다가 C를 사용할 경우 코드 수정이 불가피하다.
- 인터페이스를 통해 [느슨한 결합]을 하게 되면 B클래스와 직접 연결되는 것이 아니라 A는 I(인터페이스)하고 연결되기 때문에 B와는 직접적으로 관계가 없게 된다. 때문에 A가 갑자기 C를 사용한다고 하더라도 인터페이스는 그대로 두고 C만 바뀌면 되기 때문에 A는 전혀 수정될 것이 없다.
- 개발시간을 단축할 수 있다(그림과 같이 B가 완성될 때까지 기다리지 않아도 되기 때문에)
- 표준화가 가능하다. 만약 Oracle DB를 이용하다가 MySQL DB로 변경하여 사용할 경우에도 Java에 특화된 JDBC라는 인터페이스를 통해 아무런 문제 없이 사용할 수 있다 → ex) JDBC(인터페이스 집합)
'Java > Basic' 카테고리의 다른 글
객체지향의 기본 개념 정리1 (0) | 2021.07.14 |
---|---|
캡슐화 (0) | 2021.07.05 |
인터페이스(interface) (0) | 2021.04.15 |
추상클래스(abstract class) (0) | 2021.04.08 |
다형성(polymorphism) - 매개변수의 다형성 (0) | 2021.04.03 |
댓글