类的封装、继承与多态

原文链接:类的封装,继承与多态

类的封装、继承与多态

类的封装相当于一个黑匣子,放在黑匣子中的东西你什么也看不到。

继承是类的一个重要属性,可以从一个简单的类继承出相对复杂高级的类,这样可使程序编写的工作量大大减轻。

多态则可动态地对对象进行调用,使对象之间变得相对独立。接下来我们来讨论:封装、继承和多态。

封装

封装定义

什么是封装性?读者可以先看下面的程序,看看会产生什么问题。

class Person{
    public String name;
    public int age;
    
    public void talk(){
        System.out.println("我是" + name + ",今年" + age + "岁");
    }
}

public class TestPersonDemo{
	public static void main(String[] args){
       Person p = new Person();
        p.name = "张三";
        p.age = -25;
        p.talk();
    }
}

打印结果:我是张三,今年-25岁。

从打印结果可以发现者显然不合法。所以为了避免程序中这种错误的发生,我们可以使用类的封装来解决。接下来我们先看看封装的定义:

在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法。封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。要访问该类的代码和数据,必须通过严格的接口控制。封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。

以上代码我们可以类封装这样进行修改:

public class Person{
    private String name;
    private int age;
    
    public void setName(String name){
        this.name = name;
    }
    
    public String getName(){
        return this.name;
    }
    
    public void setAge(int age){
        if(age < 0){
            age = 0;
        }
        this.age = age;
    }
    
    public int getAge(){
        return this.age;
    }
    
    public void talk(){
        System.out.println("我是" + name + ",今年" + age + "岁");
    }
}

public class TestPersonDemo{
	public static void main(String[] args){
       Person p = new Person();
        p.setName("张三");
        p.setAge(-25);
        p.talk();
    }
}

封装的优点

    1. 良好的封装能够减少耦合。
    1. 类内部的结构可以自由修改。
    1. 可以对成员变量进行更精确的控制。
    1. 隐藏信息,实现细节。

继承

继承的概念

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

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

实现继承的格式如下:

class 子类名 extends 父类

在Java中,通过继承可以简化类的定义,扩展类的功能。在Java中支持类的单继承和多层继承,但是不支持多继承,即一个类只能继承一个类而不能继承多个类。

Java继承只能直接继承父类中的公有属性和公有方法,而隐含地(不可见地)继承了私有属性。

比如现在有一个父亲,父亲鼻子高高的,头发比较少,并且会编程,父亲有一个儿子,儿子继承了父亲的外观特征,并且成为了一个律师,使用代码实现如下:

class Grandpa{
    String nose;
    int hair;
    
    public void walk(){
        System.out.println("行走");
    }
}

class Father extends Grandpa{
    // 编程能力
    private void programmed(){
    }
}

class Son extends Father{
    public void law(){
        
    }
}

继承的特性

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

子类对象的实例化过程

以下代码Father类实例创建,构造方法怎么进行执行?

public class Main {
	public static void main(String[] args) throws Exception {
		Father son = new Father();
	}
}

class Grandpa{
    public Grandpa(){
        System.out.println("Grandpa实例化");
    }
}

class Father extends Grandpa{
    public Father(){
        System.out.println("Father实例化");
    }
}

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

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

super关键字

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

class Grandpa{
    public Grandpa(String name){
        System.out.println("Grandpa实例化");
    }
}

class Father extends Grandpa{
    public Father(){
        super("李四");
        System.out.println("Father实例化");
    }
}

final关键字

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

  • 声明类:

    final class 类名 {//类体}
    
  • 声明方法:

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

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

多态

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

多态就是同一个接口,使用不同的实例而执行不同操作,比如父亲会说话并且说的是中文,儿子因为受过高等教育,因此会说很多国家的语言:

class Father extends Grandpa{
    public void talk(){
        System.out.println("说中文");
    }
}

class Son extends Father{
    public void talk(){
        System.out.println("说很多国家语言");
    }
}

多态的实现必须具备以下几个条件:

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

“重写”的概念与“覆写”或“重载”相似,它们均是Java“多态”的技术之一。所谓“重写”,即是方法名称相同,但却可在不同的场合做不同的事。当一个子类继承一个父类,而子类中的方法与父类中的方法的名称、参数个数、类型等都完全一致时,就称子类中的这个方法覆写了父类中的方法。同理,如果子类中重复定义了父类中已有的属性,则称此子类中的属性覆写了父类中的属性。

class Father extends Grandpa{
    public void talk(){
        System.out.println("说中文");
    }
}

class Son extends Father{
    // 重新了父类的方法
    public void talk(){
        System.out.println("说很多国家语言");
    }
}

虚函数

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

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

父类引用指向子类对象
public class Test{
    public static void main(String[] args){
        Father f = new Son();
	    f.talk();
    }
}

程序结果:说很多国家语言

从程序的输出结果中可以看到,f是父类的对象,但调用talk()方法的时候并没有调用其本身的talk()方法,而是调用了子类中被覆写了的talk()方法。之所以会产生这样的结果,最根本的原因就是因为父类对象并非由其本身的类实例化,而是通过子类实例化,这就是所谓的对象的多态性,即子类实例化对象可以转换为父类实例化对象。

类的多态实例

在这里要着重讲解两个概念向上转型与向下转型,这两个概念的理解非常重要,稍有疏忽就可能引起意想不到的Bug。

  1. 向上转型
    在上面的范例Test.Java中,父类对象通过子类对象去实例化,实际上就是对象的向上转型。向上转型是不需要进行强制类型转换的,但是向上转型会丢失精度。
  2. 向下转型
    与向上转型对应的一个概念就是“向下转型”,所谓向下转型,也就是说父类的对象可以转换为子类对象,但是需要注意的是,这时则必须要进行强制的类型转换。

接下来举个例子来和大家说明:

一天,有个小孩在马路上看见了一辆跑车,他指着跑车说那是汽车。相信读者都会认为这句话没有错,跑车的确是符合汽车的标准,所以把跑车说成汽车并没有错误,只是不准确而已。不管是小轿车也好,货车也好,其实都是汽车,这在现实生活中是说得通的。在这里读者可以将这些小轿车、货车都想象成汽车的子类,它们都是扩展了汽车的功能,都具备了汽车的功能,所以它们都可以叫做汽车,那么这种概念就称为向上转型。而相反,假如说把所有的汽车都当成跑车,那结果肯定是不正确的,因为汽车有很多种,必须明确地指明是哪辆跑车才可以,需要加一些限制,这个时候就必须明确地指明是哪辆车,所以需要进行强制的说明。

上面的解释可以概括成下面的两句话。

  • 向上转型可以自动完成。
  • 向下转型必须进行强制类型转换。

并非全部的父类对象都可以强制转换为子类对象,请看下面的范例

public class Test{
    public static void main(String[] args){
        Father f = new Father();
	    Son s = (Son)f;
    }
}

此时程序将会报异常:

image-20200703200242730

父类用其本身类实例化自己的对象,但它并不知道谁是自己的子类,因此在转换的时候会出现错误。

深入探讨可以加笔者QQ:1120855315
点击获取免费Java免费视频
添加QQ群837949026可以领取更多学习资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值