面向对象与面向过程编程的区别
扫描二维码
随时随地手机看文章
一、面向对象与面向过程编程的区别
我们以一个实际例子来说明这两者的区别 , 例如:写一个计算器的软件。
面向过程程序员思考方式:
[1]定义变量保存用户的输入的数据
[2]实现一个加法函数,完成数据的加法
[3]实现一个减法函数,完成数据的减法
[4]实现一个乘法函数,完成数据的乘法
.....
面向对象程序员思考方式:
[1]计算器是一个对象
[2]这个对象应该有保存数据的变量
[2]这个对象应该有完成数据计算的方法(函数)
....
可以看的出来,好像这两个哥们思考方式几乎没啥区别,只不过面向对象的程序员从整体出发,把实现计算器的保存数据的变量和计算数据的方法封装在一个对象里面。
我们都是搞C语言的,C语言是一种典型的面向过程语言。在这里想问一个问题:如何用C语言描述面向对象程序员的思考的计算器呢?
嗯,你一定会想到用C语言中的结构体来实现,封装的结构体如下:
typedef struct
{
int data1;
int data2;
int (*calc_add)(int,int);
int (*calc_sub)(int,int);
int (*calc_mul)(int,int);
...
}calc_t;
定义一个计算器类型的变量
calc_t calc;
可以看的出来,编程语言本身并没有面向对象和面向过程之分,只是程序员的思考方式不一样罢了。
好了,我们接着思考:如果我要开发一个手机软件,这个手机软件软件中要包含打电话功能、计算器功能、播放音乐功能,这些又该如何实现呢?
面向过程序员,思考方式:
[1]定义变量保存用户的输入的数据
[2]实现一个加法函数,完成数据的加法
[3]实现一个减法函数,完成数据的减法
[4]实现一个乘法函数,完成数据的乘法
[5]实现一个打电话功能函数
[6]实现一个播放音乐功能函数
.....
面向对象程序员,思考方式:
[1]计算器是一个对象,包含一些数据和方法
[2]打电话是一个对象,包含一些数据和方法
[3]播放音乐是一个对象,包含一些数据和方法
...
面向对象程序员这时想到,自己以前写过一个计算器对象,写过一个打电话对象,写过一个播放器对象,他们之间是独立的,于是高富帅的干活方式出现了。啥也不用干,"ctrl +c" 和 "ctrl + v"把活干完了,然后去喝茶了。
面向过程程序员也不傻,看到面向对象程序员的干活方式,立马自己也"ctrl + c" 和 "ctrl + v"把自己以前编写的代码从n个文件的n行代码中寻找,找到之后发现自己的视力从+2.5下降到-2.5。不管咋地,咱就是这么任性,已经把代码拷贝过来,接下来就编译完,交给老大就可以去喝茶了。编译器疯了,变量名没有定义、变量名冲突,函数名冲突....,最后的结果是n行代码编译器无情的报了"n+1"行错误。
故事看到这,我们可以看出,面向对象编程的特点:
[1]万事万物都看成对象,对象包含数据和操作数据的方式,是一个独立的个体
[2]编写程序之前,先通过封装的方法设计出对象应该包含的内容
[3]整个软件系统由对象构成,就像这个人类世界一样,有n个人构成,每个人扮演者不同的角色
[4]代码的复用性好,更便于维护
好了,就说这么多了,想要真正理解,必须自己在实际的项目中慢慢体会,才能真正理解面向对象和面向过程的不同。
二 Java面向对象之封装
我们知道,在面向对象的编程思想中,一个软件系统由n个对象构成。而对象需要先设计,就像前面我们用C语言的结构体来描述计算器一样。
typedef struct
{
int data1;
int data2;
int (*calc_add)(int,int);
int (*calc_sub)(int,int);
int (*calc_mul)(int,int);
...
}calc_t;
在Java 中,用可以用类来描述一个对象的特点:
class Calc{
int data1;
int data2;
int calc_add(){
return data1 + data2;
}
int calc_sub(){
return data1 - data2;
}
....
};
在C语言中的结构体内部是没法直接编写函数的,在Java的中是可以直接编写函数的,可以看出Java的类封装性更强。
问题1 : Java的类和C语言的结构体是一样的吗?
回答 : 相似,都是程序员设计出来的类型。
问题2: Java的类和对象有什么联系呢?
回答: 相当于C语言的结构体类型和结构体变量。
好了,接下来我们给出Java中标准的类定义方法:
接下来我们就来实战一下吧,设计一个描述人的类:
编译出现的错误如下:
修改完后,接着编译,出现的错误如下:
问题:在Java中如何给引用类型变量初始化呢?
回答: 让引用类型变量保存一个可用内存空间首地址就可以了。
类名 引用类型变量名;
引用类型变量名 = new 类名() 或 引用类型变量名 = new 类名(参数列表);
例如:
people = new Person();
好了,知道错误后,我们接着修改代码如下:
编译没有错误,输出如下结果:
嗯,还是有哥们写错,它的写法如下:
嗯,我们应该定义一个构造器,这样我们在创建对象后就可以自动给对象的成员变量进行初始化了,修改代码如下:
问题:如果创建一个对象时,我不想给构造器传递参数,我该怎么做呢?
回答:在类中在定义一个无参数的构造器。
修改代码如下:
三 Java中的访问修饰符public和private
还是通过列子说明吧!
嗯,明白了,private 修饰的成员变量和成员方法只能在类中访问,在别的类中是不能通过对象来访问的。public 修饰的成员变量和成员方法除了在类中可以直接访问,在其他类中也可以通过对象来访问。
现在问题来了,具体什么时候用private修饰,什么时候用public修饰成员变量和成员方法呢?
大牛们这样回答你,类的成员变量都应该设为private,类的成员方法如果只是类内部使用则设为private,如果给外部使用则设为public。
试着思考这样一个问题:如果我们把成员变量设为public,这样在任何一个类中都可以随意访问。有一天编写类的人将成员变量名更改了,此时在别的类中使用过此类的成员变量的代码都需要修改。如果成员变量设为private,此时在别的类中是无法直接访问的,所以你做了修改是不会影响到别人的。
注意:我们封装的目的就是想隐藏一些细节,向外界提供统一的接口。
现在来了一个新的问题,把成员变量设为私有的,别的类中如何访问呢?嗯,聪明的你应该可以想到,通过类的公有方法,在公有方法中访问类的私有成员。于是乎代码修改成如下:
三 Java 中的this
1、表示对当前对象的引用!
2、表示用类的成员变量,而非函数参数,注意在函数参数和成员变量同名是进行区分!其实这是第一种用法的特例,比较常用,所以那出来强调一下!
3、用于在构造方法中引用满足指定参数类型的构造器(其实也就是构造方法)。但是这里必须非常注意:只能引用一个构造方法且必须位于开始。
注意:
this不能用在static方法中!所以甚至有人给static方法的定义就是:没有this的方法!虽然夸张,但是却充分说明this不能在static方法中使用!
四 Java 中的static
关于"static"这个关键字大家并不陌生,在C语言中static的用途如下:
(1)static 修饰一个局部变量,表示这个局部变量的值具有继承性,在函数调用结束的时候,static修饰的局部变量空间
(2)static 修饰一个全局变量或函数时,表示限制全局变量和函数的作用范围,此时全局变量或函数只能在本文件中使用。
在Java中,"static"关键字和C语言的用法差别很大,下面我们就一起来看看在Java中,什么时候应该使用"static"关键字呢?
1.用static 修饰类的成员变量
大牛名言:如果你想让同一个类的所有对象共享同一个变量,那么这个类的成员变量应该使用"static"关键字修饰。
解读大牛名言如下
(1)static修饰的成员变量属于类的变量,所有对象共享。也就是说当一个类加载到内存中之后,static修饰的成员变量空间就已经分配好了。接下来通过这个类创建的所有对象都共享static修饰的成员变量。
(2)static修饰的成员变量,不需要创建对象来访问,由于它属于类,所以可以通过 "类名.成员变量名"来访问。
2.用static修饰类的成员方法(静态成员方法)
大牛名言:如果你不想通过创建对象来访问成员函数,那么这个类的成员函数应该用"static"关键字来修饰。
解读大牛名言如下
类中非静态成员方法在访问的时候,必须先创建对象,然后通过对象来访问成员方法。创建对象必定会有内存开销,有些时候我们在Java中编写的一些普通的算法函数,它不需要访问类的非静态的成员变量和函数,只是自己内部完成一些计算,这样的函数很明显和对象没有联系,此时应该将这个函数用"staic"关键字修饰,让它变成静态成员方法。这样访问它的时候,就不需要创建对象了,只需要通过"类名.成员函数名"来访问就可以了。
其实这样的例子有很多,例如:main函数 , System.arraycopy,Arrays.copyOf等。
思考:如何在静态成员方法中,访问非静态成员变量和函数?
四 Java 中的static静态块和非静态块
(1)static{}(即static块),会在类被加载的时候执行且仅会被执行一次,一般用来初始化静态变量和调用静态方法。
(2){}(非静态块)每个对象生成时都会被执行一次,它可以初始化类的成员变量。非静态初始化块代码会在构造函数调用前先执行。
五 Java 中的包机制
所谓包,就是把不同特征的类隔离起来,即使这些彼此隔离的包中包含同名的类也无所谓。在一个大的软件系统中,有很多种类的对象,要想描述这些不同种类的对象就必须设计不同的类来完成。一般都是将这些类进行分类,由不同的人去完成不同的类。这样可能会产生A定义的类名和B定义的类重名,此时如果将A和B定义的类拷贝到同一个目录下,必定会产生覆盖,于是乎Java中就提出了包的机制来解决这种命名冲突的问题。
简单总结包的用途:
1) 将功能相近的类放在同一个包中,可以方便查找与使用。
2) 由于在不同包中可以存在同名类,所以使用包在一定程度上可以避免命名冲突。
3) 在Java中, 包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类
1. Java中的包的创建
package 包名;
注意:包名的命名方式 (全部小写,以公司或项目组织的顺序倒写,中间以.分隔,如: com.farsight.www.cyg)
我估计大家看到这里还是糊涂,我们还是以例子来说明:
以包的方式,编译我们的java代码:
javac -d . TestPackage.java
最终生成的效果如下:
2. Java中的使用指定包下面的类
每次都这样写,迟早都要 崩溃的,有没有更简单的方法呢?
使用import语句引入包中的类
由于采用使用长名引用包中的类的方法比较繁琐,所以Java提供了import语句来引入包中的类。
import语句的基本语法格式如下: import 包名1 [ .包名2 ...].类名 | *;
当存在多个包名时,各个包名之间使用“.”分隔,同时包名与类名之间也使用“.”分隔。
*:表示包中所有的类。
例如,引入com.wgh包中的Circ类的代码如下:
import com.wgh.Circ;
如果 com.wgh包中包含多个类,也可以使用以下语句引入该包下的全部类。
import com.wgh.*;
嗯,还有另一种写法,如下所示:
六 Java 中常用的环境变量
CLASSPATH
1. 在java的编译环境中使用
它的作用与import、package关键字有关。当你写下import java.util.*时,编译器面对import关键字时,就知道你要引入java.util这个package中的类;但是编译器如何知道你把这个package放在哪里了呢?所以你首先得告诉编译器这个package的所在位置;如何告诉它呢?就是设置CLASSPATH啦 !
当你自己开发一个package时,然后想要用这个package中的类;自然,你也得把这个package所在的目录设置到CLASSPATH中去!CLASSPATH的设定,对JAVA的初学者而言是一件棘手的事。所以Sun让JAVA2的JDK更聪明一些。你会发现,在你安装之后,即使完全没有设定CLASSPATH,你仍然能够编译基本的JAVA程序,并且加以执行。
例如:我把CLASSPATH值设为D:,此时我编译我的java程序,效果如下:
有人肯定在想,以前我们使用"System、Arrays,String"等类时,既没有设置CLASSPATH,也没有用"import"进行导包,为什么可以编译通过呢?
从上面的编译过程可以看出,javac在编译java源文件的时候,有两个搜索路径:
(1)CLASSPATH指定的路径
(2)默认的搜索路径 "安装目录Javajdkjrelib"下的xxx.jar包。例如:jrelibrt.jar文件中就有常用的java类,如:System,String等。
问题:jar包是什么呢?
回答:jar包是将我们的包进行压缩后的文件
2. 在java的运行环境中使用
当我们通过java 运行一个类时,默认是从当前目录下寻找这个类,然后将这个类加载到内存中,如果这个类在运行的时候,需要"import"导入的类,在从当前目录下开始寻找"import"指定的路径下类,然后将他们加载到内存。
注意:如果设置了CLASSPATH环境变量,则java虚拟机从CLASSPATH指定的路径下搜索需要的类加入到内存。
好了,我们已经知道了CLASSPATH环境变量用途,到目前为止,我们没有配置CLASSPATH环境变量,我们仍然可以编译和运行java程序。有人可能会认为,学这个没啥用,不配置也可以用呀。其实不然,如果我们不使用java环境自带的类进行开发,而是用别人提供的类进行开发,这个时候CLASSPATH就派上用场了,你必须将你需要类的路径设置在CLASSPATH环境变量中。