菜单
本页目录
第4单元   面向对象(下)

4.1   类的继承

       在程序中,继承描述的是事物之间的所属关系,通过继承可以使多种事物之间形成一种关系体系。

4.1.1   继承的概念

定义:在现有类(父类、基类)的基础上去构建一个新的类,构建出来的新类(子类,派生类)

作用:(1)自动继承父类(所有)成员变量和成员方法
          (2)扩展(定义)子类本身的成员变量和成员方法

类的继承语法:extends关键字
class 父类{
    .......
}
class 子类  extends 父类{
    ......
}


例子4-1: 子类自动继承父类成员变量和成员方法
class Animal {                                                   // 定义Animal类
        private String name;         			
        private int age;             			
        public String getName() {
               return name;
        }
        public void setName(String name) {
               this.name = name;
        }
        public int getAge() {
               return age;
        }
        public void setAge(int age) {
               this.age = age;
        }
}

class Dog extends Animal {                              // 定义Dog类继承Animal类
                                                                         //此处不写任何代码
}

public class Example01 {                                   // 定义测试类
       public static void main(String[] args) {
              Dog dog = new Dog();                     // 创建一个Dog类的实例对象
              dog.setName("牧羊犬");                   // 此时访问的方法是父类中的,子类中并没有定义
              dog.setAge(3);                                 // 此时访问的方法是父类中的,子类中并没有定义
              System.out.println("名称:"+dog.getName()+",年龄:"+dog.getAge());
       }
}

名称:牧羊犬,年龄:3

分析:父类:2个属性,4个方法
         子类: 2个属性,4个方法

-----------------------------------------------------
例子4-2: 子类扩展(定义)本身的成员变量和成员方法
class Animal {                                                   // 定义Animal类
        private String name;         			
        private int age;             			
        public String getName() {
               return name;
        }
        public void setName(String name) {
               this.name = name;
        }
        public int getAge() {
               return age;
        }
        public void setAge(int age) {
               this.age = age;
        }
}

class Dog extends Animal {                              // 定义Dog类继承Animal类
        private String color;                                  //定义本身的color属性			
        public String getColor() {
               return color;
        }
        public void setColor(String color) {
               this.color = color;
        }                                                                         //此处不写任何代码
}

public class Example01 {                                   // 定义测试类
       public static void main(String[] args) {
              Dog dog = new Dog();                     // 创建一个Dog类的实例对象
              dog.setName("牧羊犬");                   // 此时访问的方法是父类中的,子类中并没有定义
              dog.setAge(3);                                 // 此时访问的方法是父类中的,子类中并没有定义
              dog.setColor("黑色");                      //此时访问的方法是子类本身中的
              System.out.println("名称:"+dog.getName()+",年龄:"+dog.getAge()+"颜色:"+dog.getColor());
       }
}

名称:牧羊犬,年龄:3,颜色:黑色

分析:父类:2个属性,4个方法
          子类: 3个属性,6个方法

继承的注意:
(1)Java只支持单继承,不允许多继承,即一个类只有一个直接父类

class A{} 
class B{}
class C extends A,B{}  // C类不可以同时继承A类和B类

(2)多了个类可以继承自同一个父类

class A{}
class B extends A{}
class C extends A{}   // 类B和类C都可以继承类A

(3)在Java中,多继承通过传递继承,即一个类的父类可以再继承另外的父类

class A{}
class B extends A{}   // 类B继承类A,类B是类A的子类
class C extends B{}   // 类C继承类B,类C是类B的子类,同时也是类A的子类


(4)子类继承父类所有成员变量和方法;
         子类可以直接访问和调用父类的非私有成员(成员变量和成员方法);
         子类不能直接访问父类的私有成员变量,只能通过公有的成员方法间接访问。

4.1.2   方法的重写

定义:子类对继承自父类的方法进行修改

方法重写要求:(1)相同的返回值、方法名和参数列表
                      (2)子类重写方法访问权限,不能比父类方法严格(访问权限扩大:子类>=父类),
                              否则编译器报错

结果:当子类重写父类方法后,子类对象调用本身重写的方法,而不能调用父类同名方法

例子4-3:   子类对父类方法重写
class Animal {		                                          // 定义Animal类
        void shout() {		                                          //定义动物叫的方法: default	  
               System.out.println("动物发出叫声");
        }
}

class Dog extends Animal {                                               // 定义Dog类继承动物类
         void shout() {	                  //重写父类Animal中的shout()方法: 相同的返回类型,方法名和参数列表
	System.out.println("汪汪汪……");                           访问权限:子类=父类
         }
}

public class Example03 {	                                            // 定义测试类
        public static void main(String[] args) {
	Dog dog = new Dog();                       // 创建Dog类的实例对象
	dog.shout();                                        // 调用dog重写的shout()方法
        }
}

汪汪汪.........



4.1.3   super关键字
      
问题:当子类重写父类方法后,子类对象无法访问父类被重写的方法
解决方法:子类中,使用super关键字调用父类的普通(非私有)属性、(非私有)方法和构造方法
作用:(1)子类使用super关键词,访问父类(非私有)成员变量与成员方法
          (2)子类使用super关键字,访问父类指定的构造方法(public)
           (super()调用父类构造方法必须位于子类构造法方法第一行,且只能出现一次)

例子4-5:子类使用super关键词,访问父类(非私有)成员变量与成员方法
class Animal {		                               // 定义Animal类
      String name = "牧羊犬";	
      void shout() {			              //定义动物叫的方法	
             System.out.println("动物发出叫声");
      }
}
class Dog extends Animal {                                         // 定义Dog类继承动物类
       public void shout() {                                             //重写父类Animal中的shout()方法,扩大了访问权限			 
              super.shout();                                                //子类使用super关键字,调用父类中的shout()方法
              System.out.println("汪汪汪……");  
       }
       public void printName(){
              System.out.println("名字:"+super.name);      //子类使用super关键字,调用父类中的name属性
     }
} 
public class Example05 {	                                    // 定义测试类
       public static void main(String[] args) {
	Dog dog = new Dog();  // 创建Dog类的实例对象
	dog.shout();            // 调用dog重写的shout()方法
                dog.printName();        // 调用Dog类中的printName()方法
       }
}

动物发出叫声
汪汪汪……
名字:牧羊犬

例子4-6:子类使用super关键字,访问父类指定的构造方法
class Animal {                                                                                // 定义Animal类
       private String name;
       private int age;
       public Animal(String name, int age) {
              this.name = name;
              this.age = age;
       }
       public String getName() {
             return name;
       }
       public void setName(String name) {
              this.name = name;
       }
       public int getAge() {
            return age;
       }
       public void setAge(int age) {
            this.age = age;
       }
       public String info() {
              return "名称:"+this.getName()+",年龄:"+this.getAge();                  //此处this可以缺省
      }
}
class Dog extends Animal {                                                       // 定义Dog类继承Animal类
       private String color;                                                           
       public Dog(String name, int age, String color) {
             super(name, age);               //子类使用super()调用父类构造方法,且必须放在子类构造方法第一行
             this.setColor(color);
       }
       public String getColor() {
             return color;
       }
       public void setColor(String color) {
             this.color = color;
       }    
       public String info() {                                                          //重写父类的info()方法
              return super.info()+",颜色:"+this.getColor();          //使用super关键字调用父类的方法
       }
}
                                                                                                  // 定义测试类
public class Example06 {
       public static void main(String[] args) {
              Dog dog = new Dog("牧羊犬",3,"黑色"); // 创建Dog类的实例对象
              System.out.println(dog.info());
       }
}

名称:牧羊犬,年龄:3,颜色:黑色

注意:
(1)this、super关键字,都可以调用构造方法、普通方法和属性
(2)this,super调用构造方法是,不可能同时出现(调用构造方法是,都必须放在第一行。)


4.2   final关键字

final:“最终”。
在Java中,可以使用final关键字声明类、方法和变量(成员变量或局部变量),在声明时需要注意以下几点:
(1)使用final修饰的类(最终类)不能有子类。
(2)使用final修饰的方法(最终方法)不能被子类重写。
(3)使用final修饰的变量(成员变量或局部变量)(最终变量)是常量,不可修改。


4.2.1   final关键字修饰类:final类不能被继承

例子4-7: final关键字修饰类
 
final class Animal {                                     // 使用final关键字修饰Animal类
	                                                  // 方法体为空
}

class Dog extends Animal {                       // Dog类继承Animal类
	                                                  // 方法体为空
}
                                                                  // 定义测试类
public class Example07 {
       public static void main(String[] args) {
               Dog dog = new Dog(); 		// 创建Dog类的实例对象
       }
}

编译器报错


4.2.2   final关键字修方法:子类不能重写父类final方法

例子4-8: final关键字修饰方法

class Animal {                                                       // 定义Animal类
      public final void shout() {                              // 使用final关键字修饰shout()方法                   
      
       }
}
class Dog extends Animal {                                 // 定义Dog类继承Animal类
       public void shout() {                                     // 重写Animal类的shout()方法
        
       }
}
public class Example08 {                                       // 定义测试类
       public static void main(String[] args) {
              Dog dog=new Dog();                             // 创建Dog类的实例对象
       }
}

编译器报错


4.2.3   final关键字修变量:常量

在Java中,常量是指被final修饰的变量(字母全部大写),常量只能在声明时赋值一次。

例子4-9   final关键字修变量:final修饰的变量(字母全部大写), 即常量

public class Example09 {
       public static void main(String[] args) {
	final int AGE = 18;    // 第一次可以赋值
	AGE = 20;               // 再次赋值会报错
       }
}


注意:       
全局常量:public static final 变量类型  变量名;

public static final String NAME="哈士奇";          




4.3   抽象类和接口

方法定义
[访问控制符]  返回值类型  方法名(参数列表){
               方法体;                                       //方法实现的具体内容
}


问题:类中成员方法,具有方法体。但有时候方法体不确定,比如
           在Animal类中,定义shout()方法描述动物叫声,但不同动物叫声不同,
           因此,shout()方法体无法确定。

解决:抽象方法解决方法体无法确定的方法。


4.3.1   抽象类

1、抽象方法:

定义:使用abstract关键字修饰的成员方法,且定义时不需要实现方法体。
语法格式:
[访问控制符]  abstract   返回值类型  方法名(参数列表);                        //抽象方法声明,方法体无实现

2、抽象类

定义:包含至少一个抽象方法的类,且该类用abstract关键字修饰
语法格式:
abstract  class  抽象类名{
       成员变量;
       [访问控制符]  返回值类型  方法名(参数列表){        //成员方法;
               方法体;                                      
       }
      [访问控制符] abstract   返回值类型  方法名(参数列表);               //抽象方法
}

定义规则:
(1)包含一个及以上抽象方法的类是抽象类
(2)抽象类和抽象方法,都必须使用abstract关键字修饰
(3)抽象方法只需声明, 而不需要实现
(4)一个非抽象类继承抽象类,子类必须实现抽象类的全部抽象方法
       (即子类重写父类抽象方法,使其具有方法体)
(5)abstract修饰的方法(抽象方法)不能同时使用private或final修饰

例子4-10   抽象类使用

abstract class Animal {                                        // 定义抽象类Animal              
       abstract void shout();                                  // 定义抽象方法shout()
} 

class Dog extends Animal {                                 // 定义Dog类继承抽象类Animal
       void shout() {
              System.out.println("汪汪...");                 // 子类实现父类抽象方法shout()
       }
}
public class Example10 {                                     // 定义测试类
       public static void main(String[] args) {
              Dog dog = new Dog(); 		// 创建Dog类的实例对象
              dog.shout(); 				// 调用dog对象的shout()方法
       }
}

汪汪.......



4.3.2   接口

1、接口:interface接口关键词
基本定义:所有方法都是抽象方法,成员变量都是全局常量,该抽抽象类是接口
基本语法格式:(推荐)
public interface 接口名  extends 接口1,接口2,.....接口n{
         public static final 数据类型 变量名=常量值;             //成员变量为全局常量
         public abstract  返回值类型  方法名(参数列表);       //所有方法为抽象方法
}

JDK8定义:抽象类中不仅有抽象方法、还有静态方法和默认方法,成员变量都是全局常量,该抽抽象类是接口
public interface 接口名  extends 接口1,接口2,.....接口n{
         public static final 数据类型 变量名=常量值;             //成员变量为全局常量
         public static   返回值类型  方法名(参数列表){              //静态方法
                 方法体;                           
         }        
         default  返回值类型  方法名(参数列表){                      //默认方法
                 方法体;
         }                  
         public abstract  返回值类型  方法名(参数列表);       //所有方法为抽象方法
}

接口说明:
(1)接口使用interface关键字
(2)extends关键字表示接口继承,接口允许多继承,不同接口键使用逗号分隔
(3)接口中的抽象方法修饰(即使无修饰符或缺省,也默认):public abstract;
         成员变量是全局常量(即使无修饰符或缺省,也默认),public static final
(4)接口定义时,常常缺省public。不管是否写访问权限: 接口方法,public修饰;接口变量:public static final修饰


2、接口实现类

定义:类使用implements关键字实现接口,并实现接口中所有的抽象方法。
语法格式:

[修饰符] class 类名 implements 接口1,接口2,.....接口n{

}

说明:类可以继承一个类的同时实现多个接口


例子4-11    接口使用

interface Animal {                                  // 定义接口Animal(默认:public修饰)
       int ID = 1;                                        // 定义全局常量(默认:public static final修饰)
       String NAME = "牧羊犬";
       void shout();                	                // 定义抽象方法shout()
       static int getID(){                              //定义静态方法
             return Animal.ID;                        //静态成员变量,接口名(类名)直接访问
       }
       public void info();                             // 定义抽象方法info()
}

interface Action {                                      //定义接口Action(默认:public修饰)
    public void eat();                 	  // 定义抽象方法eat()
}

class Dog implements Animal,Action{       // 定义Dog类实现Animal接口和Action接口
        public void eat() {                                // 重写Action接口中的抽象方法eat()
               System.out.println("喜欢吃骨头");
        }
        public void shout() {                              // 重写Animal接口中的抽象方法shout()
               System.out.println("汪汪...");
        }
        public void info() {                                    // 重写Animal接口中的抽象方法info()
               System.out.println("名称:"+NAME);
    }
}

class Example11 {                                                // 定义测试类
      public static void main(String[] args) {
             System.out.println("编号"+Animal.getID());
             Dog dog = new Dog();                          // 创建Dog类的实例对象
             dog.info();
             dog.shout();                	           // 调用Dog类中重写的shout()方法
             dog.eat();                     	          // 调用Dog类中重写的eat()方法
    }
}

编号1
名称:牧羊犬
汪汪...
喜欢吃骨头


说明:
(1)类必须实现接口中,所有抽象方法
(2)静态成员,可以通过接口名直接访问,也可以通过创建的对象访问


如果某个类既要继承抽象类又要实现接口
语法形式:

[修饰符] class 类名 extends 抽象类 implements 接口名1,接口名2,....{
          ........
}

例子4-12    某类继承抽象类又要实现接口

interface Action {                                  // 定义接口Animal(默认:public修饰)
       public String NAME = "牧羊犬";      // 定义全局常量(默认:public static final修饰)
       public [abstract] void shout();                        // 定义抽象方法shout()
       public [abstract] void info();                           // 定义抽象方法info()
}

abstract class Animal{                                 //定义抽象类Action(默认:public修饰)
    public abstract void eat();                 	  // 定义抽象方法eat()
}

class Dog extends Animal  implements Action{       // 定义Dog类继承Animal类,并实现Action接口
        public void eat() {                                         // 重写Action接口中的抽象方法eat()
               System.out.println("喜欢吃骨头");
        }
        public void shout() {                              // 重写Animal接口中的抽象方法shout()
               System.out.println("汪汪...");
        }
        public void info() {                                    // 重写Animal接口中的抽象方法info()
               System.out.println("名称:"+NAME);
    }
}

class Example12{                                                // 定义测试类
      public static void main(String[] args) {
             Dog dog = new Dog();                          // 创建Dog类的实例对象
             dog.info();
             dog.shout();                	           // 调用Dog类中重写的shout()方法
             dog.eat();                     	          // 调用Dog类中重写的eat()方法
    }
}

接口不允许继承抽象类,但允许一个接口继承多个接口

例子4-13    接口继承多个接口

interface Animal {                                  // 定义接口Animal(默认:public修饰)
       public String NAME = "牧羊犬";      // 定义全局常量(默认:public static final修饰)
       public [abstract] void info();                           // 定义抽象方法info()
}

interface Color{                                      //定义抽象类Action(默认:public修饰)
    public [abstract] void black();             // 定义抽象方法black()
}

interface Action extends Animal,Color{    //接口多继承
    public [abstract] void shout();             // 定义抽象方法shout()
}


class Dog  implements Action{       // 定义Dog类实现Action接口
        public void info() {                                    // 重写Animal接口中的抽象方法info()
               System.out.println("名称:"+NAME);
        }
        public void black() {                                 // 重写Action接口中的抽象方法black()
               System.out.println("黑色");
        }
        public void shout() {                              // 重写Animal接口中的抽象方法shout()
               System.out.println("汪汪...");
        }
        
}
class Example13{                                                // 定义测试类
      public static void main(String[] args) {
             Dog dog = new Dog();                          // 创建Dog类的实例对象
             dog.info();
             dog.shout();                	           // 调用Dog类中重写的shout()方法
             dog.black();                     	          // 调用Dog类中重写的eat()方法
    }
}

名称:牧羊犬
汪汪...
黑色

4.4   多态

4.4.1   多态概述

多态:不同对象调用同一方法,表现不同行为。
实现方式:2种
(1)方法重载:同一个类中,参数不同(类型或个数)
(2)方法重写:子类重写父类方法

例子4-14    方法重写

abstract class Animal {                    // 定义抽象类Anmal
       abstract  void shout();     	      // 定义抽象shout()方法
}

class Cat extends Animal {               // 定义Cat类继承Animal抽象类
       public void shout() {                 // 实现shout()方法
              System.out.println("喵喵……");
       }
}

class Dog extends Animal {                // 定义Dog类继承Animal抽象类
        public void shout() {                  // 实现shout()方法
              System.out.println("汪汪……");
        }
}

public class Example14 {                    // 定义测试类
       public static void main(String[] args) {
               Cat an1 = new Cat();           // 创建Cat对象,使用Animal类型的变量an1引用
               Dog an2 = new Dog();       // 创建Dog对象,使用Animal类型的变量an2引用
               an1.shout();        
               an2.shout();        
       }
}


4.4.2   对象类型转换

定义:父类与子类对象的转换
类型:2种:
1、向上转型:子类对象----->父类对象

语法:
父类类名  父类对象名=子类实例;

说明:
(1)向上转型,系统自动完成
(2)向上转型,父类对象调用重写方法时,调用子类重写方法。
                           父类对象无法调用子类特有成员方法。

2、向下转型:父类对象----->子类对象

父类类名  父类对象名=子类实例;
子类类名  子类对象名=(子类类名)父类对象

说明:
(1)向下转型,必须先向上转型,再向下转型(直接向下转型,会报错)


例子4-15    向上转型

class Animal {                                        // 定义类Anmal
      public void shout(){
             System.out.println("喵喵……"); 
      }     
}
class Dog extends Animal {                     // Dog类
       public void shout() {                         // 重写shout()方法
              System.out.println("汪汪……"); 
       }
       public void eat() {
              System.out.println("吃骨头……"); 
       }
}
public class Example15 {                             // 定义测试类
   public static void main(String[] args) {
       Dog dog = new Dog();                       // 创建Dog对象
       Animal an = dog;                                //子类实例向上转型
       an.shout(); 
   }
}

汪汪……

例子4-16    向下转型

class Animal {                                                   // 定义类Anmal
       public void shout(){ 
              System.out.println("喵喵……"); 
       }     
}

class Dog extends Animal {                              // Dog类
       public void shout() {                                 // 重写shout()方法
              System.out.println("汪汪……"); 
       }
       public void eat() {
              System.out.println("吃骨头……"); 
       }
}
public class Example16 {                                    // 定义测试类
       public static void main(String[] args) {
              Animal an = new Dog();                             // 先向上转型,子类→父类
              Dog dog = (Dog)an;                                  // 再了向下转型
              dog.shout();
              dog.eat();
       }                     
}

汪汪……
吃骨头

4.4.3   instanceof关键字

作用:判定对象是否是某个类(或接口)的实例
语法:

对象 instanceof 类(接口);

结果:是返回true,否返回false

例子4-17   instanceof关键字使用

class Animal {                                         // 定义类Anmal
       public void shout(){  
              System.out.println("动物叫……"); 
       }    
}
class Dog extends Animal {                    // Dog类
        public void shout() {                      // 重写shout()方法
               System.out.println("汪汪……"); 
        }
       public void eat() {
               System.out.println("吃骨头……"); 
       }
}
public class Example17 {                                                      // 定义测试类
       public static void main(String[] args) {
              Animal a1 = new Dog();                                        // 通过向上转型实例化Animal对象
              System.out.println("Animal a1 = new Dog():"+(a1 instanceof Animal));
              System.out.println("Animal a1 = new Dog():"+(a1 instanceof Dog));
              Animal a2 = new Animal();         // 实例化Animal对象
              System.out.println("Animal a2 = new Animal():"+(a2 instanceof Animal));
              System.out.println("Animal a2 = new Animal():"+(a2 instanceof Dog));
       }
}

true 
true   (无法理解)
true
false


4.5   Object类

定义:所有类的父类(又称超类),每个类都直接或间接继承Object类。
          当一个类没有显示指定父类,该类默认继承Object类

常用方法:

boolean equals():    判定两个对象是否相等
int hashCode():        返回对象的散列码值
String toString():     返回对象的字符串表示形式

例子4-18   Object类使用

class Animal {                                                 // 定义Animal类
       void shout() {	                                      // 定义动物叫的方法 
              System.out.println("动物叫!");
       }
}
public class Example18 {
       public static void main(String[] args)  {
              Animal animal = new Animal();  	          // 创建Animal类对象
              System.out.println(animal.toString());	// 继承自Object类的toString()方法并打印
       }
}


例子4-19   子类重写Object类方法

class Animal {                                          // 定义Animal类
        public String toString(){                 //重写Object类的toString()方法
	return "这是一个动物。";
        }
}
public class Example19 {
       public static void main(String[] args) {
             Animal animal = new Animal(); 			// 创建Animal类对象
             System.out.println(animal.toString()); // 调用toString()方法并打印
       }
}

这是一个动物。


4.6   内部类

定义:在一个类(外部类)的内部定义的类。 
种类:根据内部的定义、位置和修饰符,内部类可以分为4种:
        (1)成员内部类
        (2)局部内部类
        (3)静态内部类
        (4)匿名内部类

4.6.1   成员内部类

定义:在外部类中定义的成员(内部)类,与外部类中的成员变量与成员方法并列。

功能:成员内部类可以访问外部类的所有成员(成员变量、成员方法)。

创建成员内部类对象的语法:

外部类名.内部类名 变量名=new 外部类名(). new 内部类名();


例子4-20:   成员内部类
class Outer {
    int m = 0; 						// 定义类的成员变量
    void test1() {                                                                      // 下面的代码定义了一个成员方法,方法中访问内部类
       System.out.println("外部类成员方法");
    }
                                                                                               // 下面的代码定义了一个成员内部类
    class Inner {
        int n = 1;        
        void show1() {                                                                         
           System.out.println("外部成员变量m = " + m);          // 在成员内部类的方法中访问外部类的成员变量
       }
        void show2() {                                                            
           System.out.println("内部成员方法");
       }
    }
    void test2() {
        Inner inner = new Inner();
        System.out.println("内部成员变量n = " + inner.n);
        inner.show2();
    }
}
public class Example20 {
    public static void main(String[] args) {
        Outer outer = new Outer();
        Outer.Inner inner = outer.new Inner();                  //创建内部类对象
        inner.show1();
       outer.test2();
    }
}

外部成员变量m =0
内部成员变量n =1
内部成员方法

4.6.2   局部内部类(方法内部类)

定义:在方法中定义的类,仅在方法内部有效。

功能:局部内部类可以访问外部类的所有成员(成员变量、成员方法)
           局部内部类中的变量和方法,只能在所属方法中访问


例子4-21:   局部内部类
class Outer {
     int m = 0;  					// 定义类的成员变量
    // 下面的代码定义了一个成员方法,方法中访问内部类
     void test1() {
        System.out.println("外部类成员方法");
    }
    void test2() {
        // 下面的代码定义了一个成员内部类
        class Inner {
            int n = 1;
            void show() {
                // 在成员内部类的方法中访问外部类的成员变量
                System.out.println("外部成员变量m = " + m);
                test1();
           }
        }
        Inner inner = new Inner();
        System.out.println("局部内部类变量n = " + inner.n);
        inner.show();
    }
}
public class Example21 {
    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.test2();
    }
}

局部内部类变量n =1
外部成员变量m =0
外部类成员方法

4.6.3   静态内部类

定义:使用static关键词修饰的成员内部。
功能:静态内部类仅可以访问外部类的静态成员(成员变量、成员方法)。

创建静态内部类对象的语法:

外部类名.静态内部类名 变量名=new 外部类名().静态内部类名();



例子4-22:   静态内部类
class Outer {
    static int m = 0; // 定义类的成员变量
    // 下面的代码定义了一个静态内部类
    static class Inner {
         int n = 1;
         void show() {
             // 在静态内部类的方法中访问外部类的成员变量
             System.out.println("外部静态变量m = " + m);
           }
     }
  }
public class Example22 {
    public static void main(String[] args) {
        Outer.Inner inner = new Outer.Inner();
        inner.show();
    }
}

外部静态变量m =0


4.6.4   匿名内部类

问题:在Java中,调用某个方法,该方法的参数是接口类型。
解决方法:(1)传入接口实现类
                 (2)实现接口匿名内部类,在匿名内部类中,实现接口抽象方法的重写

定义:没有名称的内部类

创建匿名内部类对象(子类对象)的基本语法:

new 父接口(){
        父接口抽象方法重写;
}

说明:(1)匿名:隐藏的是接口实现类的名字
           (2)new 父接口():创建的是实现类的对象,非父接口的对象。  (可以看作:new 父接口[实现类名]())
           (3){}:抽象方法的具体实现


例子4-23:   匿名内部类
interface Animal{
      void shout();
}
public class Example23{
       public static void main(String[] args){
              String name = "小花";
              animalShout(new Animal(){                                         //匿名内部类
                      public void shout() {                                           //接口方法重写
                              System.out.println(name+"喵喵...");          //在匿名内部类中,访问局部变量
                      }
              });
       }
       public static void animalShout(Animal an){
              an.shout();
       }
}

说明:
可以在局部内部类和匿名内部类中,访问非final修饰的局部变量


4.7   异常

4.7.1   什么是异常

1、定义:在程序运行过程中,出现的各种非正常情况。     比如:硬盘空间不足,网络中断,类不存在,数组越界等
           
2、产生异常后果:异常方生后,若不对其进行处理,程序会立即结束,无法继续向下执行。

3、解决异常:以异常类的形式对非正常情况(异常)进行封装,
                       使用异常处理机制,处理程序运行的各种情况

例子4-24   异常的实例
public class Example24 {                      
        public static void main(String[] args) {
	int result = divide(4, 0);                   // 调用divide()方法
	System.out.println(result);    
        }                   
                                                                         //下面的方法实现了两个整数相除
        public static int divide(int x, int y) { 
	int result = x / y;     		     // 定义一个变量result记录两个数相除的结果
	return result;           		     // 将结果返回
        }
}


报错:
Exception in thread "main" java.lang
   .ArithmeticException: /by zero
         at cn.itcast.Example.divide(Example08.java:9)
         at cn.itcast.Example.divide(Example08.java:4)

算术异常(ArithmeticException),参数传入时,分母为0了
异常方生后,若不对其进行处理,程序会立即结束,无法继续向下执行。

3、Java异常类

异常类:Java提供了大量的异常类,所有异常类都继承自Throwable类。(在java.lang包: lang包是常用包,自动导入,不需要人为显示导入)

异常类继承体系:
                                               Throwable
                              Error                                       Exception
                          IOError                        其他子类(编译时异常)           RuntimeException(运行时异常)
                          AWTError                                                                         ArithmeticException 
                          其他子类                                                                           ClassCaseException
                                                                                                                   其他子类

说明:
(1)Throwable类有两个直接子类Error和Exception
(2)Error类(错误类):程序本身无法处理的错误,程序运行时产生的系统内部错误或资源耗尽错误。
(3)Exception类(异常类):程序本身可以处理的错误。在Java中处理的异常类,都是针对Exception类及其子类
(4)Exception类:有两个直接子类。  其他子类(编译时异常)  ,  RuntimeException(运行时异常)


Throwable类中,获取异常信息的通用方法。(Throwable子类会自动继承)


    方法声明                                         功能描述
String getMessage()                返回异常的消息字符串
String toString()                       返回异常的简单信息描述
void printStackTrace()              获取异常类名和异常信息,以及异常出现在程序中的位置


4、Java异常处理的两种方式
(1)使用try...catch....[finally.....]语句,处理异常
(2)方法声明处,使用throws关键字声明抛出异常;方法调用处,对异常进行处理


4.7.2   try......catch和finally

1、目标:为使异常发生后,能解决异常,并能使程序正常执行下去,需解决异常

2、方法:Java提供了异常处理机制:异常捕获

3、异常捕获语法:try.....catch语句
try{
        可能会发生异常的语句块;
}catch(ExceptionType(Exception类及其子类)   e){
         异常处理的语句块;
}

4、说明:
(1)try{}中,编写可能会发生异常的Java语句块
(2)catch(){}:() 参数:指明异常类类型(必须是Exception类或其子类)
                         {} 编写针对异常的处理语句块
(3)实现过程:try代码块中的程序发生了异常,
                          系统会将异常的信息封装成一个异常对象,
                          并将这个对象传递给catch代码块进行处理

例子4-25   try…catch语句对异常进行捕获
public class Example25 {
         public static void main(String[] args) {                         //下面的代码定义了一个try…catch语句用于捕获异常
	try {                                      
	          int result = divide(4, 0);                              //调用divide()方法
	          System.out.println(result);   
	} catch (Exception e) {                                           //对异常进行处理
	          System.out.println("捕获的异常信息为:" + e.getMessage());
	}
	System.out.println("程序继续向下执行...");
         }
         public static int divide(int x, int y) {                                 //下面的方法实现了两个整数相除
	int result = x / y;                                                      //定义一个变量result记录两个数相除的结果
	return result;                                                            //将结果返回
         }
 }

捕获的异常信息为:/by zero
程序继续向下执行


注意:
(1)try{}代码块中,发生异常语句,该语句后面的语句不会被执行。
(2)catch{}代码块对异常进行处理,处理完成后,程序继续向下执行,而不会终止程序。

5、无论异常是否发生,有些语句始终要执行。

关键词:try.....catch.....finally....

语法格式:
try{
        可能会发生异常的语句块;
}catch(ExceptionType(Exception类及其子类)   e){
         异常处理的语句块;
}finally{
         无论异常是否发生,必须要执行的语句块;
}

例子4-26   try…catch....fianlly语句对异常进行捕获

public class Example26 {
        public static void main(String[] args) {              //下面的代码定义了一个try…catch…finally语句用于捕获异常
                try {
	          int result = divide(4, 0);                   //调用divide()方法
	          System.out.println(result);
	} catch (Exception e) {                                //对捕获到的异常进行处理
	          System.out.println("捕获的异常信息为:" + e.getMessage());
                           return;                                               //用于结束当前main()方法
	} finally {                             
	          System.out.println("进入finally代码块");
	}
                 System.out.println("程序继续向下执行…");
         } 
         public static int divide(int x, int y) {                       //下面的方法实现了两个整数相除 
	int result = x / y;                                          //定义一个变量result记录两个数相除的结果
	return result;                                                //将结果返回
          }
}


捕获的异常信息为:/by zero
进入finally代码块


程序说明:
(1)return语句,用于结束当前方法(main()方法),使得return之后代码块不会执行。
(2)finally语句却执行了,不受return语句影响。
     (换句话,不论程序是发生异常还是使用return语句结束,finally中的语句都会执行)

(3)finally{}作用:处理必须完成的事情。比如释放系统资源
(4)System.exit(0):退出当前Java虚拟机,任何代码都不执行


4.7.3   throws关键字:

1、问题:编写程序时,大部分是调用别人编写的程序,无法确定该方法是否发生异常

2、处理机制:在方法的声明(定义)的后面,使用throws关键字,声明该方法可能会发生异常(方法声明处,抛出异常)
                       在程序中,必须对该方法声明的异常进行处理,否则编译无法通过。(方法调用处,处理抛出异常)
                     

3、throws关键字声明抛出异常语法:

[修饰符] 返回值类型  方法名(参数1,参数2,...参数n) throws 异常类1,异常2,....{
           方法体;
}

说明:
(1)throws关键词,写在方法声明的后面(方法声明处,throw声明抛出异常)            
(2) 在方法调用处,处理抛出异常

例子4-27   方法声明处,throws关键字声明抛出异常
public class Example27 {                   
        public static void main(String[] args) {
                int result = divide(4, 2);                                                //调用divide()方法
	System.out.println(result);
         }
        public static int divide(int x, int y) throws Exception {        //下面的方法实现了两个整数相除,并使用throws关键字声明抛出异常
	int result = x / y;                                                          //定义一个变量result记录两个数相除的结果
	return result;                                                                //将结果返回
        }
}

编译器报错.....
(说明:虽然divide()方法调用时,不会发生除0异常,
              在divide()方法在声明时抛出异常,调用该方法时,
              必须对抛出的异常进行处理,否则编译报错)

例子4-28    方法声明处,throws抛出异常;方法调用处,try...catch语句处理抛出的异常
public class Example28 {
        public static void main(String[] args) {                
                try {                                                                   //下面的代码定义了一个try…catch语句用于捕获异常
	             int result = divide(4, 2);                      //调用divide()方法
	             System.out.println(result); 
	} catch (Exception e) {                                      //对捕获到的异常进行处理
	             e.printStackTrace();                             //打印捕获的异常信息
	}
        }
        public static int divide(int x, int y) throws Exception {            //下面的方法实现了两个整数相除,并使用throws关键字声明抛出异常
	int result = x / y;                                                             //定义一个变量result记录两个数相除的结果
	return result;                                                    //将结果返回
        }
}

2



4、当不知道如何处理抛出的异常,可以继续使用throws关键字继续抛出。
    (编译能通过,但程序一旦发生异常并且异常没有被处理,程序会非正常终止)

例子4-29    throws关键字继续抛出,且不做异常处理
public class Example29 {
        public static void main(String[] args)  throws Exception{                
                int result = divide(4, 0);                      //调用divide()方法
                System.out.println(result);
        }
        public static int divide(int x, int y) throws Exception {            //下面的方法实现了两个整数相除,并使用throws关键字声明抛出异常
	int result = x / y;                                                             //定义一个变量result记录两个数相除的结果
	return result;                                                    //将结果返回
        }
}

报错

(说明:在方式声明时,使用throws抛出异常,再main()方法中继续使用throws抛出,且未对异常处理
             编译能通过,但一旦发生异常,程序会终止运行。)


4.7.4   编译时异常与运行时异常
 
             (Exception)异常类有两个直接子类:编译异常和运行时异常

1、编译时异常(checked异常)
(1)定义:
         Java编译器会对该异常进行检查,必须对该异常进行处理,否则编译无法通过
(2)编译异常处理方法:2种
         1)使用try...catch....[finally.....]语句,处理异常
         2)方法声明处,使用throws关键字声明抛出异常;方法调用处,对异常进行处理


2、运行时异常(unchecked异常,RuntimeException异常)
(1)定义:
         Java编译器不会对该异常进行检查。
(2)错误原因:程序逻辑错误


例子: 运行时异常
int[]  arr=new int[5];
System.out.println(arr[6]);

4.7.5   自定义异常

1、定义:允许用户自定义异常,处理程序中特有的异常情况
2、要求:
(1)自定义异常类,必须继承自Exception类或其子类
(2)在自定义异常类构造方法中,使用super()语句调用Exception类的构造方法


例子:自定义异常类声明

public class DivideByMinusException extends Exception{
        public DivideByMinusException(){
                super();                                                          
        }
        public DivideByMinusException(String message){
                super(message);
        }  
}

3、自定义异常类对象实例化:
(1)位置:在方法声明中,声明自定义异常类
(2)语法格式:

throws new  自定义异常类名(参数列表);

例子4-30   改进例4-29的divide()方法,在divide()方法声明中判定除数是否为负数
                 如果为负数,在方法声明中使用throws关键字,向方法的调用者抛出自定义类的
                 DivideBy MinusException异常对象

public class Example30 {
       public static void main(String[] args) {
              int result = divide(4, -2);   			
              System.out.println(result);
        }
	//下面的方法实现了两个整数相除
       public static int divide(int x, int y) {
              if(y<0){ 
	           throw new DivideByMinusException("除数是负数");            //在方法声明处,向方法调用者抛出自定义类实例对象
              }
              int result = x / y;   	// 定义一个变量result记录两个数相除的结果
              return result;         	// 将结果返回
        }
}



程序报错。。。。。
(原因:在方法声明内,使用throws关键字抛出自定义异常类对象,必须使用对异常进行处理
    异常处理方法:(1)try....catch语句捕获并处理;
                            (2)在方法声明处,使用throws关键字声明抛出,由调用者负责处理)

例子4-31    完整的自定义异常类处理

public class DivideByMinusException extends Exception{
        public DivideByMinusException(){
                super();                                                          
        }
        public DivideByMinusException(String message){
                super(message);
        }  
}

public class Example31 {
        public static void main(String[] args) {             
                try {                                                                       // 下面的代码定义了一个try…catch语句用于捕获异常
	           int result = divide(4, -2);  			
                            System.out.println(result);
	} catch (DivideByMinusException e) {     // 对捕获到的异常进行处理
	           System.out.println(e.getMessage()); // 打印捕获的异常信息
	}
         }
         public static int divide(int x, int y) throws DivideByMinusException {     // 下面的方法实现了两个整数相除,并使用throws关键字声明抛出自定义异常
	if (y < 0) {
		 throw new DivideByMinusException("除数是负数");
	}
	int result = x / y;     // 定义一个变量result记录两个数相除的结果
	return result;           // 将结果返回
          }
}



DivideByMinusException e= new DivideByMinusException("除数是负数")