面向对象的特点

时间:2022-10-09 21:12:45

封装

什么是封装?

​ 封装表面意思就是*和包装;把信息进行隐藏起来;是指利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体,数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系。

好处:

​ 调用封装的对象时,只需通过接口来进行操作,无需知道对象内部的细节,最重要的是可以起到保护数据的作用。

​ 另外,封装可以隐藏内部实现细节,站在对象外部是看不到内部复杂结构的,对外只提供了简单的安全的操作入口。

如何封装?

​ 在Java语言中可以使用private修饰符,private修饰的数据表示私有的,私有的数据只能在本类中访问。当外界需要进入访问的时候,这时需要对外提供公共的访问入口,让外部程序统一通过这个入口处设立关卡,进行安全控制.

​ 对外公开的入口:一般情况下访问对象的某个属性,就两种情况,读取(get)和修改(set);所以,对外访问入口应该有两个,get方法和set方法.

  • 对需要封装的对象进行封装,使用private修饰符进行私有化;
  • 使用get方法和set方法作为外公开入口,访问对象;

继承

什么是继承?

​ 继承是面向对象的三大特征之一,封装居首位,封装之后形成独立体,独立体之间存在继承关系.继承在现实生活中处处可见的,如:父与子的关系.

继承时子类会继承父类的特征和行为,使子类对象具有父类的属性,子类继承父类的方法使子类拥有父类相同的方法.

为什么要使用继承机制?

​ 在不同类中可能会有共同的特征和动作,把这些动作和行为写成一个类中,从而可以构成一个通用类,再扩展多个特定类中,这些特定类继承通用类的方法.

继承使Java中实现重用的重要手段,避免重复,易于维护.

public class animal {
    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    void move(){
        System.out.println(name+"移动");
    }
}

public class fish extends animal {
    private String color;//颜色
    public String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }
    public static void main(String[] args) {
        //鱼与动物类的继承关系
        fish fish = new fish();
        fish.setName("鲤鱼");
        fish.setColor("红色");
        fish.move();
    }
}

​ 继承的作用中除了可以让代码复用外,还有非常重要的两个作用,这是在继承的基础之上衍生出的,方法的覆盖多态机制.

继承的相关性(特性):

  • B类继承A类,则称A类为超类(superclass),父类,基类;B类则称为子类(subclass),派生类,扩展类.
  • Java只支持单继承,Java不允许多继承;c++支持多继承.
  • Java不支持多继承,但可以产生间接继承的后果.
  • Java规定,子类继承父类,除构造方法和被private修饰的数据不能继承外,剩下都可以继承.
  • Java中的类没有显示的任何继承类,则默认继承Object类,Object类是Java语言提供的根类,也就是说,一个对象与生俱来的就有Object类型中的所有的特征.

继承也是有些缺点,例如,父类与子类的切合度非常高,父类的改变会影响子类.

覆盖

什么是方法覆盖?

在介绍覆盖之前先回顾一下重载;重载是在同一个类当中,如果功能相似,尽可能将方法名定义相同,这样代码会美观的.

重载的条件:只要在同一个类当中,方法名相同,参数列表不同(类型,个数,顺序),即构成方法重载.

覆盖:子类重写父类的方法.

如何实现方法覆盖呢?

只有当从父类继承过来的方法无法满足当前子类业务需求的时候,需要把父类中继承过来的方法进行覆盖.或是,父类中继承的方法已经不够用了,子类有必要将方法重写.所以,方法覆盖又称方法重写.

方法覆盖的条件和注意实现?

  1. 具备的条件:
    • 方法覆盖发生在具有继承关系的父子类之间;
    • 覆盖之后与原方法具有相同的返回值类型,相同的方法名,相同的形式和参数 列表;
  2. 注意事项:
    • 由于覆盖之后的方法与原方法一样,建议是复制粘贴,不建议手写;
    • 私有方法不能被继承,所以不能被覆盖;
    • 构造方法不能被继承,所以不能覆盖;
    • 覆盖之后的方法不能比原方法拥有更低的访问权限,可以更高;
    • 覆盖之后不能比原方法抛出更多的异常,只能一样或更少.
    • 静态方法(被 static 修饰的成员方法)不存在覆盖.

多态

什么是多态?

​ 多态(Polymorphism)属于面向对象三大特征之一,它的前提是封装形成独立体,独立体之间存在继承关系,从而产生多态机制.

​ 多态是同一个行为具有多个不同表现形式或形态的能力.多态就是"同一个行为"发生在"不同对象上"会产生不同的效果.

在Java中有两种语法,一种是向上转型,一种是向下转型.

  • 向上转型---->子类类型转换为父类类型,简称:自动类型转换
  • 向下转型---->父类类型转换为子类类型,简称:强制类型转换.

面向对象的特点

不管是向下转型还是向上转型,他们之间必须要有继承关系,没有继承关系,转型的时候编译器报错.

public class Test02 {
    public static void main(String[] args) {
        Animal a1 = new Cat()
        a1.move();
        Animal a2 = new Bird();
        a2.move();
    } 
}

Animal a1 = new Cat()就是一个向上转型的,自动转型的,本质上Cat就是一个Animal.

public class Test04 {
    public static void main(String[] args) {
        //向上转型
        Animal a = new Cat();
        //向下转型:为了调用子类对象特有的方法
        Cat c = (Cat)a;
        c.catchMouse();
    } 
}

Animal a = new Cat()与 Cat c = (Cat)a;这就属于向下转型,强制类转型.

以上可知:

只有在访问子类中特有的数据时候,需要先进行向下转型.

向下转型存在一定风险,如下:

public class Test05 {
    public static void main(String[] args) {
        Animal a = new Bird();
        Cat c = (Cat)a;
    } 
}

​ 本来new的对象是一只小鸟,向上转型表达的是它是动物,但向下转型也应该是一只小鸟,但变成了一只猫了.而这个语法上没有错误,编译器通过了,但会报异常,ClassCastException,翻译为类型转换异常.

这个原因是小鸟与猫之间没有继承关系的,转型会出现错误.

instanceof运算符

instanceof运算符的运算结果是布尔类型,可能是true,也可能是false,本质上是一种假设的判断.可以使用instanceof进行判断.

这样写就不会发生异常了:

public class Test05 {
    public static void main(String[] args) {
        Animal a = new Bird();
        if(a instanceof Cat){
            Cat c = (Cat)a;
            c.catchMouse();
        } 
    } 
}

多态存在的三个必要条件分别是:

  1. 继承
  2. 方法覆盖
  3. 父类引用指向子类对象

多态显然离不开覆盖机制,多态是因为在编译阶段绑定父类当中的方法,程序运行阶段自动调用子类对象上的方法.如果子类对象上的方法没有进行重写,创建多态就没有意义了.只有方法重写之后,运行时调用子类对象上的方法产生不同的效果,多态形成了.

方法覆盖与多态机制是捆绑的形式.

多态在开发中的作用

在软件开发过程中,有这样的一个开发原则:开闭原则。开闭原则(OCP)是面向对象设计中“可复用设计”的基石,是面向对象设计中最重要的原则之一。它的原文是这样: “Software entities should be open for extension,but closed for modification”。

开闭原则中“开”,是指对于组件功能的扩展是开放的,是允许对其进行功能扩展的;开闭原则中“闭”,是指对于原有代码的修改是封闭的,即修改原有的代码对外部的使用是透明的。

如下例子:


//宠物狗
public class Dog {
    String name;
    
    public Dog(String name){
        this.name = name;
    }    
    //吃的行为
    public void eat(){
        System.out.println(this.name + "在啃肉骨头!");
    }
}

//主人
public class Master {
    //喂养行为
    public void feed(Dog dog){
        //主人喂养宠物,宠物就吃
        System.out.println("主人开始喂食儿");
        dog.eat();
        System.out.println("主人喂食儿完毕");
    } 
}
public class Test {
    public static void main(String[] args) {
        //创建狗对象
        Dog dog = new Dog("二哈");
        //创建主人对象
        Master master = new Master();
        //喂养
        master.feed(dog);
    } 
}

如果在此基础之上,要增加一个Cat类,来表示宠物猫呢?

//宠物猫
public class Cat {
    String name;
    public Cat(String name){
        this.name = name;
    }
    //吃的行为
    public void eat(){
        System.out.println(this.name + "在吃鱼!");
    } 
}
public class Master {
    //喂养行为
    public void feed(Dog dog){
        //主人喂养宠物,宠物就吃
        System.out.println("主人开始喂食儿");
        dog.eat();
        System.out.println("主人喂食儿完毕");
    }
    //喂养行为
    public void feed(Cat cat){
        //主人喂养宠物,宠物就吃
        System.out.println("主人开始喂食儿");
        cat.eat();
        System.out.println("主人喂食儿完毕");
    } 
}

而这样做的话,会违背了OCP原则;而多态就可以解决:

//宠物类
public class Pet {
    String name;
    //吃的行为
    public void eat(){
    } 
}
public class Cat extends Pet{
    
    public Cat(String name){
        this.name = name;
    }
    //吃的行为
    public void eat(){
        System.out.println(this.name + "在吃鱼!");
    } 
}
//宠物狗
public class Dog extends Pet{
    
    public Dog(String name){
        this.name = name;
    }
    
    //吃的行为
    public void eat(){
        System.out.println(this.name + "在啃肉骨头!");
    } 
}
//主人
public class Master {
    //喂养行为
    public void feed(Pet pet){
        //主人喂养宠物,宠物就吃
        System.out.println("主人开始喂食儿");
        pet.eat();
        System.out.println("主人喂食儿完毕");
    }
}
public class Test {
    public static void main(String[] args) {
        //创建狗对象
        Dog dog = new Dog("二哈");
        //创建主人对象
        Master master = new Master();
        //喂养
        master.feed(dog);
        //创建猫对象
        Cat cat = new Cat("汤姆");
        //喂养
        master.feed(cat);
    } 
}

显然Master类和具体的Dog,Cat类解耦合了,依赖性弱了,这就是通常说的面向对象编程,尽量不要面向具体编程面向抽象编程会让你的代码耦合度降低,扩展能力增强,从而符合 OCP 的开发原则。

总结:

通过以上内容的学习,我们可以看到多态在开发中联合方法覆盖一起使用,可以降低程序 的耦合度,提高程序的扩展力。在开发中尽可能面向抽象编程,不要面向具体编程。

所谓多态就是同一个行为作用到不同的对象上,最终的表现结果是不同的,主要的要求就是对象是可以进行灵活切换的,灵活切换的前提就是解耦合,解耦合依赖多态机制。