面向对象程序设计(Object Oriented Programming)
面向对象的程序是由对象组成的,每个对象包含对用户公开的特定部分和隐藏的实现部分。在OOP中不关心对象或者功能的具体实现。
传统的结构化程序通过设计一系列的过程(即算法)来求解问题。一旦确定了这些过程就要开始考虑存储数据的方式。
所以说结构化程序中算法是第一位的,数据结构是第二位的。OOP却调换了这个顺序,将数据放在第一位,然后再考虑操作数据的算法。
-
类(class)
类(class)是构造对象的模板或蓝图,由类构造(construct)对象的过程称为创建类的实例(instance)。
Java编写的所有代码都位于类的内部。
封装(encapsulation)形式上看是将数据和行为组合在一个包中,并对使用者隐藏了数据的实现方式。对象中的数据称为实例域(instance field),操纵数据的过程称为方法(method)。对于每个特定的类实例(对象)都有一组特定的实例域值。这些值的集合就是这个对象的当前状态(state)。无论何时只要向对象发送一个消息他的状态就有可能发生改变。
实现封装的关键在于绝对不能让类中的方法直接的访问其他类的实例域。程序仅通过对象的方法与对象的数据进行交互。
可以通过扩展一个类来建立另外一个新的类,新类具有所扩展的类的全部属性和方法,称之为继承(inheritance)
-
对象
对象的三个主要特性
- 对象的行为(behavior)--------------可以对对象施加操作或方法
- 状态(state)---------------施加方法时对象的响应
- 标识(identity)-------------如何辨别具有相同行为与状态不同的对象
对象状态的改变只能通过调用方法实现,对象的状态影响它的行为,对象的状态不能完全描述一个对象
-
识别类
过程化的程序设计必须从顶部main函数开始编写程序。面向对象程序设计时没有相对的“顶部”。首先要从设计类开始,然后在每个类中添加方法。
识别类的简单规则是在分析问题的过程中寻找名词,而方法对应着动词。。
-
类之间的关系
类之间最常见的关系是
依赖(“uses-a”):如果一个类的方法操纵另一个类的对象,就说一个类依赖于另一个类。应尽可能减少类的相互依赖,让类之间的耦合度最小
聚合(“has-a”):类A的对象包含类B的对象,也可以叫做关联。
继承(inheritance)“is-a”:类A扩展类B,类A不但包含从B继承的方法,还拥有一些额外的功能。
使用预定义类
-
对象与对象变量
要想使用对象必须先构造对象,并指定其初始状态。然后对对象应用方法。
Java中利用构造器(constructor)构造新实例。
对象与对象变量之间有一个重要的区别
对象变量并没有实际包含一个对象,而仅仅引用一个对象。new操作符的返回值也是一个引用,可以显式的将对象变量设置为null。表明这个对象目前没有引用对象,局部变量不会自动地初始化null,而必须通过调用new或将其设置为null进行初始化。
注:C++中没有空引用,引用不能被赋值。可以将Java对象变量看做C++的对象指针。
Data deadline = null;
-
更改器方法与访问器方法
对实例域做出修改的方法叫更改器方法(mutator method),仅访问实例域不修改的方法叫做访问器方法(accessor method)。(get 和 set方法)
用户自定义类
要想创建一个一个完整的程序,应该将若干类组合在一起,其中一个类有main方法。
Java中最简单的类定义为:
class ClassName
{
field1
field2
...
constructor1
constructor2
...
method1
method2
...
}
-
多个源文件的使用
习惯性的将每个类存在一个单独的源文件中。
现在的编译很简单了,软件的功能都很强大,以前都是用通配符调用Java编译器的,或者用Make指令(Makefile等)。
-
构造器
用构造器向其中传递入参数后,构造某个类的对象时构造器便会运行,以便将实例域初始化为所希望的状态。
构造器和普通方法有一个重要不同,构造器总伴随着new操作符的执行被调用,不能对一个已经存在的对象调用构造器来达到重新设置实例域的目的
- 构造器与类同名
- 每个类可有一个以上的构造器
- 可以有0,1或者多个参数
- 构造器没返回值
- 构造器总是和new操作一起使用
不要在构造器中定义与实例域重名的局部变量。必须注意在所有的方法中不要命名与实例域同名的变量。这些变量屏蔽了同名的实例域!!!!!
-
隐式参数与显式参数
比如一个方法:
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent/100;
salary += raise;
}
调用方法:
number1.raiseSalary(5);
其实执行的是
public void raiseSalary(double byPercent)
{
double raise = number1.salary * 5 / 100;
number1.salary += raise;
}
number1对象的salary域的值增加5%。
number1为隐式参数,出现在方法名钱的类对象,5 为显式参数,列在方法声明中的。每一个方法中 this 表示隐式参数,所以可以这样写
public void raiseSalary(double byPercent)
{
double raise = this.salary * byPercent/100;
this.salary += raise;
}
这样写的好处是可以将实例域与局部变量明显地区分开来。
-
封装的优点
一个只读域一旦在构造器中设置完毕,就没有任何办法对其进行修改。对非只读域可以直接调用方法来修改。
有些时候,需要获得所设置的实例域的值。应提供:
- 一个私有的数据域;
- 一个公有的域访问器方法
- 一个公有的域更改器方法
好处:可以改变内部有实现,除了该类的方法外,不会影响其他代码。更改器方法可以执行错误检查,然而直接对域进行赋值将不会进行这些处理。
-
私有方法
实现类时,公有数据非常危险,所以应将所有数据域设为私有的。
特殊情况下方法也可以设置为私有的,比如某些辅助方法,与当前的实现机制非常紧密,或需要一个特别的协议或次序。
对于私有方法,它不会被外部的其他类调用,可以将其删除。但是对于公有方法则不行,因为可能有其他类依赖它
-
final实例域
将实例域定义为final。构件对象时必须初始化这样的域(必须确保每个构造器执行后,此域的值被设置,且在后面的操作中不能对它进行修改)。
final修饰符大多应用于基本(primitive)类型域,不可变(immutable)类的域(类中的每个方法都不会改变其对象)。例如String类。
但是。仅仅意味存储于某变量中的对象在引用对象构造之后不能被改变,但是并不意味着此对象是个常量。
方法参数
按值调用(call by value)方法接收的是调用者的值
按引用调用(call by reference)方法接收的是调用者的地址
Java总是按值调用,方法得到的是所有参数值得一个拷贝,方法不能修改传递给它的任何参数变量的内容。
方法参数两种类型:基本数据类型和对象引用。
对象引用进行的是值传递。
方法参数的使用情况:
- 一个方法不能修改一个基本数据类型的参数,即数值型和布尔型
- 一个方法可以改变一个对象参数的状态
- 一个方法不能让对象参数引用一个新的对象
对象构造
-
重载
多个方法有相同的名字但是有不同的参数
Java允许重载任何方法,而不只是构造器方法。
-
默认域初始化
如果构造器中没有显式的给域赋初值,就会自动赋默认值:数值为0、布尔型为false、对象引用为null(这不是良好的编程习惯)。
-
无参构造器
只有编写类时没编写构造器才会提供一个无参构造器,将所有的实例域设置为默认值。
当类中提供了任意一个构造器,此时构造对象如果不传参数的话就为非法了。
-
调用另一个构造器
关键字this引用方法的隐式参数。如果构造器的第一个语句形如this(...),这个构造器将调用同一个类的另一个构造器。
public Employee(double s)
{
//calls Employee
this("Employee #" + nextId, s);
nextId++;
}
当调用 new Employee(60000)时,Employee(double)将调用Employee(String, double)构造器。
-
初始化块
在一个类的声明中可以包含多个代码块。只要构造类的对象,块就会被执行。
但是这种机制不是必须的,通常直接将初始化代码块放在构造器中。
调用构造器的具体处理步骤:
- 所有数据域被初始化为默认值
- 按照在类中出现的次序,依次执行所有域初始化语句和初始化块
- 如果构造器第一行调用了第二个构造器,则执行第二个构造器主体。
- 执行这个构造器的主体
Java语言是面向对象语言
面向对象语言的三大特征是
- 封装
- 继承
- 多态
本文是在学习《Java核心技术(卷I)基础知识》是做的一些摘录,主要摘录是与面向过程的关键思想和Java语言的一些关键特性,不涉及很多Java语言的语法。花费些时间去掌握思想,然后从程序中理解Java语言我觉得是挺好方式。
还有些不明白的地方在4.6.4 4.6.5