1. 인터페이스 사용하는 이유(장점)
1. 개발 시간 단축
- 클래스를 작성하지 않아도 인터페이스를 구현한 것만으로도 프로그램 작성하는 것이 가능하다.
- 프로그램 작성 개발과 인터페이스를 구현하는 클래스 개발 -> 양쪽에서 동시 개발 가능
2. 표준화 가능
- 인터페이스를 구현하면 안에 있는 내용을 오버라이딩하기 때문에 똑같다. (기본틀)
- 일관되고 정형화된 프로그램 개발 가능
3. 서로 관계가 없는 클래스들에게 관계를 맺어줄 수 있다.
- 서로 관계가 없다 : 서로 상속 관계가 없다, 같은 조상 클래스를 가지고 있지 않다.
- 인터페이스를 공통적으로 구현하도록 함으로써 서로 관계를 맺어줄 수 있다.
- 즉, 서로 호환이 된다.
코딩 예시) 엔진인터페이스 IEngine 선언 후 H, S, K_Engine 클래스 구현
IEngine 인터페이스)
Car 클래스)
package days19;
public class Car {
// 필드
String name;
String gearType;
int door;
// [인터페이스 장점 3번째]
// IEngine 인터페이스 선언 -> 인터페스로 구현하여 S_, K_, H_Engine 클래스 선언
// 인터페이스 업캐스팅 : S,K,H 클래스를 IEngine 인터페이스로 업캐스팅하여 받겠다.
private IEngine engine = null;
// getter
public IEngine getEngine() {
return engine;
}
// [인터페이스 장점 3번째]
public void setEngine(IEngine engine) {
this.engine = engine;
}
// [인터페이스 장점 3번째]
// 디폴트 생성자 - 생성자 초기화
Car() {
this.engine = new S_Engine(); // engine을 안주면 S엔진 인스턴스 생성
}
// [인터페이스 장점 3번째]
// 생성자를 통해서 엔진 객체 주입(DI : Dependence Injection) -> 의존성 주입(자체적으로 생성안하고 외부에서 가지고 오는 것)
// 매배변수 다형성 중 인터페이스 매개변수 다형성
// 매개변수 IEngine engine = S, K, H_Engine 클래스 선언
Car(IEngine engine) {
this.engine = engine;
}
// 메서드
void speedUp(int fuel) {
this.engine.moreFuel(fuel);
}
void speedDown(int fuel) {
this.engine.lessFuel(fuel);
}
void stop() {
this.engine.stop();
}
} // Car
H_Engine 클래스)
S_Engine 클래스)
K_Engine 클래스)
main() 메서드)
4. 독립적인 프로그램이 가능하다.
- 인터페이스를 하나를 만들고나면 클래스를 만들지 않고도 코딩을 할 수 있다.
- 인터페이스를 이용하여 클래스와 클래스간의 직접적인 관계를 간접적으로 변경하여 한 클래스의 변경이 관련된 클래스에 영향을 미치지 않는 독립적인 프로그래밍이 가능
인터페이스의 장점을 볼 수 있는 예제)
package days19;
import java.io.Serializable;
public class Ex02 {
public static void main(String[] args) {
// 스타크래프트
// [테란 종족] SCV - 자원캐기, 수리하기
} // main
} // class
/*
public interface Serializable{
// 아무것도 구현된 멤버가 없음 -> interface Repairable 와 같은 의미의 인터페이스
}
*/
// [직렬화]가 가능한 클래스 -> 네트워크 수업 배울 때 다시 배울 예정
class Unit implements Serializable{
}// class Unit
class GroundUnit extends Unit{ // 지상유닛
} // GroundUnit
class AirUnit extends Unit{ // 공중유닛
} // AirUnit
class SCV extends GroundUnit implements Repairable{ // 지상유닛 SCV
void 자원캐는메서드() {
}
// 인터페이스를 구현하지 않았다면 각 유닛별로 오버라이딩을 해야하는데
// 인터페이스를 구현 했기 때문에 인터페이스 매개변수 다형성 사용가능
void unitRepaire(Repairable unit) {
// 수리 작업 코딩
}
} // SCV
// implements Repairable : SCV로 수리가 가능한 유닛인지 아닌지 알 수 있음
class Tank extends GroundUnit implements Repairable{ // 지상유닛 탱크 SCV로 수리 O
// 인터페이스 안에 아무것도 없어서 오버라이딩 할 것도 없음
} // Tank
class Dropship extends AirUnit implements Repairable{ // 공중유닛 수송선 SCV로 수리 O
} // Dropship
class Marine extends GroundUnit{ // 지상유닛 보병 SCV로 수리 X
} // Marine
interface Repairable{
// 멤버, 추상화 메서드 선언 X
// 인터페이스만 선언
} // Repairable
2. 인터페이스의 이해(본질적인 것에 대한)
1. 클래스를 사용하는 쪽 - [User]
2. 클래스를 제공하는 쪽 - [Provider]
3. 메서드를 호출하는 쪽 - [User] > 메서드의 선언부만 알면된다(구현된 내용은 몰라도 됨)
- ??? Manager 클래스 > ??? 객체를 관리하는 클래스 ex) InterfcaeManager 클래스
> 제3의 클래스의 메서드를 통해서 인터페이스를 구현한 클래스의 인스턴스를 얻어온다.
> 인스턴스를 직접 생성하지 않고, 메서드( getInstance() )를 통해 제공 받음
> 나중에 다른 클래스의 인스턴스로 변경되어도 클래스(A 클래스)의 변경없이 메서드( getInstance() )만 변경하면 된 다는 장점이 생긴다.
예제)
public class InterfaceText3 {
public static void main(String[] args) {
A a = new A();
a.methodA();
} // main
} // class
class A{
void methodA( ) {
I i = InstanceManager.getInstance();
i.methodB();
System.out.println(i.toString()); // i 로 Object 클래스의 메서드 호출가능
}
} // class A
interface I {
public abstract void methodB();
} // interface I
class B implements I {
@Override
public void methodB() {
System.out.println("methodB in B class");
}
public String toString() {return "class B";}
} // class B
class InstanceManager { // 관리하기 위한 용도의 클래스
public static I getInstance() { // 리턴타입이 인터페이스
return new B();
}
} // class InstanceManager
3. default 메서드와 static 메서드
- JDK 1.8부터 사용 가능
- 아래 두가지 메서드의 접근제어자는 public
1. default 메서드
- 설명 : 인터페이스에 새로운 메서드를 추가하면 그 인터페이스로 구현된 모든 클래스들이 에러가 뜨게된다.
- 에러발생 이유? 새롭게 추가한 메서드를 오버라이딩(재정의)하지 않아서 -> 재정의 하지 않으면 추상클래스가 되어버림
> 이와 같은 문제를 해결하기 위해서 디폴트 메서드를 가질 수 있게 되었고, 인터페이스에 디폴트 메서드를 추가하면 새롭게 추가한 메서드를 사용할 클래스에서만 오버라이딩(재정의)해서 사용하면 된다.
[충돌 해결 규칙] -> 그냥 필요한 쪽의 메서드와 같은 내용으로 오버라이딩 하면 그만~
1) 여러 인터페이스의 디폴트 메서드 간의 충돌
-> 인터페이스를 구현한 클래스에서 디폴트 메서드를 오버라이딩
2) 디폴트 메서드와 조상 클래스의 메서드 간의 충돌
-> 조상 클래스의 메서드가 상속되고, 디폴트 메서드는 무시된다.
2. static 메서드
- 설명 : 모든 메서드는 추상메서드여야 한다는 이유로 인터페이스와 관련된 static메서드는 별도의 클래스에 따로 두어야 했음
-> 별도의 클래스를 두지 않고 인터페이스 안에 static 메서드를 가질 수 있게 되어 불필요한 클래스를 가지지 않을 수 있음
4. 내부 클래스(inner class)
1. 내부 클래슨 주로 GUI 어플리케이션의 이벤트를 처리할 때 사용
- GUI(윈도우용) 어플리케이션 만들 때는 AWT, Swing 사용(이유 : 자바는 시간이 더 오래걸림)
2. 사용 빈도가 낮다.
3. 클래스 내부(안)에 클래스를 멤버처럼 선언 == 클래스 안에 클래스를 선언한 것을 내부 클래스
4. 클래스 + 긴밀한관계 + 클래스(두 클래스가 서로 긴밀한 관계)
5. 장점 2가지
1) 접근성
클래스 멤버와 내부 클래스 서로 호출이 쉬움(내부 클래스에서 외부 클래스의 멤버들을 쉽게 접근할 수 있음)
2) 보안성 + 캡슐화(코드의 복잡성을 줄일 수 있음)
class A{ // 외부 클래스
// 필드
int x;
static int y; // static 멤버처럼 사용 되어진다.
B obj = new B(); // 외부 클래스의 인스턴스 멤버처럼 사용(멤버변수)
// 내부 클래스 - 외부 클래스 필드처럼 선언되어짐
private class B{
// 외부 클래스의 필드 접근
// 외부 클래스의 메서드 접근
}
}
6. 내부 클래스의 종류 및 특징
1) 인스턴스 클래스 :
외부 클래스의 필드(멤버변수) 선언 위치, 외부 클래스의 인스턴스 멤버 처럼 사용 되어짐
2) static 클래스 : 외부 클래스의 필드(멤버변수) 선언 위치, 외부 클래스의 static 멤버 처럼 사용 되어짐
3) 지역(local) 클래스 : 메서드 안, 초기화 블럭 안에 선언 및 사용 됨
4) 익명(무명) 클래스(anonymous class) :
클래스 선언+생성(선언과 생성 동시에),
클래스 이름이 없음, 일회성 클래스(인스턴스 한 번 생성 후 사용X)
익명 클래스의 특징
(1) 클래스 선언 + 생성 -> 같이 있다.
(2) 일회성 클래스 -> 오직 한 개의 객체 생성이 된다.
(3) 익명 클래스 선언 형식
new 부모클래스() {
// 필드 선언 X
// 부모클래스의 메서드만 오버라이딩(재정의)할 수 있다.
}
new 인터페이스() {
// 인터페이스의 추상메서드만 오버라이딩(재정의)할 수 있다.
}
> 원래는 인터페이스로 클래스를 구현하고 객체를 생성할 수 있음
// class K_Engine implements IEngine{} // 클래스 선언
// Car myCar = new Car(new K_Engine()); // 객체 생성
Car 클래스와 IEngine 인터페이스를 사용한 익명클래스 예시)
익명클래스 예제)
package days19;
import java.awt.Button;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
public class Ex06_02 {
public static void main(String[] args) {
new MyForm();
// 윈도우 [x] 닫기 버튼 클릭하면 프로그램 종료
// 1. 자바 이벤트 처리 방법 2가지
// 1) 리스너(청취자) 필요
// 윈도우 이벤트 발생 -> 리스너(청취자) -> 이벤트 핸들러
// 2) 어댑터 *** : 리스너를 사용하니깐 필요없는 이벤트 핸들러도 오버라이딩
} // main
} // class
class MyForm extends Frame{
// 버튼
// Button은 클래스, btnExit 이름
Button btnExit;
// 디폴트 생성자
MyForm() {
this.setTitle("새로운 윈도우(창)");
this.setSize(400, 400);
// [리스너]
// this.addWindowListener(new MyFormWindowListener()); // 2. 윈도우리스너 객체 생성
// [리스너]
// 윈도우리스너 클래스를 선언하고 객체를 생성하지말고 익명클래스로 만들기
// 선언과 생성을 동시에 하는 익명클래스
/*
this.addWindowListener(new WindowListener() {
// 윈도우리스너 추가 (new 윈도우리스너 -> 인터페이스)
@Override
public void windowOpened(WindowEvent e) {}
@Override
public void windowIconified(WindowEvent e) { }
@Override
public void windowDeiconified(WindowEvent e) { }
@Override
public void windowDeactivated(WindowEvent e) { }
@Override
public void windowClosing(WindowEvent e) {
System.out.println("프로그램 종료!!");
System.exit(-1);
}
@Override
public void windowClosed(WindowEvent e) { }
@Override
public void windowActivated(WindowEvent e) { }
} );
*/
// [어댑터]
// 인터페이스 업캐스팅
// this.addWindowListener(new MyFormWindowAdapter());
// [어댑터] 익명클래스
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.out.println("프로그램 종료!!");
System.exit(-1);
}
} );
// btnExit 버튼 초기화(설정)
// this.setVisible(true); 눈에 보여지기 전에 설정~ -> 위치는 상관없긴함
this.btnExit = new Button("exit Button"); // 필드값 초기화
// this.btnExit.setLabel(label); // 이렇게 set메서드로 설정해도됨
// 버튼을 클릭해도 이벤트 처리가 안되니 아래 코딩으로 이벤트 처리하기 -> 익명클래스로
this.btnExit.addActionListener(new ActionListener() {
// ActionListener 클래스 안에 actionPerformed 메서드만 있어서 아래와 같이 오버라이딩 1개만 됨
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("종료 버튼 클릭됨");
}
});
this.add(btnExit); // 생성한 버튼을 추가하겠다.
// 윈도우 배치(layout) 설정 아직 안함
this.setVisible(true);
} // MyFrom 디폴트 생성자
} // MyForm class
// [어댑터]
// 1.윈도우어댑터 클래스 선언
class MyFormWindowAdapter extends WindowAdapter{
// WindowAdapter는 WindowListener 인터페이스로 구현한 클래스이고
// 그 클래스를 상속받아서 윈도우어댑터 클래스 선언
@Override
public void windowClosing(WindowEvent e) {
System.out.println("프로그램 종료!!");
System.exit(-1);
}
} // MyFormWindowAdapter
/*
// [리스너]
// 1. 리스너 중 윈도우리스너 클래스 선언
// (윈도우 이벤트를 처리해야하기 때문에 WindowListener 인터페이스로 구현)
class MyFormWindowListener implements WindowListener{
@Override
public void windowActivated(WindowEvent e) {}
@Override
public void windowClosed(WindowEvent e) {}
@Override
public void windowClosing(WindowEvent e) {
System.out.println("프로그램 종료!!");
System.exit(-1);
}
@Override
public void windowDeactivated(WindowEvent e) {}
@Override
public void windowDeiconified(WindowEvent e) {}
@Override
public void windowIconified(WindowEvent e) {}
@Override
public void windowOpened(WindowEvent e) {}
} // MyFormWindowListener
*/
5. 예외처리
- 오류와 에러는 같은 말이지만, 자바에서는 런타임 오류를 2가지로 나눈 것 중에 '에러'가 있기 때문에 밖에서 사용하는 단어는 오류로 지칭
1. 프로그램 오류(==에러)란?
프로그램 오작동 또는 비정상적으로 종료되는 원인
2. 발생시점에 따라서
-> 1) 컴파일 오류 + 2) 런타임 오류 = 빌드 오류
빌드 : 컴파일 + 런타임 -> ex) 빌드할거니? 컴파일하고 런타임할거니?
1) 컴파일 오류
ex) int x= 10
2) 런타임 오류
ex) int[] m = new int[3];
m[100] = 10; // ArrayIndexOutOfBounds[Exception]
* 자바에서는 런타임(실행) 오류를 2가지로 구분한다.
(1) 에러(error) - 메모리 부족, 스택오버플로워(스택부족) 에러 발생 -> 복수할 수 없는 심각한 오류
Error 클래스 <- 자식 클래스...
(2) 예외(exception) - 수습될 수 있는 비교적 덜 심각한 오류
exception 클래스 <- 자식 클래스...
3) 논리적 오류 :
컴파일, 런타임 오류도 발생하지 않았지만 의도와 다르게 동작하는 것 -> 논리적 오류를 잡기가 가장 어렵다.
ex) int a = Integer.MAX_VALUE;
long b = a + 100;
System.out.println(b); // -2147483549
3. 예외 클래스의 계층구조(상속관계)
[Exception 클래스 2그룹]
ㄱ) RuntimeException + 그 하위 예외 클래스 -> RuntimeException클래스들 : 주로 프로그래머의 실수
ㄴ) Exception + 그 하위 클래스 ( ㄱ)RuntimeException 제외 ) -> Exception클래스들 : 외부의 영향으로 발생
4. 예외처리(Exception handling) - 에러 X, 오류 O
6. 예외처리하기
아래 코딩과 같이 if 문을 활요한 것은 예외 처리 문이 아니다.
입력 값이 제대로 들어가도 들어가지않아도 if 문이 돈다. -> 항상 처리하게 됨
예외 처리 예제) try-catch 문을 사용하는 것이 예외 처리하는 코딩!
7. try-catch문 흐름(처리과정)
1. try 블럭 내에서 예외가 발생한 경우
1) 발생한 예외와 일치하는 catch 블럭이 있는지 확인
2) 일치하는 catch 블럭을 찾게되면, 그 catch 블럭 내의 문장들을 수행하고,
전체 try-catch 문을 빠져나가서 그 다음 문장을 계속해서 수행
만일, 일치하는 catch 블럭을 찾지 못하면, 예외는 처리되지 못한다.
2. try 블럭 내에서 예외가 발생하지 않은 경우
1) catch 블럭을 거치지않고 전체 try-catch 문을 빠져나가서 수행을 계속한다.
> e.printStackTrac() : 메서드 정보와 예외 메시지를 화면에 출력하는 메서드
> e.getMessage() : 예외 메서드를 반환하는 메서드
8. 다중 catch문과 멀티 catch문
1. 다중 catch문 : catch 문을 여러 개 선언 하는 것
2. 멀티 catch문 : | 기호 연산자를 사용하여 catch () 안에 예외를 멀티로 선언하는 것
9. 예외 발생시키기(throw)
개발자가 고의로 예외를 발생 시킬수 있다.
1. [예외 객체 생성] : 먼저, 연산자 new를 이용해서 발생시키려는 예외 클래스의 객체를 만든 다음
Exception e = new Exception("고의로 예외 발생시킴");
2. [throw 예외객체] : 키워드 throw를 이용해서 예외를 발생시킨다.
throw e;
- 컴파일러가 예외처리를 확인하지 않는 RuntimeException 클래스들은 'unchecked예외'
> RuntimeException 클래스와 자식클래스들...
- 컴파일러가 예외처리를 확인하는 Exception 클래스들은 'checked예외'
> Exception 클래스와 자식클래스들..
> 객체를 생성하지 않고 throw new Exception("고의로 예외 발생시킴"); 으로 코딩해도 상관 없음
10. 메서드에 예외 선언하기(thorws)
- 메서드의 throws에 명시하는 것은 예외를 처리하는 것이 아니라, 자신(예외가 발생할 가능성이 있는 메서드)을 호출한 메서드에게 예외를 전달하여 예외처리를 떠맡기는 것
- main() 메서드에서도 예외가 처리되지 않으면, main() 메서드가 종료되어 프로그램이 예외로 인해 비정상적으로 종료되는 것
throws 예시)
public class Ex13 {
public static void main(String[] args) throws IOException{
// IOException 처리
// int one = System.in.read(); // IOException 예외 발생, read() 메서드가 throws를 하고 있어서 예외를 처리해줘야함
try {
int kor = getScore();
System.out.println(kor);
} catch (InputMismatchException e) {
System.out.println("점수 입력 잘못!");
} catch (Exception e) {
System.out.println("그 외의 예외 발생");
}
} // main
// 0~100 사이의 점수를 입력받아서 반환하는 메서드
// 메서드 안에서 예외가 발생할 수 있으니까 메서드 호출시 예외처리문을 떠넘겨라
public static int getScore() throws InputMismatchException{
Scanner sc = new Scanner(System.in);
int score;
System.out.print("> 점수를 입력하세요?");
String data = sc.next();
if(data.matches("[1-9]?\\d|100")) { // ?가 있어야 0~100이고 ?없으면 10~100
score = Integer.parseInt(data);
return score;
} else {
// 강제로 예외를 발생시키겠다.
// [Ex18] InputMismatchException 설명 : 입력불일치예외
// 내가 만든 예외로 발생을 처리시키고싶다.
throw new InputMismatchException("점수 범위(0~100) 벗어났다.");
}
}
} // class
> void method() throws NullPointerException, ArithmeticException, IOException{ }
,(콤마)로 여러 개의 Exception을 선언할 수 있다.
예제1)
예제2)
11. finally 블럭
finally 블럭은 예외의 발생여부에 상관없이 실행되어야할 코드를 포함시킬 목적으로 사용된다.
try-catch문의 끝에 선택적으로 덧붙여 사용할 수 있으며, try-catch-finally 순서대로 구성
예제)
12. 자동 자원 반환 : try-with-resources문
JDK 1.7부터 새로 추가된 구문
주로 입출력(I/O)와 관련된 클래스를 사용할 때 유용
Scanner 자원을 사용하고 close()를 하지 않으면 리소스 누출이 된다는 노란 경고등 표시가 뜬다.
위의 코딩을 try-with-resources문으로 작성해보면 아래와 같다.
13. 사용자정의 예외 만들기
기존의 정의된 예외 클래스 외에 필요에 따라 개발자가 새로운 예외 클래스를 정의하여 사용할 수 있다.
ScoreOutOfBoundException 클래스)
main() 메서드)
'TIL > Java' 카테고리의 다른 글
[SIST] Java_days21 (0) | 2022.03.18 |
---|---|
[SIST] Java_days20 (0) | 2022.03.17 |
[SIST] Java_days18_OOP관련 (0) | 2022.03.15 |
[SIST] Java_days17 (0) | 2022.03.14 |
[SIST] Java_days16 (0) | 2022.03.11 |