【java学习】面向对象(Object Oriented,OO):封装,继承,多态

2024-02-20 12:58

本文主要是介绍【java学习】面向对象(Object Oriented,OO):封装,继承,多态,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

面向对象(Object Oriented,OO)三大特性:封装,继承,多态。

在这里插入图片描述

0,面向对象 & 面向过程 & 面向领域

领域驱动设计(Domain-Driven Design,DDD)= 领域边界+聚合+面向对象
是一种软件开发方法论,强调将业务领域作为软件设计的核心,以便更好地满足业务需求。
架构分层:应用层(负责处理用户请求和协调领域对象的交互)、领域层(实体、值对象、领域服务)、基础设施层(数据库、缓存、事件总线等,负责提供数据持久化和外部服务访问)、用户接口层(将应用层展示给用户)。
领域事件驱动:是领域模型的一种交互机制(发布-订阅模式),用于模块之间传递信息,从而解耦。
实体:通常对应数据库的一行记录,有唯一标识符。
值对象:没有唯一标识的对象或者数据类型,通常不可变,一旦创建就不可修改。只有通过创建对象才可以替换值。
聚合根:负责业务规则、本聚合内部的一致性。

  1. 对一个聚合中实体的访问或操作,必须通过这个聚合的聚合根开始,主要的目的是这样可以保证不变的一致性规则。即:不对外暴露单独值的修改,如果因为某一事件发生变化,通过事件去触发值的修改。
  2. 在对聚合进行查询或操作时,整个聚合是作为一个整体,不能直接查询聚合内部某个非根的对象。
  3. 聚合不要设计太大,否则会有性能问题以及业务规则一致性的问题。

1)区别

  1. 面向过程更注重事情的每一个步骤及顺序;
    举例:用户用商品下单,获取商品-》下单
  2. 面向对象更注重事情由哪些参与者(对象)、各自需要做什么。
    举例:用户(id、name、地址、tel等属性)、商品(id、name、描述、价格)、订单(id、用户id、订单状态、金额)三个对象,它们各自有自己的属性
  3. 面向领域先考虑领域建模,最后考虑持久化存储。
    将业务领域抽象出来,通过堆领域对象、领域服务、领域事件等概念的定义,实现业务需求。
    举例:用户、商品、订单三个领域,分别由实体、值对象、聚合、领域服务和事件等元素组成。
    1)建立实体,同上文面向对象。
    2)封装领域对象到聚合(实体+值对象)中;
    3)实现领域服务。
    如OrderService,负责创建订单、取消订单、查询订单等操作。
    4)使用领域事件传递领域对象之间的消息。如OrderCreatedEvent,表示订单已经被创建。

1,封装

  1. 抽象,把事物抽象成一个类;
  2. 封装,将事物拥有的属性和动作隐藏起来,只保留特定的方法与外界联系。

2,继承(extends)

1)优点

继承可以使现有的代码具有可重用性和可扩展性。

2)特性

  1. 构造函数不能被继承,子类可以通过super()显示调用父类的构造函数
  2. 创建子类时,编译器会自动调用父类的 无参构造函数
  3. 如果父类没有定义无参构造函数,子类必须在构造函数的第一行代码使用super()显示调用
    所以:new新对象的执行顺序:先执行父类构造函数,再执行子类构造函数。
  4. 父类的static方法与静态成员变量是与类一起加载,子类可以继承但不能重写,所以无法实现多态。
  5. 子类重写或覆盖父类方法,子类的访问权限不能低于父类

3)super、this

super指当前对象里面的父对象的引用,可以调用父类的构造方法、父类的方法和属性。
this指当前对象的引用,可以调用当前对象的某个方法或某个成员。

1>super

与this类似。
在一个类的非static成员内部使用,比如super.method()

作用:

  1. 表示调用父类的构造函数。
  2. 调用父类的被隐藏的非私有、friend成员变量、函数。
  3. 用来调用父类中被重写的方法。

2>this

  1. 在非static成员内部使用,表示当前这个对象。
  2. 调用当前对象的方法和成员变量。

编程技巧:
可以在每个方法后面,返回this,来提供链式调用。
通过链式调用,代码更加简洁易懂。
如下这个类,可通过链式调用:student.setName().setAge();

public class Student {Student setName(){return this;}Student setAge(){return this;}
}

4)构造方法和构造代码块

静态代码块—>构造代码块------>构造函数

new对象的时候:父类静态域—>子类静态域—>父类成员初始化—>父类构造块—>父类构造方法—>子类成员初始化—>子类构造块—>子类构造方法;

1>构造块(构造代码块)

{
代码。。。。。。
}

直接在类中定义且没有加static关键字的代码块称为{}构造代码块。
构造代码块在创建对象时被调用,每次创建对象都会被调用,并且构造代码块的执行次序优先于类构造函数。
构造块会每创建一个对象执行一次,且多个构造块会由上到下的执行。

2>静态代码块:

static{
代码。。。。。
}

一个用static关键字标示的一个代码块区域,定义在类中。
可以完成类的初始化,静态代码块会随着类的加载而执行一次(new多个对象也是只执行一次,new对象时才执行)。如果和主函数在同一个类中,优先于主函数执行。

静态变量和静态块关系举例:

public class A{public final static String c = "C";static{System.out.print("A");}
}
外部调用:
① A.c,只输出CA a = new A(); a.c,输出AC  (静态块是类new的时候才执行的,new多个对象只执行一次。)

3>构造方法(构造函数)

i>主要作用:

完成对象的初始化工作,把定义对象时的参数传给对象的域。

ii>特点:
  1. 与类同名
  2. 没有返回类型
  3. 构造方法可以重载,以参数的个数,类型,顺序。
  4. 一个类可以定义多个构造方法
  5. 调用子类构造器之前,会先调用父类构造器,当子类构造器中没有使用”super(参数或无参数)”指定调用父类构造器时,是默认调用父类的无参构造器,如果父类中包含有参构造器,却没有无参构造器,则在子类构造器中一定要使用“super(参数)”指定调用父类的有参构造器,不然就会报错。
    this()和super()必须放在第一行。
class Base{public Base(String s){System.out.print("B");}
}
public class Derived extends Base{public Derived (String s) {super("s");//没有这句会报错System.out.print("D");}public static void main(String[] args){new Derived("C");}
}
g.调用顺序:父类静态域——》子类静态域——》父类成员初始化——》父类构造块——》1父类构造方法——》2子类成员初始化——》子类构造块——》3子类构造方法;
include <iostream>
using namespace std;
class A{public:A(){cout << "A" << endl;}
};
class C{public:C(){cout << "C" << endl;}
};
class B : public A{public:B(){cout << "B" << endl;}C c;
};
int main(){B* b = new B();return 0;
}
输出:
A
C
B 
iii>构造方法调用场景
  1. new新对象
  2. java反射机制使用java.lang.Classjava.lang.reflect.ConstructornewInstance()方法。
  3. 不调用构造方法的场景:
    调用序列化对象(Java.io.ObjectInputStream的readObject方法);
    用对象的clone()方法创建一个对象。
iv>继承中的构造函数及方法执行顺序:

初始化过程:

  1. 初始化父类中的静态成员变量和静态代码块 ;
  2. 初始化子类中1的静态成员变量和静态代码块 ;
  3. 初始化父类的普通成员变量和代码块,再执行父类的构造方法;
  4. 初始化子类的普通成员变量和代码块,再执行子类的构造方法;
class X{Y y=new Y();public X(){System.out.print("X");}
}
class Y{public Y(){System.out.print("Y");}
}
public class Z extends X{Y y=new Y();public Z(){System.out.print("Z");}public static void main(String[] args) {new Z();}
}
输出结果为:YXYZ

5)重写和重载(Override和Overload)

  1. 重写:覆盖父类已有的方法。可通过super调用父类已覆盖的方法。
  2. 重载:多个方法,同名而参数不同,与返回值无关。
    方法重载只能发生在一个类的内部;
    构造方法能重载,静态方法也能重载

6)继承和组合区别

组合和继承是代码复用的2 种方式,推荐组合少用继承。

  1. 组合是在新类里面创建原有类的对象,重复利用已有类的功能。
  2. 组合关系在运行期决定,而继承关系在编译期就已经决定了。
  3. 使用继承关系时,可以实现类型的回溯,即用父类变量引用子类对象,这样便可以实现多态,而组合没有这个特性。
  4. 从逻辑上看,组合最主要地体现的是一种整体和部分的思想,例如在电脑类是由内存类,CPU 类,硬盘类等等组成的,而继承则体现的是一种可以回溯的父子关系,子类也是父类的一个对象。

7)java为什么不用多重继承

C++中因为引入了多重继承导致菱形继承的问题:D在调用A中的方法时,可能是B也可能是C所以存在歧义;为了解决菱形继承又引入了虚继承。然而,多重继承用的场景并不多。
菱形继承
java8:一个类实现了多个方法,且其中有一样的方法(default方法),会要求强制重写该方法。

3,多态(polymorphism)

1)概念

多态是指父类的某个方法被子类重写时,可以产生自己的功能行为,同一个操作作用于不同对象,可以有不同的解释,产生不同的执行结果。多态是封装变化的重要手段之一。

多态的三个必要条件:

  1. 继承父类。
  2. 重写父类的方法。
  3. 父类的引用指向子类对象。

2)分类

多态可以分为两种类型:编译时多态(方法的重载)和运行时多态(继承时方法的重写)。
运行时多态依赖于继承、重写和向上转型。

3)重载(编译时多态)

重载是指一个类里面(包括父类的方法)存在方法名相同,但是参数不一样的方法,参数不一样可以是不同的参数个数、类型或顺序。
java的静态多分派由方法重载来实现。(静态分派:所有依赖静态类型来定位方法执行版本的分派动作)。

4)覆盖(运行时多态)

类中存在和父类相同的方法即为覆盖。
java的动态单分派由方法覆写来实现。(动态分派:在运行期根据实际类型确定方法执行版本的分派过程)。

子类重写或覆盖父类方法,子类的访问权限不能低于父类。

5)单分派和多分派

宗量:方法的接受者(亦即方法的调用者)+ 方法的参数。
单分派:根据一个宗量对目标方法进行选择。
多分派:根据多于一个宗量对目标方法进行选择。
java是一门静态多分派、动态单分派的语言。

6)向上转型(upcasting) 、向下转型(downcasting)


public class SuperTest {public class Father{public int field = 0;public int getField() {return field;}}public class Son extends Father{public int field = 1;public int getField() {return field;}public int getFatherField() {return super.field;}}@Testpublic void test() {/*** 向上转型:* 只能调用父类定义的方法和变量* 1. 无法调用父类没有的子类方法。* 2. 变量不能被重写,重写只针对非静态方法。* 3. 动态连接:对于父类中定义的方法,如果子类中重写了该方法,那么父类类型的引用将会调用子类中的这个方法*/Father father = new Son();System.out.println(father.field + ", " + father.getField());  //0, 1/*** 向下转型:强转,不安全。*/Son son = (Son)father;System.out.println(son.field + ", " + son.getField() + ", " + son.getFatherField());//1, 1, 0}
}

4,设计模式原则(SOLID原则)

这篇关于【java学习】面向对象(Object Oriented,OO):封装,继承,多态的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/728284

相关文章

Java程序进程起来了但是不打印日志的原因分析

《Java程序进程起来了但是不打印日志的原因分析》:本文主要介绍Java程序进程起来了但是不打印日志的原因分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java程序进程起来了但是不打印日志的原因1、日志配置问题2、日志文件权限问题3、日志文件路径问题4、程序

Spring 基于XML配置 bean管理 Bean-IOC的方法

《Spring基于XML配置bean管理Bean-IOC的方法》:本文主要介绍Spring基于XML配置bean管理Bean-IOC的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一... 目录一. spring学习的核心内容二. 基于 XML 配置 bean1. 通过类型来获取 bean2. 通过

Spring Boot 集成 Quartz并使用Cron 表达式实现定时任务

《SpringBoot集成Quartz并使用Cron表达式实现定时任务》本篇文章介绍了如何在SpringBoot中集成Quartz进行定时任务调度,并通过Cron表达式控制任务... 目录前言1. 添加 Quartz 依赖2. 创建 Quartz 任务3. 配置 Quartz 任务调度4. 启动 Sprin

springboot上传zip包并解压至服务器nginx目录方式

《springboot上传zip包并解压至服务器nginx目录方式》:本文主要介绍springboot上传zip包并解压至服务器nginx目录方式,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录springboot上传zip包并解压至服务器nginx目录1.首先需要引入zip相关jar包2.然

Java数组初始化的五种方式

《Java数组初始化的五种方式》数组是Java中最基础且常用的数据结构之一,其初始化方式多样且各具特点,本文详细讲解Java数组初始化的五种方式,分析其适用场景、优劣势对比及注意事项,帮助避免常见陷阱... 目录1. 静态初始化:简洁但固定代码示例核心特点适用场景注意事项2. 动态初始化:灵活但需手动管理代

Java使用SLF4J记录不同级别日志的示例详解

《Java使用SLF4J记录不同级别日志的示例详解》SLF4J是一个简单的日志门面,它允许在运行时选择不同的日志实现,这篇文章主要为大家详细介绍了如何使用SLF4J记录不同级别日志,感兴趣的可以了解下... 目录一、SLF4J简介二、添加依赖三、配置Logback四、记录不同级别的日志五、总结一、SLF4J

将Java项目提交到云服务器的流程步骤

《将Java项目提交到云服务器的流程步骤》所谓将项目提交到云服务器即将你的项目打成一个jar包然后提交到云服务器即可,因此我们需要准备服务器环境为:Linux+JDK+MariDB(MySQL)+Gi... 目录1. 安装 jdk1.1 查看 jdk 版本1.2 下载 jdk2. 安装 mariadb(my

SpringBoot中配置Redis连接池的完整指南

《SpringBoot中配置Redis连接池的完整指南》这篇文章主要为大家详细介绍了SpringBoot中配置Redis连接池的完整指南,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以... 目录一、添加依赖二、配置 Redis 连接池三、测试 Redis 操作四、完整示例代码(一)pom.

Java 正则表达式URL 匹配与源码全解析

《Java正则表达式URL匹配与源码全解析》在Web应用开发中,我们经常需要对URL进行格式验证,今天我们结合Java的Pattern和Matcher类,深入理解正则表达式在实际应用中... 目录1.正则表达式分解:2. 添加域名匹配 (2)3. 添加路径和查询参数匹配 (3) 4. 最终优化版本5.设计思

Java使用ANTLR4对Lua脚本语法校验详解

《Java使用ANTLR4对Lua脚本语法校验详解》ANTLR是一个强大的解析器生成器,用于读取、处理、执行或翻译结构化文本或二进制文件,下面就跟随小编一起看看Java如何使用ANTLR4对Lua脚本... 目录什么是ANTLR?第一个例子ANTLR4 的工作流程Lua脚本语法校验准备一个Lua Gramm