掌握Java面向对象OOP篇(一)

发布时间 2023-11-20 14:00:47作者: 柳~

掌握面向对象OOP篇(一)

边学边记 -- OOP(Object Orientated Programing)

1. 为什么要引入面向对象?

原因:封装、继承、多态

举个例子理解面向对象代码的好处:
比如:我们有一个实际问题,假设现在一个宠物店有两只小狗, 第一只叫做小白,年龄2岁,白色;第二只叫做小红,年龄3岁,红色;现在我们的宠物店的系统可以让用户在输入不同狗的名字,能显示不同的狗的信息,如果没有,就输出该宠物店没有这条狗。

1.1 非面向对象

不使用面向对象:

package study;

import java.util.Scanner;

public class oobject1 {
    public static void main(String[] agrs){
//        小白狗
        String dogName = "小白";
        int dogAge = 2;
        String dogColor = "白色";
//        小红狗
        String dogName1 = "小红";
        int dogAge1 = 2;
        String dogColor1 = "白色";

        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入狗的名字:");
        String queryDogName = scanner.nextLine();

        if(queryDogName.equals("小红")){
            System.out.println("狗名: " + dogName1 + "\n" + "狗龄: " + dogAge1 + "\n" + "颜色: " + dogColor1);
        }else if (queryDogName.equals("小白")){
            System.out.println("狗名: " + dogName + "\n" + "狗龄: " + dogAge + "\n" + "颜色: " + dogColor);
        }else {
            System.out.println("该宠物点没有这条狗");
        }


    }
}


class oobject2{
    public static void main(String[] args){

        String[] dogName = {"小白","小红"};
        int[] dogAge = {2,3};
        String[] dogColor = {"白色","红色"};

        System.out.println("请输入狗名:");
        Scanner s = new Scanner(System.in);
        String queryDogName = s.nextLine();
        boolean flag = false;
        for(int i=0;i<dogName.length;i++){
            if(queryDogName.equals(dogName[i])){
                System.out.println("狗名: " + dogName[i] + "\n" + "狗龄: " + dogAge[i] + "\n" + "颜色: " + dogColor[i]);
                flag = true;
            }
        }
        if(!flag){
            System.out.println("该宠物点没有这条狗");
        }
    }
}


输出结果:

image-20231119135146411

1.2 面向对象:

使用面向对象

class oobject3{
    private String dogName;
    private int dogAge;
    private String dogColor;

    public oobject3(String dogName,int dogAge,String dogColor){
        this.dogName = dogName;
        this.dogAge = dogAge;
        this.dogColor = dogColor;
    }

    public void getInfo(){
        System.out.println("狗名: " + dogName + "\n" + "狗龄: " + dogAge + "\n" + "颜色: " + dogColor);
    }

    public String getDogName(){
        return dogName;
    }


    public static void main(String[] args){
//        创建两只狗,也就是两只对象
        oobject3 whiteDog = new oobject3("小白",2,"白色");
        oobject3 reDog= new oobject3("小红",3,"红色");

        // 用户输入狗的名字
        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入狗的名字:");
        String queryDogName = scanner.nextLine();

        if(queryDogName.equals(whiteDog.dogName)){
            whiteDog.getInfo();
        }else if (queryDogName.equals(reDog.dogName)){
            reDog.getInfo();
        }else {
            System.out.println("该宠物点没有这条狗");
        }

    }
}

image-20231119135216160

由上面两个例子,我们从代码的角度去看,为什么引入面向对象会更方便。

当不使用面向对象的方式,采用数组或独立变量的方式进行编程时,每个“对象”(这里是指表示狗的信息)都需要通过独立的变量或数组元素进行定义。这意味着对于每一只狗,都需要单独声明和初始化相关的变量。当我们有成千上万个这种“对象”时,就需要重复定义更多的代码,这就会导致:

1.冗余和代码膨胀:随着狗的数量的增加	,代码中的相似的定义和初始化操作变得冗余。这导致了代码的膨胀,使得程序变得庞大且难以维护。
//比如:我们需要反复的使用String dogName = "example";等等去定义属性。

2.可读性很差: 大量的重复代码使得程序的可读性变差。维护人员很难在代码中找到并理解与每只狗相关的所有信息。
//因为代码中有大量的对属性的定义,所以很难对应找到

3.难以修改:如果我们要对狗的属性进行修改、或者添加新的属性,那么整个代码的维护更新就会非常难以进行,并且容易出错。
//当代码量上来的时候,我们如果要给狗添加一个毛的长短的属性,那么我们就要添加几万行代码,很不合理。

4.缺乏结构性:独立变量或数组元素的方式缺乏结构,不易于组织和管理相关的信息。随着信息的增加,代码可能变得混乱且难以理解。
//代码看起来没有章法,不知道从哪里入手

使用面向对象的好处主要体现在以下几个方面:

1.封装和抽象: 面向对象编程强调封装,将对象的属性和行为封装在一起,提供了更高层次的抽象。这使得程序员可以专注于对象的接口,而不必关心内部的实现细节。这样的封装和抽象提高了代码的模块化和可维护性。
//比如我们的oobject3中的非主类部分,就是把属性和方法放在了(封装)类oobject3中,那么这个时候我们的主类去 new 出一个对象时,就可以直接使用这些属性和方法。因为这个方法和属性被封装成为了一个模板。我们通过这个模板new的狗子的这些属性和方法的使用都一样。那么这个模板就实现了很好的统一也降低了代码的重复量。这就是封装的好处。

2.可维护性和可扩展性: 面向对象的编程模型使得代码更易于维护和扩展。通过创建类和对象,可以轻松地添加新的功能或修改现有功能,而不必改变整个程序的结构。这种灵活性使得程序更具可维护性和可扩展性。
//当我们要添加属性时,我们只需要在oobject3中加一行代码,private String type; 那么所有的狗就可以用这个属性,不用去大量的改动代码。

3.代码重用: 面向对象的设计强调重用性。通过创建类和继承,可以在不同的地方重复使用相同的代码。这降低了重复编码的需求,提高了开发效率。


4.继承和多态: 继承和多态是面向对象编程的两个核心概念。继承允许创建一个类,继承另一个类的属性和方法,从而实现代码的重用。多态性允许同一个方法在不同的对象上表现出不同的行为,提高了代码的灵活性和可扩展性。
//在上面的例子中没有涵盖对继承和多态的理解,在下面会有

更好的组织结构: 面向对象的设计强调将现实世界的问题映射到代码结构中。通过创建类和对象,能够更自然地模拟问题领域中的实体和关系,使得代码更易于理解和维护。

团队协作: 面向对象的设计使得多人协作更为容易。每个类可以被视为一个独立的模块,不同的团队成员可以独立地开发和测试各自负责的类。这有助于提高团队的生产力和协作效率。

所以在Java中引入面向对象,以及使用面向对象都是必要的。

2. 面向对象 VS 面向过程

这里假设面试官问你面向对象和面向过程的区别,我们怎么回答?(已经汗流浃背了 ~ )

2.1 语言

面向对象语言:

  1. Java :毋庸置疑,Java一切皆对象,肯定是面向对象编程
  2. C++ :一种支持 面向对象和面向过程编程的语言
  3. C#(C Sharp): 由Microsoft开发,用于.NET平台,支持面向对象编程和其他现代编程概念。
  4. Python : Python是一种多范式的编程语言,支持面向对象编程、面向过程编程以及函数式编程。
  5. Ruby: 一种动态脚本语言,以其简洁而强大的面向对象特性而受欢迎。
  6. Swift: 由Apple开发,专为iOS和macOS设计,具有现代化的面向对象特性。
  7. Objective-C: 用于iOS和macOS的编程语言,支持面向对象编程。

面向过程语言:

  1. C: 一种广泛使用的面向过程编程语言,以其效率和直接的硬件访问而受欢迎。
  2. C++ :C++是一种多范式的编程语言,它既支持面向对象编程(具有类、继承等特性),也支持面向过程编程。在C++中,你可以采用纯粹的面向过程的编码风格,就像在C语言中一样。
  3. Python : Python也是一种多范式的编程语言,支持面向对象编程、面向过程编程以及函数式编程。尽管Python提供了丰富的面向对象特性,但也允许开发者采用面向过程的编程风格。
  4. Fortran: Fortran是一种主要用于科学和工程计算的编程语言,以其数值计算和面向过程的特性而著称。

2.2 OOP vs OPP

" 这个引用来自狂神Java "

语言的进化发展跟生物的进化发展其实是一回事,都是”物以类聚”。

相近的感光细胞聚到一起变成了我 们的眼睛,相近的嗅觉细胞聚到一起变成了我们的鼻子。 语句多了,我们将完成同样功能的相近的语句,聚到了一块儿,便于我们使用。于是,方法出现了! 变量多了,我们将功能相近的变量组在一起,聚到一起归类,便于我们调用。于是,结构体出现了! 再后来,方法多了,变量多了!结构体不够用了!我们就将功能相近的变量和方法聚到了一起,于是类 和对象出现了! 寥寥数语,就深刻的展示了语言的进化历史!其实,都非常自然,”物以类聚”。希望大家能记住这句 话。 企业的发展也是”物以类聚”的过程,完成市场推广的人员聚到一起形成了市场部。完成技术开发的人员 聚到一起形成了开发部!

面向对象和面向过程的本质区别就是处理逻辑(思维模式)的不同。

1.面向过程的思维模式就是简单的线性思维,解决问题首先陷入第一步干什么、第二部干什么的细节中。这种思维模式适合处理一些简单的事务,别入从A走到B,把垃圾倒入垃圾桶等等。如果面对复杂的问题,那么解决就会很麻烦。

2.相反,面向对象更注重的是对问题的整体抽象和建模。它将问题中的实体抽象为一个个的对象,关注对象之间的交互。让对象去解决问题,这样的思维模式更加自然,更加贴近生活。

考虑一个复杂任务,比如设计并制造一艘航天飞船,面向过程的思维模式可能会让人感到困扰,因为这需要考虑众多细节和步骤。然而,通过面向对象的思维模式,我们可以将这个庞大的任务分解成对象,例如“引擎”、“控制系统”、“舱体”等,每个对象都有特定的属性和行为。这种抽象和分解使得我们能够更清晰地理解并解决问题,而不会被淹没在无尽的步骤中。

3.感受面向对象的美

我们直接使用代码去理解这个东西!!! 去感受为什么Java中的封装、继承、多态的好处

package study;



abstract class shape {
    private String color;

    //构造方法
    public shape(String color){
        this.color = color;
    }

    public String getColor(){
        return color;
    }
    //多态的体现之处
    public abstract double getArea();

}


class Circle extends shape{
    private double radius;

    public Circle(String color ,double radius) {
        super(color);
        this.radius = radius;
    }

    @Override
    public double getArea() {
        return Math.PI * Math.pow(radius,2);
    }
}

class Rectangle extends shape{
    private double len;
    private double width;

    public Rectangle(String color,double len , double width) {
        super(color);
        this.len = len;
        this.width = width;
    }

    @Override
    public double getArea() {
        return len * width;
    }
}

public class oobject2 {
    public static void main(String[] args) {
        // 创建 Circle 对象
        Circle circle = new Circle("Red", 5.0);
        System.out.println("Circle Area: " + circle.getArea());
        System.out.println("Circle Color: " + circle.getColor());

        // 创建 Rectangle 对象
        Rectangle rectangle = new Rectangle("Blue", 4.0, 6.0);
        System.out.println("Rectangle Area: " + rectangle.getArea());
        System.out.println("Rectangle Color: " + rectangle.getColor());
    }

}

上面的例子中,我们很清楚的可以看到Java中封装、继承、多态的好处。下面我在赘述一遍 ┭┮﹏┭┮

我们在抽象类 shape中将color属性被声明为私有(private),外部类无法直接访问。颜色的访问和设置通过公共方法getColor进行,这体现了封装的概念、隐藏类内部实现细节。

CircleRectangle 子类继承了 Shape 类,并通过调用 super(color) 访问了父类的构造方法,这也是一种封装的体现,子类不需要直接访问父类的私有属性。

封装的好处:
封装提高了代码的安全性:将属性声明为私有,通过公有方法进行访问,防止外部直接修改对象的内部状态值,从而提高数据的安全性。
封装隐藏细节:封装允许隐藏内部实现细节,使得使用者只需要关注对象的公有接口。这样的设计可以降低了系统的复杂性,是代码更容易理解和维护。
封装提高灵活性:如果内部实现需要更改,只需保持公有接口不变,使用者不受影响。这种灵活性使得系统更容易适应变化。

CircleRectangel 类都是通过关键字extends继承自shape 抽象类。这使得子类获得了父类的属性和方法,实现了代码的重用。例如:两个类都继承父类的color属性和getColor方法。

继承的好处:
代码重用: 继承允许子类重用父类的代码,避免了重复编写相似的代码,降低了代码的重复率。
可维护性: 当需要修改或扩展功能时,只需在父类中进行修改,子类将自动继承这些变化。这降低了修改代码的风险,提高了可维护性。
多态支持: 继承是实现多态的基础。通过继承,不同的子类可以被视为同一类型的对象,提供了更灵活的代码结构。

shape抽象类的抽象方法getArea中体现了多态。不同的子类分别重写了这个方法,根据自己的需求提供了具体的实现。在测试类中,通过统一的接口 getArea 调用,实际上调用了各个子类的实现,展现了多态性。

多态的好处:
灵活性和扩展性: 多态允许同一类型的对象以不同的方式响应相同的方法调用。这提高了代码的灵活性和可扩展性,使得系统更容易适应变化。
简化代码: 多态性允许使用者通过一个统一的接口调用不同类型的对象,简化了代码,提高了可读性。
实现抽象概念: 多态性使得我们能够以更抽象的方式思考和设计问题,将实际对象视为抽象概念的实例,有助于更清晰地理解和解决问题。