음악 재생 프로그램 예외 처리 방법 try/catch 블록 예외 선언 방법 11. 예외 처리 음악 재생 프로그램 예외 처리 방법 try/catch 블록 예외 선언 방법
위험한 행동 예상치 못한 상황 이런 예외적인 상황을 처리하기 위한 방법이 필요합니다. 파일이 없는 경우 서버가 다운되는 경우 장치를 사용할 수 없는 경우 이런 예외적인 상황을 처리하기 위한 방법이 필요합니다. 자바의 예외 처리 메커니즘 try/catch 블록 예외 선언
음악 재생 프로그램
JavaSound API JavaSound API MIDI Sampled 악기 디지털 인터페이스(Musical Instruments Digital Interface) Sampled 자바 1.3부터 추가되어있으며 J2SE 클래스 라이브러리에 포함되어있습니다. MIDI와 Sampled의 두 부분으로 나뉘어있는데, 여기에서는 MIDI 부분만 사용하겠습니다. MIDI는 악기 디지털 인터페이스의 약자로, 서로 다른 전자 음악용 악기끼리 데이터를 주고받는 데 필요한 표준 규약입니다. 하지만 여기에서는 그냥 주어진 음악을 재생해주는 장치라고 생각하시면 됩니다. 미디 파일을 HTML 문서에 비유하자면 미디 파일을 재생하는 악기는 웹 브라우저에 비유할 수 있겠죠?
Sequencer import javax.sound.midi.*; public class MusicTest1 { public void play() { Sequencer sequencer = MidiSystem.getSequencer(); System.out.println(“We got a sequencer”); } public static void main(String[] args) { MusicTest1 mt = new MusicTest1(); mt.play(); % javac MusicTest1.java MusicTest1.java:5: unreported exception javax.sound.midi.MidiUnavailableException; must be caught or declared to be thrown Sequencer sequencer = MidiSystem.getSequencer(); ^ 1 error
위험 요소가 있는 메소드 코드에서 사용하는 메소드가 들어있는 클래스 프로그래머가 만든 코드 프로그래머 프로그래머가 다른 사람이 만든 클래스에 들어있는 메소드를 호출하는 경우를 생각해봅시다. 그런데 그 메소드에는 제대로 실행되지 않을 가능성이 있는, 즉 위험 요소가 있는 작업을 합니다. 그런 경우에 호출하려고 하는 메소드에 위험 요소가 있다는 것을 알아야 합니다. 위험하다는 것을 알아내고 나면 실패했을 경우에 그 실패 상황을 처리할 수 있는 코드를 만듭니다. 혹시 일어날지 모르는 상황에 대비해야죠. 코드에서 사용하는 메소드가 들어있는 클래스 프로그래머가 만든 코드 프로그래머
예외 처리 메커니즘 예외(exception) 예외 처리(exception handling) 메커니즘 프로그램 실행 중에 생길 수 있는 특이한 상황 예외 처리(exception handling) 메커니즘 오류 처리 코드를 한 군데로 모아놓을 수 있기 때문에 코드가 깔끔해지고 효율적이 됩니다.
try/catch 블록 예외를 처리할 것임을 알려주기 위한 용도로 쓰임 import javax.sound.midi.*; public class MusicTest1 { public void play() { try { Sequencer sequencer = Midisystem.getSequencer(); System.out.println(“Successfully got a sequencer”); } catch (MidiUnavailableException ex) { System.out.println(“Bummer”); } public static void main(String[] args) { MusicTest1 mt = new MusicTest1(); mt.play();
InterruptedException try { // 위험한 일 처리 } catch (Exception ex) { // 문제 해결 } Throwable getMessage() printStackTrace() Exception IOException InterruptedException
예외 던지기와 잡기 예외를 던지는 코드 위험한 메소드를 호출하는 코드 public void takeRisk() throws BadException { if (abandonAllHope) { throw new BadException(); } 위험한 메소드를 호출하는 코드 public void crossFingers() { try { anObject.takeRisk(); } catch (BadException ex) { System.out.println(“Aaargh!”); ex.printStackTrace();
확인 예외 확인 예외(checked exception) RuntimeException을 제외한 모든 예외 코드에서 예외를 던진다면 반드시 메소드를 선언하는 부분에서 throws 키워드를 써야 함 예외를 던지는 메소드를 호출하면 try/catch 블록으로 그 부분을 감싸거나 그 메소드에서도 예외를 선언해야 함
바보 같은 질문은 없습니다. NullPointerException, DivideByZero 같은 예외에 대해서는 왜 try/catch 블록을 사용하지 않나요? RuntimeException 및 그 하위클래스에 속하는 예외는 컴파일러에서 잡아내지 않습니다. 런타임 예외는 실행 중에 어떤 조건에 문제가 생기는 경우보다는 코드의 논리에 예측/예방할 수 없는 방식으로 문제가 생기는 경우에 발생합니다. try/catch 블록은 예외적인 상황을 처리하기 위한 것이지 코드의 문제점을 보완하기 위한 것은 아닙니다.
try/catch 블록의 흐름 제어 try { Foo f = x.doRiskyThing(); int b = f.getNum(); } catch (Exception ex) { System.out.println(“failed”); } System.out.println(“We made it!”); % java Tester We made it! % java Tester failed We made it!
무조건 실행할 내용 예외 발생 여부와 상관없이 무조건 실행할 코드는 finally 블록에... try { turnOvenOn(); x.bake(); } catch (BakingException ex) { ex.printStackTrace(); } finally { turnOvenOff(); } try { turnOvenOn(); x.bake(); turnOvenOff(); } catch (BakingException ex) { ex.printStackTrace(); }
두 개 이상의 예외 예외를 여러 개 던진다면 모든 확인 예외를 잡아야 합니다. public class Laundry { public void doLaundry() throws PantsException, LingerieException { // 두 가지 예외를 던질 수 있는 코드 } public class Foo { public void go() { Laundry laundry = new Laundry(); try { laundry.doLaundry(); } catch (PantsException pex) { } } catch (LingerieException lex) { }
예외와 다형성 Exception public void doLaundry throws ClothingException { } catch (ClothingException cex) { PantsException LingerieException ShirtException 예외도 결국은 객체기 때문에 다형성을 활용하면 모든 예외는 Exception 클래스에 속한다고 볼 수 있습니다. (Click) 예외에 있어서 다형성을 활용하는 경우를 알아보기 위해 여기에 나와있는 것 같은 클래스 트리를 살펴보겠습니다. 여기에는 옷과 관련된 예외가 나와있는데, 일단 모든 옷과 관련된 예외는 Exception 클래스의 하위클래스인 ClothingException으로 놓고, 거기에서 각 옷의 종류에 맞게……. 등의 하위클래스를 만드 ㄹ수 있습니다. 예외에서 다형성을 활용하는 첫 번째 방법은 던지고자 하는 예외의 상위클래스 유형을 이용하여 예외를 선언하는 방법입니다. (클릭) 이렇게 throws ClothingException이라고 선언하고 나면 이 doLaundry 클래스에서는 ClothingException에 속하는 모든 객체, 즉 이 하위클래스에 있는 모든 예외를 던질 수 있습니다. 또 한 가지는 catch 구문에서 던져지는 예외의 상위클래스 유형을 써서 예외를 잡아내는 것입니다. 이런 식으로 해 놓으면 ClothingException에 속하는 모든 예외 객체를 처리할 수 있겠죠? (클릭) 그리고 이런 식으로 하면 ShirtException에 속하는 TeeShirtException과 DressShirtException을 모두 처리할 수 있을 겁니다. 하지만 어떤 것을 할 수 있다고 해서 반드시 그렇게 해야 하는 건 아니죠? 여기에서도 마찬가집니다. 예외를 처리할 때 다형성을 활용할 수 있다고 해서 반드시 다형성을 활용해야 하는 건 아닙니다. (다음 페이지로 넘어가기) TeeShirtException DressShirtException } catch (ShirtException sex) {
작은 것부터 큰 것으로 여러 개의 catch 블록을 쓸 때는 상/하위클래스 관계를 잘 따져봐야 합니다. try { laundry.doLaundry(); } catch(TeeShirtException tex) { } catch(LingerieException lex) { } catch(ClothingException cex) { } 여러 개의 catch 블록을 쓸 때는 상/하위클래스 관계를 잘 따져봐야 합니다.
예외 선언 try/catch 블록을 쓰는 대신 메소드에서 예외를 선언함으로써 예외 처리를 회피하는 방법도 있습니다. public void foo() throws ReallyBadException { // try/catch 블록 없이 위험한 메소드 호출 laundry.doLaundry(); }
예외 선언 doLaundry() foo() foo() main() main() main() public class Washer { Laundry laundry = new Laundry(); public void foo() throws ClothingException { laundry.doLaundry(); } public static void main(String[] args) throws ClothingException { Washer a = new Washer(); a.foo(); doLaundry() foo() foo() main() main() main()
다시 음악 코드로… import javax.sound.midi.*; public class MusicTest1 { public void play() { try { Sequencer sequencer = MidiSystem.getSequencer(); System.out.println(“Successfully got a sequencer”); } catch(MidiUnavailableException ex) { System.out.println(“Bummer”); } public static void main(String[] args) { MusicTest1 mt = new MusicTest1(); mt.play();
예외와 관련된 규칙 try 없이 catch나 finally만 쓸 수는 없음 try와 catch 사이에 코드를 집어넣을 수 없음 void go() { Foo f = new Foo(); f.foof(); catch(FooException ex) { } } try와 catch 사이에 코드를 집어넣을 수 없음 try { x.doStuff(); int y = 43; } catch(Exception ex) { }
예외와 관련된 규칙 try 뒤에는 반드시 catch나 finally가 있어야 함 try 뒤에 finally만 있으면 예외 선언 x.doStuff(); } finally { } try 뒤에 finally만 있으면 예외 선언 void go() throws FooException { }
JavaSound API 음악을 재생하는 장치 재생할 음악 실제 정보가 들어있는 부분 실제 음악 정보 Sequencer Track 실제 음악 정보 MidiEvent
JavaSound API 356 페이지에 있는 코드를 실행시켜봅시다. 시퀀서 만들고 열기 새로운 시퀀스 만들기 Sequencer player = MidiSystem.getSequencer(); 새로운 시퀀스 만들기 Sequence seq = new Sequence(timing, 4); 시퀀스에서 새로운 트랙 가져오기 Track t = seq.createTrack(); 트랙에 MidiEvent를 채우고 시퀀스를 시퀀서에 넘기기 t.add(myMidiEvent1); player.setSequence(seq); 시퀀서에 대해 play() 메소드 호출 player.start(); 356 페이지에 있는 코드를 실행시켜봅시다.
MidiEvent Message 만들기 메시지에 지시사항 넣기 새로운 MidiEvent 만들기 MidiEvent를 트랙에 추가 ShortMessage a = new ShortMessage(); 메시지에 지시사항 넣기 a.setMessage(144, 1, 44, 100); 새로운 MidiEvent 만들기 MidiEvent noteOn = new MidiEvent(a, 1); MidiEvent를 트랙에 추가 track.add(noteOn); 메시지 유형 채널 속도 음높이
숙제 본문을 꼼꼼하게 읽어봅시다. 연필을 깎으며 및 11장 끝에 있는 연습문제를 모두 각자의 힘으로 해결해봅시다. API 문서에서 이 장에 나와있는 클래스 및 메소드에 대한 내용을 직접 찾아봅시다.