Java知识回顾 (7) 继承、多态与接口、封装

 

1、继承

1.1 继承的特性

 

  • 子类拥有父类非 private属性、方法

 

  • 子类能够拥有本身的属性和方法,即子类能够对父类进行扩展。
  • 子类能够用本身的方式实现父类的方法。
  • Java 的继承是单继承,可是能够多重继承。单继承就是一个子类只能继承一个父类,多重继承就是,例如 A 类继承 B 类,B 类继承 C 类,因此按照关系就是 C 类是 B 类的父类,B 类是 A 类的父类这是 Java 继承区别于 C++ 继承的一个特性
  • 提升了类之间的耦合性(继承的缺点,耦合度高就会形成代码之间的联系越紧密,代码独立性越差)。

1.2 继承关键字

  继承可使用 extends 和 implements 这两个关键字来实现继承,并且全部的类都是继承于 java.lang.Object,当一个类没有继承的两个关键字,则默认继承object(这个类在 java.lang 包中,因此不须要 import)祖先类。java

  final 关键字声明类能够把类定义为不能继承的,即最终类;或者用于修饰方法,该方法不能被子类重写。编程

1.3 构造器

子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)。安全

若是父类的构造器带有参数,则必须在子类的构造器中显式地经过 super 关键字调用父类的构造器并配以适当的参数列表。编程语言

若是父类构造器没有参数,则在子类的构造器中不须要使用 super 关键字调用父类构造器,系统会自动调用父类的无参构造器。 ide

 

2、重写(Override)与重载(Overload)

2.1 重写(Override)

重写是子类对父类的容许访问的方法的实现过程进行从新编写, 返回值和形参都不能改变即外壳不变,核心重写!函数

重写的好处在于子类能够根据须要,定义特定于本身的行为。 也就是说子类可以根据须要实现父类的方法。测试

重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常。例如: 父类的一个方法申明了一个检查异常 IOException,可是在重写这个方法的时候不能抛出 Exception 异常,由于 Exception 是 IOException 的父类,只能抛出 IOException 的子类异常。this

在面向对象原则里,重写意味着能够重写任何现有方法spa

 

方法的重写规则设计

  • 参数列表必须彻底与被重写方法的相同;
  • 返回类型与被重写方法的返回类型能够不相同,可是必须是父类返回值的派生类(java5 及更早版本返回类型要同样,java7 及更高版本能够不一样);
  • 访问权限不能比父类中被重写的方法的访问权限更低。例如:若是父类的一个方法被声明为public,那么在子类中重写该方法就不能声明为protected。
  • 父类的成员方法只能被它的子类重写
  • 声明为final的方法不能被重写。
  • 声明为static的方法不能被重写,可是可以被再次声明。
  • 子类和父类在同一个包中,那么子类能够重写父类全部方法,除了声明为private和final的方法。
  • 子类和父类不在同一个包中,那么子类只可以重写父类的声明为public和protected的非final方法。
  • 重写的方法可以抛出任何非强制异常,不管被重写的方法是否抛出异常。可是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更普遍的强制性异常,反之则能够。
  • 构造方法不能被重写。
  • 若是不能继承一个方法,则不能重写这个方法。

Super关键字的使用

当须要在子类中调用父类的被重写方法时,要使用super关键字。

class Animal{
   public void move(){
      System.out.println("动物能够移动");
   }
}
 
class Dog extends Animal{
   public void move(){
      super.move(); // 应用super类的方法
      System.out.println("狗能够跑和走");
   }
}
 
public class TestDog{
   public static void main(String args[]){
 
      Animal b = new Dog(); // Dog 对象
      b.move(); //执行 Dog类的方法
 
   }
}

2.2 重载(Overload)

重载(overloading) 是在一个类里面,方法名字相同,而参数不一样。返回类型能够相同也能够不一样。

每一个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。

最经常使用的地方就是构造器的重载。

重载规则:

  • 被重载的方法必须改变参数列表(参数个数或类型不同);
  • 被重载的方法能够改变返回类型;
  • 被重载的方法能够改变访问修饰符;
  • 被重载的方法能够声明新的或更广的检查异常;
  • 方法可以在同一个类中或者在一个子类中被重载。
  • 没法以返回值类型做为重载函数的区分标准。

2.3 总结

方法的重写(Overriding)和重载(Overloading)是java多态性的不一样表现,重写是父类与子类之间多态性的一种表现,重载能够理解成多态的具体表现形式。

  • (1)方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不一样或数量相同而类型和次序不一样,则称为方法的重载(Overloading)。
  • (2)方法重写是在子类存在方法与父类的方法的名字相同,并且参数的个数与类型同样,返回值也同样的方法,就称为重写(Overriding)。
  • (3)方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现

3、多态

3.1 多态的含义与应用

多态性是对象多种表现形式的体现。

如,同一个事件发生在不一样的对象上会产生不一样的结果。

多态的优势

  • 1. 消除类型之间的耦合关系
  • 2. 可替换性
  • 3. 可扩充性
  • 4. 接口性
  • 5. 灵活性
  • 6. 简化性

多态存在的三个必要条件

  • 继承
  • 重写 (Overriding)
  • 父类引用指向子类对象

当使用多态方式调用方法时,首先检查父类中是否有该方法,若是没有,则编译错误;若是有,再去调用子类的同名方法。

多态的好处:可使程序有良好的扩展,并能够对全部类的对象进行通用处理。

public class Test {
    public static void main(String[] args) {
      show(new Cat());  // 以 Cat 对象调用 show 方法
      show(new Dog());  // 以 Dog 对象调用 show 方法
                
      Animal a = new Cat();  // 向上转型  
      a.eat();               // 调用的是 Cat 的 eat
      Cat c = (Cat)a;        // 向下转型  
      c.work();        // 调用的是 Cat 的 work
  }  
            
    public static void show(Animal a)  {
      a.eat();  
        // 类型判断
        if (a instanceof Cat)  {  // 猫作的事情 
            Cat c = (Cat)a;  
            c.work();  
        } else if (a instanceof Dog) { // 狗作的事情 
            Dog c = (Dog)a;  
            c.work();  
        }  
    }  
}
 
abstract class Animal {  
    abstract void eat();  
}  
  
class Cat extends Animal {  
    public void eat() {  
        System.out.println("吃鱼");  
    }  
    public void work() {  
        System.out.println("抓老鼠");  
    }  
}  
  
class Dog extends Animal {  
    public void eat() {  
        System.out.println("吃骨头");  
    }  
    public void work() {  
        System.out.println("看家");  
    }  
}

 

3.2 虚函数

虚函数的存在是为了多态。

Java 中其实没有虚函数的概念,它的普通函数就至关于 C++ 的虚函数,动态绑定是Java的默认行为。若是 Java 中不但愿某个函数具备虚函数特性,能够加上 final 关键字变成非虚函数

重写

咱们将介绍在 Java 中,当设计类时,被重写的方法的行为怎样影响多态性。

咱们已经讨论了方法的重写(子类可以重写父类的方法)。当子类对象调用重写的方法时,调用的是子类的方法,而不是父类中被重写的方法。

要想调用父类中被重写的方法,则必须使用关键字 super

 

父类

public class Employee {
   private String name;
   private String address;
   private int number;
   public Employee(String name, String address, int number) {
      System.out.println("Employee 构造函数");
      this.name = name;
      this.address = address;
      this.number = number;
   }
   public void mailCheck() {
      System.out.println("邮寄支票给: " + 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

子类

public class Salary extends Employee
{
   private double salary; // 整年工资
   public Salary(String name, String address, int number, double salary) {
       super(name, address, number);
       setSalary(salary);
   }
   public void mailCheck() {
       System.out.println("Salary 类的 mailCheck 方法 ");
       System.out.println("邮寄支票给:" + getName()
       + " ,工资为:" + salary);
   }
   public double getSalary() {
       return salary;
   }
   public void setSalary(double newSalary) {
       if(newSalary >= 0.0) {
          salary = newSalary;
       }
   }
   public double computePay() {
      System.out.println("计算工资,付给:" + getName());
      return salary/52;
   }
}
Salary

测试

/* 文件名 : VirtualDemo.java */
public class VirtualDemo {
   public static void main(String [] args) {
      Salary s = new Salary("员工 A", "北京", 3, 3600.00);
      Employee e = new Salary("员工 B", "上海", 2, 2400.00);
      System.out.println("使用 Salary 的引用调用 mailCheck -- ");
      s.mailCheck();
      System.out.println("\n使用 Employee 的引用调用 mailCheck--");
      e.mailCheck();
    }
}

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

Employee 构造函数
Employee 构造函数
使用 Salary 的引用调用 mailCheck -- 
Salary 类的 mailCheck 方法 
邮寄支票给:员工 A ,工资为:3600.0

使用 Employee 的引用调用 mailCheck--
Salary 类的 mailCheck 方法 
邮寄支票给:员工 B ,工资为:2400.0

3.3 多态的实现方式

方式一:重写:

方式二:接口

  • 1. 生活中的接口最具表明性的就是插座,例如一个三接头的插头都能接在三孔插座中,由于这个是每一个国家都有各自规定的接口规则,有可能到国外就不行,那是由于国外本身定义的接口类型。

  • 2. java中的接口相似于生活中的接口,就是一些方法特征的集合,但没有方法的实现

方式三:抽象类和抽象方法

 

 

 

4、 接口

接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口一般以interface来声明。一个类经过继承接口的方式,从而来继承接口的抽象方法。

接口并非类,编写接口的方式和类很类似,可是它们属于不一样的概念。类描述对象的属性和方法。接口则包含类要实现的方法

除非实现接口的类是抽象类,不然该类要定义接口中的全部方法。

接口没法被实例化,可是能够被实现。一个实现接口的类,必须实现接口内所描述的全部方法,不然就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们能够成为一个空指针,或是被绑定在一个以此接口实现的对象

接口与类类似点:

  • 一个接口能够有多个方法。
  • 接口文件保存在 .java 结尾的文件中,文件名使用接口名。
  • 接口的字节码文件保存在 .class 结尾的文件中。
  • 接口相应的字节码文件必须在与包名称相匹配的目录结构中。

接口与类的区别:

  • 接口不能用于实例化对象。
  • 接口没有构造方法。
  • 接口中全部的方法必须是抽象方法。
  • 接口不能包含成员变量,除了 static 和 final 变量。
  • 接口不是被类继承了,而是要被类实现。
  • 接口支持多继承。

接口特性

  • 接口中每个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其余修饰符都会报错)。
  • 接口中能够含有变量,可是接口中的变量会被隐式的指定为 public static final 变量(而且只能是 public,用 private 修饰会报编译错误)。
  • 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。

抽象类和接口的区别

  • 1. 抽象类中的方法能够有方法体,就是能实现方法的具体功能,可是接口中的方法不行。
  • 2. 抽象类中的成员变量能够是各类类型的,而接口中的成员变量只能是 public static final 类型的。
  • 3. 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是能够有静态代码块和静态方法。
  • 4. 一个类只能继承一个抽象类,而一个类却能够实现多个接口。

 

重写接口中声明的方法时,须要注意如下规则:

  • 类在实现接口的方法时,不能抛出强制性异常,只能在接口中,或者继承接口的抽象类中抛出该强制性异常。
  • 类在重写方法时要保持一致的方法名,而且应该保持相同或者相兼容的返回值类型。
  • 若是实现接口的类是抽象类,那么就不必实现该接口的方法。

在实现接口的时候,也要注意一些规则:

  • 一个类能够同时实现多个接口。
  • 一个类只能继承一个类,可是能实现多个接口。
  • 一个接口能继承另外一个接口,这和类之间的继承比较类似。

接口的继承

一个接口能继承另外一个接口,和类之间的继承方式比较类似。接口的继承使用extends关键字,子接口继承父接口的方法。

在Java中,类的多继承是不合法,但接口容许多继承。

在接口的多继承中extends关键字只须要使用一次,在其后跟着继承接口。

 

5、Java 封装

在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部份包装、隐藏起来的方法。

封装能够被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。

要访问该类的代码和数据,必须经过严格的接口控制。

封装最主要的功能在于咱们能修改本身的实现代码,而不用修改那些调用咱们代码的程序片断。

适当的封装可让程式码更容易理解与维护,也增强了程式码的安全性。

封装的优势

  • 1. 良好的封装可以减小耦合。

  • 2. 类内部的结构能够自由修改。

  • 3. 能够对成员变量进行更精确的控制。

  • 4. 隐藏信息,实现细节。

 

6、抽象类、方法

6.1 抽象类

在面向对象的概念中,全部的对象都是经过类来描绘的,可是反过来,并非全部的类都是用来描绘对象的,若是一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。

抽象类除了不能实例化对象以外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类同样。

因为抽象类不能实例化对象,因此抽象类必须被继承,才能被使用。也是由于这个缘由,一般在设计阶段决定要不要设计抽象类。

父类包含了子类集合的常见的方法,可是因为父类自己是抽象的,因此不能使用这些方法。

在Java中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却能够实现多个接口。

6.2 抽象方法

若是你想设计这样一个类,该类包含一个特别的成员方法,该方法的具体实现由它的子类肯定,那么你能够在父类中声明该方法为抽象方法。

Abstract 关键字一样能够用来声明抽象方法,抽象方法只包含一个方法名,而没有方法体。

抽象方法没有定义,方法名后面直接跟一个分号,而不是花括号。

public abstract class Employee
{
   private String name;
   private String address;
   private int number;
   
   public abstract double computePay();
   
   //其他代码
}

声明抽象方法会形成如下两个结果:

  • 若是一个类包含抽象方法,那么该类必须是抽象类。
  • 任何子类必须重写父类的抽象方法,或者声明自身为抽象类。

继承抽象方法的子类必须重写该方法。不然,该子类也必须声明为抽象类。最终,必须有子类实现该抽象方法,不然,从最初的父类到最终的子类都不能用来实例化对象。

6.3 抽象类总结规定

  • 1. 抽象类不能被实例化(初学者很容易犯的错),若是被实例化,就会报错,编译没法经过。只有抽象类的非抽象子类能够建立对象。

  • 2. 抽象类中不必定包含抽象方法,可是有抽象方法的类一定是抽象类。

  • 3. 抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能。

  • 4. 构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法。

  • 5. 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。

相关文章
相关标签/搜索