java程序员从笨鸟到菜鸟之(三十三)异常之浅谈
2017-11-22 11:38
197 查看
本节讲解异常的分类,异常的处理机制(原理),异常的解决方案
由于本节是I/O流的前奏,会详细讲解相关内容
有一个人骑车去西藏旅游,可能会发生如下情况:
1)在骑行的过程中,发现前方的山路坍塌了,导致无法骑行了
2)在骑行前,发现轮胎没气了
3)在骑行的过程中,发现轮胎没气了
4)山路两边是绝壁,中间是平坦的大道,但是这个人就喜欢在边上骑行,导致gameOver
分析:第一种情况属于不可抗力的因素,管不了;
第二种情况在未出发时出现问题(异常),提醒我们去处理:骑行前应该处理问题;
第三种情况最坑爹,问题没考虑全面:以后还是随身带个打气筒吧!!!
第四种情况,no zuo no die!!!;设置防护机制(快到边上了,自动提醒吧!!!)
两个具体实现的子类:Error、Exception
分析:
第一种情况所属范畴:Error-----开发中:内存溢出!
举例:假设加载大量图片的时候---ImageLoader
解决方案:开发团队,集体研究这个问题!
第二种情况所属范畴:编译时异常
编译时期异常:只要不是RuntimeException中的异常都属于编译时期异常
举例:IOException(IO流中的),ParseException(解析异常)
出现的原因:要么语法有问题或者是Sun公司提供的一些类的中的方法(这个方法本身调用的时候就会异常存在),调用者必须处理,不处理编译不通过。
思考:类中方法的编译时期的异常,可能是历史遗留问题(不管出错不出错都会给予警示,意识问题) ,按照我的想法是如果出错了, 给予异常提醒,不出错就正常编译;一个例子有助于理解这个问题:驾驶员遇到交警时(异常),驾驶员本身没错,只需要处理这个异常(接受交警的检查),异常处理后继续上路
第三、四种情况:运行时异常
运行时期异常:RuntimeException
出现原因:可能由于我们代码的逻辑不够严谨导致的问题,不够健壮!!!
举例:NullPointerException---空指针异常!
解决方案:考虑全面;这里需要个对象进行非空判断,来防止该问题的出现!
程序健壮性:程序在多数情况下能够正常运行,返回预期的正确结果;如果偶尔遇到异常情况,程序也能采取周到的解决措施。
1)异常处理的能力有限,单靠方法的返回值难以表达异常情况的所有信息;eg:对于上班迟到(异常),相关的信息:迟到时间和原因不可知
2)异常流程的代码和正常流程的代码混在一起,影响程序的可读性,容易增加程序结构的复杂性;
3)系统规模不断扩大,这种传统的方式成为创建大型可维护应用程序的障碍
java异常处理机制优点:
1)由于面向对象的思维,把不同情况的异常分类,得到异常类,可以发挥类的可扩展和可重用优势
2)异常流程的代码和正常流程的代码分离,提高程序可读性,简化了程序结构
3)灵活处理异常,如果当前方法有能力处理异常,就捕获并处理;否则就抛出异常,由方法调用者本身来处理
方式1:程序中只有一种异常方式
实例1
方式2:如果有多个异常存在,如何
4000
处理呢?
处理方式(1):针对多个异常分别的try......catch
说明:在实际开发中,这种做法比较麻烦
处理方式(2):try...catch...catch
实例2
处理方式(3):try{...}catch(异常类名1 |异常类名2 ....变量名){...}
注意事项:1)针对多个异常类名之间是一种平级关系
2)这种格式在实际开发中,虽然有多个异常,但是针对具体的异常给出具体的处理!
说明:“|”符号特点---两边都需要判断,如果不平级的话系统就不知道执行哪一个,出现错误
实例3
上面我们提到过,对于编译时期的异常,Java程序必须给予处理,否则编译不通过(必须显示处理)
运行时期异常:可以处理,也可以像编译时期一样进行显示处理!
实例4 编译时异常
public String getMessage():消息字符串,出现错误原因
public String toString():描述字符串
(1)当前类的全路径名称(指的是当前异常类名所在的包的全路径名称)
(2)": "(冒号和一个空格)消息字符串的内容
public void printStackTrace():此方法一般返回三个信息------跟踪错误----直接在控制台打印(返回值是void)
(1)描述字符串
(2)使用哪个方法出现错误
(3)代码中具体的错误出现第几行
说明:此方法从字面意思---打印跟踪方法调用栈获得详细异常信息
实例5 异常方法的描述
前面我们提到过,如果一个方法出现异常,但是此方法没有能力处理这种异常,可以在方法声明上抛出异常,让其他方法来解决。道理:问题出现了,自己解决不了,别人或许能帮你
注意:实际开发中,尽量的不要在main()方法抛出异常,在子方法中可以抛出异常
面试题1:throws和throw的区别?
throws:抛出
后面跟的异常类名,可以跟多个异常类名,中间用逗号隔开
throws在方法声明上抛出,表示异常的一种可能性,由调用者去处理
throw:抛出
后面跟的异常对象(匿名对象),只能跟具体的一个异常类的对象
throw在方法中的语句中抛出,表示异常的绝对性,由方法中某些语句处理
在实际开发中:throws要比throw用的比较多,而try...catch...又比throws用的比较多!
实例6 throw和throws区别与联系
捕获异常的标准格式:try...catch...finally
对finally的几点说明:
finally经常用在数据库中或者IO流中,用来释放资源的
finally中的代码一定会执行
补充:finally中的不执行只有一种情况----就是Jvm退出了---------->System.exit(0) ;
面试题2:final,finalize,和finally的区别?
(1)final:字面意思:表示最终的,终态的意思,可以修饰类、方法、变量
可以修饰类:类不能继承
可以修饰成员方法:成员方法不能被重写(而不是不能被重载)
可以修饰成员变量:此变量是一个自定义常量,不可修改;思考:方法中内部类的成员变量也是被final修饰,为什么可以修改
(2)finalize:是一个方法,它的功能通过gc垃圾回收器回收不用的对象或者是变量;System.gc()的实质:调用的是重写了Object类中的finalize()方法,表示的是回收当前没有指向更多的引用对象或变量等等...
(3)finally:不能单独使用,和try...catch...finally中一块使用,(应用场景:异常,IO以及数据库中中使用的),是用来释放资源的,finally中的代码一定会执行,并且除非Java虚拟机退出了,才不会执行!
实例7
作用域的问题:各自代码块中的变量只能在各自的代码块中使用,相当于局部变量
面试题3
问题1:如果catch代码块中有return语句,那么finally中的代码还会执行吗?
问题2:如果可以,是在return前执行还是return后执行?
答:前面已经提到过,除非Jvm退出,不执行finally代码块;所以会执行,并且在return 前执行!
总结:return语句用于退出方法,在执行try或catch代码块中的return语句时,如果有finally代码块,回先执行finally代码块
实例8
通俗理解:当catch代码块中准备return时,发现finally没有执行,就回过头先执行finally代码块中的语句,再返回catch语句中执行return语句(前提是finally代码中没有return语句)
说明:上述打印结果,延伸出一个新的问题:try...catch...finally各自代码块执行情况
try:方法体中出现异常语句的后面都不会执行或者有return语句的情况下(在异常之前):执行return前的语句;如果没有异常,全部执行,在return前跳转到finally方法体
catch:try中出现异常后,方法体中在遇到return之前都会执行;try中无异常,不执行catch方法体
finally:return语句之前的或者(没有return)全部执行
实例9 finally代码块中有return语句情况
注意:代码出现了一个警告---Finally代码块未正常完成
警告原因:finally内不建议使用return,因为函数的执行过程是:try语句出现异常,在catch中对异常处理且调用了return前,执行finally中的代码,直接在finally代码块中return了,这与try...catch...finally最初设计相悖。最初设计中finally中只能放一些资源释放类的代码段,不能带return。会出现潜在的错误:覆盖finally或者catch代码块中的return语句
补充:finally方法体中有return语句出现情况:丢失异常(后续补充)
链接:点击打开链接,点击打开链接
下节:异常与继承关系,补充问题
由于本节是I/O流的前奏,会详细讲解相关内容
异常的引出
举例有一个人骑车去西藏旅游,可能会发生如下情况:
1)在骑行的过程中,发现前方的山路坍塌了,导致无法骑行了
2)在骑行前,发现轮胎没气了
3)在骑行的过程中,发现轮胎没气了
4)山路两边是绝壁,中间是平坦的大道,但是这个人就喜欢在边上骑行,导致gameOver
分析:第一种情况属于不可抗力的因素,管不了;
第二种情况在未出发时出现问题(异常),提醒我们去处理:骑行前应该处理问题;
第三种情况最坑爹,问题没考虑全面:以后还是随身带个打气筒吧!!!
第四种情况,no zuo no die!!!;设置防护机制(快到边上了,自动提醒吧!!!)
异常的分类
异常的超类:Throwable两个具体实现的子类:Error、Exception
分析:
第一种情况所属范畴:Error-----开发中:内存溢出!
举例:假设加载大量图片的时候---ImageLoader
解决方案:开发团队,集体研究这个问题!
第二种情况所属范畴:编译时异常
编译时期异常:只要不是RuntimeException中的异常都属于编译时期异常
举例:IOException(IO流中的),ParseException(解析异常)
出现的原因:要么语法有问题或者是Sun公司提供的一些类的中的方法(这个方法本身调用的时候就会异常存在),调用者必须处理,不处理编译不通过。
思考:类中方法的编译时期的异常,可能是历史遗留问题(不管出错不出错都会给予警示,意识问题) ,按照我的想法是如果出错了, 给予异常提醒,不出错就正常编译;一个例子有助于理解这个问题:驾驶员遇到交警时(异常),驾驶员本身没错,只需要处理这个异常(接受交警的检查),异常处理后继续上路
第三、四种情况:运行时异常
运行时期异常:RuntimeException
出现原因:可能由于我们代码的逻辑不够严谨导致的问题,不够健壮!!!
举例:NullPointerException---空指针异常!
解决方案:考虑全面;这里需要个对象进行非空判断,来防止该问题的出现!
程序健壮性:程序在多数情况下能够正常运行,返回预期的正确结果;如果偶尔遇到异常情况,程序也能采取周到的解决措施。
传统异常处理方式与java异常处理方式比较
传统异常(面向过程)处理缺点:1)异常处理的能力有限,单靠方法的返回值难以表达异常情况的所有信息;eg:对于上班迟到(异常),相关的信息:迟到时间和原因不可知
2)异常流程的代码和正常流程的代码混在一起,影响程序的可读性,容易增加程序结构的复杂性;
3)系统规模不断扩大,这种传统的方式成为创建大型可维护应用程序的障碍
java异常处理机制优点:
1)由于面向对象的思维,把不同情况的异常分类,得到异常类,可以发挥类的可扩展和可重用优势
2)异常流程的代码和正常流程的代码分离,提高程序可读性,简化了程序结构
3)灵活处理异常,如果当前方法有能力处理异常,就捕获并处理;否则就抛出异常,由方法调用者本身来处理
异常的处理方式1
try-catch语句捕获异常的几种方式方式1:程序中只有一种异常方式
try{ //try""{}方法体内容:可能出现异常情况的代码块 }catch (Exception e) { // catch"()"中的内容:应给出执行异常代码块出现的异常类(声明) // catch"{}"方法体内容:应该是处理异常的解决方案 }
实例1
package org.westos_02; public class ExceptionDemo2 { public static void main(String[] args) { int a = 10 ; int b = 0 ; try { System.out.println(a/b); } catch (Exception e) { //e.printStackTrace(); System.out.println("除数不能为0"); } System.out.println("over"); } }
方式2:如果有多个异常存在,如何
4000
处理呢?
处理方式(1):针对多个异常分别的try......catch
try { //可能出现问题代码 } catch (ArithmeticException e) { //异常处理 } try{ //可能出现问题代码 }catch(ArrayIndexOutOfBoundsException e){ //异常处理 } ......
说明:在实际开发中,这种做法比较麻烦
处理方式(2):try...catch...catch
try{ //可能会出现问题的多个语句 }catch(异常类名1 变量名){ //输出语句处理 }catch(异常类名2 变量名){ //输出语句处理 ...... }catch(异常类名n 变量名){ //输出语句处理 }特点:类似switch-case语句,一旦与其中某一个异常类匹配就执行这个catch语句,就不再执行其它catch语句了
实例2
package org.westos_02; public class ExceptionDemo3 { public static void main(String[] args) { method2(); } private static void method2() { int a = 10 ; int b = 0 ; int[] arr = {1,2,3} ; try{ //可能出现问题的代码 System.out.println(arr[3]); System.out.println(a/b); }catch(ArrayIndexOutOfBoundsException e){ System.out.println("您访问了数组中不存在的索引"); }catch(ArithmeticException e){ System.out.println("除数不能玩为0"); }catch(Exception e){ System.out.println("程序可能出现问题了"); } System.out.println("over"); } }为了简化类型,从jdk1.7允许一个catch子句中同时捕获多个不同的类型的异常
处理方式(3):try{...}catch(异常类名1 |异常类名2 ....变量名){...}
try{ //可能会出现问题的代码 //.... //... }catch(异常类名1 | 异常类名2 ....变量名){ //处理异常... }
注意事项:1)针对多个异常类名之间是一种平级关系
2)这种格式在实际开发中,虽然有多个异常,但是针对具体的异常给出具体的处理!
说明:“|”符号特点---两边都需要判断,如果不平级的话系统就不知道执行哪一个,出现错误
实例3
package org.westos_02; public class ExceptionDemo4 { public static void main(String[] args) { //定义变量及数组 int a = 10 ; int b = 0 ; int[] arr = {1,2,3} ; try{ System.out.println(a/b); System.out.println(arr[3]); }catch(ArithmeticException | ArrayIndexOutOfBoundsException e){ System.out.println("程序出问题了..."); e.printStackTrace() ; } System.out.println("over"); } }说明:以上几个例子都是运行时的异常
上面我们提到过,对于编译时期的异常,Java程序必须给予处理,否则编译不通过(必须显示处理)
运行时期异常:可以处理,也可以像编译时期一样进行显示处理!
实例4 编译时异常
package org.westos_03; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class ExceptionDemo { public static void main(String[] args) { method() ;//ctrl+shift+M→抽取成一个方法 } //调用parse()这个方法,本身就会抛出一个异常! //注意:告诉调用者,注意了:我有问题,必须处理 private static void method() { //String日期"文本"格式---->Date格式:解析 String s = "2018-6-25" ; //创建SimpleDataFormat对象 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd") ; //调用parse()方法 Date d; try { d = sdf.parse(s);//可能出现出现问题的代码 System.out.println("d:"+d); } catch (ParseException e) { System.out.println("解析出问题了..."); /** * 为啥没执行catch的方法体呢? * 原因:可能出现问题的代码没有出现问题,当然不执行catch语句了 */ } System.out.println("over"); } }异常中的常用方法:
public String getMessage():消息字符串,出现错误原因
public String toString():描述字符串
(1)当前类的全路径名称(指的是当前异常类名所在的包的全路径名称)
(2)": "(冒号和一个空格)消息字符串的内容
public void printStackTrace():此方法一般返回三个信息------跟踪错误----直接在控制台打印(返回值是void)
(1)描述字符串
(2)使用哪个方法出现错误
(3)代码中具体的错误出现第几行
说明:此方法从字面意思---打印跟踪方法调用栈获得详细异常信息
实例5 异常方法的描述
package org.westos_03; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class ExceptionDemo2 { public static void main(String[] args) { //解析日期的文本格式 String str = "2014-5-20" ; //创建SimpleDataFormat对象 SimpleDateFormat sdf = new SimpleDateFormat("yyy8y-MM-dd") ; try { Date date = sdf.parse(str) ; /* try语句中的代码一旦有问题,JVM就会抛出一个异常,和catch里面进行匹配, 如果一致,就会产生一个异常对象 换而言之,如果代码没有问题,就不会执行catch语句。产生异常对象 */ System.out.println(2);//上面的代码一旦出现问题,就调转到catch语句中,寻找解决办法 } catch (ParseException e) { //等价于:ParseException e = new ParseException() ; e.printStackTrace();//相当于给出了页面:一般包含3个信息 System.out.println(e.getMessage());//消息字符串:错误原因 System.out.println(e.toString()); //将消息字符串转化成字符串 } } }说明:关于方法的输出内容,最好自己手动编写,效果更好
异常处理第二种方式
throws:抛出异常前面我们提到过,如果一个方法出现异常,但是此方法没有能力处理这种异常,可以在方法声明上抛出异常,让其他方法来解决。道理:问题出现了,自己解决不了,别人或许能帮你
注意:实际开发中,尽量的不要在main()方法抛出异常,在子方法中可以抛出异常
面试题1:throws和throw的区别?
throws:抛出
后面跟的异常类名,可以跟多个异常类名,中间用逗号隔开
throws在方法声明上抛出,表示异常的一种可能性,由调用者去处理
throw:抛出
后面跟的异常对象(匿名对象),只能跟具体的一个异常类的对象
throw在方法中的语句中抛出,表示异常的绝对性,由方法中某些语句处理
在实际开发中:throws要比throw用的比较多,而try...catch...又比throws用的比较多!
实例6 throw和throws区别与联系
package org.westos_03; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class ExceptionDemo3 { public static void main(String[] args) throws ParseException,ArithmeticException{ //告诉调用者,抛出异常了,所以这里必须处理 method();//异常可以不处理(前提是没错) /** * 想说明问题: * throw抛出的异常必须处理 * throws抛出的异常可以不用处理: * {如果是运行时异常,必须处理;如果是编译时期异常,如果本身没有错误,只把异常抛出就Ok了} */ try { method2(); } catch (Exception e) { e.printStackTrace(); } } //throw private static void method2() throws Exception { int a = 10 ; int b = 0 ; if(b==0){ System.out.println(a/b); throw new ArithmeticException() ; //跟的异常对象,必须要处理的异常 }else{ System.out.println("不会出现问题"); } } private static void method() throws ParseException { String str = "2017-11-19" ; //创建SimpleDataFormat对象 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd") ; //解析 Date d = sdf.parse(str) ; System.out.println(d); } }
捕获异常的标准格式:try...catch...finally
对finally的几点说明:
finally经常用在数据库中或者IO流中,用来释放资源的
finally中的代码一定会执行
补充:finally中的不执行只有一种情况----就是Jvm退出了---------->System.exit(0) ;
面试题2:final,finalize,和finally的区别?
(1)final:字面意思:表示最终的,终态的意思,可以修饰类、方法、变量
可以修饰类:类不能继承
可以修饰成员方法:成员方法不能被重写(而不是不能被重载)
可以修饰成员变量:此变量是一个自定义常量,不可修改;思考:方法中内部类的成员变量也是被final修饰,为什么可以修改
(2)finalize:是一个方法,它的功能通过gc垃圾回收器回收不用的对象或者是变量;System.gc()的实质:调用的是重写了Object类中的finalize()方法,表示的是回收当前没有指向更多的引用对象或变量等等...
(3)finally:不能单独使用,和try...catch...finally中一块使用,(应用场景:异常,IO以及数据库中中使用的),是用来释放资源的,finally中的代码一定会执行,并且除非Java虚拟机退出了,才不会执行!
实例7
package org.westos_04; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class FinallyDemo { public static void main(String[] args) { //String--->Date String s = "2014-5-20" ; //创建SimpleDateFormat对象 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd") ; Date d = null ; try { d = sdf.parse(s) ; System.out.println("d:"+d); } catch (ParseException e) { // e.printStackTrace(); System.out.println("解析出问题了..."); System.exit(0) ;//可能出现的异常没有出现,不走catch语句,直接走finally语句 }finally{ System.out.println("Jvm不退出,我就一定会执行..."); } System.out.println("over"); } }衍生了一种新的异常处理规则:try代码块后面直接跟finally语句
作用域的问题:各自代码块中的变量只能在各自的代码块中使用,相当于局部变量
面试题3
问题1:如果catch代码块中有return语句,那么finally中的代码还会执行吗?
问题2:如果可以,是在return前执行还是return后执行?
答:前面已经提到过,除非Jvm退出,不执行finally代码块;所以会执行,并且在return 前执行!
总结:return语句用于退出方法,在执行try或catch代码块中的return语句时,如果有finally代码块,回先执行finally代码块
实例8
package org.westos_04; public class FinallyDemo2 { public static void main(String[] args) { System.out.println(getInt()); } /** * 说明问题: * (1)作用域问题 * (2)try或catch代码块中有return,而finally中无return时,执行次序 * */ private static int getInt() { int a = 1 c2bd 0 ; try{ System.out.println(a/0);//出现异常了 a = 20 ; System.out.println("我是try代码块"); }catch(ArithmeticException e){ a = 30 ; System.out.println("我是catch代码块"); return a ;//return:一个方法返回路径 }finally{ a = 40 ; System.out.println("我是finally代码块"); } return a;//(1):如果把这行注释掉,出现编译时的异常 } }
通俗理解:当catch代码块中准备return时,发现finally没有执行,就回过头先执行finally代码块中的语句,再返回catch语句中执行return语句(前提是finally代码中没有return语句)
说明:上述打印结果,延伸出一个新的问题:try...catch...finally各自代码块执行情况
try:方法体中出现异常语句的后面都不会执行或者有return语句的情况下(在异常之前):执行return前的语句;如果没有异常,全部执行,在return前跳转到finally方法体
catch:try中出现异常后,方法体中在遇到return之前都会执行;try中无异常,不执行catch方法体
finally:return语句之前的或者(没有return)全部执行
实例9 finally代码块中有return语句情况
package org.westos_04; public class FinallyDemo2 { public static void main(String[] args) { System.out.println(getInt()); } private static int getInt() { int a = 10 ; try{ System.out.println(a/0);//出现异常了 a = 20 ; System.out.println("我是try代码块"); }catch(ArithmeticException e){ a = 30 ; System.out.println("我是catch代码块"); return a ;//return:一个方法返回路径 }finally{ a = 40 ; System.out.println("我是finally代码块"); return a; } } }说明:与例8区别,finally有return语句
注意:代码出现了一个警告---Finally代码块未正常完成
警告原因:finally内不建议使用return,因为函数的执行过程是:try语句出现异常,在catch中对异常处理且调用了return前,执行finally中的代码,直接在finally代码块中return了,这与try...catch...finally最初设计相悖。最初设计中finally中只能放一些资源释放类的代码段,不能带return。会出现潜在的错误:覆盖finally或者catch代码块中的return语句
补充:finally方法体中有return语句出现情况:丢失异常(后续补充)
链接:点击打开链接,点击打开链接
下节:异常与继承关系,补充问题
相关文章推荐
- java程序员从笨鸟到菜鸟之(三十三)异常之再谈
- Java程序员从笨鸟到菜鸟之(三十三)大话设计模式(三)单例模式
- Java程序员从笨鸟到菜鸟之(十二)java异常处理机制
- Java程序员从笨鸟到菜鸟之(十二)java异常处理机制
- Java程序员从笨鸟到菜鸟之(十二)java异常处理机制
- Java程序员从笨鸟到菜鸟之(十二)java异常处理机制
- Java程序员从笨鸟到菜鸟之(十二)java异常处理机制
- Java程序员从笨鸟到菜鸟之(十二)java异常处理机制
- Java程序员从笨鸟到菜鸟之(三十三)大话设计模式(三)单例模式
- Java程序员从笨鸟到菜鸟之(三十三)大话设计模式(三)单例模式
- Java程序员从笨鸟到菜鸟之(十一)多线程讲解
- Java程序员从笨鸟到菜鸟之(十八)JSP基本语法与动作指令
- Java程序员从笨鸟到菜鸟之(一百零四)java操作office和pdf文件(二)利用POI实现数据导出excel报表
- Java程序员从笨鸟到菜鸟之(十三)java网络通信编程
- Java程序员从笨鸟到菜鸟之(十)枚举,泛型详解
- Java程序员从笨鸟到菜鸟之(五)java开发常用类(包装,数字处理集合等)(下)
- Java程序员从笨鸟到菜鸟之(九)——数据库有关知识补充(事务、视图、索引、存储过程)
- Java程序员从笨鸟到菜鸟之(五十六)细谈Hibernate(七)Hibernate自身一对多和多对多关系映射
- Java程序员从笨鸟到菜鸟之(六十)细谈Hibernate(十一)hibernate复合主键映射
- Java程序员从笨鸟到菜鸟之(一)流程控制