面对对象第九天

---------------------- android培训java培训、期待与您交流! ----------------------


(一)内部类访问规则

定义:将一个类定义在另一个类的里面,对里面那个类就称为内部类(内置类,嵌套类)。

特点:

1.因为要访问一个类的成员需要创建这个类的对象,但是内部类不需要创建对象,即可直接访问外部类的成员,而且还能访问外部类中的私有成员,由于私有成员只在本类中能被访问,而内部类就是在本类中,所以也能访问私有成员.

2.外部类要访问内部类的成员,需要建立内部类的对象.

3.当内部类定义在外部类的成员位置上时,其他类要访问这个内部类,则需要在创建对象的时候声明是创建的哪个外部类的内部类,因为多个类的内部类可以同名,所以在创建对象的时候要声明.

其格式是:外部类名.内部类名 变量名=new 外部类名().new 内部类名();

4.由于通常情况下不会出现其他类来访问另一个类的内部类,所以当这个内部类定义在成员位置上时,这个内部类就可以被成员修饰符修饰,比如private,static.当被private修饰的时候,其实就是将内部类在外部类中进行封装,当被static修饰的时候,就只能访问外部类的静态成员了,所以说类是可以被私有的,比如内部类.

class Outer
{
    private int x=3;
    class Inner
    {
        void function()
        {
            System.out.println("inner:"+x);
        }
    }
    void method()
    {
        Inner in=new Inner();
        in.function();
    }
}

class InnerClassDemo
{
    public static void main(String[] args)
    {
        Outer out=new Outer();
        out.method();
        Outer.Inner in=new Outer().new Inner();//注意格式,不过一般是不会这样去访问内部类的.
        in.function();
    }
}

注意:当外部类成员变量,内部类成员变量,内部类中某个函数的局部变量都同名的时候,假如是内部类中的这个函数要访问这个变量,则是访问的是局部变量,因为局部有的,就访问局部的,假如要访问内部类的成员变量,则加上this声明,假如要访问外部类的成员变量,除了要声明this以外,还要声明外部类名.比如:

class Outer
{
    int x=3;
    class Inner
    {
        int x=4;
        void function()
        {
            int x=5;
            System.out.println("inner:"+x);//这个是访问内部类中的局部变量.是5
        }
    }
}

--------------------------------------------------------------------------------------------------

class Outer
{
    int x=3;
    class Inner
    {
        int x=4;
        void function()
        {
            int y=5;
            System.out.println("inner:"+x);//这个是访问内部类中的成员变量.是4
        }
    }
}

--------------------------------------------------------------------------------------------------

class Outer
{
    int x=3;
    class Inner
    {
        int x=4;
        void function()
        {
            int x=5;
            System.out.println("inner:"+this.x);//这个也是访问内部类中的成员变量.是4
        }
    }
}

--------------------------------------------------------------------------------------------------

class Outer
{
    int x=3;
    class Inner
    {
        int x=4;
        void function()
        {
            int x=5;
            System.out.println("inner:"+Outer.this.x);//这个是访问外部类中的成员变量.是3
        }
    }
}

--------------------------------------------------------------------------------------------------

class Outer
{
    int x=3;
    class Inner
    {
        void function()
        {
            System.out.println("inner:"+x);//这个是访问外部类中的成员变量.是3,所以此时x前面其实是省略了Outer.this.
        }
    }
}

所以内部类之所以能直接访问外部类中的成员,是由于内部类中持有一个外部类的引用,即外部类名.this.


  (二)内部类访问规则

当内部类定义在成员位置上时,可以加static,但是此时内部类就只能访问外部类中的静态成员了.当内部类被静态修饰以后,其他类如何来访问这个内部类中的非静态成员,格式是:

new 外部类名.内部类名().内部类中的某个函数,比如:

class Outer
{
    private static int x=3;
    static class Inner//此时内部类已经加了静态
    {
        void function()//内部类的非静态成员
        {
            System.out.println("inner:"+x);
        }
    }
    void method()
    {
        Inner in=new Inner();
        in.function();
    }
}

class InnerClassDemo
{
    public static void main(String[] args)
    {
        Outer out=new Outer();
        out.method();
        new Outer.Inner().function();//要直接访问静态内部类成员的格式,由于Inner已经是静态了,所以可以被类名调用,所以是Outer.Inner()
    }
}

2.其他类如何访问另一个类中的静态内部类的静态成员

格式是:外部类名.内部类名.内部类中的成员,比如,将function前面加上static,然后在主函数调用的时候这样写即可:

Outer.Inner.function();

小结:

1.访问非静态内部类中的成员:

Outer.Inner in=new Outer().new Inner();
        in.function();

2.访问静态内部类中的非静态成员:

new Outer.Inner().function();

3.访问静态内部类中的静态成员:

Outer.Inner.function();

注意:

当内部类中定义了静态成员,内部类必须是静态的.

当外部类中的静态方法访问内部类中成员的时候,内部类必须是静态的,因为静态方法只能访问静态成员.


  (三)内部类定义原则

当描述事物的时候,事物内部还有事物,该事物用内部类来描述,因为内部事务在描述外部事物.


  (四)匿名内部类

内部类除了可以定义在成员位置,还能定义在局部位置,局部位置的内部类前面就不能加静态和私有修饰符了,由于当内部类中定义了静态成员,内部类必须是静态的,所以此时内部类中还不能定义静态成员,否则内部类也要是静态的了,但局部位置的内部类是不能静态的.所以,局部位置的内部类不管是类还是类里面的成员都不能是静态.

class Outer
{
    int x=3;
    void method()
    {
        class Inner
        {
            void function()
            {
                System.out.println(x);
            }
        }
        new Inner().function();//外部类要访问内部类必然要创建内部类对象
    }

}
class InnerClassDemo
{
    public static void main(String[] args)
    {
        new Outer().method();
    }
}

注意:当内部类定义在局部位置,比如定义在外部类某个方法中,内部类要访问这个方法中的某个局部变量的时候,这个局部变量必须是最终类型.

class Outer
{
    int x=3;
    void method()
    {
        final int x=4;//注意加上final
        class Inner
        {
            void function()
            {
                System.out.println(x);
            }
        }
        new Inner().function();
    }
}

或者是

class Outer
{
    int x=3;
    void method(final int x)
    {
        class Inner
        {
            void function()
            {
                System.out.println(x);
            }
        }
        new Inner().function();
    }

}
class InnerClassDemo
{
    public static void main(String[] args)
    {
        Outer out=new Outer();
       out.method(1);
        out.method(2);
    }
}

注意:上面例子中,虽然x是被final修饰,但是当创建一个Outer类的对象,调用method方法之后,向x传值1,此时当method方法在栈内存中执行完一次,就被释放了,所以x的值就不存在了,尽管是被final修饰,仍然是可以再次被赋值的.所以在主函数调用的时候这样写out.method(1);out.method(2);是没问题的.


定义:

匿名内部类没有类名,其实就是内部类的简写格式.

前提:

匿名内部类必须是继承一个类或者是实现一个接口.一个类的内部类可以继承或者实现另一个类或者接口.比如:

abstract class AbsDemo
{
    abstract void show();
}
class Outer
{
    int x=3;
    class Inner extends AbsDemo
    {
        void show()
        {
            System.out.println(x);
        }
    }
    public void function()
    {
        new Inner().show();
    }    
}
class InnerClassDemo
{
    public static void main(String[] args)
    {
        new Outer().function();
    }
}

如果要不写内部类名,用匿名内部类的方式写则这样写:

格式是:new  父类或者接口名(){匿名内部类中那个覆盖的方法}.覆盖的方法名()

abstract class AbsDemo
{
    abstract void show();
}
class Outer
{
    int x=3;
    public void function()
    {
        new AbsDemo()//注意格式,其实这个里面的方法就是那个内部类中的方法,和父类同名的就覆盖掉,不同名的就是自己特有的.
        {
             void show()
            {
                System.out.println(x);
            }
            void haha()
            {
                System.out.println("haha");
            }
        }.show();//调用haha()也行.
    }    
}
class InnerClassDemo
{
    public static void main(String[] args)
    {
        new Outer().function();
    }
}

其实匿名内部类就是一个匿名子类对象.也可以理解为带内容的对象.什么时候用匿名内部类,匿名只是为了简化代码,当父类方法一般不超过三个的时候,可以使用匿名内部类,如果父类方法很多,那全部覆盖之后阅读性就很差.


练习:

interface Inter
{
    void method();
}
class Test
{
    //补足代码,通过匿名内部类.
}
class InnerClassDemo
{
    public static void main(String[] args)
    {
        Test.function().method();
    }
}

分析:Test.function(),这是类名调用方法,那么Test类中肯定有一个function(),但是返回类型不知道,参数列表是空参数,调用了function()之后再点上一个method(),说明在function()运算完之后一定产生了一个对象,对象才能调用method().所以function()的返回类型一定是类类型,又由于Inter接口中定义了method()方法,所以这个类类型一定是Inter,所以function()的返回类型为Inter,那么用内部类的形式写就是:

interface Inter
{
    void method();
}
class Test
{
    static class Inner implements Inter
    {
        public void method()
        {
            System.out.println("method run");
        }
    }
    static Inter function()
    {
        return new Inner();
    }
}
class InnerClassDemo
{
    public static void main(String[] args)
    {
        Test.function().method();
    }
}

换成匿名内部类就是:

interface Inter
{
    void method();
}
class Test
{    
    static Inter function()
    {
        return new Inter()
        {
            public void method()
            {
                System.out.println("method run");
            }
        };
    }
}
class InnerClassDemo
{
    public static void main(String[] args)
    {
        Test.function().method();
    }
}

其实Test.function().method();这句话就是

Inter i=Test.function();
        i.method();


什么时候使用匿名内部类呢?

比如:我要调用show()方法,而show()方法的参数类型是接口类型的,那么一般步骤是定义一个类去实现接口,然后往show()方法中传入接口类型的对象.

interface Inter
{
    void method();
}
class Test implements Inter
{
    public void method()// 注意别漏掉了public
    {
        System.out.println("method run");
    }
}
class InnerClassDemo
{
    public static void main(String[] args)
    {
        show(new Test());
    }
    public static void show(Inter i)
    {
        i.method();
    }
}

假如接口Inter里面的方法三个以内,那么可以简化代码,向show()里面传入匿名内部类,这样就简化了代码,省得再定义新的类.

interface Inter
{
    void method();
}
class InnerClassDemo
{
    public static void main(String[] args)
    {
        show(new Inter()
        {
            public void method()//注意别漏掉了public
            {
                System.out.println("method run");
            }
        });
    }
    public static void show(Inter i)
    {
        i.method();
    }
}

所以说匿名内部类其实就是父类的子类对象,只是说这个对象带上了子类中要用到的那个方法.省去了重新定义类的繁琐.


注意:虽然匿名内部类必须要用到父类,假如不定义父类或者接口,还是能写匿名内部类,因为任何类的父类都是Object,比如:

class InnerClassDemo
{
    public static void main(String[] args)
    {
        new Object()
        {
            public void function()
            {
                System.out.println("method run");
            }
        }.function();
    }    
}

  (五)异常概述

1.异常:就是程序在运行时出现的不正常情况.

2.异常由来:问题也是现实生活中的一个具体事物,也可以通过java的类来进行描述,并封装成对象.其实异常就是java对不正常情况进行描述后的对象体现,

3.java对于问题的划分分为两种:一种是严重的问题,一种是非严重的问题.对于严重问题java通过Error类进行描述,非严重问题,java通过Exception类进行描述,Exceptionj就是异常.

4.对于Error一般不编写针对性的代码对其进行处理,而对于Exception可以编写针对性代码进行处理.

5.无论Error还是Exception都有一些共性内容.比如:不正常情况的信息,引发原因等.将两个类的共性向上抽取就形成了父类Throwable.


  (六)异常try-catch

对于异常,java提供了特有的语句进行处理:

try
{
    需要被检测的代码;
}
catch ()
{
    处理异常的代码;(处理方式)
}
catch(异常类 变量){}
finally
{
    一定会执行的语句;
}

比如:

class Demo
{
    int div(int x,int y)
    {
        return x/y;
    }
}
class ExceptionDemo
{
    public static void main(String[] args)
    {
        System.out.println("A");
        Demo d=new Demo();
        int x=d.div(4,0);
        System.out.println("x:"+x);
        System.out.println("over"); 
    }    
}

java对于异常的默认处理是当执行到异常语句的时候就停止程序的运行了.并显示异常信息和一场位置,所以结果只会打印A,后面的就不会执行了


现在给要出错的语句加上try-catch语句

class Demo
{
    int div(int x,int y)
    {
        return x/y;
    }
}
class ExceptionDemo
{
    public static void main(String[] args)
    {
        System.out.println("A");
        Demo d=new Demo();
        try
        {
            int x=d.div(4,0);

           //注意这句也要写在try里面,因为x是定义在try里面的,只会在try这个代码块中起作用,所以要打印x的语句也要写在try里面,要是卸载try代码块外面,会提示找不到符号。
            System.out.println("x:"+x);
        }
        catch(Exception e)
        {
            System.out.println("除零了");                
        }
        System.out.println("over");        
    }    
}


  对于捕获到的异常对象进行常见的方法操作,

class Demo
{
    int div(int x,int y)
    {
        return x/y;
    }
}
class ExceptionDemo
{
    public static void main(String[] args)
    {
        System.out.println("A");
        Demo d=new Demo();
        try
        {
            int x=d.div(4,0);
            System.out.println("x:"+x);
        }
        catch(Exception e)
        {
            System.out.println("除零了");
            System.out.println(e.getMessage());//结果显示/ by zero
            System.out.println(e.toString());//结果显示java.lang.ArithmeticException: / by zero,不仅带着异常信息,还有异常类名
            e.printStackTrace();//打印异常类名,异常信息,异常出现的位置,其实jvm默认的异常处理机制,就是在调用printStackTrace()方法,打印异常在堆栈的跟踪信息.
        }
        System.out.println("over");        
    }    

}

小结:从api文档可以看出,java.lang里面有Throwable,接着是Exception,RuntimeException,ArithmeticException,之所以上面catch里面Exception类型的引用e能使用getMessage(),toString(),printStackTrace(),是因为java在Throwable里面已经定义了上述三种方法,所以其子类对象能直接调用。需要注意java默认的异常显示内容和printStackTrace()还是有区别的:

默认的结果:

Exception in thread "main" java.lang.ArithmeticException: / by zero
        at Demo.div(01.java:5)
        at ExceptionDemo.main(01.java:13)

printStackTrace()的结果:

java.lang.ArithmeticException: / by zero
        at Demo.div(01.java:5)
        at ExceptionDemo.main(01.java:13)


  (七)异常声明throws

上面的例子中,如果开发者觉得某些功能可能会出现问题,希望调用者检测下代码,此时需要在开发的时候声明,即在需要被检测的方法后面加上throws Exception声明该方法可能会出现问题.

class Demo
{
    int div(int x,int y)throws Exception
    {
        return x/y;
    }
}
class ExceptionDemo
{
    public static void main(String[] args)
    {
        Demo d=new Demo();
        int x=d.div(4,1);
        System.out.println("x:"+x);
        System.out.println("over");        
    }    
}

此时在编译的时候就会出错,提示"未报告的异常错误Exception; 必须对其进行捕获或声明以便抛出",意思就是调用者调用声明异常的方法必须要要进行try-catch处理,不然就不能调用,所以在编辑的时候就会报错.但是有两种处理方式,捕获或者声明,捕获就是用try-catch处理,声明就是在方法后面(这里是主函数)声明throws Exception.

比如:

1.声明的处理方式:

class Demo
{
    int div(int x,int y)throws Exception
    {
        return x/y;
    }
}
class ExceptionDemo
{

    //此时在调用的时候也声明了,由于已经抛给了主函数,不能再抛了,所以此时交由jvm来处理,所以编译能通过,运行的时候就会调用printStackTrace()报错了。

    //可以多次抛,但最后都是抛给了jvm。

    public static void main(String[] args)throws Exception
    {
        Demo d=new Demo();
        int x=d.div(4,0);
        System.out.println("x:"+x);
        System.out.println("over");        
    }    
}

当主函数也就是调用者也声明之后,由于没有捕获,没有处理方式,那么jvm就会按照默认的处理方式来处理,也就是在异常处停止运行,并提示错误。所以打印over就不会执行了。

2.捕获的处理方式:

class Demo
{
    int div(int x,int y)throws Exception
    {
        return x/y;
    }
}
class ExceptionDemo
{
    public static void main(String[] args)
    {
        Demo d=new Demo();
        try
        {
            int x=d.div(4,0);
            System.out.println("x:"+x);
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        System.out.println("over");        
    }    
}

由于有了捕获,有了处理方式,所以会执行打印over。

注意:在捕获的时候,同时也可以声明的,比如上面的代码可以这样写:

class Demo
{
    int div(int x,int y)throws Exception
    {
        return x/y;
    }
}
class ExceptionDemo
{
    public static void main(String[] args)throws Exception//注意这里多了throws
    {
        Demo d=new Demo();
        try
        {
            int x=d.div(4,0);
            System.out.println("x:"+x);
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        System.out.println("over");        
    }    
}

即使这个函数声明了,但是有响应的处理方式,还是会执行try-catch。


  (八)多异常处理

1.上面在声明的时候用的是throws Exception,不是很具体,最好是声明具体的异常,方便处理,所以改成throws ArithmeticException比较好,这样在处理的时候就按照算术异常来处理.

2.在声明异常的时候假如一个方法里面有多个异常,可以同时声明多个异常.比如:

这里既有角标越界又有除零异常.当然在处理的时候也要写分别写各自的处理方案,那么当遇到什么异常就会执行什么异常,但是注意,这些异常不会同时发生,因为当遇到某一步异常就会被try检测到然后运行对应的catch捕获,也就是不会同时提示角标越界和除零.只会提示其中某一个.

class Demo
{
    int div(int x,int y)throws ArithmeticException,ArrayIndexOutOfBoundsException
    {
        int[] arr=new int[x];
        System.out.println(arr[4]);
        return x/y;
    }
}
class ExceptionDemo
{
    public static void main(String[] args)
    {
        Demo d=new Demo();
        try
        {
            int x=d.div(4,0);
            System.out.println("x:"+x);
        }
        catch(ArithmeticException a)
        {
            System.out.println(a.toString());
            System.out.println("除零了");
        }
        catch(ArrayIndexOutOfBoundsException b)
        {
            System.out.println(b.toString());
            System.out.println("角标越界了");
        }
        System.out.println("over");        
    }    
}

当然在catch的时候可以只写一个catch,也就是

catch(Exception e)
        {
            System.out.println(e.toString());
        }

结果同样都是哪步出错提示哪步,但是这样写没有针对性,标准写法还是开发者抛出几个异常就写几个catch,不要写多余的catch.

注意:

1.如果多个catch出现了继承关系,那么父类catch写在最下面,比如:

    catch(ArithmeticException a)
        {
            System.out.println(a.toString());
            System.out.println("除零了");
        }
        catch(ArrayIndexOutOfBoundsException b)
        {
            System.out.println(b.toString());
            System.out.println("角标越界了");
        }
        catch(Exception e)
        {
            System.out.println(e.toString());
        }

2.如果出现了开发者没有声明的异常,需要立即停止程序运行排错,或者添加声明.

3.写catch的时候应该写明具体的处理方式,而不要简单的toString()或者写输出语句,比如"出错了",但是可以把错误记录并保存到日志.


  (九)自定义异常

小结:在java中,对于已经定义好的异常,不需要thy-catch,java会按照默认的方式,比如除零异常,在运行的时候,java会默认报错Exception in thread "main" java.lang.ArithmeticException: / by zero,因为java对已经定义的异常会自动创建对象并且抛出捕获,如果自己写了try-cathch,就会按照catch里面的方式对异常进行处理。但是对于没有定义好的异常,那么就需要自定义异常类。

举例:

小知识:java在编译的时候先报语法错误,最后才会报异常错误,所以如果在编译的时候看到提示未报告的异常,那么说明代码编写排错已经接近尾声.

需求:在上面的除法例子中,除了除零是异常以外,除以负数也是异常.

注意:那么此时就需要自定义除以负数的异常类了.但是java已经定义好的异常类在调用的时候是自动创建对象并抛给catch的,而自定义的异常类就需要自己手动创建对象并抛出.如果用throw抛出了异常对象,那么不仅要在throw所在函数用throws声明异常类名,而且还要在调用的时候进行处理,要么声明要么捕获..但是有一个特例,就是如果异常类是RuntimeException的子类,则throw了之后就不需要再throws抛出了.

class FuShuException extends Exception
{}
class Demo
{
    int div(int x,int y)throws FuShuException
    {
        if(y<0)
            throw new FuShuException();//手动通过throw关键字抛出一个自定义异常对象.
        return x/y;
    }
}
class ExceptionDemo
{
    public static void main(String[] args)
    {
        Demo d=new Demo();
        try
        {
            int x=d.div(4,-1);
            System.out.println("x:"+x);
        }
        catch(FuShuException f)//这里就相当于FuShuException f=new FuShuException
        {
            System.out.println(f.toString());
            System.out.println("除以负数了");
        }
        System.out.println("over");        
    }    
}

但是此时发现,结果只提示了异常类名,并没有异常信息,因为自定义的异常类中没有定义内容,所以toString()没有可调用的getMessage()方法,如何定义异常信息.可以先这样写:

class FuShuException extends Exception
{
    private String msg;
    FuShuException(String msg)
    {
        this.msg=msg;
    }
    public String getMessage()
    {
        return msg;
    }
}
class Demo
{
    int div(int x,int y)throws FuShuException
    {
        if(y<0)
            throw new FuShuException("/by fushu");//这里定义异常信息
        return x/y;
    }
}
class ExceptionDemo
{
    public static void main(String[] args)
    {
        Demo d=new Demo();
        try
        {
            int x=d.div(4,-1);
            System.out.println("x:"+x);
        }
        catch(FuShuException f)
        {
            System.out.println(f.toString());//toString()调用了getMessage()方法
            System.out.println("除以负数了");
        }
        System.out.println("over");        
    }    
}


先补充个小知识:

子类可以继承父类的方法和变量,但是有private,final 关键字声明的变量或者方法,则不可以被子类所继承。

class Person//一般是给对象某个属性赋值,然后再获取这个属性
{
    String name;
    Person(String name)
    {
        this.name=name;
    }
    public String getName()
    {
        return name;
    }
}
class Student extends Person
{
    Student(String name)
    {
        super(name);//子类构造函数第一行必然有super,要么是空参数,要么是指定的
    }
}
class Demo
{
    public static void main(String[] agrs)
    {
        System.out.println(new Student("zhangsan").getName());
    }
}

当执行super(name)的时候,"zhangsan"传给了Person类的成员变量name.Student类在调用父类方法getName时其实返回的还是Person类的成员变量name.由于继承关系,子类要继承父类所有的除了私有的变量和方法,所以父类的成员变量name被赋值"zhangsan",那么子类也有这个成员变量name,且值为"张三",所以创建的Student对象也有name属性且值为"zhangsan".

把上面稍微改一下就明白了:

class Person
{
    String name;
    Person(String name)
    {
        this.name=name;
    }
    public String getName()
    {
        return name;
    }
}
class Student extends Person
{

//这里可以写name也可以写n,都是个参数而已."zhangsan"传给n再传给了Person(String name)里面的name,再传给Person的成员变量name.
    Student(String n)

   {
        super(n);
    }
}
class Demo
{
    public static void main(String[] agrs)
    {
        Student s=new Student("sssss");
        System.out.println(s.name);//由于继承关系,Student也具有成员变量name且值为sssss,所以s对象的name也是sssss.
    }
}

上面例子演示了当父类中把方法定义好了,包括方法体也写好了之后,子类要使用同样方法,如果是使用父类构造方法,可以直接用super调用,如果是一般方法,直接调用就是.

小结:this能表示什么:

1.单独使用表示对当前对象的引用.

2.this.用于在成员变量和局部变量同名时区分,表示所在类成员变量.

3.用于在构造函数中调用其他满足参数列表的构造函数.其实就是表示其他满足类型的构造函数.只能用于构造函数间调用,只能调用一个并且写在构造函数第一行.

注意this不能用在static方法中.


回到上面定义异常类,由于FuShuException的父类Exception里面已经定义好了赋值和获取值的方法体,所以子类在定义相同方法的时候直接调用父类的方法即可.

所以把这段代码

class FuShuException extends Exception
{
    private String msg;
    FuShuException(String msg)
    {
        this.msg=msg;
    }
    public String getMessage()
    {
        return msg;
    }
}

改成

class FuShuException extends Exception
{
    FuShuException(String msg)
    {
        super(msg);
    }
}

所以说在自定义异常类的时候,只需把自定义类中的构造方法通过super语句传递异常信息给父类,就能通过getMessage方法来获取自定义信息了.


假如说在自定义的异常类里面除了定义如何获取异常信息,我还要获取出现异常的那个值是多少,比如我想知道是除以了哪个负数.也就是在抛出异常的时候还要抛出哪个出问题的负数.:

class FuShuException extends Exception
{
    private int value;
    FuShuException(String msg,int value)//在赋值的时候同时定义了异常信息和那个出问题的数值
    {
        super(msg);
        this.value=value;
    }
    public int getValue()//定义获取数值的方法
    {
        return value;
    }
}
class Demo
{
    int div(int x,int y)throws FuShuException
    {
        if(y<0)
            throw new FuShuException("/by fushu",y);//要抛出异常信息和那个数值
        return x/y;
    }
}
class ExceptionDemo
{
    public static void main(String[] args)
    {
        Demo d=new Demo();
        try
        {
            int x=d.div(4,-1);
            System.out.println("x:"+x);
        }
        catch(FuShuException f)
        {
            System.out.println(f.toString());
            System.out.println("负数是"+f.getValue());//调用获取数值的方法
        }
        System.out.println("over");        
    }    
}

注意:

1.抛出异常类对象的时候是throw,抛出异常类的时候是throws.

2.自定义异常类必须继承Exception.当然后面会讲到RuntimeException,那个时候自定义异常类就不是继承Exception而是继承RuntimeException了.

3.继承Exception的原因:由于异常类和异常类对象都具有可抛性,这是Throwable体系独有的特点,只有这个体系中的类和对象才能够被抛出.为了准确性当然要继承Exception了,当然属于Error的就要继承Error了,只有继承了才能被抛出.


  (十)throw和throws的区别

区别:

throws抛出异常类,后面跟的是异常类名,多个用逗号隔开.

throw抛出异常类对象.


  (十一)RuntimeException

上面的算术异常信息是/by zero,看起来似乎不爽,如果我想自定义这个算术异常信息.则这样写:

class Demo
{
    int div(int x,int y)
    {
        if(y==0)
            throw new ArithmeticException("除数是零");
        return x/y;
    }
}
class ExceptionDemo
{
    public static void main(String[] args)
    {
        Demo d=new Demo();
        int x=d.div(4,0);
        System.out.println("x:"+x);        
        System.out.println("over");        
    }    
}

注意:在上面throw了ArithmeticException对象之后,并没有在div函数后面用throws声明,调用的时候也没有try-catch处理,原因是:在Exception中有一个特殊的子类异常RuntimeException,叫运行时异常,如果在函数内用throw抛出了这个异常类或者子类的对象,那么不需要在函数后面throws声明,另一种情况是,如果在函数后面声明了这个异常类名或者是子类名,也不需要在调用的时候进行声明或者捕获的处理了,编译都是能通过的.

之所以不需要声明,因为一旦声明了,就需要调用者处理,但是这类异常通常会导致后面的程序无法运行,所以就不需要调用者来处理,而是要立即停止,希望开发者修复.

所以说如果一个异常将会导致程序无法在运行,需要开发者修改而不是调用者处理,在定义这种异常类的时候,就让其继承RuntimeException类.比如上面的除以负数的例子,现在不需要调用者修改,那么就这样写:

class FuShuException extends RuntimeException//注意不在是继承Exception,而是RuntimeException
{
    FuShuException(String msg)
    {
        super(msg);
    }
}
class Demo//没有throws抛出
{
    int div(int x,int y)
    {
        if(y<0)
            throw new FuShuException("除以了负数");
        return x/y;
    }
}
class ExceptionDemo
{
    public static void main(String[] args)
    {
        Demo d=new Demo();
        int x=d.div(4,-1);
        System.out.println("x:"+x);        
        System.out.println("over");        
    }    
}

小结:对于异常分为两种:

1.编译时被检测的异常.也就是编译的时候会检测是否有throws抛出,是否有try-catch捕获.

2.编译时不被检测的异常,就是运行时异常,也就是RuntimeExcption及其子类.



  (十二)异常练习

小结:当函数内部有异常类对象被throw抛出,那么在这个函数后面就得throws抛出那个对象所属的异常类.如果有其他类创建了这个类的对象并调用了这个被throws声明的函数,那么在调用的时候就得try-catch.

/*
需求:毕老师用电脑上课.
开始思考上课中出现的问题.
比如问题是:
电脑蓝屏,
电脑冒烟.
要对问题进行描述,因为要封装成对象.
蓝屏能处理,继承Exceotion.
冒烟不能处理,继承RuntimeException.但是这里先让冒烟也继承Exception
异常对象要建立在电脑的run()里面,情况有三种,一种是电脑运行正常,另外两种是蓝屏和冒烟,所以再定义一个值来表示不同的异常情况.
*/

class LanPingException extends Exception
{
    LanPingException(String l)
    {
        super(l);
    }
}
class MaoYanException extends Exception
{
    MaoYanException(String m)
    {
        super(m);
    }
}
class Computer
{
    private int state=3;//用1表示运行正常,2表示蓝屏,3表示冒烟
    public void run()throws LanPingException,MaoYanException
    {
        if(state==2)
            throw new LanPingException("电脑蓝屏了");
        if(state==3)
            throw new MaoYanException("电脑冒烟了");
        System.out.println("电脑运行");
    }
    public void reset()
    {
        System.out.println("电脑重启");
        state=1;
    }
}
class Teacher
{
    private String name;
    private Computer cmpt;
    Teacher(String name)
    {
        this.name=name;
        cmpt =new Computer();
    }
    public void prelect()throws MaoYanException//由于函数里面有用throw抛出对象,所以在函数后面要声明被抛出的对象所属类名,既然这里被抛出,那么在调用这个函数的时候就得try-catch
    {
        try
        {
            cmpt.run();
        }
        catch(LanPingException l)
        {
            cmpt.reset();
        }
        catch(MaoYanException m)
        {
            throw m;//由于冒烟调用者无法处理,所以就将这个问题继续抛出,对于catch处理不了的问题就把这个异常类对象再抛出去
        }
        System.out.println("讲课");
    }
}
class ExceptionTest
{
    public static void main(String[] args)
    {
        Teacher t=new Teacher("毕老师");
        try
        {
            t.prelect();
        }
        catch(MaoYanException m)
        {
               System.out.println(m.toString()+"毕老师处理不了");
        }
    }
}

注意:上面代码里面 ,MaoYanException被抛了两次,在抛两次之后接收的时候仍然写MaoYanException m

抛第一次:throw new MaoYanException("电脑冒烟了");

抛第二次:catch(MaoYanException m)
        {
            throw m;
        }

接收:catch(MaoYanException m)
        {
               System.out.println(m.toString()+"毕老师处理不了");
        }

新的问题就是,毕老师不能处理电脑冒烟,所以catch这里该如何写呢?而且电脑冒烟产生了新的后果就是毕老师的课时计划无法完成.所以还要定义新的异常类,就是课时计划无法完成,这个异常是能够处理的,所以继承Exception.并且在 catch(MaoYanException m)里面抛出的不再是m,而是课时计划无法完成的对象,并且在prelect()方法后面相应地也要抛出课时无法完成的类名.既然抛出的课时无法完成,而不是冒烟,那么毕老师就能处理了,所以在catch里面就能写东西了.

class LanPingException extends Exception
{
    LanPingException(String l)
    {
        super(l);
    }
}
class MaoYanException extends Exception
{
    MaoYanException(String m)
    {
        super(m);
    }
}
class NoPlanException extends Exception
{
    NoPlanException(String n)
    {
        super(n);
    }
}
class Computer
{
    private int state=3;
    public void run()throws LanPingException,MaoYanException
    {
        if(state==2)
            throw new LanPingException("电脑蓝屏了");
        if(state==3)
            throw new MaoYanException("电脑冒烟了");
        System.out.println("电脑运行");
    }
    public void reset()
    {
        System.out.println("电脑重启");
        state=1;
    }
}
class Teacher
{
    private String name;
    private Computer cmpt;
    Teacher(String name)
    {
        this.name=name;
        cmpt =new Computer();
    }
    public void prelect()throws NoPlanException
    {
        try
        {
            cmpt.run();
        }
        catch(LanPingException l)
        {
            cmpt.reset();
        }
        catch(MaoYanException m)
        {
            throw new NoPlanException("课时无法继续");
        }
        System.out.println("讲课");
    }
}
class ExceptionTest
{
    public static void main(String[] args)
    {
        Teacher t=new Teacher("毕老师");
        try
        {
            t.prelect();
        }
        catch(NoPlanException n)
        {
            System.out.println("补课加时间");
        }
    }
}

另一种情况是,假如Teacher还有一个test()方法,暂且叫练习方法,我想在throw new NoPlanException("课时无法继续");也就是抛出之后加上这个练习方法,编译会报错,提示找不到符号,原因是在java中除了return表示程序结束之外,throw也能表示程序结束,所以会提示找不到test(),所以要把test()放在throw new NoPlanException("课时无法继续");的前面就行,比如:注意红色改动的部分,

class LanPingException extends Exception
{
    LanPingException(String l)
    {
        super(l);
    }
}
class MaoYanException extends Exception
{
    MaoYanException(String m)
    {
        super(m);
    }
}
class NoPlanException extends Exception
{
    NoPlanException(String n)
    {
        super(n);
    }
}
class Computer
{
    private int state=3;
    public void run()throws LanPingException,MaoYanException
    {
        if(state==2)
            throw new LanPingException("电脑蓝屏了");
        if(state==3)
            throw new MaoYanException("电脑冒烟了");
        System.out.println("电脑运行");
    }
    public void reset()
    {
        System.out.println("电脑重启");
        state=1;
    }
}
class Teacher
{
    private String name;
    private Computer cmpt;
    Teacher(String name)
    {
        this.name=name;
        cmpt =new Computer();
    }
    public void prelect()throws NoPlanException
    {
        try
        {
            cmpt.run();
        }
        catch(LanPingException l)
        {
            cmpt.reset();
        }
        catch(MaoYanException m)
        {
            throw new NoPlanException("课时无法继续");
            test();
        }
        System.out.println("讲课");
    }
    public void test()
    {
        System.out.println("练习");
    }

}
class ExceptionTest
{
    public static void main(String[] args)
    {
        Teacher t=new Teacher("毕老师");
        try
        {
            t.prelect();
        }
        catch(NoPlanException n)
        {
            System.out.println("补课加时间");
        }
    }
}

另外,我想打印toString(),看看NoPlanException异常类的异常信息,也就是"课时无法继续",在创建对象的时候,throw new NoPlanException("课时无法继续");尽管是传入字符串,我还可以传入多个字符串,中间有加号连接,比如:throw new NoPlanException("课时无法继续"+"kkk");这样是可以的,另外在这段代码中有MaoYanException的对象引用m

catch(MaoYanException m)
        {
            throw new NoPlanException("课时无法继续");
            test();
        }

所以,可以调用getMessage()返回MaoYanException类中的异常信息,也就是返回throw new LanPingException("电脑蓝屏了");里面的"电脑蓝屏了",所以可以再把代码改成:

throw new NoPlanException("课时无法继续"+m.getMessage());改完之后在

catch(NoPlanException n)
        {
            System.out.println("补课加时间");
        }

就要相应的使用toString()来返回异常类名和异常信息了,注意红色改动部分:

class LanPingException extends Exception
{
    LanPingException(String l)
    {
        super(l);
    }
}
class MaoYanException extends Exception
{
    MaoYanException(String m)
    {
        super(m);
    }
}
class NoPlanException extends Exception
{
    NoPlanException(String n)
    {
        super(n);
    }
}
class Computer
{
    private int state=3;
    public void run()throws LanPingException,MaoYanException
    {
        if(state==2)
            throw new LanPingException("电脑蓝屏了");
        if(state==3)
            throw new MaoYanException("电脑冒烟了");
        System.out.println("电脑运行");
    }
    public void reset()
    {
        System.out.println("电脑重启");
        state=1;
    }
}
class Teacher
{
    private String name;
    private Computer cmpt;
    Teacher(String name)
    {
        this.name=name;
        cmpt =new Computer();
    }
    public void prelect()throws NoPlanException
    {
        try
        {
            cmpt.run();
        }
        catch(LanPingException l)
        {
            cmpt.reset();
        }
        catch(MaoYanException m)
        {
            test();
            throw new NoPlanException("课时无法继续"+m.getMessage());
        }
        System.out.println("讲课");
    }
    public void test()
    {
        System.out.println("练习");
    }
}
class ExceptionTest
{
    public static void main(String[] args)
    {
        Teacher t=new Teacher("毕老师");
        try
        {
            t.prelect();
        }
        catch(NoPlanException n)
        {
            System.out.println(n.toString());
            System.out.println("补课加时间");
        }
    }
}


---------------------- android培训java培训、期待与您交流! ----------------------详细请查看:http://edu.csdn.net/heima

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值