面向对象
2025-09-17 09:36:06 0 举报
AI智能生成
初学者理解
作者其他创作
大纲/内容
概念介绍
面向对象语言特点
封装
继承
多态
组合
过程式和面向对象
对象
数据
行为
例如人
属性
身高,年龄等
行为
走路,说话等
过程式
有输入输出
程序为函数
区别
面向对象:属性行为包含一个对象中
网络通讯:数据和操作都封装在一个对象,数据和行为都会被传输
实际上行为本身不会发送,但两端都有代码副本,可以理解为是对象传输(employee)
例子:浏览器,不会提前知道对象是干什么
过程:属性行为分开,如PLC ,程序和变量分离,且数据是全局
存在问题,数据访问时空,不可预期。
存在问题,数据访问时空,不可预期。
网络通讯:只发送数据,没有代码
结构体有对象(类)的很多特性,但对象更丰富,既包含Int,string,bool灯变量(属性)
也包含方法(行为),用来操作数据和行为。同时可以控制某些成员的访问
也包含方法(行为),用来操作数据和行为。同时可以控制某些成员的访问
对比结构体,多了行为和隐藏功能
将属性和方法合并到一个实体称为封装
PLC传统意义封装是做功能块
面向对象是把属性,功能块,隐藏合并
面向对象是把属性,功能块,隐藏合并
例如在Labview中建的类,上层想要调用只能通过类去调用行为
当一个对象MO想获取另外对象‘求和’的数据时,只需调用‘求和’的接口即可
‘mo’可以发送信息并获取结果,但不对‘求和’内部数据做操作
labview 类自己的行为,外部只要调用就行
什么是对象
对象数据
存放对象的数据代表对象的状态,也称属性
生日,性别,电话等
对象行为
表示对象可以做什么
过程式语言:通过函数FC,子程序FB来定义行为
面向对象语言:包含在方法中:通过发消息来调用方法
例如有个性别的属性,那就会有get sex() 获取性别 set sex()设置性别的方法
其他对象需要信息时,可以发消息来询问
其他对象需要信息时,可以发消息来询问
public string name 属性
public void setname(string n){name=n}方法设置姓名 void 没有返回值
public string getname(return name)方法 返回姓名
public void setname(string n){name=n}方法设置姓名 void 没有返回值
public string getname(return name)方法 返回姓名
取值和赋值方法就是数据隐藏,这样其他对象就不会直接操作另外一个对象的数据。
也称为访问方法 (accessor method)设值方法(mutator method)
也称为访问方法 (accessor method)设值方法(mutator method)
有了方法后,只是一个接口,还需要给出方法的实现。一个方法包含
方法名(接口)
传给方法的参数
方法的返回值
UML类图
组成
类名
employee
数据(属性)
身份证号
年龄
生日
行为(方法)
例如
读写身份证号
读写年龄
读写生日
创建3个人
JONE,MARY,CARY
每个对象都有employee类的所有属性和方法。
没有必要为每个对象都做一个实现方法,因为
每个对象都指向了同一个实现。
每个对象都指向了同一个实现。
建模工具
建模工具提供一种方式来使用统一建模语言
unified modeling language (UML)创建和操作类图
unified modeling language (UML)创建和操作类图
什么是类
类是对象的蓝图,你要实例化一个对象,就要基于类来构建这个对象。
类是EXCLE表格的行序列,对象是表格的行
类是对象的模具,类用来创建对象,也可以认为是一种更高级别的数据类型。
定义数据类型 int x
int 是数据类型 ,x是变量
定义类 myclass myobject
myclass是类,myobject 是对象
创建对象前必须设计类
public class person
//属性
private string name
private string address
//方法
public string getname(){
return name;
}
public void setname(string n){
name =n;
}
public string get address(){
return address;
}
public void setaddress(string adr){
sddress=adr;
}
}
//属性
private string name
private string address
//方法
public string getname(){
return name;
}
public void setname(string n){
name =n;
}
public string get address(){
return address;
}
public void setaddress(string adr){
sddress=adr;
}
}
创建了类的属性和方法
属性
每个类通过定义属性来存放类实例化对象的状态,如上面的name,address
访问控制:当数据类型或方法定义为public 时其他对象可以访问,当定义为private只有对象自己可以访问
方法
实现一个类需要的行为
消息
对象之间的通信机制
对象A调用对象B的一个方法,给B发一个消息,B响应后返回值
对象只能调用另外对象的公共方法。
person p = new person() 创建一个person 类的实例
string = p.getname() 发送消息获取这个P的name
string = p.getname() 发送消息获取这个P的name
封装和数据隐藏
使用对象的好处是无需暴露他的所有属性和行为
对象仅暴露必要的接口来和其他对象交互,其他细节都隐藏起来
对象仅暴露必要的接口来和其他对象交互,其他细节都隐藏起来
接口
定义了对象之间的通讯手段,设计接口保证对象能正确实例化和操作
大部分接口定义为public方法
大部分接口定义为public方法
属性不是接口一部分,不能将属性定义为pbulic 否则破坏数据隐藏
类接口是公共方法,方法签名是在使用方法时传递的方法名和参数列表
实现
通过接口交互时,不能看到对象任何的内部实现。
例子,电机和插座,电机的使用无需关注电力的产生
以计算平方为例
public class intsquare{ 定义一个平方类
//属性
private int squarevalue; 类里有属性平方值
//公共接口
public int getsquare(int value){ 供给外部使用的接口getsquare
squarevalue=calculatesquare(value);
return squarevalue;
}
//私有实现
private int calculatesquare(int value){ 内部自己实现的方法,不对外公开
return value*value;
}
}
//属性
private int squarevalue; 类里有属性平方值
//公共接口
public int getsquare(int value){ 供给外部使用的接口getsquare
squarevalue=calculatesquare(value);
return squarevalue;
}
//私有实现
private int calculatesquare(int value){ 内部自己实现的方法,不对外公开
return value*value;
}
}
外部只看到 接口getsquare
修改内部实现不影响外部使用
修改内部实现不影响外部使用
继承
最强大的功能代码重用
结构化设计一个功能块,多次使用
面向对象更进一步,允许你定义类之间的关系,通过组织和识别不同类的共性,实现代码重用。
允许一个类继承另一个类的属性和方法,通过抽象公共属性和行为创建新类
识别多个类的共性
狗类,猫类
都有共同属性眼睛,毛发等
过程化建模,分开建自己的属性
面向对象:上移一个层级哺乳动物类,定义公共属性,让猫和狗来继承
定义完后,狗有继承父类的属性如眼睛,毛发。也有自己的属性狗叫
超类和子类
超类(父类)
如哺乳动物类,所有哺乳动物都有的属性和行为
子类
狗类,猫类 从父类继承 了属性和行为
抽象
子类可以作为其他类的父类
狗类 哈士奇,斗牛犬等
Java 一个类只能有一个父类(单继承),C++可以有多个父类(多重继承)
is-a关系
形状,和圆形,矩形,星型 都是继承父类‘形状’这种关系是is-a关系
任何父类能做的事子类也能做,当设计‘形状’类时,对各种形状标准化,无论绘制什么形状,都调用draw。绘制园,星型,是他们自己对象的职责。父类不管怎么实现。展示了多态的概念
多态
多种形态,可以理解为多种实现方法
一个对象发送一个消息,对象定义了一个方法来响应这个消息,子类从父类继承接口,然而子类是单独实体,每个子类
需要对同一个消息有自己的应答。
需要对同一个消息有自己的应答。
例如对‘形状类’发消息绘画,不能完成,因为‘形状’父类是抽象概念,必须有具体形状(子类)‘圆’或‘方’提供
具体的实现。重载:子类覆盖父类的一个实现。
具体的实现。重载:子类覆盖父类的一个实现。
public abstract class shape{ 创建父类‘形状’
private double area; 私有属性 ‘面积’
public abstract double getarea(); 抽象接口获取面积
}
private double area; 私有属性 ‘面积’
public abstract double getarea(); 抽象接口获取面积
}
方法前有abstract 子类必须提供方法的实现。
public class circle extends shape{ 创建'圆‘’
double radius; 变量 radius 半径
public circle (double r) { 同名的方法,称为构造函数
radius=r;
}
public double get area(){ 公共接口读取圆面积
area=3.14*(radius*radius);
return(area);
}
double radius; 变量 radius 半径
public circle (double r) { 同名的方法,称为构造函数
radius=r;
}
public double get area(){ 公共接口读取圆面积
area=3.14*(radius*radius);
return(area);
}
方法名和类名相同,没有返回值,这种特殊的方法称为构造函数
是类的入口,对象在这里被构造,可以进行初始化操作和执行一些启动任务
是类的入口,对象在这里被构造,可以进行初始化操作和执行一些启动任务
circle 的构造函数接收单个参数半径,赋值给类的类的属性radius
矩形子类
public class rectangle extends shape{ 创建矩形类,extends继承形状类
double length ; 属性长和宽
double width;
public rectangle(double l,double w){ 构造函数
lenght=l;
width=w;
}
public double getarea(){ 方法:计算面积
area = length*width;
return (area);
}
}
double length ; 属性长和宽
double width;
public rectangle(double l,double w){ 构造函数
lenght=l;
width=w;
}
public double getarea(){ 方法:计算面积
area = length*width;
return (area);
}
}
如果子类继承了父类的抽象方法,必须提供方法的具体实现,否则
他自己也是抽象类
他自己也是抽象类
栈
一种数据结构,后进先出的系统每次取的是最顶部的数据
组合
对象包含其他对象的情况,如对象计算机包含了对象显示器,使用其他对象来构建或结合成新对象,这就是组合
抽象
和继承一样,组合也是构建对象的机制
构建对象两种方式
继承
is-a 狗是哺乳动物
组合
车里有发动机 has-a
面向对象的方式思考
清楚接口和实现的区别
当设计类时,考虑用户应该暴露什么,隐藏什么非常重要
接口
一个通用规则:一个类的接口应该只包含需要用户知道的东西
设计类最重要的考虑就是识别类的读者
实现
实现的细节对于用户都是隐藏的,修改实现不需要变动用户代码
使用抽象思维设计接口
面向对象的优势是可以重用类,通常可以重用的类比具体类更加抽象
抽象类接口
送我去机场
上下料项目中的左取料,不关心里面气缸具体动作
具体接口
右转,左转等
尽可能提供最小化用户接口
开始设计一个类时,从最小化接口开始
类的设计是迭代式,后期需要时在添加接口。
确定用户
实际使用系统的人
客户至少其中之一,因为设计费用问题,必须考虑到出租车司机
对象行为
识别用户后需要确定用户行为,可以通过UML聚合来进行决策
识别公共接口
使用出租车
上车,付钱,告知位置,下次等
使用出租车对象
听乘客位置,知道去哪,收钱
刚开始只要考虑如何使用,而不去思考如何构建
接口是一个迭代过程
识别实现
设计完类后,保证类的正常工作
任何非公共接口可视为实现
高级面向对象概念
构造函数
不同语言有不同的构造方法,Java 是与类同名VB是 INIT
编译器会意识到这个方法名和类名完全相同,所以认为是构造函数
通常没有返回值
通常没有返回值
public int cabbie() {
构造的代码
}
构造的代码
}
构造函数调用
cabbie mycabbie= new cabbie();
new 关键词创建了cabbie类的一个实例mycabbie,分配内存。通过参数传递,
进行初始化
进行初始化
构造函数包含
最重要的功能是遇到NEW关键字分配内存,新创建一个对象并完成初始化
默认构造函数
没有构造函数的类,你可以编译和使用他,那么类会有一个默认构造函数
无论你是否自定义构造函数,类始终包含一个构造函数,系统为你提供
无论你是否自定义构造函数,类始终包含一个构造函数,系统为你提供
使用多个构造函数
public class count {
intcount;
public count(){ 构造函数1 初始化为0
count=0;
}
public count(int number) 构造函数2 设定任意值,也称为重载方法,不止是构造函数
count = number;
}
intcount;
public count(){ 构造函数1 初始化为0
count=0;
}
public count(int number) 构造函数2 设定任意值,也称为重载方法,不止是构造函数
count = number;
}
重载方法:可以让程序员重复使用相同的方法名,只要每次方法签名不同即可
public void getcab();
public void getcab(string cabbie name) 不同的签名
public void getcab(int number of passengers):
public void getcab(string cabbie name) 不同的签名
public void getcab(int number of passengers):
使用UML对类建模
构造数据库阅读不同重载
传入数据库名,及设置游标在数据库的起始位置
传入数据库名称以及设置游标在数据库中的期望位置
如何构建父类
如果使用了继承,必须熟悉父类的数据和行为。
任何继承的属性都是可见的,
对构造函数的继承是不可见的,遇到new就会分配对象
对构造函数的继承是不可见的,遇到new就会分配对象
构造函数会调用父类的构造函数
对象所有属性会初始化,这些属性是类定义的属性(实例变量
),不是构造函数或其他方法的属性。
),不是构造函数或其他方法的属性。
执行构造函数中的其余代码
设计构造函数
设计类的最初就是初始化好自己的属性,也就是构造函数确保程序处于稳定状态
错误处理
编写一个类不能一开始就做到完美无缺,例如PLC写气缸程序,不停改进才设计完美
想在代码加入错误识别和错误处理能力
忽略错误
容易引发程序灾难,以丑陋的方式崩溃
检测问题并中止程序
比忽略要好,可以优雅地退出
检测问题试图修复
抛出异常
大部分面向对象语言通过关键字catch ,throw 来处理
作用域的重要性
每个对象有唯一的标识和状态,有自己的内存
方法有三种属性
局部属性
对象属性
类属性
局部属性
public class number{
public method1(){
int count;
}
public method2{
}
}
public method1(){
int count;
}
public method2{
}
}
method1 包含了一个count 局部变量,
只能再method1访问,method2 不知道有这个变量
只能再method1访问,method2 不知道有这个变量
Java用{}定义作用域,count只在method1有作用
当完成method1调用后,会删除count这个副本
当完成method1调用后,会删除count这个副本
对象属性
可以在同一个对象的多个方法共享的属性
public class number{
int count;//对method1,method2 都可见
public method1(){
count=1;
}
public method2{
count=2;
}
}
int count;//对method1,method2 都可见
public method1(){
count=1;
}
public method2{
count=2;
}
}
count 放到method1,method2作用域外,两个行为均可操作
属性count,而且是对象的同一个内存副本,单不同对象不共享
属性count,而且是对象的同一个内存副本,单不同对象不共享
类属性
两个以上对象共享属性,Java,C#,C++设置为static
public class number{
static int count;
public method1(){}
}
static int count;
public method1(){}
}
声明count为static类型,那么所有实例化对象值分配一个单独内存
所有对象都操作这个属性,类似于全局数据
所有对象都操作这个属性,类似于全局数据
一个对象会覆盖另外对象的操作
操作符重载
允许你修改一个操作符的含义,如‘+’修改为字符串连接
多重继承
非常强大的技术,没有它一些问题变得难解决
对程序员和编译器,都会增加系统复杂度
对象操作
当处理复杂的数据结构和对象时,编程中很多最基本的操作会变得越来越复杂。
类和引用
复杂的对象可能包含引用,对引用复制和比较是不行的,因对对其所在的数据进行复制和比较
深拷贝和浅拷贝
对所有引用对象都创建拷贝称为深拷贝,开销达
浅拷贝:只简单拷贝引用,不深入层级
类的剖析
类名
识别作用,且必须是描述性
public class cabbie 出租车
注释
帮助我们理解类
//,/*/
属性
private
私有
大部分属性都是,不公开
static
所有对象操作同一个属性
构造函数
方法名与类名相同
都是cabbie
null不存在
用来检查状态,如是不是有配偶,没有为null
访问器
对象
实际上不会为每个非静态创建一个物理副本,而是每个对象都指向一个物理代码,调用后释放
静态属性
静态属性的修改是全局的,会作用于所有的对象
公共接口方法
对外暴露是他们使用类的重要方式,倾向于抽象化,而实现倾向于具体化
public void givedestination(){}
接口是高速司机去哪,而不是具体怎么走
私有实现方法
类的某些方法对其他类隐藏
private void turnright(){}
这个方法前缀是private 不是公共接口public 用于类内部调用
类的设计指导
优秀设计类的建议
对现实世界系统建模
按照人们实际所想方式对现实世界系统建模
过程化设计是自上而下的数据和行为是分离的,如PLC主程序在上,子程序在下
面向对象把数据和行为封装在交互的对象,我们处理问题不再已事件和程序操作来处理
而是展示对象与真实事件的交互
而是展示对象与真实事件的交互
类的交互与真实世界交互相似,如人喝车交互,当设计类时必须设计这些对象的真实行为
司机和出租车对象封装了各自的数据和行为
第一次使用面向对象开发时,冗余创建只有行为没有数据的类,实际上是结构化思维创建一组函数或子程序
识别公共接口
最小化公共接口
保证类尽可能简单,提供用户需要的
拓展接口
隐藏实现
改变类不影响到用户使用接口是设计良好的类
设计健壮的构造函数
把对象设置为安全初始状态
初始化
类设计错误处理
使用注释
构造可以合作的对象
设计类考虑重用
设计是考虑拓展
使用描述性的名称
抽象不可移植的代码
尽可能小的作用域
考虑维护性
开发过程的迭代
测试接口
使用对象持久化
当程序运行是,如果没有使用保存对象,对象消亡后不会恢复,大多数系统需要保存对象状态,便于后期使用
对象持久化
平面文件系统
存储到文件,受限制
关系型数据库
对象装换为关系模型
面向对象数据库
序列化及封送对象
传输的对象,系统必须解析对象,然后重新构造,称为序列化对象如网络通讯
通过网线发送对象称为封送
使用对象
设计指导
借助UML工具
早点识别出需求可以使变更最小化
试用版交给客户做测试,可能会损害公司的声誉
设计步骤
提供正确的分析
用户必须与开发人员紧密合作
编写工作陈述文档
供应商使用该文档确定是否投标
收集需求
对需求文档拆分为单独条目
开发用户接口的原型
确保用户理解系统最好的方式是创建一个原型
可以是一个实际的控制界面
识别类
在需求文档后,可以开始识别类,
确定类的职责
如职员类,必须有计算薪水且转账的职责
确定类的协作
类不是孤立的,类和类发送消息和获取的信息
创建类模型来描述系统
使用UML来模块化系统
创建用户接口原型
同开发用户接口原型类似
包装对象
结构化和面向对象不相驳
结构化代码
面向对象
封装,继承,多态,组合
结构化编程
序列
step=0...100 ,从顶部有逻辑地运行到底部
条件判断
if then.... end_if;
迭代
for ...
注意****结构化代码一般会被包含在对象的行为中****。
例如
class somemath{
public int add(int a ,int b){
return a+b;
}
}
public int add(int a ,int b){
return a+b;
}
}
a+b结构代码包含在add方法中
当用户想使用该方法
只需要使用方法签名即可
只需要使用方法签名即可
public class testmath{
public static void main(string[]args){
int x=0;
somemath match = new somemath();用New some math()构造创建一个叫match的实例
x= math.add(1,2); 使用add签名来计算1+2
system.out.printin("x="+x);
}
}
public static void main(string[]args){
int x=0;
somemath match = new somemath();用New some math()构造创建一个叫match的实例
x= math.add(1,2); 使用add签名来计算1+2
system.out.printin("x="+x);
}
}
包装不可移植的代码
隐藏不可移植的代码,这些代码只能在一个平台执行。包装到一个方法中,成为一个接口
例如
WINDOWS发出声音的代码是 system.out.printin("\007")
为了免除程序员记住这个代码,可以提供一个sound类,提供一个beep的方法,程序员无需知道代码,只需调用beep即可
class sound{
public void beep(){
system.out.printin("\007");
}
}
public void beep(){
system.out.printin("\007");
}
}
程序员调用的方法
public class testbeep{
public static void main(string[] args){
sound mysound =new sound();
mysound.beep();
}
}
public static void main(string[] args){
sound mysound =new sound();
mysound.beep();
}
}
包装已有的类
二次包装,在不修改源代码的情况下,修改实现和接口
精通继承和组合
继承和组合扮演重要角色,最困难的就算决定使用继承还是组合
重用对象
继承和组合主要的目的是对象重用
继承is-a 狗是哺乳动物
组合has-a 装配集合构建更复杂的类 汽车和引擎
两种都是重要的技术,需要理解其优缺点
继承
例子
父类狗,行为 叫
子类金毛狗,继承父类,获得行为 叫
继承的优势
无需重复编写叫的程序
在编写子类之前就应该把父类的方法完成测试
在程序中只存在一份叫的代码,需要修改叫的程序时,只需修改父类,无需修改其他类
继承的问题
鸟的父类,如果子类如企鹅,鸵鸟继承那么父类的方法fly 子类不适用
如果代码在关键场合如航天系统会带来严重后果
通用和特例
继承是从通用到具体的过程,在顶端的父类是最通用的类,底端的类越具体
设计决策
找到越多的共同点越好,但是系统会变复杂
决策方法
计算机不擅长的事情
模型的复杂度
大型系统尽可能保持简单
做设计决策考虑未来情况
组合
使用UML表示组合
聚合以带菱形的线表示,比如方向盘是汽车的一部分
联合是一个线没有菱形,比如独立的键盘和计算机的关系
为什么封装是面向对象的本质
继承如何减弱封装
封装是把类打包为公共接口和私有实现的过程
一个类应该隐藏其他类无需了解的任何事情
继承意味着对其他类强封装,但弱化了父类和子类的封装
如果你修改父类的实现,那么会波及其他的子类,这种波及会导致出现难以预料的问题
为了避免这个问题,继承是要严格考量is-a的调节,如果子类是父类的特殊类型,那么父类会影响子类
多态的例子
父类形状shape, 行为draw 是抽象的,子类circle,需要重载该方法并提供自己的draw方法。
对象职责
父类shape不能被实例化,子类圆,矩形可以实例。但他们有自己的面积计算方法。这些方法不能放在shape中
0 条评论
下一页
为你推荐
查看更多