博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java 封装 继承 多态
阅读量:6098 次
发布时间:2019-06-20

本文共 11717 字,大约阅读时间需要 39 分钟。

Java 继承


继承的概念

继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。

继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。

生活中的继承:

兔子和羊属于食草动物类,狮子和豹属于食肉动物类。

食草动物和食肉动物又是属于动物类。

所以继承需要符合的关系是:is-a,父类更通用,子类更具体。

虽然食草动物和食肉动物都是属于动物,但是两者的属性和行为上有差别,所以子类会具有父类的一般特性也会具有自身的特性。

类的继承格式

在 Java 中通过 extends 关键字可以申明一个类是从另外一个类继承而来的,一般形式如下:

类的继承格式

class 父类 {
} class 子类 extends 父类 {
}

为什么需要继承

接下来我们通过实例来说明这个需求。

开发动物类,其中动物分别为企鹅以及老鼠,要求如下:

  • 企鹅:属性(姓名,id),方法(吃,睡,自我介绍)
  • 老鼠:属性(姓名,id),方法(吃,睡,自我介绍)

企鹅类:

public class Penguin {     private String name;     private int id;     public Penguin(String myName, int  myid) {         name = myName;         id = myid;     }     public void eat(){         System.out.println(name+"正在吃");     }    public void sleep(){        System.out.println(name+"正在睡");    }    public void introduction() {         System.out.println("大家好!我是"         + id + "号" + name + ".");     } }

 

老鼠类:

public class Mouse {     private String name;     private int id;     public Mouse(String myName, int  myid) {         name = myName;         id = myid;     }     public void eat(){         System.out.println(name+"正在吃");     }    public void sleep(){        System.out.println(name+"正在睡");    }    public void introduction() {         System.out.println("大家好!我是"         + id + "号" + name + ".");     } }

 

从这两段代码可以看出来,代码存在重复了,导致后果就是代码量大且臃肿,而且维护性不高(维护性主要是后期需要修改的时候,就需要修改很多的代码,容易出错),所以要从根本上解决这两段代码的问题,就需要继承,将两段代码中相同的部分提取出来组成 一个父类:

公共父类:

public class Animal {     private String name;      private int id;     public Animal(String myName, int myid) {         name = myName;         id = myid;    }     public void eat(){         System.out.println(name+"正在吃");     }    public void sleep(){        System.out.println(name+"正在睡");    }    public void introduction() {         System.out.println("大家好!我是"         + id + "号" + name + ".");     } }

 

这个Animal类就可以作为一个父类,然后企鹅类和老鼠类继承这个类之后,就具有父类当中的属性和方法,子类就不会存在重复的代码,维护性也提高,代码也更加简洁,提高代码的复用性(复用性主要是可以多次使用,不用再多次写同样的代码) 继承之后的代码:

企鹅类:

public class Penguin extends Animal {     public Penguin(String myName, int myid) {         super(myName, myid);     } }

 

老鼠类:

public class Mouse extends Animal {     public Mouse(String myName, int myid) {         super(myName, myid);     } }

 

继承类型

需要注意的是 Java 不支持多继承,但支持多重继承。


继承的特性

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

  • 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。

  • 子类可以用自己的方式实现父类的方法。

  • Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 A 类继承 B 类,B 类继承 C 类,所以按照关系就是 C 类是 B 类的父类,B 类是 A 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。

  • 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。


继承关键字

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

extends关键字

在 Java 中,类的继承是单一继承,也就是说,一个子类只能拥有一个父类,所以 extends 只能继承一个类。

extends 关键字

public class Animal {     private String name;       private int id;     public Animal(String myName, String myid) {         //初始化属性值    }     public void eat() {  //吃东西方法的具体实现  }     public void sleep() { //睡觉方法的具体实现  } }  public class Penguin  extends  Animal{ }

 

  

implements关键字

使用 implements 关键字可以变相的使java具有多继承的特性,使用范围为类继承接口的情况,可以同时继承多个接口(接口跟接口之间采用逗号分隔)。

implements 关键字

public interface A {    public void eat();    public void sleep();} public interface B {    public void show();} public class C implements A,B {}

 

super 与 this 关键字

super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。

this关键字:指向自己的引用。

实例

class Animal {  void eat() {    System.out.println("animal : eat");  }} class Dog extends Animal {  void eat() {    System.out.println("dog : eat");  }  void eatTest() {    this.eat();   // this 调用自己的方法    super.eat();  // super 调用父类方法  }} public class Test {  public static void main(String[] args) {    Animal a = new Animal();    a.eat();    Dog d = new Dog();    d.eatTest();  }}

 

  

输出结果为:

animal : eatdog : eatanimal : eat

 

  

final关键字

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

  • 声明类:

final class 类名 {
//类体}

 

  • 声明方法

    

修饰符(public/private/default/protected) final 返回值类型 方法名(){
//方法体}

 

  

 

:实例变量也可以被定义为 final,被定义为 final 的变量不能被修改。被声明为 final 类的方法自动地声明为 final,但是实例变量并不是 final


构造器

子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)。如果父类的构造器带有参数,则必须在子类的构造器中显式地通过 super 关键字调用父类的构造器并配以适当的参数列表。

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

实例

class SuperClass {  private int n;  SuperClass(){    System.out.println("SuperClass()");  }  SuperClass(int n) {    System.out.println("SuperClass(int n)");    this.n = n;  }}// SubClass 类继承class SubClass extends SuperClass{  private int n;    SubClass(){ // 自动调用父类的无参数构造器    System.out.println("SubClass");  }      public SubClass(int n){     super(300);  // 调用父类中带有参数的构造器    System.out.println("SubClass(int n):"+n);    this.n = n;  }}// SubClas2 类继承class SubClass2 extends SuperClass{  private int n;    SubClass2(){    super(300);  // 调用父类中带有参数的构造器    System.out.println("SubClass2");  }      public SubClass2(int n){ // 自动调用父类的无参数构造器    System.out.println("SubClass2(int n):"+n);    this.n = n;  }}public class TestSuperSub{  public static void main (String args[]){    System.out.println("------SubClass 类继承------");    SubClass sc1 = new SubClass();    SubClass sc2 = new SubClass(100);     System.out.println("------SubClass2 类继承------");    SubClass2 sc3 = new SubClass2();    SubClass2 sc4 = new SubClass2(200);   }}

 

  

输出结果为:

------SubClass 类继承------SuperClass()SubClassSuperClass(int n)SubClass(int n):100------SubClass2 类继承------SuperClass(int n)SubClass2SuperClass()SubClass2(int n):200

 

Java 封装


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

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

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

封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。

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

封装的优点

  • 1. 良好的封装能够减少耦合。

  • 2. 类内部的结构可以自由修改。

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

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


实现Java封装的步骤

1. 修改属性的可见性来限制对属性的访问(一般限制为private),例如:

public class Person {    private String name;    private int age;}

 

这段代码中,将 name 和 age 属性设置为私有的,只能本类才能访问,其他类都访问不了,如此就对信息进行了隐藏。

2. 对每个值属性提供对外的公共方法访问,也就是创建一对赋取值方法,用于对私有属性的访问,例如:

public class Person{    private String name;    private int age;​    public int getAge(){      return age;    }​    public String getName(){      return name;    }​    public void setAge(int age){      this.age = age;    }​    public void setName(String name){      this.name = name;    }}

 

采用 this 关键字是为了解决实例变量(private String name)和局部变量(setName(String name)中的name变量)之间发生的同名的冲突。


实例

让我们来看一个java封装类的例子:

EncapTest.java 文件代码:

/* 文件名: EncapTest.java */public class EncapTest{    private String name;   private String idNum;   private int age;    public int getAge(){      return age;   }    public String getName(){      return name;   }    public String getIdNum(){      return idNum;   }    public void setAge( int newAge){      age = newAge;   }    public void setName(String newName){      name = newName;   }    public void setIdNum( String newId){      idNum = newId;   }}

 

以上实例中public方法是外部类访问该类成员变量的入口。

通常情况下,这些方法被称为getter和setter方法。

因此,任何要访问类中私有成员变量的类都要通过这些getter和setter方法。

通过如下的例子说明EncapTest类的变量怎样被访问:

RunEncap.java 文件代码:

/* F文件名 : RunEncap.java */public class RunEncap{   public static void main(String args[]){      EncapTest encap = new EncapTest();      encap.setName("James");      encap.setAge(20);      encap.setIdNum("12343ms");       System.out.print("Name : " + encap.getName()+                              " Age : "+ encap.getAge());    }}

 

以上代码编译运行结果如下:

Name : James Age : 20

 

  

Java 多态


多态是同一个行为具有多个不同表现形式或形态的能力。

多态就是同一个接口,使用不同的实例而执行不同操作,如图所示:

 

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

现实中,比如我们按下 F1 键这个动作:

  • 如果当前在 Flash 界面下弹出的就是 AS 3 的帮助文档;
  • 如果当前在 Word 下弹出的就是 Word 帮助;
  • 在 Windows 下弹出的就是 Windows 帮助和支持。

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

多态的优点

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

多态存在的三个必要条件

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

比如:

Parent p = new Child();

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

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

以下是一个多态实例的演示,详细说明请看注释:

 

Test.java 文件代码:

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("看家");      }  }

 

执行以上程序,输出结果为:

吃鱼抓老鼠吃骨头看家吃鱼抓老鼠

 

虚函数

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

Java 中其实没有虚函数的概念,它的普通函数就相当于 C++ 的虚函数,动态绑定是Java的默认行为。如果 Java 中不希望某个函数具有虚函数特性,可以加上 final 关键字变成非虚函数。

重写

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

我们已经讨论了方法的重写,也就是子类能够重写父类的方法。

当子类对象调用重写的方法时,调用的是子类的方法,而不是父类中被重写的方法。

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

 

Employee.java 文件代码:

/* 文件名 : 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("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类:

Salary.java 文件代码:

/* 文件名 : Salary.java */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;   }}

 

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

VirtualDemo.java 文件代码:

/* 文件名 : 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

例子解析

  • 实例中,实例化了两个 Salary 对象:一个使用 Salary 引用 s,另一个使用 Employee 引用 e。

  • 当调用 s.mailCheck() 时,编译器在编译时会在 Salary 类中找到 mailCheck(),执行过程 JVM 就调用 Salary 类的 mailCheck()。

  • 因为 e 是 Employee 的引用,所以调用 e 的 mailCheck() 方法时,编译器会去 Employee 类查找 mailCheck() 方法 。

  • 在编译的时候,编译器使用 Employee 类中的 mailCheck() 方法验证该语句, 但是在运行的时候,Java虚拟机(JVM)调用的是 Salary 类中的 mailCheck() 方法。

以上整个过程被称为虚拟方法调用,该方法被称为虚拟方法。

Java中所有的方法都能以这种方式表现,因此,重写的方法能在运行时调用,不管编译的时候源代码中引用变量是什么数据类型。


多态的实现方式

方式一:重写:

这个内容已经在上一章节详细讲过,就不再阐述,详细可访问:。

 

方式二:接口

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

转载于:https://www.cnblogs.com/nongzihong/p/10494453.html

你可能感兴趣的文章
自我介绍&友情链接
查看>>
我的友情链接
查看>>
修改mysql用户密码
查看>>
Windows Server 笔记(五):DHCP(2)
查看>>
查看Linux下网卡状态或 是否连接
查看>>
Firefox vimperator vi 快捷键
查看>>
【转】[教程] CSS入门3:如何插入CSS样式
查看>>
安装数据库提示无法解析机器名处理
查看>>
我的友情链接
查看>>
用Python的Turtple画图形
查看>>
浅谈软件硬件联系
查看>>
Linux 防火墙 ,iptables
查看>>
关于ERP的基础知识(一)
查看>>
mfc的总结
查看>>
java 工程师面试题
查看>>
数据库oracle 11g 的启停操作
查看>>
数组的一些操作方法,如join(),split(),slice()等
查看>>
python3_redis随手学习笔记
查看>>
Django1.8 关于 静态文件配置
查看>>
linux异步信号handle浅析
查看>>