第5章 面向对象设计

面向对象是当前计算机界关心的重点也是Java语言的基本特征。传统开发方法中存在软件重用性差、可维护性差以及不能够满足用户需要等问题。面向对象设计的应用程序具有简单、直观、接近人类的自然思维方式等特点,弥补和改进了传统开发中的不足。

面向对象具有抽象性、封装性、继承性、多态性。抽象性是将具有一致的数据结构(属性)和行为(操作)的对象抽象成类,一个类就是一种抽象。封装性体现在Java中的数据除了基本类型的数据外,都以对象的形式存在,对象是方法与数据的封装体。继承性是子类自动共享父类数据结构和方法的机制,这是类之间的一种关系。在定义和实现一个类的时候,可以在一个已经存在的类的基础之上来进行,把这个已经存在的类所定义的内容作为自己的内容,并加入若干新的内容。多态性指相同的操作或函数、过程可作用于多种类型的对象上并获得不同的结果。不同的对象,收到同一个消息可以产生不同的结果。多态性允许每个对象以适合自身的方式去响应共同的消息,增强了软件的灵活性和重用性。本章将具体介绍面向对象的特征及应用。

实例21 图形面积与周长(抽象类)

抽象类是Java中一种特殊的类。抽象类不能创建对象,而只能由其派生子类创建。抽象类是专门作其他类的父类来使用的。本实例介绍如何运用抽象类获得不同图形的面积。

技术要点

运用抽象类获得图形面积的技术要点如下:

• 用关键字abstract修饰符来定义抽象类。abstract放在关键字class之前,语法格式:(public) abstract class类名{}。并用abstract来定义抽象方法,abstract放在方法返回类型之前。语法格式:abstract返回类型方法名()。

• 抽象类必须作为其他类的父类,并且,子类要全部实现父类中的抽象方法,否则也要声明为抽象类。

实现步骤

(1)新建一个类名为TextAbstract.java。

(2)代码如下所示:

package com.zf.s5;                               //创建一个包
abstract class Geometric{                        //创建抽象类
    String color="block";
    int weight=2;
    abstract float getArea();                    //抽象构造方法求面积
    abstract float getPerimeter();               //抽象构造方法求周长
}
class Circle extends Geometric{                  //继承Geometric,求圆的面积和周长
    float radius;
    Circle(float number){                        //带参数的构造方法
          radius=number;
    }
    protected float getArea(){                   //实现父类抽象方法求圆的面积
          return 3.14f*radius*radius;
    }
    protected float getPerimeter(){              //实现父类抽象方法求圆的周长
          return 2*3.14f*radius;
    }
}
class Rectangle extends Geometric{               //继承Geometric求长方形的面积和周长
    float width;
    float height;
    Rectangle(float width,float height){         //带参数的构造方法
          this.width=width;
          this.height=height;
    }
    float getArea(){                             //实现父类抽象方法求长方形的面积
          return width*height;
    }
    float getPerimeter(){                        //实现父类抽象方法求长方形的周长
          return 2*(width*height);
    }
}
public class TextAbstract {                      //操作抽象类求图形面积的类
    public static void main(String []args){      //Java程序主入口处
          System.out.println("1.获得圆的面积与周长");
          Circle circle=new Circle(4);           //创建圆对象实例
          System.out.printf("圆的面积:%s%n",circle.getArea());
          System.out.printf("圆的周长:%s%n",circle.getPerimeter());
          System.out.println("2.获得长方形的面积与周长");
          Rectangle rectangle=new Rectangle(3,4); //创建长方形对象实例
          System.out.printf("长方形的面积:%s%n",rectangle.getArea());
          System.out.printf("长方形的周长:%s%n",rectangle.getPerimeter());
    }
}

(3)运行结果如下所示:

1.获得圆的面积与周长
圆的面积:50.24
圆的周长:25.12
2.获得长方形的面积与周长
长方形的面积:12.0
长方形的周长:24.0

源程序解读

(1)用关键字abstract创建Geometric抽象类,并声明两个抽象构造方法。默认的抽象方法拥有受保护的访问权限,即默认用protected访问修饰符修饰。简单地说,只有类内部和子类可以访问该成员。

(2)Circle和Rectangle类继承Geometric抽象类,必须实现所有的抽象方法,否则需要在关键字class前加abstract成为抽象类。

实例22 宠物结婚(封装)

本实例实现判断两个宠物是否可以结婚,运用Java面向对象特征的封装性,对类进行封装。

技术要点

实现宠物结婚的技术要点如下:

定义一个宠物,包括名称、年龄、性别、配偶等基本属性。结婚必须满足三个条件:必须是异性,同性不允许结婚;有配偶者不能结婚;要达到结婚年龄方可结婚:雄性满5岁,雌性满4岁。

实现步骤

(1)创建一个类名为TextEncapsulation.java。

(2)代码如下所示:

package com.zf.s5;                                                 //创建一个包
class Pet{                                                         //定义宠物类
    private String name;                                           //宠物名称
    private int age;                                               //宠物年龄
    private boolean gender;                                        //宠物性别
    private Pet partner;                                           //配偶
    public Pet(){}                                                 //默认构造方法
    public Pet(String name,int age,boolean gender,Pet partner){    //带参数的构造方法
          this.name=name;
          this.age=age;
          this.gender=gender;
          this.partner=partner;
    }
    public int getAge() {
          return age;
    }
    public void setAge(int age) {
          this.age = age;
    }
    public boolean isGender() {
          return gender;
    }
    public void setGender(boolean gender) {
          this.gender = gender;
    }
    public String getName() {
          return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Pet getPartner() {
        return partner;
    }
    public void setPartner(Pet partner) {
        this.partner = partner;
    }
    public boolean isOverAgeForMarry(){                  //判断是否到结婚年龄
        if(this.gender && this.getAge()>=5){
              return true;
        }
        if(!this.gender && this.getAge()>=4){
              return true;
        }
        return false;
    }
    public boolean marry(Pet pet){                       //判断是否符合结婚条件
        if(this.gender==pet.gender){
              System.out.println("根据规定,不允许同性结婚");
              return false;
        }
        if(this.partner!=null || pet.partner!=null){
              System.out.println("一方已结婚,不能再结");
              return false;
        }
        if(!this.isOverAgeForMarry()||!pet.isOverAgeForMarry()){ //未达到结婚年龄
              System.out.println("未达到结婚年龄,不能结婚,再过几年吧");
              return false;
        }
        System.out.println("恭喜 "+this.name+"和"+pet.name+"结婚!");
        this.partner=pet;
        pet.partner=this;
        return true;
    }
}
public class TextEncapsulation {                         //操作封装类模拟宠物结婚类
    public static void main(String []args){              //Java程序主入口处
        Pet pet1=new Pet("Cat1",5,true,null);
        Pet pet2=new Pet("Cat2",4,false,null);
        Pet pet3=new Pet("Cat3",6,true,null);
        Pet pet4=new Pet("Cat4",5,true,null);
        Pet pet5=new Pet("Cat5",3,false,null);
        System.out.println("1.双方都满足结婚条件,可以结吗?");
        pet1.marry(pet2);                                 //Cat1与Cat2符合结婚条件
        System.out.println("2.如果一方已经结婚,可以再结吗?");
        pet2.marry(pet3);                                 //一方结婚不能再结
        System.out.println("3.如果是同性,可以结婚吗?");
        pet3.marry(pet4);                                 //同性不能结婚
        System.out.println("4.如果一方未到结婚年龄,双方可以结婚吗?");
        pet4.marry(pet5);                                 //一方未到结婚年龄不能结婚
        System.out.println("5.一方离婚,可以与其他宠物结婚吗?");
        pet2.setPartner(null);                   //当一方离婚,就可以和其他宠物结婚了
        pet2.marry(pet3);
    }
}

(3)运行结果如下所示:

1. 双方都满足结婚条件,可以结吗?
恭喜Cat1和Cat2结婚!
2. 如果一方已经结婚,可以再结吗?
一方已结婚,不能再结
3. 如果是同性,可以结婚吗?
根据规定,不允许同性结婚
4. 如果一方未到结婚年龄,双方可以结婚吗?
未达到结婚年龄,不能结婚,再过几年吧
5. 一方离婚,可以与其他宠物结婚吗?
恭喜Cat2和Cat3结婚!

源程序解读

(1)在main()主方法中,创建多个Pet对象,全面测试宠物结婚可能出现的不同情况,保证程序的正确性。

(2)marry()方法中对不同性别、年龄以及婚配情况的五个对象分别进行匹配,满足结婚条件的即可结婚,并修改配偶的值;不满足结婚条件的宠物,拒绝结婚并将理由打印输出。

实例23 一个盒子(继承)

继承是面向对象编程技术的一块基石,因为它允许创建分等级层次的类。运用继承,能够创建一个通用类,定义一系列相关项目的一般特性。该类可以被更具体的类继承,每个具体的类都增加一些自己特有的东西。本实例介绍如何运用继承以及继承的用法。

技术要点

本实例根据二分搜索法对数组元素进行搜索。技术要点如下:

• 继承一个类,只要用extends关键字把一个类的定义合并到另一个中便可。语法格式:(public) class 子类名 extends 父类名。在Java术语中,被继承的类叫超类或基类,继承超类的类叫派生类或子类。

• 尽管子类包括超类的所有成员,但它不能访问超类中被声明成private的成员。

• 超类的一个引用变量可以被任何从该超类派生的子类的引用赋值,即超类变量可以引用子类对象。当一个子类对象的引用被赋给一个超类引用变量时,只能访问超类定义的对象的一部分。

实现步骤

(1)创建一个类名为TextExtends.java。

(2)代码如下所示:

package com.zf.s5;                                             //创建一个包
class Box{                                                     //盒子的超类
    double width;                                              //盒子的宽度
    double height;                                             //盒子的高度
    double depth;                                              //盒子的深度
    Box(Box box){                                              //带对象的构造方法
          this.width=box.width;
          this.height=box.height;
          this.depth=box.depth;
    }
    Box(double width,double height,double depth){              //带参数构造方法
          this.width=width;
          this.height=height;
          this.depth=depth;
    }
    Box(){                                                     //默认构造方法
          this.width=-1;                                       //用-1表示
          this.height=-1;
          this.depth=-1;
          System.out.println("I am a Box");
    }
    Box(double len){                                           //构造正方体
          this.width=this.height=this.depth=len;
    }
    double volume(){                                           //求体积
          return width*height*depth;
    }
}
class BoxWeight extends Box{
    double weight;
    BoxWeight(double w,double h,double d,double m){            //带参数的构造方法
          width=w;
          height=h;
          depth=d;
          weight=m;
    }
    BoxWeight(){
          System.out.println("I am a small Box");
    }
}
public class TextExtends {                                     //操作一个盒子的继承的类
    public static void main(String []args){                    //Java程序主入口处
          BoxWeight weightBox=new BoxWeight(10,20,15,34.0);    //子类实例化对象
          Box box=new Box();                                   //超类实例化对象
          double vol;
          vol=weightBox.volume();                              //子类调用超类方法
          System.out.printf("盒子box1的体积:%s%n",vol);
          System.out.printf("盒子box1的重量:%s%n",weightBox.weight);
          box=weightBox;
          vol=box.volume();                                    //调用方法
          System.out.printf("盒子box的体积:%s%n",vol);
          //System.out.printf("盒子box的重量:%s%n",box.weight);
          Box box2=new BoxWeight();                            //超类引用子类对象
      }
}

(3)运行结果如下所示:

I am a Box
I am a Box
盒子box1的体积:3000.0
盒子box1的重量:34.0
盒子box的体积:3000.0
I am a Box
I am a small Box

源程序解读

(1)程序中定义一个超类及继承超类的子类。其中在超类中定义一个盒子的高度、宽度和深度,子类继承超类的所有特片并为自己增添一个重量成员。继承可以让子类没有必要重新创建超类中的所有特征。

(2)在main()主方法中在子类带参数实例化对象时,由于继承超类,则需要打印超类默认构造方法中的语句I am a Box。当超类不带参数实例化对象时调用默认构造方法也打印出I am a Box。将子类对象赋给超类变量,weight是子类的一个属性,超类不知道子类增加的属性,则超类不能获取子类属性weight的值。超类变量引用子类对象,可以看作是先实例化超类接着实例化子类对象,则打印出超类和子类默认构造方法中的语句。

实例24 学生的生活(多态)

继承是多态的基础。多态是面向对象编程的一个重要特性,也是一个重要思想。本实例通过介绍学生的各色生活来了解多态的的定理和核心,以及重写与重载的运用。

技术要点

运用面向对象中的多态性展示学生的生活的技术要点如下:

• 多态性是面向对象编程的基础。它允许多个方法使用同一个接口。Java从多个方面支持多态性,最为突出的是:每个方法都可以被子类重写;设立interface关键字。

• 当把子类对象当作父类对象来看时,就只能调用父类中原有定义的属性和方法。子类自己扩展的属性和方法就不能调用了。

• 当把子类对象当作父类对象来看时,如果子类重写了父类中的方法,则调用该方法时调用的是子类重写后的方法。

实现步骤

(1)创建一个类名为TextPolymiorphism.java。

(2)代码如下所示:

package com.zf.s5;                                      //创建一个包
import java.util.Date;                                  //引入类
class Student{                                          //学生父类
    String name;                                        //学生名称
    Date date=new Date();
    int hour=date.getHours();                           //获得时间
    public void goToSchol(Student student){             //去上学的方式
          Student stu=new Student();
          if(this.hour<=7 && this.hour>5){
              this.clockMe(stu);
          }else{
              System.out.println("洗脸刷牙");
          }
    }
    public void clockMe(Student stu){
          System.out.println("叮铃铃...叮铃铃..."+this.name+"起床了");
    }
}
class Pupil extends Student{                           //小学生
    public void goToSchool(Student student){           //去上学的方式
          System.out.println("我是小学生");
          Pupil pupil=new Pupil();
          if(hour<=6 && hour>5){
              this.clockMe(pupil);
          }else{
              System.out.println("要锻炼身体!!!");
          }
    }
    public void clockMe(Student stu){
          System.out.println("小鸟咕咕叫..."+this.name+"起床了");
    }
    public void showInfo(){
          System.out.println("我是小学生!");
    }
}
class Undergraduate extends Student{                   //大学生
    public void goToSchool(Student stu){
    System.out.println("我是大学生");
    Undergraduate graduate = new Undergraduate();
          if (hour <= 9 && hour > 5){
              this.clockMe(graduate);
          }else{
              System.out.println("继续睡觉!!!");
          }
    }
    public void clockMe(Student me){
          System.out.println("小鼓咚咚咚..."+this.name + "起床了");
    }
    public void showInfo(){
          System.out.println("我是大学生!");
    }
}
public class TextPolymiorphism {                       //操作运用多态展示学生生活的类
    public static void main(String[] args) {           //Java程序主入口处
        System.out.println("1.当时间在5-7点时");
        //System.out.println("2.当时间不在5-7点时");
        Student student=new Pupil();                   //实例化对象
        student.name="Susan";
        student.goToSchol(student);                    //调用去上学方式
        //student.showInfo();
        student=new Undergraduate();                   //实例化对象
        student.name="Tom";
        student.goToSchol(student);                    //调用去上学方式
        Pupil pupil=new Pupil();                       //实例化小学生对象
        pupil.goToSchool(pupil);
        pupil.showInfo();
    }
}

(3)运行结果如下所示:

1. 当时间在5-7点时
小鸟咕咕叫...Susan起床了
小鼓咚咚咚...Tom起床了
我是小学生
要锻炼身体!!!
我是小学生!
2. 当时间不在5-7点时
洗脸刷牙
洗脸刷牙
我是小学生
要锻炼身体!!!
我是小学生!

源程序解读

(1)程序中定义学生超类以及继承超类的两个子类。在超类中定义去上学方式以及铃声起床两个方法。两个子类根据起床时间不同重写超类的方法。

(2)在main()主方法中,超类变量引用子类对象,创建可以把子类对象当作超类对象的实例。这样子类自己扩展的属性和方法就不能调用了,只能调用父类的,比如子类方法中的showInfo()方法在这种情况下子类就不能调用。由于子类重写父类的方法,在调用方法goToSchool()时,由于时间不同,子类重写clockMe()方法,则调用该方法时调用的是子类重写后的方法。当实例化子类对象时,子类可以调用自己扩展及重写的属性及方法。

实例25 员工薪资(接口)

Java中的类不支持多重继承,即一个类只能有一个超类。接口作为一种程序结构,很好地解决了这一问题,实现多重继承的功能。本实例介绍如何使用接口模拟员工薪资。

技术要点

使用接口模拟员工薪资的技术要点如下:

• 定义一个接口通过使用关键字interface实现。接口的定义格式:interface 接口名{接口中的变量和方法}。

• 接口中的方法都是公开的抽象方法,并且不需要提供public和abstract关键字。接口没有构造方法。

• 一个类通过使用关键字implements声明自己使用一个或多个接口。若一个类实现多个接口,接口名之间用逗号分隔。实现接口的类必须实现接口中的所有方法。要在类中实现接口的方法,方法的名字、返回类型、参数个数及类型必须和接口中的完全一致。

• 接口可以继承多个接口,但不可以继承类,不存在这种关系。

实现步骤

(1)创建一个类名为TextInterface.java。

(2)代码如下所示:

package com.zf.s5;                                     //创建一个包
class Employee {                                       //员工类
    private String name;                               //员工名称
    private String gender;                             //员工性别
    private int age;                                   //员工年龄
    private int salary;                                //员工薪资
    public Employee(String name, String gender, int age, int salary) {
          super();
          this.name = name;
          this.gender = gender;
          this.age = age;
          this.salary = salary;
    }
    public int getAge() {
          return age;
    }
    public void setAge(int age) {
          this.age = age;
    }
    public String getGender() {
          return gender;
    }
    public void setGender(String gender) {
          this.gender = gender;
    }
    public String getName() {
          return name;
    }
    public void setName(String name) {
          this.name = name;
    }
    public int getSalary() {
          return salary;
    }
    public void setSalary(int salary) {
          this.salary = salary;
    }
}
interface PersonForm {                               //定义输出二维表的接口
    public int getFormCol();                         //获得表格的列数
public int getFormRow(); //获得表格的行数
public String getValue(int row, int col); //获得指定的某行某列的值
public String getColName(int col); //获得指定的列名
} class FormA implements PersonForm { //定义一个类实现接口 String[][] data; //定义一个二维数组 public FormA(String[][] data) { //带参数的构造方法 this.data = data; } public String getColName(int col) { //获得指定的列名 return data[0][col]; } public int getFormCol() { //获得表格的列数 return data[0].length; } public int getFormRow() { //获得表格的行数 return data.length - 1; } public String getValue(int row, int col) { //获得指定的某行某列的值 return data[row + 1][col]; } } class FormB implements PersonForm { //定义一个类实现接口 private Employee[] data; public FormB(Employee[] data) { //带参数的构造方法 this.data = data; } public String getColName(int col) { switch (col) { case 0: return "姓名\t|"; case 1: return "性别\t|"; case 2: return "年龄\t|"; case 3: return "工资\t|"; default: return null; } } public int getFormCol() { return 4; } public int getFormRow() { return data.length; } public String getValue(int row, int col) { switch (col) { case 0: return data[row].getName(); case 1: return data[row].getGender(); case 2: return data[row].getAge() + ""; case 3: return data[row].getSalary() + ""; default: return null; } } } class Table { //表格类 private PersonForm form; public Table(PersonForm form) { //带参数的构造方法 this.form = form; } public void display() { //显示格式和取值 for (int i = 0; i < form.getFormCol(); i++) { //循环显示列名 System.out.print(form.getColName(i)); } System.out.println(); System.out.println("---------------------------------"); for (int i = 0; i < form.getFormRow(); i++) { //循环显示行信息 for (int j = 0; j < form.getFormCol(); j++) { //循环显示列信息 System.out.print(form.getValue(i, j) + "\t|"); } System.out.println(); } } } public class TextInterface { //操作接口的类 public static void main(String[] args) { //Java程序主入口处 String[][] str = new String[][] { //创建二维数组存储数据 { "name\t|", "gender\t|", "age\t|", "salary\t|"}, { "Tom", "male", "20", "2000" }, { "Lingda", "female", "21", "2100" }, { "Susan", "female", "22", "2200" }, { "Ansen", "female", "24", "2500" }}; PersonForm form=new FormA(str); //接口变量引用类对象 Table table1=new Table(form); //创建表格实例 table1.display(); //显示员工薪资信息 System.out.println("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"); Employee em1= new Employee("汤姆","男",20,2000); //创建员工对象用一维数组存储 Employee em2 = new Employee("玲达", "女", 21, 2100); Employee em3 = new Employee("苏萨", "女", 22, 2200); Employee em4 = new Employee("爱瑞卡", "男", 23, 2300); Employee em5 = new Employee("安臣", "女", 24, 2500); Employee[] data = { em1, em2, em3, em4, em5 }; //创建员工数组 PersonForm form1 = new FormB(data); //接口变量引用类对象 Table table2 = new Table(form1); //创建表格实例 table2.display(); //显示员工薪资信息 } }

(3)运行结果如下所示:

name      |gender |age   |salary |
-------------------------------------------------
Tom       |male     |20    |2000     |
Lingda    |female |21    |2100     |
Susan     |female |22    |2200     |
Ansen     |female |24    |2500     |
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
姓名       |性别     |年龄   |工资     |
-------------------------------------------------
汤姆       |男       |20    |2000     |
玲达       |女       |21    |2100     |
苏萨       |女       |22    |2200     |
爱瑞卡     |男       |23    |2300     |
安臣       |女       |24    |2500     |

源程序解读

(1)程序中定义一个员工类、一个查看员工薪资的接口和两个实现接口的类。在main()主方法中首先创建二维数组并将数据信息填入数组,定义接口的引用使用了多态。接口的引用必须是实现了接口的类的对象,所以使用类FormA创建form对象,再根据form来创建Table表格的对象table1,调用display()方法输出数据。

(2)再创建5个Employee对象,用数组存储。接口引用同样使用多态,方式与创建form对象一样。

实例26 我的类型(instanceof运算符)

instanceof是Java的一个二元操作符,用于检测对象的类型。检测它左边的对象与右边的类的实例,返回boolean类型的数据。本实例运用instanceof进行类型判断以及类型转换。

技术要点

使用instanceof运算符的技术要点如下:

• instanceof用来判断某个对象是不是这个类的实例并返回一个布尔值。如果是,则返回true,否则返回false。在运算时,会做一个自动类型兼容的处理。

• 类的对象与类作instanceof操作会返回true;子类对象与父类作instanceof操作返回true;所有对象与Object作instanceof操作返回的都是true。

实现步骤

(1)创建一个类名为TextInstanceOf.java。

(2)代码如下所示:

package com.zf.s5;                                     //创建一个包
public class TextInstanceOf {                          //操作instanceof运算符的类
    static class ObjectA{                              //静态内部超类
          static String A="Object";
    }
    static class ObjectB extends ObjectA{              //静态内部子类
          static void showInfo(){
              System.out.printf("超类的静态属性%s的值:%s%n","A",A);
          }
    }
    public static void main(String []args){            //Java程序主入口处
          ObjectA a=new ObjectA();
          ObjectB b=new ObjectB();
          if(a.A instanceof Object){                    //静态属性A是否是Object类型
              System.out.println("静态属性A是Object类型");
          }else{
              System.out.println("静态属性A不是Object类型");
          }
          if(a.A instanceof String){                    //静态属性A是否是String类型
              System.out.println("静态属性A是String类型");
          }else{
              System.out.println("静态属性A不是String类型");
          }
          if(null instanceof Object){                   //null是否是Object类型
              System.out.println("null是Object类型");
          }else{
              System.out.println("null不是Object类型");
          }
          if(a instanceof ObjectA){                     //检测对象a是否为ObjectA类型
              System.out.println("对象a是ObjectA类型");
          }else{
              System.out.println("对象a不是ObjectA类型");
          }
          if(b instanceof ObjectA){                     //检测对象b是否为ObjectA类型
              System.out.println("对象b是ObjectA类型");
          }else{
              System.out.println("对象b不是ObjectA类型");
          }
          if(a instanceof ObjectB){                     //检测a是否为ObjectB类型
              System.out.println("对象a是ObjectB类型");
          }else{
              System.out.println("对象a不是ObjectB类型");
          }
          if(b instanceof ObjectB){                     //检测b是否为ObjectB类型
              System.out.println("对象b是ObjectB类型");
          }else{
          System.out.println("对象b不是ObjectB类型");
          }
      }
}

(3)运行结果如下所示:

静态属性A是Object类型
静态属性A是String类型
null不是Object类型
对象a是ObjectA类型
对象b是ObjectA类型
对象a不是ObjectB类型
对象b是ObjectB类型

源程序解读

(1)程序中定义静态内部类。需要注意的是:仅仅只有内部类能够被声明为static类型,通常声明一个普通类是不能使用static的,否则编译出错。编译器会自动给内部类加上一个reference,指向产生它的那个外部类的对象,如果不想要或者说不需要这个reference,那么就可以把这个内部类声明为static,禁止这个reference产生。

(2)在main()主方法中进行了一系列的判断:字符串是否是对象类型、null是否是对象、子类是否是超类类型以及超类是否是子类类型。子类是超类的扩展,所以子类属于超类类型。由于是单一继承,则超类不能继承子类,所以超类不属于子类类型。

实例27 匿名内部类

匿名内部类是一种特殊的局部内部类,这种内部类没有类名。适用于只使用一次并且不需要多次创建对象的类。使用匿名内部类可以使类代码与创建对象同时完成,提高代码的可维护性与灵活性。本实例介绍如何使用匿名内部类及使用规范。

技术要点

使用匿名内部类的技术要点如下:

• 匿名内部类的语法格式:“new实现的接口名或继承的类名(){};”。

• 匿名内部类是惟一一种没有构造方法的类。所以匿名内部类的使用范围有限。大部分匿名内部类用于接口的回调。

• 匿名内部类在编译时由系统自动起名为Out$1.class。

• 匿名内部类用于继承其他类或实现接口,并不需要增加额外的方法,只是对继承方法的实现与重写。

实现步骤

(1)新建一个类名为TextAnonymity.java。

(2)代码如下所示:

package com.zf.s5;                                     //创建一个包
interface AnonymityInter {                             //定义一个接口
    public void method();
}
abstract class AnonymityAbstract {                          //定义一个抽象类
    int count;
abstract void method(); } class AnonymityClass{ //定义一个类 public void method(){ System.out.println("这是一个普通的类"); } } public class TextAnonymity { //操作匿名内部类的类 public static void main(String[] args) { //Java程序主入口处 AnonymityInter inter = new AnonymityInter() { //实现接口的匿名内部类 public void method() { //实现接口中的方法 System.out.println("在匿名内部类中实现接口的方法"); } }; inter.method(); //调用方法 AnonymityAbstract aa = new AnonymityAbstract() { //实现抽象类的匿名内部类 { count = (int) (10 * (Math.random()+1)); //随机获得数字 } @Override void method() { //重写方法 System.out.println("您的幸运数字是:" + count); } }; aa.method(); //调用方法 AnonymityClass ac=new AnonymityClass(){ //实现普通类的匿名内部类 public void method(){ //覆盖类的方法 System.out.println("覆盖类的方法"); } }; ac.method(); //调用方法 } }

(3)运行结果如下所示:

在匿名内部类中实现接口的方法
您的幸运数字是:13
覆盖类的方法

源程序解读

(1)在程序中包含用匿名内部类实现接口的方法、继承抽象类重写方法以及对普通类的方法进行重写。在重写抽象类的匿名内部类中,在语句中定义要初始化的数字需要将初始化的数字放入大括号中,否则编译不通过。

(2)在实现普通类的匿名内部类中,对method()方法进行重写,覆盖了原普通类中方法的实现。重写也称为覆盖,满足覆盖需要注意:覆盖的方法的返回值必须和被覆盖的方法的返回值一致;覆盖的方法所抛出的异常必须和被覆盖的方法所抛出的异常一致,或者是其子类;被覆盖的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行覆盖;覆盖的方法的标志必须要和被覆盖的方法的标志完全匹配,才能达到覆盖的效果。

实例28 静态内部类

静态内部类作为类的静态成员存在于某个类中,也称为嵌套类。在创建静态内部类时不需要外部类对象的存在。其实质是一个放置在某个类内部的普通类。要定义一个静态类只需在类的定义中加入关键字static即可。本实例介绍如何使用静态内部类以及静态内部类的使用规范。

技术要点

使用静态内部类的技术要点如下:

• 声明静态内部类的语法格式:static class类名。静态内部类不可以用private进行定义。

• 关键字static说明静态内部类在创建对象时不依赖于外部对象的存在,并不是说这个类本身是静态的。

• 静态内部类只能访问外部类的静态成员,不能访问外部类的非静态成员。它可以定义静态或者非静态的成员。

• 静态内部类的对象可以直接生成,不需要外部类的对象来生成,可以看作静态内部类是一顶级类。

实现步骤

(1)新建一个类名为TextStaticInnerClass.java。

(2)代码如下所示:

package com.zf.s5;                               //创建一个包
public class TextStaticInnerClass {              //操作静态内部类的类
    private static int num=1;
    private int count=10;
    public static void outer(){
          System.out.println("外部类的静态方法!");
    }
    public void outer_1(){
          System.out.println("外部类的非静态方法!");
    }
    static class Inner{
          static int inner_num=100;
          int inner_count=200;
          static void inner_outer(){
              System.out.println("访问外部类的静态成员"+num);
              outer();                             //访问外部类的静态方法
          }
          void inner_outer_1(){
              System.out.println("静态内部类的非静态方法");
              outer();                             //访问外部类的静态方法
          }
    }
    public void outer_2(){             //外部类访问静态内部类的静态成员:内部类.静态成员
        System.out.println(Inner.inner_num);
        Inner.inner_outer();           //访问静态内部类的静态方法
        Inner inner=new Inner();       //实例化对象
        inner.inner_outer_1();         //访问静态内部类的非静态方法
    }
    public static void main(String[] args) {//Java程序主入口处
        TextStaticInnerClass inner=new TextStaticInnerClass();
        inner.outer_2();               //调用方法
    }
}

(3)运行结果如下所示:

100
访问外部类的静态成员1
外部类的静态方法!
静态内部类的非静态方法
外部类的静态方法!

源程序解读

(1)在程序类中定义静态变量、非静态变量、静态方法、非静态方法以及一个静态内部类。在静态内部类中定义静态变量、非静态变量、静态主法以及非静态方法。默认的非静态方法的访问修饰符为protected,可以被该类所在的包内成员以及非包内的子类成员访问。

(2)在静态内部类的方法中只能访问外部类的静态变量及静态方法。外部类访问静态内部类的语法格式为:静态内部类.静态变量(或静态方法())。如果外部类访问内部类的非静态成员,需要先实例化内部类。

实例29 成员内部类

成员内部类可以看作是外部类的非静态成员的内部类。内部类和外部类的其他成员是同一级别的,也是外部类的一个成员。本实例介绍如何使用成员内部类以及成员内部类的使用规范。

技术要点

使用成员内部类的技术要点如下:

• 成员内部类的语法格式:class外部类名{(public/private/protected) class 内部类名{}...}。在成员内部类中,可以拥有自己的成员变量和方法,是一个独立的类,内部类和外部类的实例变量是可以共存的。

• 成员内部类可以访问外部类的私有成员或属性。即使将外部类声明为private,成员内部类依然是可见的。

• 内部类和外部类在编译时是两个不同的类,内部类对外部类没有任何依赖。成员内部类一旦编译成功,就会成为与外部类完全不同的类。

实现步骤

(1)新建一个类名为TextMemberInnerClass.java。

(2)代码如下所示:

package com.zf.s5;                                         //创建一个包
class OuterClass{
    String outer="outer class";
    private int number=1;
    private static int count=10;
    public OuterClass(){}                                  //默认构造方法
    public OuterClass(int number,int count){               //带参数的构造方法
          this.number=number;
          this.count=count;
          System.out.println("传入的数分别为:"+this.number+" 与 "+this.count);
    }
    private void outer_1(){                                 //外部类非静态方法
          System.out.println("调用外部类的非静态方法");
          InnerClass inner=this.new InnerClass();
    }
    private static void outer_2(){                          //外部类的静态方法
          System.out.println("调用外部类的静态方法");
          //InnerClass inner=this.new InnerClass();
    }
    class InnerClass{                                       //定义成员内部类
          String outer="inner class";
          public InnerClass(){                              //默认构造方法
              System.out.println("成员内部类的构造方法");
          }
          protected void communicate(){                     //交流的方法
              System.out.println("外部类的字符串:"+OuterClass.this.outer);
              System.out.println("内部类的字符串:"+this.outer);
          }
          protected void inner_1(){                         //成员内部类的非静态方法
              System.out.println("调用外部类的私有方法");
              outer_1();
              outer_2();
          }
    }
}
public class TextMemberInnerClass {                         //操作成员内部类的类
    public static void main(String []args){                 //Java程序主入口处
          OuterClass outer=new OuterClass(10,20);           //实例化对象
          OuterClass.InnerClass inner=outer.new InnerClass(); //实例化成员内部类
          System.out.println("OuterClass="+outer.outer);
          System.out.println("InnerClass="+inner.outer);
          inner.communicate();                                    //调用方法
          inner.inner_1();                                        //调用方法
    }
}

(3)运行结果如下所示:

传入的数分别为:10 与 20
成员内部类的构造方法
OuterClass=outer class
InnerClass=inner class
外部类的字符串:outer class
内部类的字符串:inner class
调用外部类的私有方法
调用外部类的非静态方法
成员内部类的构造方法
调用外部类的静态方法

源程序解读

(1)程序中定义外部类及其内部的成员变量和方法以及成员内部类。OuterClass类中的非静态方法outer_1()可以实例化InnerClass成员内部类,但非静态方法不能实例化成员内部类。在成员内部类InnerClass中communicate()方法可以访问外部类的静态变量或静态方法,语法格式为:“外部类名.this.属性(或方法())”。inner_1()方法可以调用外部类的私有方法。

(2)在main()主方法中要实例化成员内部类,需要先实例化外部类。语法格式:Outer o=new Outer();Outer.Inner in=o.new.Inner()。

实例30 局部内部类

局部内部类和局部变量一样,是指在方法中定义的内部类。它是一个只有在局部有效的内部类,所以只能在其有效的范围的位置访问或创建其对象。本实例介绍如何使用局部内部类以及局部内部类的使用规范。

技术要点

使用局部内部类的技术要点如下:

• 局部内部类的语法格式:

class 外部类名{
    (访问修饰符) 返回类型 方法名(参数){
          class 局部内部类名{}}}

• 局部内部类可以访问外部类的私有实例变量和局部常量。与局部变量类似,在局部内部类前不加修饰符public和private,其范围为定义它的代码块。

• 在外部类的外部不能直接访问局部内部类,这样就有效地保证了局部内部类对外是不可见的。为了能够用外部类访问到局部内部类,可以通过局部内部类和接口达到一个强制的弱耦合关系,用局部内部类来实现接口,并在方法中返回接口类型,这样便可一方面屏蔽类的可见性;另一方面在类的外部访问到局部内部类。

实现步骤

(1)新建一个类名为TextLocalInnerClass.java。

(2)代码如下所示:

package com.zf.s5;                                    //创建一个包
interface LocalInterface {                            //定义一个接口
    public void local_1();                            //定义三个方法
    public void local_2();
    public void local_3();
}
class Outer {                                        //外部类
    private int a1=10;
    private void privateShow(){
        System.out.println("外部类的私有非静态变量");
    }
    public void show(){
        System.out.println("外部类的非静态方法");
    }
    public LocalInterface method() {                 //方法返回局部内部类实现的接口
        final int number = 20;
        class Inner implements LocalInterface {      //定义局部内部类实现接口
              public void local_1() {
              show();
        }
        public void local_2() {
              System.out.println("显示局部内部类的成员常量:"+number);
              System.out.println("调用外部类的成员变量:"+a1);
        }
        public void local_3() {
              privateShow();
              }
        }
        return new Inner();
    }
}
public class TextLocalInnerClass {                    // 操作局部内部类的类
    public static void main(String[] args) {          //Java程序主入口处
        Outer out = new Outer();                      //实例化对象
        LocalInterface local = out.method();          //调用方法返回接口
        local.local_1();                              //调用接口的方法
        local.local_2();
        local.local_3();
    }
}

(3)运行结果如下所示:

外部类的非静态方法
显示局部内部类的成员常量:20
调用外部类的成员变量:10
外部类的私有非静态变量

源程序解读

(1)程序中定义接口、外部类以及外部类的方法和属性。其中在method()方法中创建局部内部类Inner来实现接口中的方法并返回接口类型,这样才能访问到局部内部类的方法。其中local_1()方法中调用外部类的非静态方法,local_2()方法中显示局部内部类的成员常量和调用外部类的成员变量。local_3()方法中调用外部类的私有的非静态方法。

(2)需要注意的是:在局部内部类中声明局部常量必须是final型的,不是final型的是无法访问的。

实例31 单例模式(Singleton)

单例模式能够确保一个类只有一个实例。自行提供这个实例并向整个系统提供这个实例。本实例介绍如何使用这种设计模式及单例模式的用法。

技术要点

实现单例模式的技术要点如下:

• 单例模式有两种实现方式:一种是将类的构造方法私有化,用一个私有的类变量instance保存类的实例,在加载类时,创建类的实例,并将实例赋给instance;再提供一个公有的静态方法getInstance,用于获取类的惟一实例,该方法直接返回instance。另一种是将类的构造方法私有化,用一个私有的类变量instance保存类的实例,在加载类时,将null赋给instance;再提供一个公有的静态方法getInstance,用于获取类的惟一实例,该方法首先判断instance是否为null,如果为null,则创建实例对象,否则,直接返回instance。

• 两种方式的区别在于:前者被加载时,类的惟一实例被创建;后者在第一个调用getInstance()方法时,类的惟一实例被创建,但需要在getInstance()方法的声明中使用synchronized关键字,保证某一时刻只有一个线程调用此方法。

实现步骤

(1)新建一个类名为TextSingleton.java。

(2)代码如下所示:

package com.zf.s5;                          //创建一个包
class OneSingleton {                        //第一种方式实现单例模式
    private static int number = 0;          //私有属性
    private static OneSingleton instance = new OneSingleton();//OneSingleton的惟一实例
    private OneSingleton() {                //构造函数私有防外界构造OneSingleton实例
    }
    public static OneSingleton getInstance(){ //获取OneSingleton的实例
          return instance;
    }
    public synchronized int getNumber() {   //synchronized关键字表示方法是线程同步
          return number;                    //任一时刻最多只能有一个线程进入该方法
    }
    public synchronized void nextNumber() { //将number加1
          number++;
    }
}
class TwoSingleton {
    private static int number = 0;          //私有属性
    private static TwoSingleton instance = null;     //TwoSingleton的惟一实例
    private TwoSingleton() {                //构造函数私有防外界构造TwoSingleton实例
    }
    //synchronized关键字表示方法是线程同步
    public static synchronized TwoSingleton getInstance() {
                                                      //任一时刻最多只能有一个线程进入该方法
        if (instance == null) {              //判断是否instance为空,为空则创建
              instance = new TwoSingleton();
        }
        return instance;
    }
    public synchronized int getNumber() {
        return number;
    }
    public synchronized void nextNumber() {
        number++;
    }
}
public class TextSingleton {                              //操作单例模式的类
    public static void main(String[] args) {              //Java程序主入口处
        OneSingleton one1 = OneSingleton.getInstance();   //调用方法获得实例
        OneSingleton one2 = OneSingleton.getInstance();   //调用方法获得实例
        System.out.println("用OneSingleton实现单例模式");
        System.out.println("调用nextNumber方法前:");
        System.out.println("one1.number=" + one1.getNumber());
        System.out.println("one2.number=" + one2.getNumber());
        one1.nextNumber();                                //调用方法
        System.out.println("调用nextNumber方法后:");
        System.out.println("one1.number=" + one1.getNumber());
        System.out.println("one2.number=" + one2.getNumber());
        TwoSingleton two1 = TwoSingleton.getInstance();   //调用方法获得实例
        TwoSingleton two2 = TwoSingleton.getInstance();   //调用方法获得实例
        System.out.println("用TwoSingleton实现单例模式");
        System.out.println("调用nextNumber方法前:");
        System.out.println("two1.number=" + two1.getNumber());
        System.out.println("two2.number=" + two2.getNumber());
        two1.nextNumber();                                //调用方法
        System.out.println("调用nextNumber方法后:");
        System.out.println("two1.number=" + two1.getNumber());
        System.out.println("two2.number=" + two2.getNumber());
    }
}

(3)运行结果如下所示:

用OneSingleton实现单例模式
调用nextNumber方法前:
one1.number=0
one2.number=0
调用nextNumber方法后:
one1.number=1
one2.number=1
用TwoSingleton实现单例模式
调用nextNumber方法前:
two1.number=0
two2.number=0
调用nextNumber方法后:
two1.number=1
two2.number=1

源程序解读

(1)OneSingleton和TwoSingleton类都实现了单例模式,区别是前者在类被加载的时候就创建类的惟一对象,而后者是在第一次调用getInstance()方法时才创建类的惟一实例,因此也被称为lazy initialization。

(2)在TwoSingleton类中,getInstance()方法声明中使用了synchronized(同步)关键字,以保证同一时刻只有一个线程进入该方法,这样,就保证了只会新建一个对象。

(3)单例模式的实现方法是将构造函数私有,以防止外界通过调用构造函数创建类的对象。将类的惟一对象保存为静态私有属性,然后提供一个静态公有方法获取该惟一对象,可以保证每次返回的都是同一个对象。

实例32 开车(简单工厂模式)

工厂模式提供创建对象的接口,是最常用的设计模式。本实例根据工厂模式的分类不同,介绍工厂模式之一的简单工厂模式的使用方法及使用规则。

技术要点

实现简单工厂模式的技术要点如下:

• 简单工厂模式又称静态工厂模式。从命名上就可以看出这个模式很简单:定义一个用于创建对象的接口。

• 简单工厂模式由工厂类角色、抽象产品角色和具体产品角色组成。

• 工厂类角色是本模式的核心,含有一定的商业逻辑和判断逻辑,它往往由一个具体类实现。

• 抽象产品角色一般是具体产品继承的父类或者实现的接口,由接口或者抽象类来实现。

• 具体产品角色由一个具体类实现。

实现步骤

(1)新建一个类名为TextSimpleFactory.java。

(2)代码如下所示:

package com.zf.s5;                                    //创建一个包
interface Car {                                       //车的父类
    public void driver();                             //开车
}
class Benz implements Car {                           //奔驰车
    public void driver() {
          System.out.println("今天咱开奔驰!");
    }
}
class Bike implements Car {                           //自行车
    public void driver() {
        System.out.println("唉,现在经济危机,只能骑自行车了呀!");
    }
}
class Bmw implements Car {                            //宝马
    public void driver() {
        System.out.println("今天开宝马吧!");
    }
}
class Driver {                                        //车的工厂
    public static Car driverCar(String s) throws Exception {
        if (s.equalsIgnoreCase("Benz")) {             //判断传入参数返回不同的实现类
              return new Benz();
        } else if (s.equalsIgnoreCase("Bmw")) {
              return new Bmw();
        } else if (s.equalsIgnoreCase("Bike")) {
              return new Bike();
        } else {
              throw new Exception();                  //抛出异常
        }
    }
}
public class TextSimpleFactory {                      //操作简单工厂模式的类
    public static void main(String[] args) {          //Java程序主入口处
        try {
              Car car = Driver.driverCar("Bike");     //调用方法返回车的实例
              System.out.println("经理,今天开什么车呀?");
              car.driver();                           //调用方法开车
        } catch (Exception e) {                       //捕获异常
              System.out.println("开车出现问题......");
        } finally {                                   //代码总被执行
              System.out.println("......");
        }
    }
}

(3)运行结果如下所示:

经理,今天开什么车呀?
唉,现在经济危机,只能骑自行车了呀!
......

源程序解读

程序中定义一个车的接口、三个实现接口的开车方式类以及调用车的工厂类。在Driver类中的driverCar()方法根据传入参数的不同,判断返回不同的实现接口的类。这样实现了开车的方便选择性。在程序中将多个类放在一个文件中,需要注意的是只有一个类前被声明为public,该类的类名必须与文件名相同。

实例33 旅游(工厂方法模式)

工厂模式提供创建对象的接口,是最常用的设计模式。本实例根据工厂模式的分类不同,介绍工厂模式之二的工厂方法模式的使用方法及使用规则。

技术要点

实现工厂方法模式的技术要点如下:

• 工厂方法模式去掉了简单工厂模式中工厂方法的静态属性,使得它可以被子类继承。这样在简单工厂模式里集中在工厂方法上的压力可以由工厂方法模式里不同的工厂子类来分担。

• 工厂方法模式由抽象工厂角色、具体工厂角色、抽象产品角色和具体产品角色组成。

• 抽象工厂角色是工厂方法模式的核心,它与应用程序无关,是具体工厂角色必须实现的接口或者必须继承的父类,它由抽象类或者接口来实现。

• 具体工厂角色含有与具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。抽象产品角色是具体产品继承的父类或者是实现的接口,一般由抽象类或者接口来实现。

• 具体产品角色由具体的类来实现。

实现步骤

(1)新建一个类名为TextMethodFactory.java。

(2)代码如下所示:

package com.zf.s5;                                    //创建一个包
interface Tour {                                      //旅游类
    public void visit();                              //旅游方式
}
interface Visit {                                     //具体旅游方式
    public Tour visitStyle();
}
class GoAbroad implements Tour {                      //出国旅游
    public void visit() {                             //实现接口的方法
          System.out.println("咱们去国外旅游吧!");
    }
}
class Hangzhou implements Tour {                      //杭州旅游
    public void visit() {
          System.out.println("杭州风景优美,咱去那儿吧");
    }
}
class Home implements Tour {                          //在家
    public void visit() {
          System.out.println("唉,没有钱哪也去不了!");
    }
}
class GoAbroadVisit implements Visit {                //返回出国旅游对象
    public Tour visitStyle() {
          return new GoAbroad();
    }
}
class HangZhouVisit implements Visit {                //返回杭州旅游对象
    public Tour visitStyle() {
          return new Hangzhou();
    }
}
class HomeVisit implements Visit {                    //返回在家对象
    public Tour visitStyle() {
          return new Home();
    }
}
public class TextMethodFactory {                      //操作工厂方法模式的类
    public static void main(String[] args) {          //Java程序主入口处
          try {
              Visit visit = new HangZhouVisit();/      /接口变量引用实现类对象
              Tour tour = visit.visitStyle();          //调用方法返回旅游实例
              System.out.println("今年咱们去哪儿旅游呀?");
              tour.visit();                           //调用方法获得旅游方式
          } catch (Exception e) {                     //捕获异常
              System.out.println("今年旅游计划取消?");
          } finally {                                 //代码总被执行
              System.out.println("......");
          }
    }
}

(3)运行结果如下所示:

今年咱们去哪儿旅游呀?
杭州风景优美,咱去那儿吧
......

源程序解读

程序中定义两个接口、三个接口的实现类以及三个调用实现类的工厂类。可以看出使用工厂方法模式,使得对象的数量成倍增长。当旅游(对象)的方式非常多时,会出现大量的与之对应的工厂对象,这并不是我们所希望的。因为如果不能避免这种情况,可以考虑使用简单工厂模式与工厂方法模式相结合的方式来减少工厂类,即对于旅游(对象)方式类似的种类使用简单工厂模式。

实例34 花园布局(抽象工厂模式)

工厂模式提供创建对象的接口,是最常用的设计模式。本实例根据工厂模式的分类不同,介绍工厂模式之三的抽象工厂模式的使用方法及使用规则。

技术要点

实现抽象工厂模式的技术要点如下:

• 抽象工厂模式和工厂方法模式的区别就在于需要创建对象的复杂程度上。而且抽象工厂模式是这三种模式中最为抽象、最具一般性的。

• 使用抽象工厂模式需要满足以下条件:系统中有多个产品族,而系统一次只可能消费其中一族产品;同属于同一个产品族的产品。

• 抽象工厂模式的组成部分与工厂方法模式的组成部分相同。

实现步骤

(1)新建一个类名为TextAbstractFactory.java。

(2)代码如下所示:

package com.zf.s5;                                    //创建一个包
class Plant {                                         //植物
    String name;                                      //植物名称
    public Plant(String name) {                       //带参数的构造方法
          this.name = name;
    }
    public String getName() {
          return name;
    }
    public void setName(String name) {
          this.name = name;
    }
}
abstract class Garden {                               //花园类
    public abstract Plant getShade();                 //花台上的植物
    public abstract Plant getCenter();                //中间的植物
    public abstract Plant getBorder();                //边上的植物
}
class Elegant extends Garden {                        //典雅型
    public Plant getBorder() {
          return new Plant("兰草");
    }
    public Plant getCenter() {
          return new Plant("榕树");
    }
    public Plant getShade() {
          return new Plant("郁金香");
    }
}
class Practical extends Garden                        //实用型
{
    public Plant getShade() {
          return new Plant("葡萄");
    }
    public Plant getCenter() {
          return new Plant("石榴");
    }
    public Plant getBorder() {
          return new Plant("丝瓜");
    }
}
class Lazy extends Garden                              //懒人型
{
    public Plant getShade() {
          return new Plant("月季");
    }
    public Plant getCenter() {
          return new Plant("茶花");
    }
    public Plant getBorder() {
          return new Plant("竹");
    }
}
class GardenMaker {                                    //抽象工厂类
    private static Garden garden;
    public static Garden getGarden(String type) {
          garden = new Elegant();                      //默认情况
          if (type.equals("实用型"))
              garden = new Practical();
          if (type.equals("懒人型"))
              garden = new Lazy();
          return garden;
    }
}
public class TextAbstractFactory {                     //操作抽象工厂模式的类
    public static void main(String[] args) {           //Java程序主入口处
          Garden garden=GardenMaker.getGarden("实用型");//传入参数调用方法获得实例
          Plant shade=garden.getShade();               //获取花园植物
          Plant center=garden.getCenter();
          Plant border=garden.getBorder();
          System.out.println("花台上的植物:"+shade.getName());
          System.out.println("中间的植物:"+center.getName());
          System.out.println("边上的植物:"+border.getName());
    }
}

(3)运行结果如下所示:

花台上的植物:葡萄
中间的植物:石榴
边上的植物:丝瓜

源程序解读

(1)程序中定义一个植物类、一个花园的抽象类、三个不同花园的抽象类的实现类以及一个抽象工厂类。程序中使用抽象工厂类实现对不同花园的选择。使用这样模式能够将实现类和它们的生成过程完全分割开来。实现类被隐藏在工厂类中,调用者不必知道实现类的任何信息。

(2)虽然抽象工厂中的子类继承了同一个超类,但是它们也可以拥有与其他子类不同的方法。

实例35 几何图形(适配器模式)

适配器(Adapter)模式将一个类的接口转换成客户希望的另外一个接口,能够使得原本由于接口不兼容而不能一起工作的那些类可以协同工作了。本实例介绍如何使用适配器模式以及使用的规则。

技术要点

实现适配器模式的技术要点如下:

• 目标类是Adapter的父类,Adapter需要继承目标类,以具备目标类提供的功能。

• Adapter是适配器模式中的适配者,它本身并不实现任何功能,但是能提供功能,即它能将其他类的功能据为己有。

• Adapter是适配器模式中的被适配者,Adapter需要实现Adapter的接口,以提供Adapter提供的功能。在Adapter的构造方法中,传入一个具体的Adapter对象,在实现Adapter接口定义的方法中,调用Adapter对象的相应方法。

实现步骤

(1)新建一个类名为TextAdapter.java。

(2)代码如下所示:

package com.zf.s5;                                   //创建一个包
interface Shape {                                    //定义形状接口
    public void setPosition(int position);           //设置位置
    public int getPositioin();                       //获得位置
    public void move();                              //移动位置
    public void display();                           //展示形状
}
class Oblong implements Shape { //定义一个矩形形状 int position; public void setPosition(int position) { this.position = position; } public int getPositioin() { return this.position; } public void move() { System.out.println("矩形图形已经移动到:" + 3 * position); } public void display() { System.out.println("矩形图形的位置:" + position); } } class Circular{ //定义一个圆形形状,没有实现Shape接口 int position; public int getPosition() { return position; } public void setPosition(int position) { this.position = position; } public void display(){ System.out.println("圆形图形的位置:"+position); } } class CircularAdapter implements Shape{ private Circular circular; public CircularAdapter(){ circular = new Circular(); } public void display() { circular.display(); } public int getPositioin() { return circular.getPosition(); } public void move() { //为Circle扩展move()方法 System.out.println("圆形图形已经移动到:"+3*getPositioin()); } public void setPosition(int position) { circular.setPosition(position); } } public class TextAdapter { //操作适配器模式的类 public static void main(String[] args) { //Java程序主入口处 Oblong oblong = new Oblong(); //实例化对象 oblong.setPosition(100); //设置位置 oblong.display(); //显示位置 oblong.move(); //移动位置 CircularAdapter circular = new CircularAdapter(); //实例化适配器 circular.setPosition(200); //设置位置 circular.display(); //显示位置 circular.move(); //移动位置 } }

(3)运行结果如下所示:

矩形图形的位置:100
矩形图形已经移动到:300
圆形图形的位置:200
圆形图形已经移动到:600

源程序解读

程序中定义一个接口、一个接口的实现类、普通类以及普通类的适配器类。普通类Circular看作是目标类,CircularAdapter是Adapter, Shape是Adaptee角色。将CircularAdapter和目标(Circular)进行适配,CircularAdapter将具有Shapepg定义的所有功能。定义CircularAdapter来完成没有实现的方法,而不用重新写一个Circular类。Adapter模式实际上是将组合方法和继承方法综合运用。