第九天(异常与封装类)

      补回昨天,并看完了封装类部分,到242页了

2011-05-24(异常与封装类)
1、自定义异常。有时系统提供的异常不能满足需求,则是需抛出自定义的异常对象。一般格式如下:
class 类名 extends Exception(或其他捕获异常类)
自定义的异常一般有两个构造方法:无参构造和以字符串为参数的构造方法,后者可调用父类以字符串为参数的构造方法。Exception类中有如下几个常用方法:
                        public void printStackTrace()//打印异常栈的信息
                        public Strin toString()//放回异常对象的字符串表示
                        public String getMessage()//返回异常对象携带的错误信息
以上方法是Exception类继承Throwable类的,基本上自定义的异常如果继承了某个Throwable子类均可使用。下面给个例子说明: 运行结果:
                                              70
                                              NumberException: 数字异常
                                              at Test.printNumber(Test.java:13)
                                              at Test.main(Test.java:19)
                                              2、NumberException: 数字异常
                                              3、数字异常
1-6行声明有NumberException这么一个异常。前面的只是声明,并未说明何时会抛出异常,这需要在具体方法中给出抛出异常的条件,一般是一个方法对应一种异常(因为如果某个方法给出抛出异常条件后,另一个方法也需抛出这种异常,直接调用这个方法即可)。当然可以将所有会抛出异常的方法封装在一个类中,通过类来调用它,这样可读性会好点。或言,其实也不需要异常,只需在执行方法前给个判断语句,也有同样的效果。话是这么说没错,但如果多次调用同样的方法,岂不是会平白多出许多代码。
另外从运行结果看,printStackTrace()打印的是哪一行出现错误;toString()打印的是异常的对象(包含它在哪一个包,类名及错误信息);getMessage()打印错误信息,没有则打印null。这里的错误信息是指构造函数中的字符串:mseeage。
2、昨天提到,显性和隐性再抛出的主要不同是一个使用throw抛出,一个自动抛出。如果直接抛出异常,则不需要显性,但是如果是早抛出之前要做某些处理,就需要在throw语句之前把要作地处理的代码写上,这是隐性所不能做到的。
3、catch语句的规则。
(1)catch中的异常可以捕获本身及其子类异常,如果这个异常类无子类,则只能捕获该类异常。如Exception类时所有异常的父类,可以捕获下标越界异常,也可以捕获空引用异常;
(2)如果多个catch语句中的异常有派生的关系,则必须子类型在前,父类在后;(如果父类在前,异常都被它捕获,子类就没戏了)如果无甚派生关系,则位置随意。
4、断言。断言是辅助程序员检查逻辑性错误的(因为这种错误编译时是无法找到的),正常的程序运行时默认关闭的,DOS中用:
                        java -ea 类名 或者 java-enableassertions 类名
来在运行程序时开启断言,检查逻辑性错误。断言有以下形式:
                  assert <逻辑表达式> 或者 assert<逻辑表达式>:<提示信息>
如果逻辑为false,则抛出AssertionException异常,在使用第二种形式情况下会输出提示信息。注意这里的异常不是用来捕获的而是用来提示程序员哪里出现了逻辑错误。另外,断言是辅助工具,不能使用++、--等来影响程序运行状态。给个例子:
如果不启用断言,则打印“程序继续正常运行”,但如果要求参数i必须大于0,则程序是不能运行的。因为这是逻辑错误,编译时无法检测。若是整个程序很大,而这个myFunction()方法只是其中一部分,找出这个逻辑错误是件痛苦的事。而如果启用断言,结果为:
           Exception in thread "main" java.lang.AssertionError: 断言失败,数值i小于0,其值为i=-12
           at Test.myFunction(Test.java:6)
           at Test.main(Test.java:3)
就明确标出了逻辑错误的位置,并且给出逻辑错误的具体信息,方便修改。说到这里,可能有人想到可以用if判断i是否大于0,完全可以替代断言,话是这么说没错,但会加长代码的行数,亦降低程序可读性。
5、java每种基本数据类型都有其对应的封装类:注意的是,char对应的Character。除了char外,都可以用字符串来在创建封装了对象的时候作为参数,如Integer i=new Integer("456");Boolean flag=new Boolean("True")。这里Boolean特殊一点,里面的字符串字母是不区分大小写得,并且,只要所接受的字符串不是"true",都一律认为是false,而不会报错。
6、封装类对象一旦创建就不能变了,但可改变引用的指向达到改变对象值一样的效果,如:
                                      Integer i=new Integer(13);
                                      Integer i=new Integer(23);
这里的引用i改变了指向,达到了改变它的值的效果,但是两句所创建的对象的值均没有变化。另外,封装类时不能继承的,都是final型的类。 
7、下面提下封装类中的方法,因为方法很多,只能提下知道有这么一种方法,具体要查API。
I、首先是基本类型转为字符串的方法:
(1)非静态,toString(),所有封装类均有。
(2)静态,toString(),灵活一点,整形数据提供进制转换:toString(xxx,int a)将基本基本数据类型xxx转换为指定s进制的数,再用字符串表示。如果是2、8、16进制,方法更具体:toBinaryString(xxx);toOctalString(xxx);toHexString(xxx)。
II、字符串转化为基本数据类型:
(1)非静态,xxxValue()(如intValue()、floatValue())。这类方法应用于将封装类对象转化为基本数据类型,这种应用起来比较麻烦,要创建对象,再通过对象调用这个方法。如:
                              Integer integer=new Integer("52");
                              double d=integer.doubleValue();
(2)静态,parseXxx()(如Integer.parseInt(s),Double.parseDouble(s),提供进制转换(parseLong(String s,int a)),返回的是基本数据类型。提下的是Boolean.parseBoolean(s)中的s,只要s不是true(不区分大小写),都会返回false。
III、其他
静态工厂方法:valueOf(),也提供进制的转换(valueOf(String s,int a))。可以将任意类型的数据转换为别的类型,只不过这里返回的是对象的引用(不同于pasrseXxx()返回基本数据类型)。
以上各种方法有重合之处,特别是涉及字符串一块。主要的区别是,是否是通过类调用(即静态与非静态的区别)、返回的是基本数据类型还是封装类的引用。
8、前面提到,整型除以0,编译报错;浮点除以0,无穷;求某数对0的摸,为NaN。其中NaN是不能通过==或!=判断的,只能用封装类中的isNaN()来判断。isNaN()也分静态和非静态。用法:
                                            Float f=new Float(12%0);
                                            f.isNaN()==true;
                                            Double.isNaN(12%0)==true;
9、封装类的比较要用equals方法,因为普通的==和!=只能比较两个封装类的引用是否指向同一对象。equals是非静态方法。
10、语句int i1=10和Integer i2=new Integer(10);是有区别的,i1、i2是两个不同类型的东西。但是,我们却可以用语句i1+i2而不报错。因为编译器会自动帮程序员打包和解包。比如,如果要将i1和i2加起来,变成Integer类的对象,直接写成:
                                Integer i3=new Integer(i1+i2);
而不是:
                        Integer i3=new Integer(new Integer(i1)+i2);
就是说,编译器会帮我们加上这句话,将i1封装成Integer的对象。而运行起来,JRE是按照第二种情况运行的,即相当于某个人在程序员写完第一种代码后,写了后面那段隐形的代码,只有JRE能看到。另一种情况是,将i1、i2加起来,最后储存在int型数据i3中:
                                                  int i3=i1+i2;
实际上是:
                                           int i3=i1+i2.intValue();
这个过程是上面过程的逆过程,称自动解包,亦是编译器自动添上代码。
上面的自动解包打包其实是使基本数据类型与封装类之间的界限更加模糊了,但也更加方便了。虽然可以更方便的使用,但心里也应该存在明确的分界线,毕竟是两不同的东西。
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值