文章概要:本文简洁描述了面向对象思想的主要特性。包含“面向对象”思想、对象的方法、封装、可复用代码、继承、多态、超类、集合、泛型、对象生命周期、异常处理、多线程等内容。
编程语言的产生始于对机器的模仿,在研究一门编程语言的过程中,我们需要反复地思考,如何让计算机像人一样完成动作。
面向对象编程(Orientd Object Program 英文在编程中有助接近事物本质)是当下热门的计算机编程思想,OOP的一条基本原则是计算机程序由单个能够起到子程序作用的单元或对象组合而成。
“面向对象”思想
要解决软件开发的复杂性,则需要一个精炼的抽象过程,以思维方式为基础提炼出一门计算机语言。
面向对象思想,将问题空间中的元素表示为“对象”。使每个对象看起来都有点像一个“微型计算机”,它具有属性和行为,而用户可以要求该计算机对象执行这些操作。
为实现抽离对象的思想,我们就必须将现实世界中各种杂乱无章的元素抽象为一个整体,在此我们必须了解抽象的概念:从具体事物抽出、概括出它们共同的方面、本质属性与关系等,而将个别的、非本质的方面、属性与关系舍弃,这种思维过程,称为抽象。
面向对象抽象的五个基本特性——
(1)、万物皆为对象。抽取待求解问题的概念化构件(猫、狗、建筑物)等等,将它们视为奇特的变量,并赋予存储数据,和操作自身行为的能力。
(2)、程序是对象的集合,它们通过互相发送消息来告知对方彼此要做的。
(3)、每个对象都有自己由其他对象构成的存储。
(4)、每个对象拥有其类型(创建时创建属于一个类型的对象)。
(5)、某一特定类型的所有对象可以接收同样的消息。
对象的方法
通过关键字class,程序执行期间,具有不同执行状态而其他方面相似的对象,会被分配到对象的类当中。创建 “抽象数据类型” 是面向对象程序设计的基本概念之一。
一句话 :类,描述了具有相同特性和行为的对象集合。
如何在问题空间的元素和解空间的对象之间创建一对一的映射,是面向对象的设计挑战。JAVA通过方法确定某一对象所能发出的请求
Car mycar = new Car(); //定义一辆汽车
mycar.run(); //调用对象的方法,来请求实现汽车对象的定义
对于一辆汽车,我们可以定义它的方法接口,开启run() 关闭off() 加速speedup()等等。方法确定了对汽车对象所能发出的请求,并且,在汽车对象程序中必须拥有实现代码,并与隐藏的数据一同实现。可以将对象理解为服务提供者。
实现的封装
开发过程前后端分别为“类创建者”与“客户端程序员”两个角色。
访问控制的两个好处: 1、保护对象内部脆弱生态 2、方便修改时不影响其他内容开发
为了实现控制访问,JAVA使用以下关键字:public(公共)、protected(子类及包)、default(包)、private(私人) 记忆口诀:公子包
可复用实现
代表一个可复用代码单元:例如工具类。新的类根据需要来实现任意类型,任意数量的其他对象并组合成新的类。
在开发中“组合大于继承”是十分重要的思想,也就是优先使用对象组合,而不是类继承。优点在于,对象组合的设计更加简单灵活。
继承
类的疑难:在创建了一个类之后,即使另一个新类与其具有相似的功能,还是需要创建一个新的类。这会造成重复的开发工作。怎么办呢?
通过创建基类型,基类型包含其所有导出类型所共享的特性和行为。创建一个基类型来表示系统中某些对象的核心概念,从基类型中导出其他类型,来表示该类型可以被实现的各种不同方式。
从真实世界中对系统的表述过渡到代码对系统进行描述举例。
Shape(形状)为基类 Rectangle Circle Triangle (圆形、长方形、三角形)为子类
子类(它们都属于形状)包含基类的所有成员,并复制了基类的所有方法
那么,子类与导出类的差异怎么体现呢?
(1)、直接在导出类中添加新方法。
(2)、覆盖(也就是重写),在子类中创建基类同名方法的心定义。
多态
class Car{
Car mycar = new BMW(); //定义一辆汽车,视为宝马
mycar.run(); //调用对象的方法,来请求实现汽车对象的定义
private void run(){} //在汽车类中创建run()方法
}
class BMW extends Car{
public void run(){
System.out.print("宝马正在疾驰"); //在子类进行实现
}
}
运行结果:宝马正在疾驰
class Car{
Car mycar = new Benz (); //此处做了更改———定义一辆汽车,视为奔驰
mycar.run(); //调用对象的方法,来请求实现汽车对象的定义
private void run(){} //在汽车类中创建run()方法
}
class BMW extends Car{
public void run(){
System.out.print("宝马正在疾驰"); //在子类进行实现
}
}
class Benz extends Car{
public void run(){
System.out.print("奔驰正在疾驰"); //在子类进行实现
}
}运行结果:奔驰正在疾驰
这种把导出类看作基类的过程被称为向上转型。
以上代码就展现了多态的意义,在将子类对象看作基类对象来对待时,创建的引用是基类的引用,基类(车)不需要知道子类(宝马,奔驰)的具体行为,但即便忽略他们的具体类型,编译器也能将他们的
方法看作具体的子类来进行操作。那么这是如何做到的?
答案是:面向对象程序设计语言采用了后期绑定的概念。当向对象发送消息时,被调用的代码直到运行时才能确定。编译器的作用是,第一个保证被调用方法的存在,其次对调用参数和返回值
执行类型检查。
如何通俗地理解向上转型的思想?
以上代码,在JAVA程序员的理解中,也常出现这样的错误:通俗讲,“如果是宝马,请这样奔跑,如果是奔驰,你那样奔跑....”,但这是不正确的,这里所要表达的意思是,"你是一个汽车,你可以奔
跑你自己,但是如果奔跑,请注意奔跑的细节"
单一基类、集合及参数化类型
超类:JAVA拥有单根继承结构,什么意思呢?就是所有的类都会有一个共同的终极父类,叫做Object。单根继承结构保证了所有对象都具备某些功能。单根继承结构的最大好处,是使垃圾回收器的实现变得简单很多,也利于实现系统级操作异常处理,所有对象都具有类型信息,都可以明确为同一个类型。
集合:不知道在解决某个问题时需要创建多少个对象(比如坦克大战的游戏创建多个坦克),那么就不可能知道如何存储这些对象。解决方案是:通过集合来存储多个相同类型的对象。JAVA提供了不同设计的容器,比如List、ArrayList等,对不同操作具有不同效率。
泛型:泛型是JAVASE5以后体提供将集合中所能存储的对象进行了向下转型后的约束(比如原来是存List<Object>,向下转型成为List<Shape>)
ArrayList<Car> cars = new ArrayList<Car>(); //用这样一个语句来创建一个只允许存储Car类型的ArrayList集合
对象生命周期
C、C++与JAVA在内存分配方式上的区别——
C、C++:创建对象于堆中,设置其存在于消亡。
JAVA:动态内存分配,用到时才创建。
可以说,JAVA完全采用了动态内存分配方式,通过关键字new构建新实例。而垃圾回收器则负责处理内存释放问题,监控对象什么时候不被使用,并自动销毁对象,释放内存。
异常处理
在JAVA中,异常是一种对象,它从出错地点被“抛出”,并被专门设计用来处理特定类型错误的异常处理器“捕获”,异常处理就像是与程序正常运行路径并行的、错误发生时执行的另一条路径。
异常处理并不是面向对象编程的特征,它早在面向对象语言出现前就已存在。
并发编程
JAVA通过多线程解决并发问题,将问题拆分为多个独立运行的任务。
编程是一种为单一处理器分配执行时间的手段,通过时间片轮流分配不同任务的处理时间,避免卡死在一个任务上,提高程序的响应能力。在多处理器的操作系统上,任务也可以指派到不同的处理器执行。
举例:火车站开放多个售票窗口,使得并发完成售票操作,避免出现单一窗口拥塞的情况。
总结
综上列举了OOP思想的主要特性。
OOP使我们能够在过程型语言的基础上,认识了许多新概念。而事实上,这些概念使得JAVA语言的编写变得方便、简单许多,也更加易于理解。对于设计良好的OOP程序,我们通过阅读就应该能看懂它。并且,许多问题都可以通过现有的类库来解决。