> 文档中心 > Java面向过程与面向对象(一篇讲透)

Java面向过程与面向对象(一篇讲透)

面向过程与面向对象

  • 前言
    • 一、面向过程(POP) 与 面向对象(OOP)
      • 1. 两者的联系与区别
      • 2. 面向对象的三大特征
    • 二、面向对象的特征一:封装性
      • 1. 概述
      • 2. 使用
      • 3. 注意
      • 4. 应用举例
    • 三、面向对象特征之二:继承性
      • 1. 概述
      • 2.使用
      • 3. 注意
      • 4. 应用举例
    • 四、面向对象特征之三:多态性
      • 1. 概述
      • 2. 使用
      • 3. 注意
      • 4. 应用举例
      • 5. 区分方法的重载与重写(从编译和运行的角度)
      • 6. 面试题:证明多态是编译时行为还是运行时行为?


前言

本博主将用CSDN记录软件开发求学之路上亲身所得与所学的心得与知识,有兴趣的小伙伴可以关注博主!
也许一个人独行,可以走的很快,但是一群人结伴而行,才能走的更远!让我们在成长的道路上互相学习,欢迎关注!

上一篇:类和对象解析

一、面向过程(POP) 与 面向对象(OOP)

1. 两者的联系与区别

1.1 联系

二者都是一种思想,面向对象是相对于面向过程而言的,程序员从面向过程的执行者转化成了面向对象的指挥者。

1.2 区别

⭕ 面向过程,强调的是功能行为,以函数为最小单位,考虑怎么做

⭕ 面向对象,将功能封装进对象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做,更加强调运用人类在日常的思维逻辑中采用的思想方法与原则,如抽象、分类、继承、聚合、多态等。

⭕ 面向对象分析方法分析问题的思路和步骤:

  1. 根据问题需要,选择问题所针对的现实世界中的实体
  2. 从实体中寻找解决问题相关的属性和功能,这些属性和功能就形成了概念世界中的类
  3. 把抽象的实体用计算机语言进行描述,形成计算机世界中类的定义,即借助某种程序语言,把类构造成计算机能够识别和处理的数据结构。
  4. 将类实例化成计算机世界中的对象。对象是计算机世界中解决问题的最终工具

1.3 图解举例
在这里插入图片描述
在这里插入图片描述

2. 面向对象的三大特征

(1)封装 (Encapsulation)
(2)继承 (Inheritance)
(3)多态 (Polymorphism)



二、面向对象的特征一:封装性

1. 概述

1.1 引入:为什么需要封装?

原因一:

我要用洗衣机,只需要按一下开关和洗涤模式就可以了。有必要了解洗衣机内部的结构吗?有必要碰电动机吗?
答案是:NO!没必要!

原因二:

当我们创建一个类的对象以后,我们可以通过"对象.属性"的方式,对对象的属性进行赋值。这里,赋值操作要受到属性的数据类型和存储范围的制约。除此之外,没有其它制约条件。
但是,在实际问题中,我们往往需要给属性赋值加入额外的限制条件。这个条件就不能在属性声明时体现,我们只能通过方法进行限制条件的添加,(比如:setLegs())。
同时,我们需要避免用户再使用"对象.属性"的方式对属性进行赋值。则需要将属性声明为私有的(private)。此时,针对于属性就体现了封装性。

1.2 特点

(1)高内聚 :类的内部数据操作细节自己完成,不允许外部干涉。
(2)低耦合 :仅对外暴露少量的方法用于使用。
(3)设计思想:隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露的暴露出来

2. 使用

⭕ 封装性的体现,需要权限修饰符来配合。

四种修饰符的修饰范围:

修饰符 类内部 同一个包 不同包的子类 同一个工程
private Yes
(缺省) Yes Yes
protected Yes Yes Yes
public Yes Yes Yes Yes

⭕Java规定的4种权限(从小到大排列):private缺省(什么都不写)protectedpublic。置于类的成员定义前,用来限定对象对该类成员的访问权限。

在这里插入图片描述

⭕Java中通过将数据声明为私有的(private),再提供公共的(public)方法:getXxx()setXxx()实现对该属性的操作,以实现下述目的:

  1. 隐藏一个类中不需要对外提供的实现细节。
  2. 使用者只能通过事先定制好的方法来访问数据,可以方便地加入控制逻辑,限制对属性的不合理操作。
  3. 便于修改,增强代码的可维护性。

总结封装性:Java提供了4种权限修饰符来修饰类及类的内部结构,体现类及类的内部结构在被调用时的可见性的大小。

⭕ 封装性的体现:

  1. 如下代码演示
  2. 不对外暴露的私有的方法
  3. 单例模式 …

3. 注意

(1)4种权限可以用来修饰类及类的内部结构:属性、方法、构造器、内部类。

(2)对于class的权限修饰只可以用publicdefault(缺省)

  1. public类可以在任意地方被访问。
  2. default类只可以被同一个包内部的类访问。

4. 应用举例

public class AnimalTest {public static void main(String[] args) {Animal a = new Animal();a.name = "大黄";//a.age = 1;//The field Animal.age is not visible//a.legs = 4;//The field Animal.legs is not visiblea.show();//a.legs = -4;//a.setLegs(6);a.setLegs(-6);//a.legs = -4;//The field Animal.legs is not visiblea.show();System.out.println(a.name);}}class Animal{String name;private int age;private int legs;//腿的个数//对属性的设置public void setLegs(int l){if(l >= 0 && l % 2 == 0){legs = l;}else{legs = 0;//抛出一个异常(暂时没有讲)}}//对属性的获取public int getLegs(){return legs;}public void eat(){System.out.println("动物进食");}public void show(){System.out.println("name = " + name + ",age = " + age + ",legs = " + legs);}//提供关于属性age的get和set方法public int getAge(){return age;}public void setAge(int a){age = a;}}


三、面向对象特征之二:继承性

1. 概述

1.1 引入:为什么要有继承?

多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承(extends)那个类即可。此处的多个类称为子类(派生类),单独的这个类称为父类(基类或超类)。可以理解为:“子类 is a 父类”

2.使用

2.1 类继承语法规则

class A extends B{ }
A: 子类、派生类、subclass
B: 父类、超类、基类、superclass

2.2 作用

(1)继承的出现减少了代码冗余,提高了代码的复用性。
(2)继承的出现,更有利于功能的扩展。
(3)继承的出现让类与类之间产生了关系,提供了多态的前提。

3. 注意

(1)一个类可以被多个子类继承。

(2)Java只支持单继承和多层继承,不允许多重继承

  1. 一个子类只能有一个父类。
  2. 一个父类可以派生出多个子类。

代码演示:

   class SubDemo extends Demo{ } //ok   class SubDemo extends Demo1,Demo2...//error

图解:
在这里插入图片描述

在这里插入图片描述

(3)子类不能直接访问父类中私有的(private)的成员变量和方法,当子类继承父类以后,仍然认为获取了父类中私有的结构。只是因为封装性的影响,使得子类不能直接调用父类的结构而已,但子类仍可通过setter( )getter( )的方法访问父类中私有的(private)的成员变量和方法。

(4)子类和父类的关系,不同于子集和集合的关系。在Java中,继承的关键字用的是“extends”,即子类不是父类的子集,而是对父类的“扩展”。

(5)一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有的属性和方法,可以使用父类中定义的方法和属性,也可以创建新的数据和方法。

(6)不要仅为了获取其他类中某个功能而去继承。

(7)子父类是相对的概念。

(8)子类直接继承的父类,称为:直接父类。间接继承的父类称为:间接父类

(9)子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法

(10)如果我们没有显式的声明一个类的父类的话,则此类继承于java.lang.Object类。

(11)所有的java类(除java.lang.Object类之外)都直接或间接的继承于java.lang.Object类,意味着,所有的java类具有java.lang.Object类声明的功能。

(12)子类继承父类
⭕ 若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类里的方法转移到子类中。
⭕ 对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量。

4. 应用举例

Java面向过程与面向对象(一篇讲透)
Java面向过程与面向对象(一篇讲透)
Java面向过程与面向对象(一篇讲透)

//为描述和处理个人信息,定义类Person:class Person {    public String name;    public int age;    public Date birthDate;    public String getInfo() { //...    } }//为描述和处理学生信息,定义类Studentclass Student {    public String name;    public int age;    public Date birthDate;    public String school;    public String getInfo() {    // ...    } }
//通过继承,简化Student类的定义:class Person {    public String name;    public int age;    public Date birthDate;    public String getInfo() {    // ...    } }class Student extends Person {    public String school; }/*Student类继承了父类Person的所有属性和方法,并增加了一个属性school。Person中的属性和方法,Student都可以使用。*/


四、面向对象特征之三:多态性

1. 概述

1.1 何为多态性?

对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)

2. 使用

2.1 作用

提高了代码的通用性,常称作接口重用。

2.2 虚拟方法调用

⭕ Java引用变量有两个类型:编译时类型运行时类型

  1. 编译时类型由声明该变量时使用的类型决定。
  2. 运行时类型由实际赋给该变量的对象决定。

总结:编译时,看左边;运行时,看右边

  • “看左边”:看的是父类的引用(父类中不具备子类特有的方法)
  • “看右边”:看的是子类的对象(实际运行的是子类重写父类的方法).

⭕ 若编译时类型和运行时类型不一致,就出现了对象的多态性(Polymorphism)。

⭕ 有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。

2.3 使用前提

⭕ 需要存在继承或者实现关系 。
⭕ 有方法的重写。

3. 注意

⭕ 对象的多态性,只适用于成员方法,不适用于成员变量,因为对于成员变量,编译和运行都是看左边。

  • 成员方法:
    • 编译时:要查看引用变量所声明的类中是否有所调用的方法。
    • 运行时:调用实际new的对象所属的类中的重写方法。
  • 成员变量:
    • 不具备多态性,只看引用变量所声明的类

代码演示:

public class Test {    public static void main(String[] args){ Sub s = new Sub(); System.out.println(s.count);//20 s.display();//20 Base b = s; System.out.println(b == s);//true System.out.println(b.count);//10 b.display();//20    }}class Base {    int count = 10;    public void display() { System.out.println(this.count);    } }    class Sub extends Base {    int count = 20;    public void display() { System.out.println(this.count);    } }

⭕ 对象的多态===>在Java中,子类的对象可以替代父类的对象使用

  • 一个变量只能有一种确定的数据类型。
  • 一个引用类型变量可能指向(引用)多种不同类型的对象。

代码演示:

   Person p = new Student();   Object o = new Person();//Object类型的变量o,指向Person类型的对象   o = new Student(); //Object类型的变量o,指向Student类型的对象

⭕ 子类可看做是特殊的父类,所以父类类型的引用可以指向子类的对象:向上转型(upcasting)

⭕ 一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能再访问子类中添加的特有的属性和方法。因为有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。

代码演示:

    Student m = new Student();    m.school = “pku”; //合法,Student类有school成员变量    Person e = new Student();     e.school = “pku”; //非法,Person类没有school成员变量/*属性是在编译时确定的,编译时e为Person类型,没有school成员变量,因而编译错误。*/

4. 应用举例

(1)方法声明的形参类型为父类类型,可以使用子类的对象作为实参调用该方法

public class Test {   public void method(Person e) {    // ……    e.getInfo();   }public static void main(Stirng args[]) {   Test t = new Test();   Student m = new Student();   t.method(m); // 子类的对象m传送给父类类型的参数e    } }

(2)虚拟方法调用(Virtual Method Invocation)

正常的方法调用 :

   Person e = new Person();   e.getInfo();   Student e = new Student();   e.getInfo();

虚拟方法调用(多态情况下) :
子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。

    Person e = new Student();    e.getInfo(); //调用Student类的getInfo()方法/*  编译时类型和运行时类型    编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类的getInfo()方法。——动态绑定*/

5. 区分方法的重载与重写(从编译和运行的角度)

⭕ 重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。它们的调用地址在编译期就绑定了。

⭕ Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”

⭕ 而对于多态,只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”。 引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚绑定,它就不是多态。”

6. 面试题:证明多态是编译时行为还是运行时行为?

//证明如下:class Animal  { protected void eat() {System.out.println("animal eat food");}}class Cat  extends Animal  { protected void eat() {System.out.println("cat eat fish");}}class Dog  extends Animal  { public void eat() {System.out.println("Dog eat bone");}}class Sheep  extends Animal  {public void eat() {System.out.println("Sheep eat grass");} }public class InterviewTest {public static Animal  getInstance(int key) {switch (key) {case 0:return new Cat ();case 1:return new Dog ();default:return new Sheep ();}}public static void main(String[] args) {int key = new Random().nextInt(3);System.out.println(key);Animal  animal = getInstance(key);animal.eat(); }}

音乐圈资讯