Java学习笔记之九 对象与类
面向对象程序设计概述
在20世纪70年代,流行“结构化”过程化程序设计开发技术。
在20世纪末,面向对象程序设计成为主流的程序设计范型。
面向对象的程序是由对象组成的,每个对象包含对用户公开的特定功能部分和隐藏的实现部分。
程序中的许多对象来自于标准库,还有一些是自定义的。
是自己构造对象,还是从外界购买对象,完全取决于预算和时间。
从根本上说,只要对象能够满足要求,就不必关心其功能的具体实现过程。
结构化程序设计通过设计一系列的过程(算法)来求解问题。这些过程一旦被确定,就要考虑存储数据的方式。所以,Pascal语言设计者Niklaus Wirth说:算法+数据结构=程序。(他1975年发布的书名)。这里,算法是第一位的,数据结构是第二位的。
面向对象程序设计中,数据被放在了第一位,之后再考虑操作数据的算法。
类(class)是构造对象的模板或者蓝图。由类构造(constuct)对象的过程称为创建类的实例(instance)。
用Java编写的代码都位于某个类的内部。
封装(encapsulation,有时称为数据隐藏)是与对象有关的一个重要概念。对象中的数据称为实例域(instance fields),操纵数据的过程称为方法(method)。对于每个特定的类实例(对象)都有一组特定的实例域值。这些值的集合就是这个对象的当前状态(state)。无论何时,只要向对象发送一个消息,它的状态就有可能发生改变。
实现封装的关键,是绝对不能让类中的方法直接接触其他类的实例域。程序仅通过对象的方法域对象数据交互。
封装给予了对象“黑盒”特征。一个类可以全面地改变存储数据的方式,只要依旧使用同样的方法操作数据。其他对象不会知道也不介意所发生的变化。
面向对象的另一个原则,是类可以通过扩展另一个类来建立。
对一个已有的类扩展时,这个扩展后的新类,具有所扩展的类的全部属性和方法。在新类中,只需要提供仅适用于这个类的新方法和数据域即可。通过扩展一个类来建立另一个类的过程,称为继承(inheritance)。
对象有3个主要特性:
- 对象的行为(behavior)。可以对对象施加哪些操作。
- 对象的状态(state)。施加操作时,对象如何响应。
- 对象标识(identity)。区分具有相同行为与状态的不同对象。
传统的过程化程序设计,必须从顶部的main函数开始编写程序。
面向对象的系统没有顶部。
定义类时,在分析问题的过程中寻找名词和动词。这是一种粗略的方法。具体创建类,完全取决于个人的开发经验。
类之间的关系,常见的有:
- 依赖。use-a。
- 聚合。has-a。
- 继承。is-a。
应尽可能地将相互依赖地类减至最少。让类之间地耦合度最小。
使用现有类
在Java中,没有类就没法做任何事情。
并不是所有的类都具有面向对象特征。比如Math类。Math类只封装了功能,它没有数据。
要想使用对象,就必须先构造对象,并指定其初始状态。然后,对对象施加方法。
在Java语言中,使用构造器(constuctor)构造新实例。构造器是一种特殊的方法,用来构造并初始化对象。
构造器的名字应该与类名相同。构造对象时,在构造器前面加上new操作符。
一个对象变量并没有实际包含一个对象,仅仅引用一个对象。
用户自定义类
要想创建一个完整的应用程序,应该将若干类组合在一起,其中只要一个类有main方法。
在一个源文件中,只能有一个公有类,但可以有任意数目的非公有类。
多个源文件的编译:
方法一:使用通配符调用Java编译器。
javac Some*.java
方法二:仅编译入口类。
Java编译器相当于内置了make功能,会自动查找对应的类的class文件,如何不存在,则会从源文件编译。
final实例域不是常量。
静态域与静态方法
如果将域定义为static,每个类中只能由一个这样的域。意思是,这个实际数据只有一份,而不是只能定义一个域为静态的。
静态域属于类,不属于任何独立的对象。
静态常量。静态常量是类的常量。所有类的实例仅此一份。
静态方法。静态方法是一种不能向对象实施操作的方法。不能在静态方法中访问实例域。静态方法可以访问自身类的静态域。
调用静态方法时,不需要使用对象。
main方法也是一个静态方法。main方法不对任何对象进行操作。
在启动程序时,还没有任何一个对象。静态的main方法将执行并创建程序所需要的对象。
每一个类可以由一个main方法。
方法参数
- 一个方法不能修改一个基本数据类型的参数
- 一个方法可以改变一个对象参数的状态
- 一个方法不能实现让对象参数引用一个新的对象
对象构造
Java提供了多种编写构造器的方式。
重载。overloading。如果多个方法有相同的名字、不同的参数,便产生了重载。
Java允许重载任何方法。
要完整描述一个方法,需要指出方法名、参数类型。这叫做方法的签名,signature。
返回类型不是签名的一部分。
默认域初始化。如果在构造器中没有显示地给域赋予初值,会自动赋为默认值:数值型为0,布尔型为false,对象引用为null。
默认域初始化并不是一种良好地编程习惯。
默认构造器。指没有参数地构造器。
在编写类时如果没有编写构造器,系统会提供一个默认构造器。
仅当类没有提供任何构造器地时候,系统才会提供一个默认地构造器。
显式域初始化。这是一种良好地编程习惯。
对象析构。finalize方法。
Java有自动地废料回收器,不需要人工回收内存,所以Java不支持析构器。
可以为任何一个类添加finalize方法。finalize方法将在废料回收器清除对象之前调用。
在实际应用中,不要依赖于finalize方法回收任何短缺地资源,因为很难知道这个方法什么时候才能够调用。
包
Java允许使用包(package)将类组织起来。
借助包,可以方便地组织自己地代码,并将自己地代码与别人提供地代码库分开管理。
标准地java类库分布在多个包中。
标准地java包具有层次结构,可以嵌套层次组织包。
所有标准地java包都处于java和javax包层次中。
使用包地主要原因是确保类名地唯一性。
为了保证包地绝对唯一性,Sun公司建议将公司地因特网域名以逆序地形式作为包名,并且对于不同的项目使用不同的子包。
例如,horstmann.com是《Core Java》作者注册的郁闷。逆序形式为com.horstmann。这个包可以进一步划分子包,如com.horstmann.corejava。
从编译器的角度来看,嵌套的包之间没有任何关系。每一个包都拥有独立的类集合。
类的导入。一个包可以使用所属包中的所有类,以及其他包中的公有类(public class)。
采用两种方式访问另一个包中的公有类:
方式一:在每个类名之前添加完整的包名。
方式二:使用import语句。一旦使用了import语句,使用类时,就不必写出包的全名了。
可以使用impot语句导入一个特定的类或者整个包。
import语句应该位于源文件的顶部(但位于package语句的后面)。
如果导入包时,出现类名冲突,应使用包全称。
在包中定位类时编译器的工作。类文件中的字节码肯定使用完整的包名来引用其他类。
静态导入。从Java SE 5.0开始,import语句不仅可以导入类,还可以导入静态方法和静态域。
将类放入包中。将包的名字放在源文件的开头,包中定义类的代码之前。
package com.horstmann.corejava;
public class Employee
{
...
}
如果没有在源文件中放置package语句,这个源文件中的类会被放置在一个默认包(default package)中。默认包是一个没有名字的包。
将包中的文件放到与完整的包名匹配的子目录中。
编译:
javac com/mycompany/PayrollApp.java
java com.mycompany.PayrollApp
编译器对文件进行操作,java解释器加载类。
编译器在编译源文件时不检查目录结构。
包作用域。public的部分可以被任意类使用。private的部分只能被定义它们的类使用。如果没有指定public和private,这个部分(类、方法、变量)可以被同一个包中的所有方法访问。
类路径
类存储在文件系统的子目录中。类的路径必须与包名匹配。
类文件也可以存储在JAR(Java归档)文件中。在一个JAR文件中,可以包含多个压缩形式的类文件和子目录,这样可以节省空间、改善性能。
在程序中用到第三方(third-party)的库文件时,通常会给出一个或者多个需要包含的JAR文件。
JDK也提供了许多JAR文件。
JAR文件使用ZIP格式组织文件和子目录。
为了使类能被多个程序共享,可以:
- 将类放在一个目录中。树状层次结构。
- 将JAR文件放在一个目录中。
- 设置类路径(class path)。类路径是所有包含类文件的路径的集合。
类路径包括:
- 基目录
- 当前目录
- JAR文件
javac编译器总是在当前的目录中查找文件。
java虚拟机仅在类路径中有"."目录的时候才查看当前目录。
设置类路径:
- 使用java 的-classpath或-cp选项指定类路径。所有指令应该写在一行中。
- 也可以设置CLASSPATH环境变量。
文档注释
javadoc工具可以由源文件生成一个HTML文档。
类注释。类注释必须放在import语句之后,类定义之前。
方法注释。方法注释必须放在所描述的方法之前。
域注释。只需要对公有域(通常是静态常量)建立文档。
包与概述注释。
要想产生包注释,由两种选择。
一,提供一个package.html命名的HTML文件。<BODY></BODY>之间的所有文本会被抽取出来。
二,提供一个以package-info.java命名的java文件。这个文件必须包含一个初始的以/**
和*/
界定的javadoc注释,跟随在一个包语句之后。它不应该包含更多的代码或注释。
概述注释。overview.html文件中的<BODY></BODY>部分会被提取出来。
注释的抽取。
- 切换到包含想要生成文档的源文件目录。对于嵌套的包,应切换到基目录。
- 运行命令。
对于一个包:javadoc -d docDirectory nameOfPackage
对于多个包:javadoc -d docDiectory nameOfpackage1 nameOfPackage2
如果文件在默认包中,运行:javadoc -d docDirectory *.java
用link选项为标准类库添加超链接。
javadoc -link http://java.sun.com/javase/6/docs/api *.java
类设计技巧
一定将数据设计为私有。绝对不要破坏封装性。数据的表示形式很可能会改变,但它们的使用方式却不会经常发生变化。
一定要对数据初始化。
不要在类中使用过多的基本数据类型。用其他的类代替多个相关的基本数据类型的使用。
不是所有的域都需要独立的域访问器或域更改器。
使用标准格式进行类的定义。一定采用下面的形式书写类的内容:
- 公有访问特性部分
- 包作用域访问特性部分
- 私有访问特性部分
在每一部分中,应该按照下列顺序列出:
- 实例方法
- 静态方法
- 实例域
- 静态域
对职责过多的类进行分解。
类名和方法名要能够体现它们的职责。