刚接触JAVA或者其他面向对象的思维以及类与对象
2021-06-08
对于刚接触JAVA或其他面向对象编程语言的朋友来说,一开始可能很难理解面向对象的概念以及类与对象的关系。笔者曾经参加过一次短期培训班,讲授java基础知识。在课程结束时,许多学生没有理解面向对象的思想以及类和对象的含义。这几天有空,想整理一下思路,谈谈我对面向对象和类和对象的理解。
面向对象
首先百度会得到如下定义:
一切都是对象。通过面向对象的方法,将现实世界中的事物抽象为对象,将现实世界中的关系抽象为类并继承,帮助人们实现对现实世界的抽象化和数字化建模。
我们知道编写程序的目的是解决现实生活中的问题,编程中的思维方式也应该接近现实生活中的思维方式。面向对象编程就是为了达到以上两个目的。它使编程工作更加直观和易于理解。需要注意的是,这里提到的编程不仅包括设计过程,还包括面向对象
为什么说面向对象更接近现实生活?
想象一下,当我们向别人描述某事时,我们都会说什么? “它的嘴像鸭子”,“它有四个退缩”,“它的爪子上有网”,“它是哺乳动物,但它是卵生的。”
HAS A 和 IS A 的这种表达方式往往可以简单有效地描述同一事物。 HAS A描述事物的属性或行为,IS A描述事物的类别。
当我们结合这一系列属性时,我们得到鸭嘴兽的类别。同时,哺乳动物这个词简洁明了地表达了所有哺乳动物的特征,而不是一一列举。这是继承特性的体现。同时,卵子发育是多态性的一种表现。
这是面向对象思维的特点,它提取(抽象)有用的属性和行为(丢弃那些不需要关系的)并组织(封装)成一个类。在这个过程中,你可能会发现很多属性或者方法和另一个类是一样的,那么你可以使用继承来避免重复(当然这个过程也可能是你设计了每个类之后,发现它们有共同点,然后提取基类)。更重要的是,继承不能照原样复制。我们可以通过重载来实现相同行为或属性的唯一实现。此功能称为多态性。比如同样的生产行为,实现方式从胎生变为卵生。请大声朗读并牢记面向对象的四个特点:
抽象、封装、继承、多态,与早期结构化编程相比
早期结构化编程是面向过程的(功能)。换句话说,程序由一组函数组成数据结构用面向对象方法与c++描述,调用者作为函数的参数传入。在面向对象的程序中,对象是主体,程序由对象的集合组成。一个对象包含一系列符合设计的函数,供其他对象调用。所以它可能很抽象,
比如我们在设计五子棋游戏的时候,流程化的设计思路就是先分析问题:1、开始游戏,2、黑子先走,3、画图,4、判断输赢,5、转白子,6、画屏,7、判断胜负,8、返回第2步,9、输出最终结果。用单独的函数分别执行以上步骤,问题就解决了。
面向对象的设计是从另一种思维方式解决问题。整个五子棋可以分为:1、黑白,两方的行为完全一样,2、棋盘系统,负责画屏,3、rule系统,负责判断犯规,获胜或输等。第一类对象(玩家对象)负责接受用户输入并通知第二类对象(棋盘对象)棋子布局的变化。棋盘对象负责在接收到棋子的变化后在屏幕上显示变化,同时使用前三种对象(规则系统)来确定对局。 (以上例子来自国内某知名问答社区)
随便写点代码,看看就行,别太认真...
/** 玩家类 **/ public class Player { String name; //棋手名称 boolean isFirst; //是否先手 int color_flag; //代表颜色 0-白 1-黑 Table table;//棋盘对象 public Player(String name,boolean isFirst;int color_flag){ this.name=name; this.isFirst=isFirst; this.color_flag=color_flag; } /** 下棋,x,y为落子坐标 **/ public void play(int x,int y) throws Exception{ if(this.table==null){ throw new IllegalArgumentException("玩家还未注册到棋盘!"); } table.setNewPieces(x,y); } public void setTable(Table table){ this.table=table; } } /** 棋盘类 **/ public class Table{ List playerList=new ArrayList(); Referee referee ; public Table(){ referee =new Referee(this); } /** 注册玩家 **/ public void registPlayer(Player player) throws Exception { //检测棋盘中的玩家是否已满,先手玩家和玩家选色是否冲突。 ....... playerList.add(player); player.setTable(this); } /** 落子 **/ public void setNewPieces(int x , int y){ //重新绘制棋盘 ...... //调用裁判对象,判断结果 if(referee.isEnd){ End(); } } public void End(){ ....... } } /** 裁判类 **/ public class Referee(){ Table table; public Referee(Table table){ this.table=table; } public boolen isEnd(){ //判断输赢 .... return false; } }
但是,其实通过上面的例子代码,不难发现,即使我们使用面向对象的方式,也已经实现了上面例子中提到的几个象棋过程,只不过过程是封装的进入类的方法。所以其实面向对象和面向过程不是编程的区别(需要实现的业务逻辑量不会变),而是设计的区别!
类和对象
类是抽象的,而对象是具体的
如何理解上面的单词?例如,鸭嘴兽是一种类型,特定的鸭嘴兽 A 和鸭嘴兽 B 是对象。在 JAVA 中,对象是通过 new 关键字声明的。再比如,《红色警戒》中的美军士兵是一种单位,点击后从军营中射出的那个就是目标:
类的定义是一个模板,它描述了一类对象的属性和行为。类通常是抽象的,没有实体。哺乳动物是属的概念,是抽象的。实际上,没有哺乳动物这样的实体。只有具体的东西,比如老虎和狮子。在编程工作中应用这种思维方式,我们将程序中的实例抽象为类。例如,如果一个系统中的用户有三、李四,我们将它们抽象为类,或者称它们为命名数据类型。
对象是基于它们所属的类模板创建的真实事物。在程序中,我把这个有形的东西称为实例。我们给它的属性赋予特定的值,让它成为张三或李斯。在内存中,对象代表特定的数据。
我上面说的都是概念性的东西,下面说说实际应用过程中的理解。
在数据类型方面
以java为例,数据类型分为基本数据类型和引用数据类型。
基本数据类型为byte,,,int,long,,char,;其他需要使用new关键字赋值的都是引用数据类型。类和对象指的是被引用数据的类型和值(这里指的类不仅包括接口、数组、枚举和注解)。引用是指对内存地址的引用,后面讲内存的时候会详细讨论这个。看下面的代码:
int a =1; Person b=new Person();
a 和 b 都是无意义的变量名。需要注意的是:a的类型是基本数据类型int 为1,b的类型是引用类型,指的是对象new()。我们常说对象xx,比如这里的对象b。但实际上b只是一个对象的引用,真正的对象是new()!
需要注意的是,它也是一种引用数据类型,但是因为使用率非常高,所以可以像JVM的基本数据类型一样使用:a = "abc";等同于 a = new ("abc") ;
简而言之,简单来说,类是指引用数据的类型,对象是具体的赋值。为了更深入的理解,我们需要在下面解释这个引用是如何反映的。
什么是引用(来自记忆)
要深入理解什么是类,什么是对象,什么是引用,都离不开Java的内存使用。
在Java中,内存大致分为栈()和堆(heap)(之所以粗略是因为它还包括其他几个部分,这里就不赘述了)。我不会在这里详细说明什么是堆栈和堆。有时间我会整理一篇文章详细讲解。
这里只讲一点:在Java中,基本的数据类型和对象引用是存放在栈()中,而对象是存放在堆(heap)中的,比如下面的代码:
int a=1; Person p;
内存中的状态大致如下:
int a = 1 直接在栈中开辟空间,没有实例化的p的值为null,因为没有有效的内存地址来引用它。而当代码修改如下:
int a =1 ; Person p = new Person();
内存中的状态大致如下:
p=new();使p的值=即对象的地址new();在堆中。所以不难理解前面提到的对象的引用,所谓引用其实就是对堆内存地址的引用。总结
随着计算机技术的不断进步,现在计算机不仅仅是用来解决计算问题,而是用来解决越来越接近现实生活的复杂问题。面向对象正是这一发展过程的产物,它使编程工作更贴近人们的思维方式,从而大大提高了编程效率。我们必须明白的是,面向对象不是一种编程方式,而是一种编程思维方式,包括分析、设计、编码等。在面向对象编程中数据结构用面向对象方法与c++描述,程序的基本单元是对象,数据被封装在对象中。类是一个对象模板,它是一个预定义的结构。所谓类的实例化,就是将数据填充到模板中。
最后,我的文笔不是很好,需要改进。写文章和博客的最大目的是梳理自己的想法,其次是分享自己的想法。希望大家多多投诉,共同进步。