python面向对象三大特性详解 - 封装 继承 多态

前言

面向对象编程有三大特性:封装、继承、多态,本文带大家来认识和了解这三个特性~

补充 - 新式类 & 经典类

在python2.x中,新式类是继承了object类的子类,以及该子类的子类 子子类...;经典类就是没有继承没有继承object类的子类,以及该子类的子类 子子类...。

在python3.x中,如果没有继承任何类,默认就会继承object类,没有经典类和新式类之分。

object类提供了一些常用内置方法的实现,比如用来在打印对象时返回字符串的内置方法__str__,后续文章会做介绍的哦~

封装

封装是面向对象编程最重要的特性,封装就是将数据和功能整合到一起,就是我们之前说的将数据与功能在一起。

针对封装到对象或者类中属性,我们可以严格控制在类外部对它们的访问,即隐藏属性和开放接口。

隐藏属性

如何隐藏属性

在类体代码中,如果在一个属性名前加__前缀,就会实现一个对外部隐藏该属性的效果,虽然在类外部无法直接通过对象.属性的方式直接访问双下划线开头的属性,但是知道类名和属性名就可以得到外部访问类属性的方式即对象._类名__属性名,所以说这种操作并没有严格意义上的限制外部访问类内部定义的属性。

这种隐藏对外不对内,这种变形操作只在检查类体语法的时候发生一次,在类外部定义的__开头的属性都不会发生变形

class Test():
    __name = 'test'  # 函数外部访问时变为_Test__name
    
    def __init__(self):
        self.__x = 'x'
        
    def test(self):
        self.__x = 'test'  # 在类体代码内部可以正常使用属性
        print('test')
        
print(Test.__name)  # AttributeError: type object 'Test' has no attribute '__name'
print(Test._Test__name)  # test
print(Test.__dict__) 

mappingproxy(..., '_Test__name': 'test', '_Test__test': <function Test.__test at 0x0000026E75F6E3A0>, ...})

test_obj = Test()
test_obj.test()  # 正常执行
print(test_obj.__x)  # AttributeError: 'Test' object has no attribute '__x'
test_obj.__a = 'a'  # 类外部定义的__的属性不会发生变形
print(test_obj.__a)  # a

隐藏属性的目的

定义属性就是为了使用,所以隐藏并不是最终的目的,将数据属性隐藏起来就限制了了类外部对数据的直接操作,但是类内部应该提供相应的接口来允许类外部间接的操作数据,可以在接口上增加额外的逻辑来对外部操作类内定义的数据进行严格的控制。比如:

class People:
    
    def __init__(self,name):
        self.__name = name
        
    def tell_info(self):
        pwd = input('请输入暗号>>').strip()
        if pwd == '0':
            print(self.__name)
        else:
            print('暗号错误')
            
    def set_name(self,new_name):
        if type(new_name) is str:
            self.__name = new_name
            print(self.__name)
        else:
            print('请输入字符串')

p = People('python')
p.tell_info()
p.set_name(123)

隐藏函数属性的目的是只将用户用到的接口暴露出去,在该接口内可能会用到类内定义的其他方法,但是这些方法用户无需直接调用,就可以将这些方法进行隐藏,隔离程序的复杂度。

隐藏属性和暴露接口就是为了明确的区分类的内外部分,类的内部可以修改类内定义的数据但是不影响外部调用,类外部只需要有一个接口,只要接口的名字、参数不变,那么无论类内部的代码如何改变,外部调用方式都不会发生改变。

继承与派生

继承是创建新类的一种方式,通过继承得到的新类可以称为子类或者派生类,被继承的类被称为父类或者基类。

通过继承得到的子类会继承父类的所有属性,python支持多继承,子类可以继承一个或者多个父类。

通过使用类的继承,可以解决类与类之间的代码冗余问题。

类的继承语法如下:

class Parent1():
    x = 111
class Parent2():
    pass

class Foo(Parent1): # 单继承,父类是Parent1
    pass

class Foo1(Parent1,Parent2): # 多进程,父类是Parent1 Parent2
    pass

可以通过__bases__查看子类继承的所有父类。

print(Foo.__bases__)
# (<class '__main__.Parent1'>,)
print(Foo1.__bases__)
# (<class '__main__.Parent1'>, <class '__main__.Parent2'>)

如果想要找出类和类之间的关系,需要先总结一下多个类的相似之处,就可以得到父类,类与类之间的继承指的就是什么是什么的关系,比如金毛是狗,狗是动物比如:

子类可以继承父类的所有属性,比如我们还是以上篇文章中的外卖系统作为案例,在这个程序中至少存在三个类:商家类、外卖小哥类、顾客类,我们可以先定义出这三个类:

# 商家类
class Merchants():
    
    platform = 'python外卖平台'
    
    def __init__(self, name, addr, tel):
        self.name = name  # 商家名字
        self.addr = addr  # 商家地址
        self.tel = tel  # 商家联系电话
        
    def reveive_order(self):
        print(f'{self.name}已经接单')
        
# 外卖小哥类
class Rider():
    
    platform = 'python外卖平台'
    
    def __init__(self, name, addr, tel):
        self.name = name  # 外卖小哥姓名
        self.addr = addr  # 外卖小哥配送地址
        self.tel = tel  # 外卖小哥电话
        
    def distribution(self):
        print(f'{self.name}正在配送')
        
# 顾客类
class Customer():
    
    platform = 'python外卖平台'
    
    def __init__(self, name, addr, tel):
        self.name = name  # 顾客姓名
        self.addr = addr  # 收货地址
        self.tel = tel  # 顾客电话
        
    def accept_delivery(self):
        print(f'{self.name}已经收到外卖')

以上三个类之间存在重复的代码,都属于python外卖平台,都需要独有的name、addr、tel,因此可以得出以下继承关系,实现代码重用:

class Personnel_information():
    platform = 'python外卖平台'
    
    def __init__(self, name, addr, tel):
        self.name = name  
        self.addr = addr 
        self.tel = tel 
        
# 商家类
class Merchants(Personnel_information):
        
    def reveive_order(self):
        print(f'{self.name}已经接单')
        
# 外卖小哥类
class Rider(Personnel_information):
    
    def distribution(self):
        print(f'{self.name}正在配送')
    
# 顾客类
class Customer(Personnel_information):
    
    def accept_delivery(self):
        print(f'{self.name}已经收到外卖')

商家类、外卖小哥类和顾客类中并没有定义__init__方法,但是会从它们的父类中找到__init__方法,因为子类会继承父类所有的属性和方法,因此仍然可以正常实例化:

mer = Merchants('川菜', '山清水秀区', '11111')
rider = Rider('小庄', '依山傍水区', '2222')
customer = Customer('xu', '养生区', '33333')

属性查找

单继承属性查找

对象的属性可以通过对象.__dict__查看,有了继承关系后,对象在查找属性时先从自己的__dict__中查找,如果没有则去实例化自己的类中找,找不到再去父类中找,都找不到会抛出异常。注意,属性的查找一定是以定义阶段为准。

class Test():
    
    def t1(self):
        print('from Test t1')
        
class Foo(Test):
    
    def f1(self):
        print('Foo f1')
        
f = Foo()
f.t1
# 运行结果
from Test t1

父类如果不想让子类覆盖自己的方法,可以采用双下划线开头的方式将方法设置为私有的。

class Test():
    
    def __t1(self):
        print('from Test t1')
    
    def t2(self):
        self.__t1() 
        print('from Test t2')
        
class Foo(Test):
    
    def __t1(self):
        print('Foo t1')
        
f = Foo()
f.t2()
# 运行结果
from Test t1
from Test t2

多继承属性查找

python面向对象支持多继承,好处是子类可以同时继承多个父类的属性,最大限度的重用代码,但是多继承也有很多缺点,多继承违背了继承表达的什么是什么的关系,而且如果一个类同时继承多个父类,代码的可读性就会变得很差,可能会出现菱形继承的问题。

菱形继承是不同子类拥有同一个非object的父类,而不同的子类又是另一个子类的父类,如果A类中有一个方法,B类或者C类对这个方法进行了重写,D类继承的是哪个版本的方法?

想要弄清楚上面的问题就需要知道类查找属性是按照什么规则查找的,在python中,每定义一个类都会计算出一个方法解析顺序列表(MRO),MRO列表就是一个简单的所有基类的先行顺序列表。

>>> D.mro() # 新式类内置了mro方法可以查看线性列表的内容,经典类没有该内置该方法
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。

有菱形继承问题,就会有非菱形继承,如下所示:

 不管是菱形继承还是非菱形继承,只需要记住:

如果是类实例化得到的对象发起的属性查找,会从对象自身的属性里检索,没有则会按照对象的类.mro()规定的顺序依次找下去;如果是由类发起的属性查找,会按照当前类.mro()规定的顺序依次找下去。

Mixins机制

多继承实际上提高了程序的耦合程度,但是面向对象的编程思想就是解耦合,解耦合的目的就是提高代码的可读性和可扩展性,那么到底要不要用多继承呢?

python提供了多继承就是为了使用,但是在用多继承的过程中,需要规避两点问题:

1.继承结构尽量不要过于复杂;

2.推荐使用mixins机制,在多继承的背景下满足什么是什么的关系。

mixins机制就是在多继承的背景下尽可能提升多继承代码的可读性,简单来说Mixins机制指的是子类混合(mixin)不同类的功能,而这些类采用统一的命名规范(例如Mixin后缀),以此标识这些类只是用来混合功能的,并不是用来标识子类的从属关系,所以Mixins机制本质仍是多继承,但同样遵守什么是什么关系,如下代码:

# 定义父类---交通工具类
class Vechicle:
    print('我是交通工具')

# 定义飞的功能---Mixin表示该类只是作为一个功能
class FlyableMixin():
    def __init__(self,fly):
        self.fly = fly


class CivilAircraft(FlyableMixin, Vechicle):  # 民航飞机
    pass

class Helicopter(FlyableMixin, Vechicle):  # 直升飞机
    pass

class Bus(Vechicle):
    pass

# 民航飞机对象
c = CivilAircraft('我会飞')
print(c.__dict__)
# 直升飞机对象
h = Helicopter('fly')
print(h.__dict__)
# 汽车对象
b = Bus()
print(b.__dict__)

车 、船 、直升机、民航飞机都是交通工具,但是只有飞机具有的功能,如果将 的功能添加到交通工具这个父类中是不合理的,所以可以将“飞”这个功能另外定义,采用统一的命名规范(例如Mixin后缀),这个后缀会明确的告诉读代码的人,这个类是作为一个功能添加到子类中,而不是作为父类,当bus这个类混合 的功能,bus就是会飞的一个交通工具。

但是在使用mixins机制的时候需要注意一下几点:

1.必须表示某一种功能,而不是某类物品

2.python 对于mixin类的命名方式一般以 Mixin, able, ible 为后缀

3.如果有多个功能,那就写多个Mixin类

4.定义的Minx类越多,子类的代码可读性就会越差

派生

子类继承父类后会继承父类所有的属性,子类可以直接继承父类的属性,也可以派生出自己的属性,也可以重写父类的属性,子类重用父类属性有以下四种派生方式,我们还是以外卖系统为例:

class Personnel_information():
    platform = 'python外卖平台'
    
    def __init__(self, name, addr, tel):
        self.name = name  
        self.addr = addr 
        self.tel = tel 
        
# 商家类
class Merchants(Personnel_information):
        
    def reveive_order(self):
        print(f'{self.name}已经接单')
        
# 外卖小哥类
class Rider(Personnel_information):
    
    def distribution(self):
        print(f'{self.name}正在配送')
    
# 顾客类
class Customer(Personnel_information):
    
    def accept_delivery(self):
        print(f'{self.name}已经收到外卖')
        print(f'{self.name}已经收到外卖')

直接继承父类属性

如上述代码,子类直接继承了父类的所有方法和属性。

覆盖父类属性

商家类除了基本的name、addr、tel属性外,还有菜品信息这个属性,但是父类中__init__方法中并没有定义这个属性,因此可以在子类中重写__init__方法,覆盖父类的__init__方法。

class Personnel_information():
    platform = 'python外卖平台'
    
    def __init__(self, name, addr, tel):
        self.name = name  
        self.addr = addr 
        self.tel = tel 
        
# 商家类
class Merchants(Personnel_information):
    
    def __init__(self, name, addr, tel, food_info):
        self.name = name  
        self.addr = addr 
        self.tel = tel 
    	self.food_info = food_info
        
    def reveive_order(self):
        print(f'{self.name}已经接单')

向父类索要方法

覆盖父类属性这种写法虽然可以满足要求,但是子类中的__init__方法中存在重复代码,因此子类可以向父类索要该方法,并在该方法内增加自己独有的属性。这种方式不依赖继承关系,可以向任意类索要方法。

class Personnel_information():
    platform = 'python外卖平台'
    
    def __init__(self, name, addr, tel):
        self.name = name  
        self.addr = addr 
        self.tel = tel 
        
# 商家类
class Merchants(Personnel_information):
    
    def __init__(self, name, addr, tel, food_info):
        Personnel_information.__init__(self, name, addr, tel)  # 向类索要方法
    	self.food_info = food_info  # 定制自己独有的属性
        
    def reveive_order(self):
        print(f'{self.name}已经接单')

super调用父类中的属性

这种方式严格依赖继承关系,调用super会得到一个特殊的对象,该对象会参照发起属性查找的那个类的mro,去当前类的父类中找属性。

class Personnel_information():
    platform = 'python外卖平台'
    
    def __init__(self, name, addr, tel):
        self.name = name  
        self.addr = addr 
        self.tel = tel 
        
# 商家类
class Merchants(Personnel_information):
    
    def __init__(self, name, addr, tel, food_info):
		super().__init__(name, addr, tel)  # 调用的是绑定方法,自动传入self
        # super(Merchants, self).__init__(name, addr, tel)  # 与上一行代码等价,但是在python2中必须这么写,python3可以简写
        self.food_info = food_info 
        
    def reveive_order(self):
        print(f'{self.name}已经接单')

多态

多态指的是一类事物有多种形态,比如说动物有多种形态,猫、狗、羊。

# 🐱 🐕 都属于动物一类
class Animal:
    pass
class cat(Animal):
    pass
class Dog(Animal):
    pass

多态性指的是在不考虑对象具体类型的情况下直接使用对象,具体来说就是把对象使用的方法统一起来,比如动物都有发出声音的功能,比如下述代码:

class Animal:
    
    def talk(self):
        print('动物会发出的声音...')
        
class Cat(Animal):
    
    def talk(self):
        super().talk()
        print('喵喵')
        
class Dog(Animal):
    
    def talk(self):
        super().talk()
            print('汪汪')
            
# 产生对象
cat = Cat()
dog = Dog()
# 调用方法
cat.talk()
dog.talk()

# 运行结果
动物会发出的声音...
喵喵
动物会发出的声音...
汪汪

多态的本质就是在于不同的类中定义有相同的方法名,可以不考虑类而统一用一种方式去使用对象,python提供了抽象类的概念来硬性限制子类必须有某些方法名。

抽象类

使用抽象类需要注意一下几点:

1.在父类中指定指定metaclass属性将类设置为抽象类,抽象类本身只是用来约束子类的,不能被实例化;

2.但凡继承抽象类的子类都必须遵循抽象类规定的标准,若子类中没有遵循抽象类的标准则会抛出异常TypeError,无法实例化。

import abc  # 使用抽象类必须导入abc包

# 指定metaclass属性将类设置为抽象类,抽象类本身只是用来约束子类的,不能被实例化
class Animal(metaclass=abc.ABCMeta):
    
    @abc.abstractmethod # 该装饰器限制子类必须定义有一个名为talk的方法
    def talk(self): # 抽象方法中无需实现具体的功能
        pass

class Cat(Animal): # 但凡继承Animal的子类都必须遵循Animal规定的标准
    def talk(self):
        pass

cat=Cat() # 若子类中没有一个名为talk的方法则会抛出异常TypeError,无法实例化

鸭子类型

不过python更加推崇的是鸭子类型 - 如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子。

比起使用抽象类和继承来硬性规定某种事物一定属于什么类,鸭子类型在某种程度上实现了 程序的解耦合,我们只需要在定义类的时候创建出外观和行为相同的对象就可以了,同样可以实现不考虑对象类型而使用对象。

# 像什么就是什么,就具有什么功能

class Cpu:
    def read(self):
        print('cpu read')

    def write(self):
        print('cpu write')

class Memory:
    def read(self):
        print('mem read')

    def write(self):
        print('mem write')
        
obj1=Cpu()
obj2=Mem()

obj1.read()
obj1.write()

obj2.read()
obj2.write()

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你! 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/386788.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

新项目,从0到1,SpringBoot+Vue.js权限管理系统,拿去做毕设

大家好&#xff0c;我是 jonssonyan 最近把以前做的权限管理系统重新整理了一下&#xff08;将一些不规范的地方规范了一下&#xff0c;并且在关键地方写了注释&#xff09;&#xff0c;代码全部开源&#xff0c;这个项目是以现在主流的前后端分离模式开发的&#xff0c;包含前…

二叉树基础总结

目录 树的定义&#xff1a; 深度和高度&#xff1a; 二叉树 由来 二叉树种类&#xff1a; 满二叉树&#xff1a; 完全二叉树&#xff1a; 严格二叉树&#xff08;Strict Binary Tree&#xff09;&#xff1a; 平衡二叉树&#xff08;Balanced Binary Tree&#xff09;&…

Android ·移动应用开发 创建第一个Android项目

文章目录 一、创建第一个Android项目1.1 准备好Android Studio1.2 运行程序1.3 程序结构是什么app下的结构res - 子目录&#xff08;所有图片、布局、字符串等资源&#xff09;AndroidManifest.xml 有四大组件&#xff0c;程序添加权限声明 Project下的结构 二、开发android时&…

顾问聘请协议(模板)

甲方&#xff1a;________________   乙方&#xff1a;________________ 诚信合作是一切事业发展的基础&#xff0c;外部智力是企业进步的源泉。甲、乙双方经友好协商达成本协议&#xff0c;甲方愿意聘请乙方为特邀管理顾问&#xff0c;乙方愿按本协议内容与甲方合作。 一、合…

算法学习——LeetCode力扣二叉树篇8

算法学习——LeetCode力扣二叉树篇8 669. 修剪二叉搜索树 669. 修剪二叉搜索树 - 力扣&#xff08;LeetCode&#xff09; 描述 给你二叉搜索树的根节点 root &#xff0c;同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树&#xff0c;使得所有节点的值在[low, high…

Git中Idea操作git及Git Flow

目录 一、Idea中使用Git 1.idea配置Git和Gitee 2.实践操作 1.将本地项目推送到远程 2.从远程库克隆项目到本地 二、Git Flow 1.什么是Git Flow 2.工作流程 3.实践操作 一、Idea中使用Git 1.idea配置Git和Gitee 第一步&#xff1a;设置git.exe的安装路径 在设置中的…

[计算机提升] 备份系统:设置备份

6.5 备份系统&#xff1a;设置备份 1、进入到控制面板系统和安全\备份和还原&#xff0c;点击右侧的设置备份&#xff1a; 2、在弹出的设置备份对话框中&#xff0c;选择要保存的位置&#xff0c;点击下一步开始备份。 3、选择要备份的内容。根据需要选择即可。这种备份的…

计算机毕业设计springboot_vue房屋租赁系统_ku668

1.掌握Html&#xff0c;Css&#xff0c;JavaScript等基础编程语言。 2.掌握Vue框架&#xff0c;node环境&#xff0c;数据库等知识。 3.掌握开发系统的基本流程。 …

【原创 附源码】Flutter集成Apple支付详细流程(附源码)

最近有时间&#xff0c;特意整理了一下之前使用过的Flutter平台的海外支付&#xff0c;附源码及demo可供参考 这篇文章只记录Apple支付的详细流程&#xff0c;其他相关Flutter文章链接如下&#xff1a; 【原创 附源码】Flutter集成谷歌支付详细流程(附源码) 【原创 附源码】F…

爬虫——ajax和selenuim总结

为什么要写这个博客呢&#xff0c;这个代码前面其实都有&#xff0c;就是结束了。明天搞个qq登录&#xff0c;这个就结束了。 当然也会更新小说爬取&#xff0c;和百度翻译&#xff0c;百度小姐姐的爬取&#xff0c;的对比爬取。总结嘛&#xff01;&#xff01;&#xff01;加…

VUE基础知识(JAVA后端入门篇)

VUE基础知识&#xff08;JAVA后端入门篇&#xff09; Vue是一套前端框架&#xff0c;免除原生JavaScriptr中的DOM操作&#xff0c;简化书写基于MVVM(Model–View-ViewModel)思想&#xff0c;实现数据的双向绑定&#xff0c;将编程的关注点放在数据上Vue.js - 渐进式 JavaScrip…

JSP知识点

1、JSP概述 1.1 什么是JSP html java代码 JSP动态标签 jsp JavaServer page 在静态页面上添加动态信息就可以了&#xff0c;如果是Servlet还需要一行一行的输出。 通常在前台开发人员给出静态页面后&#xff0c;后台开发人员只需在静态页面中添加动态信息即可&#xff…

算法学习——LeetCode力扣回溯篇3

算法学习——LeetCode力扣回溯篇3 491. 非递减子序列 491. 非递减子序列 - 力扣&#xff08;LeetCode&#xff09; 描述 给你一个整数数组 nums &#xff0c;找出并返回所有该数组中不同的递增子序列&#xff0c;递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。…

鸿蒙视频播放器,主要包括视频获取和视频播放功能:

鸿蒙视频播放器&#xff0c;主要包括视频获取和视频播放功能&#xff1a; 1 获取本地视频或者网络视频。 2 通过media.createAVPlayer创建播放器AVPlayer&#xff0c;然后进行视频播放。 3 通过VideoController进行AVPlayerState的状态管理&#xff0c;如开始&#xff0c;停止&…

【Linux】yum软件包管理器

目录 Linux 软件包管理器 yum 什么是软件包 Linux安装软件 查看软件包 关于rzsz Linux卸载软件 查看yum源 扩展yum源下载 Linux开发工具 vim编辑器 上述vim三种模式之间的切换总结&#xff1a; 命令模式下&#xff0c;一些命令&#xff1a; vim配置 Linux 软件包管理…

【VTKExamples::PolyData】第二十七期 KochanekSpline

很高兴在雪易的CSDN遇见你 VTK技术爱好者 QQ:870202403 前言 本文分享VTK样例KochanekSpline & KochanekSplineDemo,并解析接口vtkParametricSpline & vtkParametricFunctionSource,希望对各位小伙伴有所帮助! 感谢各位小伙伴的点赞+关注,小易会继续努力分享,…

ros自定义msg记录

文章目录 自定义msg1. 定义msg文件2. 修改 package.xml3. 修改 CMakeLists.txt4. message_publisher.py5. message_subscriber.py6. 运行 catkin build 测试 自定义msg ros 版本&#xff1a;kinetic 自定义test包的文件结构如下 |-- test | |-- CMakeLists.txt | |-- msg…

x86汇编通用寄存器用途一览

文章目录 写在前面通用寄存器参考资料 写在前面 intel官方文档链接&#xff1a;Intel64和IA-32架构软件开发者手册 具体在Combined Volume Set of Intel 64 and IA-32 Architectures Software Developer’s Manuals这本手册 &#xff08;五千页我的天。。。&#xff09; 不想…

代码随想录算法训练营DAY17 | 二叉树 (4)

一、LeetCode 110 平衡二叉树 题目链接: 110.平衡二叉树https://leetcode.cn/problems/balanced-binary-tree/ 思路&#xff1a;设置深度计算函数&#xff0c;进行递归处理。 class Solution {public boolean isBalanced(TreeNode root) {if(root null){return true;}boolean…

maven创建webapp+Freemarker组件的实现

下载安装配置maven Maven官方版下载丨最新版下载丨绿色版下载丨APP下载-123云盘123云盘为您提供Maven最新版正式版官方版绿色版下载,Maven安卓版手机版apk免费下载安装到手机,支持电脑端一键快捷安装https://www.123pan.com/s/9QRqVv-TcUY.html链接为3.6.2-3.6.3的版本 下载解…