创建MIDI音乐播放器
1.首先我们需要一个Sequencer
package MidiPlayer;
import javax.sound.midi.*;
public class MusicTest1 {
public void play() {
//这个对象的作用是将Midi信息组合成乐曲;
Sequencer sequencer = MidiSystem.getSequencer();
System.out.println("We got a sequencer");
}//关闭播放
public static void main(String[] args) {
MusicTest1 mt = new MusicTest1();
mt.play();
}
}
进行编译时发现出错,错误
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
Unhandled exception type
MidiUnavailableException
表示有异常情况必须要处理。
Java的异常处理(exception-handling)机制是个简捷轻量化的执行运行期间例外状况处理方式,让你知道所调用的方法是有风险的,就可以预先对其进行处理。
查找API时,如果提示throws就可以了解该方法可能抛出的异常
getSequencer()这个方法可能会在执行期间出问题,所以必须声明调用它时可能出现的风险
try/catch块会告诉编译器你确实知道所调用的方法有风险,并且也已经准备好处理它,它只会注意你有没有表示你会注意到异常。
因此对以上程序做一下修改:
package MidiPlayer;
import javax.sound.midi.*;
public class MusicTest1 {
public void play() {
//这个对象的作用是将Midi信息组合成乐曲;
try{
Sequencer sequencer = MidiSystem.getSequencer();
System.out.println("We got a sequencer");//把有风险的块放在try块
}catch(MidiUnavailableException ex){
System.out.println("Bummer");//用catch块摆放异常状况的处理程序
ex.printStackTrace(); //如果无法从异常中恢复至少也要使用PrintStackTrace()来列出有用的信息
}
}//关闭播放
public static void main(String[] args) {
MusicTest1 mt = new MusicTest1();
mt.play();
}
}
异常是一种Exception类型的对象,它是对象,所以我们catch住的也是对象,线面代码中catch的参数是Exception类型的ex引用变量
try{
//危险动作
} catch(Exception ex) {
//尝试恢复
}
在编写可能会抛出异常的方法时,他们都必须声明有异常
有风险会抛出异常的程序代码
public void takeRisk() throws BadException { //必须声明他会抛出BadException
if(abandonAllHope) {
throw new BadException();//创建Exception对象并抛出
}
}
异常类型
编译器会核对除了RunTimeException以外的异常,RunTimeException成为不检查异常,编译器保证:
1如果你有抛出异常,则你一定要用throw来声明这件事
2.如果你调用会抛出异常的方法,你必须得确认你知道异常的可能性,将调用包在try/catch快中是一种满足编译器的方法
finally:无论如何都要执行的部分
try {
turnOvenOn();
x.bake;
} catch (BakingException ex) {
ex.printStackTrace();
} finally {
turnOvenOff();
}
无论try是否成功,都会跳到finally继续执行
注意如果try或catch快中有return指令,会先跳到finally中执行然后再回到return中执行。
如果有必要的话程序可以抛出多个异常。
编译器会检查你是否处理所有可能的异常,将个别的catch块逐个放在try块下。
public class Laundry() {
public void doLaundry() throws PantsException, LingerieException {
//有可能抛出两个异常的程序代码
}
}
public class Foo {
public void go() {
Laundary laundary = new Laundary();
try {
laundary.doLaundary()
} catch (PantsException pex) {
//恢复程序代码
} catch (LingerieException lex) {
//恢复程序代码
}
}
}
异常也是多态的。
1.以异常的父型来声明会抛出的异常
public void doLaundry() throws ClothingException {
//声明成ClothingException可让你抛出任何他的自类
2.以所抛出的异常父型来catch异常
try {
laundry.doLaundry();
} catch (ClothingException ex) {
//可以catch任何ClothingException的自类
}
可以把异常处理程序代码写成只有一个catch块以父型Exception来捕获,可以抓到任何可能被抛出的异常,但你会搞不定这是哪里出错。
为每个需要单独处理的异常编写不同的catch块。
有多个catch块时要注意从小排到大
不想处理异常时,可以把它duck掉来避开。
void foo() throws ClothingExceotion {
laundry.doLaundry();
}
//有声明会抛出异常,去没有try/catch块,所以就会duck掉异常给调用方
这意味着foo()的程序的调用方必须要处理或者也跟着声明异常
异常处理规则:
1.catch和finally不能没有try
2.try与catch之间不能有程序
3.try一定要有catch或者finally
4.只带有finally的try必须要声明异常
void go() throws FooException {
try {
x.doStuff();
} finally {}
}
回到midi音乐播放程序
4项必备的条件
史上第一个声音播放程序
package MidiPlayer;
import javax.sound.midi.*;
public class MiniMiniMusicApp {
public static void main(String[] args) {
MiniMiniMusicApp mini = new MiniMiniMusicApp ();
mini.play();
}//关闭main
void play() {
try {
Sequencer player = MidiSystem.getSequencer();
player.open(); //取得Sequencer并将其
Sequence seq = new Sequence(Sequence.PPQ, 4);
//先不看参数的意义
Track track = seq.createTrack();
//要求取得track
ShortMessage a = new ShortMessage();
a.setMessage(144, 1, 44, 100);//代表发送44音符 类型,频道,音符,音道
//144表示打开,128表示关闭, 频道代表不同的演奏者,例如1号是吉他,2号是贝斯 音符:0~127表示不用音高 音道:用多大的音道按下,0表示听不到,100算是差不多
MidiEvent noteOn = new MidiEvent(a, 1);//第一拍启动a这个Message
track.add(noteOn);//Track带有全部的MidiEvent对象,Sequence会根据事件事件组织它们,然后Sequencer会根据此顺序来播放,同一时间可以执行多个操作
ShortMessage b = new ShortMessage();
b.setMessage(128, 1, 44, 100);
MidiEvent noteOff = new MidiEvent(b, 16);
track.add(noteOff);
//对Track加入几个MidiEvent,要注意的是setMessage()的参数,以及MidiEvent的constructor
player.setSequence(seq);//将Sequence送到Sequencer上
player.start();//开始播放
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
第二版:使用命令列参数
这个版本还是播放单一的音符而已,但你可以使用命令列参数来改变乐器和音符,试试看传入两个介于0~127的整数参数,第一个参数设定乐器,第二个整数设定音符
package MidiPlayer;
import javax.sound.midi.*;
public class MiniMusicCmdLine {
public static void main(String[] args ) {
MiniMusicCmdLine mini = new MiniMusicCmdLine();
if (args.length < 2) {
System.out.println("Don't forget the instrument and note args");
} else {
int instrument = Integer.parseInt(args[0]);
int note = Integer.parseInt(args[1]);
mini.play(instrument, note);
}
}//关闭main
public void play(int instrument, int note) {
try {
Sequencer player = MidiSystem.getSequencer();
player.open();
Sequence seq = new Sequence(Sequence.PPQ, 4);
Track track = seq.createTrack();
MidiEvent event = null;
ShortMessage first = new ShortMessage();
first.setMessage(192, 1, instrument, 0);
MidiEvent changeInstrument = new MidiEvent(first, 1);
track.add(changeInstrument);
ShortMessage a = new ShortMessage();
first.setMessage(144, 1, note, 100);
MidiEvent noteOn = new MidiEvent(a, 1);
track.add(noteOn);
ShortMessage b = new ShortMessage();
first.setMessage(128, 1, note, 100);
MidiEvent noteOff = new MidiEvent(b, 16);
track.add(noteOff);
player.setSequence(seq);
player.start();
} catch (Exception ex) {ex.printStackTrace();}
}//关闭播放
}//关闭类