추상클래스 개념
추상(abstract) : " 여러 가지 사물이나 개념에서 공통되는 특성이나 속성 따위를 추출하여 파악하는 작용."
추상 클래스 : 클래스 선언 시 앞에 abstract,
- 추상클래스 = 필드 + 일반 메서드 + 추상 메서드
- 메서드 선언 시 본문을 구현하지 않으면 abstract로 선언(abstract 선언 메서드는 본문X)
- 추상 메서드를 포함하는 클래스는 선언부에 abstract 명시(추상메서드를 하나라도 포함하면 추상클래스)
- 실체 클래스(객체를 직접 생성할 수 있는 클래스)들의 공통되는 필드와 메서드 정의한 클래스(부모 클래스 역할 : 단독 객체 X)
- 기능이 무엇인지만을 정의하고 어떻게 구현되는지는 정의 하지 않는다.(메소드가 미완성이므로 추상클래스로는 객체(인스턴스)를 생성할 수 없다.)
- 그래서 하나의 추상클래스에 정의된 기능을 여러 개의 하위클래스에서 서로 다른 형태로 구현하여 사용할 수 있다.(반드시 재정의 해 주어야 한다.)
- 설계와 구현을 분리한다.(슈퍼 클래스에서는 개념 정의, 각 서브 클래스에서 구체적 행위 구현)
- 계층적 상속 관계를 갖는 클래스 구조를 만들 때
[접근제어자 abstract class 클래스이름{
접근제어자 abstract 반환형 추상메서드명();
}]
용도
- 실체클래스의 공통된 필드와 메소드의 이름 동일할 목적
- 실체 클래스를 작성할 때 시간 절약(추가적인 필드와 메소드만 선언)
- 실체 클래스 설계 규격을 만들고자 할 때(필드와 메소드를 추상 클래스에 미리 정의, 추상클래스를 무조건 상속 받아 작성)
추상메소드(몸체가 없어 항상 세미콜론으로 종료)와 오버라이딩
- 메소드 이름은 동일하지만, 실행내용이 실체 클래스마다 다른 메소드.
- 선언되어 있으나 구현되어 있지 않은 메소드 서브클래스에서 오버라이딩하여 구현해야함.
- 추상클래스에는 메소드의 선언부만 작성(추상메서드)
- 실체 클래스에서 메서드의 실행 내용 작성(오버라이딩)
추상클래스 종류
- 추상 메서드를 하나라도 가진 클래스 클래스 앞에 abstract
- 추상 메서드가 하나도 없지만 abstract로 선언된 클래스
다형성 : 동일한 부모 클래스에서 상속된 서브 클래스의 객체들을 하나의 타입으로 취급할 수 있게 해준다. 따라서 서로 다른 타입들을 받아서 하나의 코드로 처리할 수 있다. 상속관계에서는 부모 타입의 변수에 모든 자식 객체를 대입해 이용할 수 있다. 하나의 타입에 여러 객체를 대입할 수 있다는 의미.(다양한 실행 결과를 얻는 것) 다형성은 코드 구조가 향상되고 가독성을 증가시키며, 가능한 프로그램을 작성하는 객체지향의 중요한 개념 중의 하나. 메소드의 매개변수로 사용(유지보수 용이)
구현기술 : 상속 또는 인터페이스의 자동 타입 변환, 오버라이딩
#업캐스팅
[슈퍼클래스참조변수 = new 서브클래스생성자(); ]
- 상위클래스로 형변환
- 컴파일러에 의해 자동으로 형변환된다.
- 업캐스팅이 된 이후 상위 클래스형의 참조변수는 상위 클래스로부터 상속받은 멤버만 호출할 수 있다.
- 업캐스팅이 되더라도 하위 클래스에서 재정의된 메서드는 참조할 수 있고. 상위 클래스로부터 상속받은 메서드는 은닉된다.
#다운캐스팅(강제타입변환) : 부모타입을 자식 타입으로 변환하는 것, 서브 클래스 참조 변수로 슈퍼 클래스 객체를 참조하는 것으로 본다면 컴파일 오류가 발생. //자식 타입을 부모 타입으로 자동 변환 후, 다시 자식 타입으로 변환할 때
[자식클래스참조변수 = (자식클래스)부모클래스타입; ]
- 하위클래스 = 상위클래스
- 하위 클래스로 형변환
- 컴파일러에 의해 자동으로 형변환 X
- 참조 가능한 영역이 확대되는 의리를 갖는다.
package practice;
class A{
int a = 10;
void b() {
System.out.println("A");
}
}
class AA extends A{
int a = 20;
@Override
void b() {
System.out.println("AA");
}
void c () {
System.out.println("C");
}
}
class BB extends A{
int a = 30;
@Override
void b() {
System.out.println("BB");
}
void d() {
System.out.println("D");
}
}
public class ClassTest {
public static void main(String args[]) {
A z = new AA(); //AA객체를 생성해서 A 타입의 레퍼런스에 할당한다. 업캐스팅(자식클래스를 부모클래스로)
System.out.println(z.a); //슈퍼 클래스의 필드 접근
z.b();//슈퍼 클래스의 메소드 접근. 단 서브클래스에서 메서드 오버라이딩되면 오버라이딩 메서드가 우선접근.
//z.c(); A에는 c 메소드가 없음.
((AA)z).c();//서브클래스의 메서드 접근
System.out.println(((AA)z).a);//서브클래스의 필드 접근
AA a1 = (AA)z;
System.out.println(a1.a);//서브클래스의 필드 접근
a1.c();
z= new BB(); //업캐스팅. 슈퍼클래스참조변수= new 서브클래스의 생성자()
System.out.println(z.a);
z.b();
((BB)z).d();
System.out.println(((BB)z).a);
BB b1 = (BB)z; //다운 캐스팅
b1.d();
System.out.println(b1.a);
}
}
package practice;
class Shape{
public void draw() {
System.out.println("Shape");
}
}
class Line extends Shape{
@Override
public void draw() {
System.out.println("Line");
}
}
public class MethodOverridingExample {
public static void paint(Shape p) {
p.draw();
}
public static void main(String args[]) {
Line line = new Line();
paint(line);//Line.draw
paint(new Shape());
paint(new Line());
}
}
객체의 타입을 알아내는 방법 : 객체가 특정 클래스로부터 생성된(상속된) 객체인지를 판별하기 위해 사용할 수 있는 연산자가 instanceof. 반환값. true, false
[참조변수 instanceof 클래스명]
#인터페이스: 여러 프로그램네서 사용할 멤버(필드, 메서드)를 일관되게 하기 위한 기술 명세, 표준규격을 나타냄 인터페이스 변경시키려면 이 인터페이스를 구현하였던 모든 클래스가 동작하지 않게 된다. 이런 경우를 대비하여서 인터페이스도 상속을 받아서 확장시킬 수 있도록 한다.
[public interface 인터페이스명{
( public static final) 자료형 변수명 = 값 ; //인터페이스에 선언된 필드는 모두 상수이기 때문에 생략가능
(public abstract) 반환형 메서드명1(); //추상메서드만 정의할 수 있기에 생략가능
public abstract 반환형 메서드명2();
}
접근제어자 interface 인터페이스명{
(접근제어자 제어자 ) 자료형 변수명 = 값 ; //인터페이스에 선언된 필드는 모두 상수이기 때문에 생략가능
(접근제어자 제어자) 반환형 메서드명1(); //추상메서드만 정의할 수 있기에 생략가능
public abstract 반환형 메서드명2();
}
]
- 추상메소드와 상수(객체를 생성할 수 없는 인터페이스에서 필드를 선언한다는 것은 객체 생성과 관계없이 사용할 수 있는 static으로 선언 데이터 저장하지 않음)로 이루어짐. 상수명은 대문자로 작성.선언과 동시에 초기값 지정
- 메서드를 미리 선언(어떻게 입력 받고 어떤 결과값을 반환할 것이라는 사용법)
- 구현은 비워 놓은 추상 메서드로 만든다. 메서드의 헤더를 선언.
- 인터페이스에는 필드(멤버변수) 선언불가
#구현 클래스 선언 : 메서드의 선언부가 정확히 일치 해야한다.
[public class 클래스명 implements 인터페이스명{
반환형 추상메서드1(){//재정의
}
반환형 추상메서드2(){//재정의
}
}]
#디폴트 메소드 선언 : 인터페이스를 구현하는 클래스에 자동 상속되는 메소드, default 키워드를 반드시 붙여야한다. 기본적으로 public 접근제한
[public default 리턴타입 메서드명(매개변수,...){...}]
#정적 메서드 선언 : 인스턴스 생성과 상관없이 바로 사용할 수 있다. 인타페이스를 구현하고 있는 모든 객체에서 자주 사용하는 유용한 기능을 제공하는데 있다.
[public static 리턴타입 메서드명(매개변수, ...){...}]
#private 메소드 선언 : 외부접근 제한, default 메서드에서만 사용할 목적으로 본문을 구현
[private 리턴타입 메서드명(매개변수,...){...}]
일반 클래스 | 추상 클래스 | 인터페이스 | |
매서드의 재정의 | 선택적 | 강제적 | 강제적 |
상속, 구현시 예약어 | extends | extends | implements |
필드 선언 가능 여부 | 가능 | 가능 | 불가능 |
상수 선언 가능 여부 | 가능 | 가능 | 가능 |
생성자 선언 여부 | 가능 | 가능 | 불가능 |
추상 메서드 포함 여부 | 불포함 | 포함 | 포함 |
객체 생성 가능 여부 | 가능 | 불가능 | 불가능 |
다중 상속 가능 여부 | 불가능 | 불가능 | 가능 |
내부클래스: 클래스 멤버로 선언된 클래스<클래스 생성 시 바이트 코드 따로 생성>
멤버 클래스 | 인스턴스 멤버 클래스 | class outer{ class inner{...} } |
outer객체를 생성해야만 사용할 수 있는 inner 내부 클래스 |
멤버 클래스 | 정적 멤버 클래스 | class outer{ static class inner{...} } |
outer 클래스로 바로 접근할 수 있는 inner 내부 클래스 |
멤버 클래스 | 바이트코드 파일의 이름 | 외부 클래스명$내부클래스.class |
로컬 클래스 | class outer{ void method(){ class inner{...} } } |
method()가 실행할 때만 사용할 수 있는 inner 중첩 클래스 |
로컬 클래스 | 바이트코드 파일의 이름 | 외부 클래스명$1내부클래스.class |
- 클래스가 여러 클래스와 관계를 맺는 경우에는 독립적으로 선언하는 것이 좋으나, 특정(하나의) 클래스와 관계를 맺을 경우에는 관계 클래스를 클래스 내부에 선언하는 것이 좋다.
- 내부 클래스를 사용하면 두 클래스의 멤버들을 서로 쉽게 접근할 수 있다.
- 외부에는 불필요한 관계 클래스를 감춤으로써 코드의 복잡성을 줄일 수 있다.(하나의 장소에서만 사용되는 클래스들을 한 곳에 모을 수 있다.)
#인스턴스 멤버 클래스(non-static 내부 클래스) : 내부 클래스의 멤버들은 static이 될수 없다.
[접근제어자 class 외부클래스명{
[public | private: 외부 클래스 내부에서 접근가능] class 내부클래스명{
}
}]
#정적 멤버 클래스(static 내부 클래스): 내부 클래스는 외부 클래스의 일반멤버 변수는 참조할 수 없다. 정적멤버 클래스는 주로 default or public 접근 제한을 가짐
[접근제어자 class 외부클래스명{
[public | private: 외부 클래스 내부에서 접근가능] static class 내부클래스명{
}
}]
지역 내부 클래스(로컬클래스): 외부 클래스의 메서드에서 사용하기 위해 메서드 내에 선언하는 내부 클래스
- 생성자 또는 메소드 내부에 선언된 클래스를 늬미하며 생성자와 메소드가 실행될 동안에만 객체를 생성할 수 있다.
- 보통 메소드 내부에 선언하여 사용하며, 메소드의 실행이 끝나는 순간 메소드의 모든 멤버가 사라진다.
- 로컬 클래스 내부에는 필드, 생성자, 메서드 선언이 올 수 있으며 정적 필드와 정적 메서드는 JDK17부터 선언이 가능하다.
내부 인터페이스: 클래스 멤버로 선언된 인터페이스
#무명(익명) 클래스 : 클래스 몸체는 정의되지만 이름이 없는 클래스, 정의부와 생성부가 하나로 묶여 있는 클래스로 선언과 동시에 객체가 생성된다. 한번만 사용하며 재참조가 불가능, 하나의 객체만을 생성하는 일회용 클래스
[new 상위클래스(일반클래스,추상클래스, 인터페이스 모두가능)명(생성자 매개변수){ }]
#명시적인 구현 클래스 작성 생략하고 바로 구현 객체를 얻는 방법(이름 없는 구현 클래스 선언과 동시에 객체 샹성)
[인터페이스 참조형 = new 인텨페이스(){
인터페이스에 선언된 추상 메서드의 실체 메서드 선언
}]