欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java 学习笔记 ------第六章 继承与多态

程序员文章站 2022-06-12 20:45:19
...

本章学习目标:

  • 了解继承的目的
  • 了解继承与多态的关系
  • 知道如何重新定义方法
  • 认识java.lang.object
  • 简介垃圾回收机制

一、继承

继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。继承可以理解为一个对象从另一个对象获取属性的过程。
如果类A是类B的父类,而类B是类C的父类,我们也称C是A的子类,类C是从类A继承而来的。在Java中,类的继承是单一继承,也就是说,一个子类只能拥有一个父类
继承中最常使用的两个关键字是extends和implements。
这两个关键字的使用决定了一个对象和另一个对象是否是IS-A(是一个)关系。
通过使用这两个关键字,我们能实现一个对象获取另一个对象的属性。
所有Java的类均是由java.lang.Object类继承而来的,所以Object是所有类的祖先类,而除了Object外,所有类必须有一个父类。
通过过extends关键字可以申明一个类是继承另外一个类而来的,一般形式如下:

// A.java
public class A {
    private int i;
    protected int j;
 
    public void func() {
 
    }
}
 
// B.java
public class B extends A {
}

以上的代码片段说明,B由A继承而来的,B是A的子类。而A是Object的子类,这里可以不显示地声明。
作为子类,B的实例拥有A所有的成员变量,但对于private的成员变量B却没有访问权限,这保障了A的封装性。

IS-A关系

IS-A就是说:一个对象是另一个对象的一个分类。
下面是使用关键字extends实现继承。

public class Animal{
}

public class Mammal extends Animal{
}

public class Reptile extends Animal{
}

public class Dog extends Mammal{
}

基于上面的例子,以下说法是正确的:
Animal类是Mammal类的父类。
Animal类是Reptile类的父类。
Mammal类和Reptile类是Animal类的子类。
Dog类既是Mammal类的子类又是Animal类的子类。
分析以上示例中的IS-A关系,如下:
Mammal IS-A Animal
Reptile IS-A Animal
Dog IS-A Mammal
因此 : Dog IS-A Animal
通过使用关键字extends,子类可以继承父类所有的方法和属性,但是无法使用 private(私有) 的方法和属性。
我们通过使用instanceof 操作符,能够确定Mammal IS-A Animal
实例

public class Dog extends Mammal{

   public static void main(String args[]){

      Animal a = new Animal();
      Mammal m = new Mammal();
      Dog d = new Dog();

      System.out.println(m instanceof Animal);
      System.out.println(d instanceof Mammal);
      System.out.println(d instanceof Animal);
   }
}

以上实例编译运行结果如下:
true
true
true
介绍完extends关键字之后,我们再来看下implements关键字是怎样使用来表示IS-A关系。
Implements关键字使用在类继承接口的情况下, 这种情况不能使用关键字extends。
实例

public interface Animal {}

public class Mammal implements Animal{
}

public class Dog extends Mammal{
}

instanceof 关键字

可以使用 instanceof 运算符来检验Mammal和dog对象是否是Animal类的一个实例。

interface Animal{}

class Mammal implements Animal{}

public class Dog extends Mammal{
   public static void main(String args[]){

      Mammal m = new Mammal();
      Dog d = new Dog();

      System.out.println(m instanceof Animal);
      System.out.println(d instanceof Mammal);
      System.out.println(d instanceof Animal);
   }
} 

以上实例编译运行结果如下:
true
true
true

HAS-A 关系

HAS-A代表类和它的成员之间的从属关系。这有助于代码的重用和减少代码的错误。
例子

public class Vehicle{}
public class Speed{}
public class Van extends Vehicle{
    private Speed sp;
} 

Van类和Speed类是HAS-A关系(Van有一个Speed),这样就不用将Speed类的全部代码粘贴到Van类中了,并且Speed类也可以重复利用于多个应用程序。
在面向对象特性中,用户不必担心类的内部怎样实现。
Van类将实现的细节对用户隐藏起来,因此,用户只需要知道怎样调用Van类来完成某一功能,而不必知道Van类是自己来做还是调用其他类来做这些工作。
Java只支持单继承,也就是说,一个类不能继承多个类。
下面的做法是不合法的:

public class extends Animal, Mammal{} 

Java只支持单继承(继承基本类和抽象类),但是我们可以用接口来实现(多继承接口来实现),脚本结构如:

public class Apple extends Fruit implements Fruit1, Fruit2{}

一般我们继承基本类和抽象类用extends关键字,实现接口类的继承用implements关键字。

二、多态

多态是同一个行为具有多个不同表现形式或形态的能力。
多态性是对象多种表现形式的体现。
比如我们说"宠物"这个对象,它就有很多不同的表达或实现,比如有小猫、小狗、蜥蜴等等。那么我到宠物店说"请给我一只宠物",服务员给我小猫、小狗或者蜥蜴都可以,我们就说"宠物"这个对象就具备多态性。
接下来让我们通过实例来了解Java的多态。

public interface Vegetarian{}
public class Animal{}
public class Deer extends Animal implements Vegetarian{}

因为Deer类具有多重继承,所以它具有多态性。以上实例解析如下:
一个 Deer IS-A(是一个) Animal
一个 Deer IS-A(是一个) Vegetarian
一个 Deer IS-A(是一个) Deer
一个 Deer IS-A(是一个)Object
在Java中,所有的对象都具有多态性,因为任何对象都能通过IS-A测试的类型和Object类。
访问一个对象的唯一方法就是通过引用型变量。
引用型变量只能有一种类型,一旦被声明,引用型变量的类型就不能被改变了。
引用型变量不仅能够被重置为其他对象,前提是这些对象没有被声明为final。还可以引用和它类型相同的或者相兼容的对象。它可以声明为类类型或者接口类型。
当我们将引用型变量应用于Deer对象的引用时,下面的声明是合法的:

Deer d = new Deer();
Animal a = d;
Vegetarian v = d;
Object o = d;

所有的引用型变量d,a,v,o都指向堆中相同的Deer对象。
虚方法
我们将介绍在Java中,当设计类时,被重载的方法的行为怎样影响多态性。
我们已经讨论了方法的重载,也就是子类能够重载父类的方法。
当子类对象调用重载的方法时,调用的是子类的方法,而不是父类中被重载的方法。
要想调用父类中被重载的方法,则必须使用关键字super。

/* 文件名 : Employee.java */
public class Employee
{
   private String name;
   private String address;
   private int number;
   public Employee(String name, String address, int number)
   {
      System.out.println("Constructing an Employee");
      this.name = name;
      this.address = address;
      this.number = number;
   }
   public void mailCheck()
   {
      System.out.println("Mailing a check to " + this.name
       + " " + this.address);
   }
   public String toString()
   {
      return name + " " + address + " " + number;
   }
   public String getName()
   {
      return name;
   }
   public String getAddress()
   {
      return address;
   }
   public void setAddress(String newAddress)
   {
      address = newAddress;
   }
   public int getNumber()
   {
     return number;
   }
}

假设下面的类继承Employee类:

/* 文件名 : Salary.java */
public class Salary extends Employee
{
   private double salary; //Annual salary
   public Salary(String name, String address, int number, double
      salary)
   {
       super(name, address, number);
       setSalary(salary);
   }
   public void mailCheck()
   {
       System.out.println("Within mailCheck of Salary class ");
       System.out.println("Mailing check to " + getName()
       + " with salary " + salary);
   }
   public double getSalary()
   {
       return salary;
   }
   public void setSalary(double newSalary)
   {
       if(newSalary >= 0.0)
       {
          salary = newSalary;
       }
   }
   public double computePay()
   {
      System.out.println("Computing salary pay for " + getName());
      return salary/52;
   }
}

现在我们仔细阅读下面的代码,尝试给出它的输出结果:

/* 文件名 : VirtualDemo.java */
public class VirtualDemo
{
   public static void main(String [] args)
   {
      Salary s = new Salary("Mohd Mohtashim", "Ambehta, UP", 3, 3600.00);
      Employee e = new Salary("John Adams", "Boston, MA", 2, 2400.00);
      System.out.println("Call mailCheck using Salary reference --");
      s.mailCheck();
      System.out.println("\n Call mailCheck using Employee reference--");
      e.mailCheck();
    }
}

以上实例编译运行结果如下:

Constructing an Employee
Constructing an Employee
Call mailCheck using Salary reference --
Within mailCheck of Salary class
Mailing check to Mohd Mohtashim with salary 3600.0

Call mailCheck using Employee reference--
Within mailCheck of Salary class
Mailing check to John Adams with salary 2400.0

例子中,我们实例化了两个Salary对象。一个使用Salary引用s,另一个使用Employee引用。
编译时,编译器检查到mailCheck()方法在Salary类中的声明。
在调用s.mailCheck()时,Java虚拟机(JVM)调用Salary类的mailCheck()方法。
因为e是Employee的引用,所以调用e的mailCheck()方法则有完全不同的结果。
当编译器检查e.mailCheck()方法时,编译器检查到Employee类中的mailCheck()方法。
在编译的时候,编译器使用Employee类中的mailCheck()方法验证该语句, 但是在运行的时候,Java虚拟机(JVM)调用的是Salary类中的mailCheck()方法。
该行为被称为虚拟方法调用,该方法被称为虚拟方法。
Java中所有的方法都能以这种方式表现,借此,重写的方法能在运行时调用,不管编译的时候源代码中引用变量是什么数据类型。

三、关于设计模式

学习设计模式能更好的掌握和巩固封装、继承和多态以及接口和类之间的联系。有兴趣的同学请点击(http://www.runoob.com/design-pattern/design-pattern-tutorial.html

四、垃圾回收(GC)

说到垃圾回收(Garbage Collection,GC),很多人就会自然而然地把它和Java联系起来。在Java中,程序员不需要去关心内存动态分配和垃圾回收的问题,这一切都交给了JVM来处理。顾名思义,垃圾回收就是释放垃圾占用的空间,那么在Java中,什么样的对象会被认定为“垃圾”?(Java垃圾回收机制:http://www.cnblogs.com/dolphin0520/p/3783345.html