일반적인 클래스는 오직 객체를 생성하기 위한 목적으로 작성된다.
그러나 추상적인 내용이 포함되면 객체화 할 수 없다는 단점이 있는데 이를 보완하기 위해 추상 클래스를 사용한다.
※ 추상클래스는 생성자를 통하여 직접 객체를 생성할 수 없다는 특징이 있다.
package abstractClass;
abstract class Test1 {
Test1() {
System.out.println("Test1 생성자 호출 !!");
}
public void funcion1() {
System.out.println("내용을 포함하는 구체적인 메서드");
}
public abstract void funcion2(); // 추상 메서드는 오직 추상 클래스 안에서만 생성 가능하다
}
class Test2 extends Test1 { // 추상 클래스를 상속 받으면 미구현된 추상클래스를 만들거나 자신이 추상 클래스가 되야한다
@Override // 형식은 그대로 유지하면서, 내용을 새로 작성하여 덮어쓴다
public void funcion2() {
System.out.println("미구현 메서드를 구현한 내용");
}
}
public class Ex01 {
// 추상화 : 실제 객체의 몇몇 특성만 추출하여 프로그램 내에 반영한다
// 필드는 속성을 작성하고, 메서드는 기능을 표현하는데, 메서드의 구체적인 내용이 담기지 않을 수도 있다
// 추상 메서드 : 형식은 정의되어 있으나, 내용은 정의되지 않은 메서드
// 추상 클래스 : 추상 메서드를 포함할 수 있는 클래스 (일반 메서드도 포함 가능하다)
public static void main(String[] args) {
// Test1 ob1 = new Test1(); ※ 추상 클래스의 생성자는 직접 호출할 수 없다(객체화가 불가능하다) 말 그대로 추상적이라 객체화가 불가능하다.
Test2 ob2 = new Test2();
ob2.funcion1(); // 반드시 생성자가 한번은 호출된다 만약 funcion1이 존재하지 않더라도 funcion2에서 실행된다
ob2.funcion2();
}
}
package abstractClass;
abstract class Button {
protected abstract void onclick();
public void click() {
this.onclick();
}
}
class MessageButton extends Button {
@Override
protected void onclick() {
System.out.println("Hello, world !!");
}
}
class NotepadButton extends Button {
@Override
protected void onclick() {
try {
Runtime rt = Runtime.getRuntime(); // 운영체제 런타임을 객체로 받아온다
String cmd = "notepad"; // 실행할 명령어
Process pro = rt.exec(cmd); // 명령어를 실행하여 생성된 프로세스를 저장
Thread.sleep(2000); // 2초 기다렸다가
pro.destroy(); // 프로세스를 강제로 종료한다
} catch (Exception e) {}
}
}
public class Ex02 {
public static void main(String[] args) {
// Button btn1 = new Button();
MessageButton btn2 = new MessageButton();
btn2.click();
NotepadButton btn3 = new NotepadButton(); // 서브 클래스의 객체를 슈퍼 클래스로 참조한다
btn3.click();
// 추상 클래스가 직접 객체를 생성할 수는 없지만, 자료형으로써 참조변수를 사용하는 것은 가능하다
Button btn5 = new Button() { // 이름 없이 만들어 낸 클래스 익명 내부 클래스
// 바로 실행되는 것 처럼 보이나 클래스를 생성하는 형식이다. Ex02$1.class 형태의 이름으로 생성된다
// Ex02 클래스 내부의 $(이름이 없는) 1번째 클래스
private String cmd = "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe";
private String url = " https://www.naver.com/";
@Override // 바로 실행하는 것 처럼 보이지만
protected void onclick() {
try {
Runtime rt = Runtime.getRuntime();
cmd += url;
System.out.println(cmd);
rt.exec(cmd);
} catch (Exception e) {
e.printStackTrace();
}
}
};
btn5.click();
}
}
package abstractClass;
interface Test3 {
int n1 = 10;
public static final int n2 = 20; // n1과 n2의 형태는 같다
// static이므로 어떤 객체에서 접근해도 동일한 값이다
// final이므로 한번 지정된 값은 변경할 수 없으며, 반드시 초기값을 지정해야만 한다
void method1(); // public abstract이므로 body를 작성할 수 없다
// void method2() {} 바디 생성 불가 static을 붙히면 객체 생성에 관여가 되질 않아서 가능하긴 하다
static void method2() {
System.out.println("static은 객체 생성 여부에 상관없이 호출할 수 있으니 바디 생성 가능함");
}
}
class Test4 implements Test3 {
@Override
public void method1() {
System.out.println("인터페이스 Test3의 추상메서드를 구현한 메서드1");
}
}
public class Ex03 {
public static void main(String[] args) {
// Test3 ob = new Test3(); // 인터페이스는 추상 클래스이므로, 생성자를 통한 객체 직접 생성이 불가능하다
Test4 ob1 = new Test4(); // implements는 extends 대신에 인터페이스 전용으로 사용하는 상속문 "구현한다" 라고 표현 인터페이스는 미구현 덩어리
ob1.method1();
// 인터페이스는 추상클래스이므로, 익명 내부 클래스를 활용하여 객체를 생성할 수 있다
Test3 ob2 = new Test3() {
@Override
public void method1() {
System.out.println("익명 내부 클래스 !!");
}
};
ob2.method1();
}
}
package abstractClass;
interface USB { // 인터페이스는 추상클래스, 추상 클래스는 클래스, 클래스는 자료형
void action();
}
// 인터페이스는 슈퍼클래스가 정의되어 있어도 씌울수가 있다.
class PowerChargeCable extends Object implements USB {
@Override
public void action() {
System.out.println("전원 충전 중...");
} // 전력 전송
}
class PortableSSD implements USB{
@Override
public void action() {
System.out.println("E: 연결됨. 용량 1TB");
} // 데이터 입출력
}
class Speaker implements USB {
@Override
public void action() {
System.out.println("스피커 연결됨. 현재 볼륨 20");
} // 음성 데이터 전송
}
class PC {
// 컴퓨터에 외부 장치를 연결하기 위해서는 USB 인터페이스 규격을 만족하면 된다
// => 자료형이 USB이면 가능하다 (다형성)
void connect(USB device) {
device.action();
}
}
public class Ex04 {
public static void main(String[] args) {
// 서로 다른 자료형의 3가지 장치가 모두 하나의 함수에 의해 컴퓨터와 연결될 수 있다
PC pc = new PC();
PowerChargeCable cable = new PowerChargeCable();
PortableSSD ssd = new PortableSSD();
Speaker sp = new Speaker();
pc.connect(cable); // 케이블 연결 시의 반응이 정상적으로 호출됨
pc.connect(ssd); // SSD 연결 시의 반응이 정상적으로 호출됨
pc.connect(sp); // 스피커 연결 시의 반응이 정상적으로 호출됨
// 자바 8 이후 함수형 인터페이스 형식이 추가되었음
// 함수형 인터페이스란, 단 하나의 추상 메서드만 가지는 인터페이스를 말한다
// 함수형 인터페이스는 람다식에 의해 객체를 생성할 수 있다
// 익명 클래스에 의한 객체 생성보다 식이 간편하고, 내부 작동도 다르다
USB mouse = () -> System.out.println("마우스 연결됨..."); // 미구현된 함수의 객체만 적어주면 출력 가능 익명 클래스 방식이다
// 실행하는 코드가 한줄이면 {} 중괄호 생략가능 (if처럼)
pc.connect(mouse);
// 위 코드의 람다식 구조
// (매개변수) -> { 실행할 내용 };
USB keyboard = () -> {
System.out.println("키보드 연결됨...");
};
pc.connect(keyboard);
// 1) 함수의 내용이 한줄이면 {} 생략 가능
// 2) 함수의 매개변수가 하나이면 () 생략 가능 알아보기 쉽게 하기 위해 왠만하면 넣어준다
// 3) 함수의 반환형이 void가 아니고, 한줄만에 값을 반환한다면 return 생략 가능
// Interface ob = a -> a > 0 ? a : -a; // a를 매개변수로 받고 (하나이면 괄호 생략가능)
// 삼항연산자 a > 0 ? return을 생략한 a : -a 반환
// Interface ob = (a) -> { return a > 0 ? a : -a }; 위 람다식을 생략하지 않은 코드
}
}
위와 같이 모든 클래스를 인터페이스 자료형으로 받아줄 수 있고 오버라이드를 이용하여 원하는 반환값이나 출력값을 얻을 수 있다.