java复习
第1章 Java语言基础知识
1.1 Java语言与面向对象的程序设计
1 简述面向对象的编程语言与以往编程语言的根本不同点
面向对象的编程语言与以往编程语言的根本不同在于抽象机制的不同。
机器语言和汇编语言几乎没有抽象,对于机器而言是最合适的描述,它可以直接操作机器的硬件,并且任何操作都是面向机器的,这就要求人们在使用机器语言或汇编语言编写程序时,必须按照机器的方式去思考问题。因为没有抽象机制,所以程序员不得不陷入复杂的事物之中。
面向过程的语言使程序员可以离开机器层次,在更抽象的层次上表达意图。但它所关注的只是处理过程,即执行预期计算所需要的算法。
面向对象的编程语言将客观事物看作具有状态和行为的对象,通过抽象找出同一类对象的共同状态(静态特征)和行为(动态特征),构成模型——类。而程序需要解决的问题便反映为各种不同属性的对象以及对象之间的关系和消息传递。面向对象的方法在程序设计领域是一种相对较新的方法,它更接近于人类处理现实世界问题的自然思维方法。
2 封装的好处
封装带来的好处是:隐藏类的数据、控制用户对类的修改和控制数据访问权限
3 面向对象技术给软件发展带来的益处
● 可重用性:一个设计好的类可以在今后的程序开发中被部分或全部地重复利用。
● 可靠性:每一个类作为一个独立单元可以单独进行测试、维护,大量代码来源于可靠的类库,因而开发新程序时新增代码明显减少,这是程序可靠性提高的一个重要原因。
4 面向对象语言的基本特征
● 抽象和封装:抽象的结果形成类,类中的数据和方法是受保护的,可以根据需要设置不同的访问控制属性。这便保证了数据的安全性,隐藏了方法的实现细节,也方便了使用。
● 继承性:可以对已有类增加属性和功能,或进行部分修改来建立新的类,实现代码的重用。
● 多态性:在面向对象的程序中,同一个消息被不同对象接收后可以导致不同的行为。
5 Java语言的特点
● 面向对象:Java是完全面向对象的语言。Java语言提供类的机制,在对象中封装了成员变量和方法,实现了数据的封装和信息隐藏;类提供了一类对象的模型,通过继承和多态,实现了代码的复用。
● 安全性:Java不支持指针,一切对内存的访问都必须经过对象的实例变量实现,防止了以不法手段访问对象的私有成员,同时避免了指针操作中容易产生的错误。Java的内部安全措施保证了Java程序在Java虚拟机规则下操作,防止未授权的程序访问含有专用信息的系统资源或危及客户机的完整性。
● 操作平台无关性:Java编译器生成与平台无关的字节码指令,只要安装了Java运行系统,其程序就可以在任意的处理器上运行。这些字节码对应于Java虚拟机中的表示,Java解释器得到字节码后,对其进行解释,使之能够在不同的平台下运行。不同的操作系统有不同的虚拟机,与平台无关的特性使得Java程序可以方便地移植到不同的机器上。
● 多线程:Java是第一个在语言级提供内置多线程支持的高级语言,这大大简化了多线程程序的编写。而一些其他语言要通过调用操作系统的原语来支持多线程。
● 动态内存分配:Java中所有的对象都是通过动态内存分配建立的,Java对内存自动进行管理并进行垃圾回收,防止了因程序员失误而导致的内存分配错误,进而更好地利用了系统资源。
1.2 Java程序概述
6 Java程序编译执行的过程
首先把源文件(.java文件)编译成字节码文件,即类文件(.class);然后由解释器负责解释执行类文件。
7 Java平台
Java平台包括Java应用程序接口(API)和Java虚拟机(Java virtual machine,JVM)。Java应用程序接口是指经过编译的,可在程序中直接使用的Java代码标准库。Java虚拟机负责解释和执行Java程序。
Java程序运行于Java平台之上,Java虚拟机将程序和硬件隔离开来。
1.3 基本数据类型与表达式
8 变量的作用域
变量的作用域指可以访问该变量的程序代码范围。按照作用域的不同,变量可以分为类成员变量和局部变量。类成员变量在类的声明体中声明,其作用域为整个类;局部变量在方法体或方法的代码块中声明,其作用域为它所在的代码块(即花括号{}的范围)。
9 基本数据类型(基本数据类型有几种?分别是什么?)
基本数据类型是指Java固有的数据类型,可以分为数字型、布尔型和字符型
数字型基本数据类型有六种,其说明如下表:
类型 | 说明 | 长度 | 最小值 | 最大值 |
byte | 带符号位整数 | 8位 | -128 | 127 |
short | 带符号短整数 | 16位 | -215 | 215-1 |
int | 带符号整数 | 32位 | -231 | 231-1 |
long | 带符号长整数 | 64位 | -263 | 263-1 |
float | 单精度浮点数 | 32位 | 2-149 | (2-2-23).2127 |
double | 双精度浮点数 | 64位 | 2-1074 | (2-2-52).21023 |
布尔型数据表示一个逻辑量,只有两个值true和false,它们不对应于任何整数值。
字符类型数据用于存储单个字符,字符以代码形式存储。Java字符数据类型是16位,最小值0,最大值65535,放置Unicode符号。
10 类型转换的分类
类型转换可以分成扩展转换和窄化转换两类。
byte, short, int, long, float, double
char
扩展
double, float, long, int, short, byte,char
窄化
按照扩展方向转化,从一种整型转换到另一种整型,或者从float转换到double时,不损失任何信息。从整型转换到float或者double将损失精度。
窄化转换可能会丢失信息。
11 发生类型转换的四种可能情况
● 赋值转换
● 方法调用转换
● 强制类型转换
● 字符串转换
1.4 数组的概念
12 数组的概念
数组是由同类型的数据元素构成的一种数据结构。数据元素可以是基本数据类型也可以是引用类型。通过数组名和数组元素下标(或称为索引)可以引用数组中的元素。
每个数组都有一个名为length的成员变量,用来表示数组所包含的元素的个数,length只能是正整数或零。数组创建之后length就不能被改变。
1.5 数组的创建和引用
13 图示下面创建并初始化一个三行的二维数组的过程
int[ ][ ] myArray;
myArray = new int[3][ ] ;
myArray[0] = new int[3];
int[ ] x = {0, 2};
int[ ] y = {0, 1, 2, 3, 4} ;
myArray[1] = x ;
myArray[2] = y ;
第2章 类与对象的基本概念
2.1 面向对象的程序设计方法概述
1 抽象
抽象就是忽略问题中与当前目标无关的那些内容,以便更充分地注意与当前目标有关的方面。计算机软件开发中所使用的抽象有过程抽象及数据抽象两类。
过程抽象将整个系统的功能划分为若干部分,强调功能完成的过程和步骤,而隐藏其具体的实现。基于过程抽象的两个标准程序设计技术是过程分解及递归技术。
数据抽象是将系统中需要处理的数据和这些数据上的操作结合在一起,抽象成不同的抽象数据类型,每个抽象数据类型既包含了数据,也包含了针对这些数据的操作。相对于过程抽象,数据抽象是更为合理的抽象方法。
面向对象的软件开发方法的主要特点之一就是采用了数据抽象的方法来构建程序的类及对象。
2 封装
封装是一种信息隐蔽技术,就是利用抽象数据类型将数据和基于数据的操作封装在一起。用户只能看到对象的封装界面信息,对象的内部细节对用户是隐蔽的。封装的目的在于将对象的使用者和设计者分开,使用者不必知道行为实现的细节,只需使用设计者提供的消息来访问对象 。
3 继承
继承是指新的类可以获得已有类(称为超类、基类或父类)的属性和行为,称新类为已有类的派生类(也称为子类)。在继承过程中派生类继承了基类的特性,包括方法和实例变量。派生类也可修改继承的方法或增加新的方法,使之更适合特殊的需要。继承有助于解决软件的可重用性问题,使程序结构清晰,降低了编码和维护的工作量。
4 单继承
任何一个派生类都只有单一的直接父类,类层次结构为树状结构。
5 多继承
一个类可以有一个以上的直接父类,类层次结构为网状结构,设计及实现比较复杂。
6 多态
多态是指一个程序中同名的不同方法共存,主要通过子类对父类方法的覆盖来实现。不同类的对象可以响应同名的消息(方法) ,具体的实现方法却不同。多态性使语言具有灵活、抽象、行为共享、代码共享的优势,很好地解决了应用程序方法同名问题。
2.2 类与对象
7 类和对象
一个类是对一类对象的描述。类是构造对象的模板,对象是类的具体实例
8 类的声明语法格式
[public] [abstract | final] class 类名称
[extends 父类名称]
[implements 接口名称列表]
{
变量成员声明及初始化;
方法声明及方法体;
}
说明:
● class:表明其后声明的是一个类
● extends:如果所声明的类是从某一父类派生而来,那么,父类的名字应写在extends之后
● implements:如果所声明的类要实现某些接口,那么,接口的名字应写在implements之后
● 修饰符可以有多个,用来限定类的使用方式
⏹ public:表明此类为公有类
⏹ abstract:指明此类为抽象类
⏹ final:指明此类为终结类
9 对象的创建
生成实例的格式:new <类名>()
其作用是: 在内存中为此对象分配内存空间,返回对象的引用(reference ,相当于对象的存储地址)。
10 数据成员
数据成员表示Java类的状态。声明数据成员必须给出变量名及其所属的类型,同时还可以指定其他特性。在一个类中成员变量名是唯一的。数据成员的类型可以是Java中任意的数据类型(简单类型,类,接口,数组) 。分为实例变量和类变量。
数据成员的声明格式:
[public | protected | private][static][ final][transient] [volatile]变量数据类型
变量名1[=变量初值], 变量名2[=变量初值], … ;
格式说明
● public、protected、private 为访问控制符
● static指明这是一个静态成员变量
● final指明变量的值不能被修改
● transient指明变量是临时状态
● volatile指明变量是一个共享变量
11 实例变量
没有static修饰的变量称为实例变量(Instance Variables)。用来存储所有实例都需要的属性信息,不同实例的属性值可能会不同。
可通过下面的表达式访问实例属性的值
<实例名>.<实例变量名>
12 类变量
类变量也称为静态变量,声明时需加static修饰符。不管类的对象有多少,类变量只存在一份,在整个类中只有一个值。类初始化的同时就被赋值。
适用情况:
● 类中所有对象都相同的属性
● 经常需要共享的数据
● 系统中用到的一些常量值
引用格式:
<类名 | 实例名>.<类变量名>
13 方法成员
方法成员定义类的行为:
● 一个对象能够做的事情
● 我们能够从一个对象取得的信息
可以没有,也可以有多个;一旦在类中声明了方法,它就成为了类声明的一部分。分为实例方法和类方法
声明格式:
[public | protected | private]
[static][ final][abstract] [native] [synchronized]
返回类型 方法名([参数列表]) [throws exceptionList]
{
方法体
}
格式说明:
⏹ 方法修饰
◆ public、protected、private 为存取控制符
◆ static指明方法是一个类方法
◆ final指明方法是一个终结方法
◆ abstract指明方法是一个抽象方法
◆ native用来集成java代码和其它语言的代码
◆ synchronized用来控制多个并发线程对共享数据的访问
⏹ 返回类型
◆ 方法返回值的类型,可以是任意的Java数据类型
◆ 当不需要返回值时,返回类型为void
⏹ 参数类型
◆ 简单数据类型,
◆ 引用类型(数组、类或接口)
◆ 可以有多个参数,也可以没有参数,方法声明时的参数称为形式参数
⏹ throws exceptionList
◆ 用来处理异常
14 包的作用
● 将相关的源代码文件组织在一起;
● 不同包中的类名可以相同,从而可以避免名字冲突;
● 提供包一级的封装及存取权限。
15 编译单元的组成
● 所属包的声明(省略,则属于默认包);
● Import (引入)包的声明,用于导入外部的类;
● 类和接口的声明。
16 类的访问控制
类的访问控制只有public(公共类)及无修饰符(缺省类)两种
访问权限符与访问能力之间的关系如表
类型 | 无修饰 | public |
同一包中的类 | yes | yes |
不同包中的类 | no | yes |
17 类成员的访问控制
类型 | private | 无修饰 | protected | public |
同一类 | yes | yes | yes | yes |
同一包中的子类 | no | yes | yes | yes |
同一包中的非子类 | no | yes | yes | yes |
不同包中的子类 | no | no | yes | yes |
不同包中的非子类 | no | no | no | yes |
18 关键字this的使用
如果形式参数名与实例变量名相同,则需要在实例变量名之前加this关键字,否则系统会将实例变量当成形式参数。
2.3 对象的初始化和回收
19 构造方法
构造方法(Constructor)是一种特殊的方法。Java中的每个类都有构造方法,用来初始化该类的一个新的对象。构造方法具有和类名相同的名称,而且不返回任何数据类型。系统在产生对象时会自动执行。
20 构造方法的特点(简述构造方法的特点)
1) 构造方法的方法名与类名相同;
2) 构造方法没有返回类型(修饰符void也不能有);
3) 构造方法通常被声明为公有的(public);
4) 构造方法可以有任意多个参数;
5) 构造方法的主要作用是完成对象的初始化工作;
6) 构造方法不能在程序中显式的调用;
7) 在生成一个对象时,系统会自动调用该类的构造方法为新生成的对象初始化。
2.4 应用举例
2.5 UML简介
第3章 类的方法
1
2
3
3.1 方法的控制流程
1 Java中的流程控制
Java中的流程控制主要有顺序结构、选择结构和循环结构三种。
顺序结构即是按照从上到下的顺序执行语句,没有转移和重复。
选择结构是根据给定的条件成立与否,执行不同的语句或语句组。Java的选择结构主要有二路选择结构(if选择结构)及多路选择结构(switch选择结构)两种。
循环控制结构是在一定的条件下,反复执行某段程序的流程结构,被反复执行的程序称为循环体。
2 循环包括几个部分
1) 初始化部分:用来设置循环的一些初始条件,如计数器清零等。
2) 循环体部分:这是反复执行的一段代码,可以是单一的一条语句,也可以是复合语句。
3) 迭代部分:这是当前循环结束,下次循环开始执行的语句,常常用来使计数器进行增减操作。
4) 终止部分:通常是布尔表达式,每一次循环要对该表达式求值,以验证是否满足循环终止条件。
3 break语句的用法
break语句可用于三种情况:
1) 在switch结构中,break语句用来终止switch语句的执行。
2) 在for循环及while循环结构中,用于终止break语句所在的最内层循环。
3) break语句也可用在代码块中,用于跳出它所指定的块。
4 break与label一同使用举例
public class Examp3_7{
public static void main(String[] args){
outer:
for (int i=1; i<=9;i++) {
for (int j=1; j<=9;j++){
if (j > i) break;
if (i==6) break outer;
System.out.print(" "+i+"*"+j+"="+i*j);
}
System.out.println();
}
}
}
5 continue语句的用法
continue语句必须用于循环结构中,continue语句有两种使用格式:
1) 不带标号的continue语句。它的作用是终止当前这一轮循环,跳出本轮循环剩余的语句,直接进入当前循环的下一轮。在while或do-while循环中,不带标号的continue语句会使流程直接跳转至条件表达式;在for循环中,不带标号的continue语句会跳转至表达式update-expression,计算并修改循环变量后再判断循环条件。
2) 带标号的continue语句。带标号的continue语句使程序的流程直接转入标号标明的循环层次。
6 带标号的continue语句举例
public class Examp3_10{
public static void main (String args[]) {
outer:
for (int i=1; i<10; i++){
inner:
for (int j=1; j<10; j++){
if (i<j){
System.out.println();
continue outer;
}
System.out.print(" "+i+"*"+j+"="+i*j);
}
}
}
}
7 求出100以内的素数,并将这些数在屏幕上5个一行地显示出来。
public class primeIn100{
public void prime(){
int count = 0;
Outer:
for (int i = 2; i < 100; i++){
for (int j = 2; j <= Math.floor(Math.sqrt(i)); j++){
if (i % j == 0) continue Outer;
}
System.out.print(i + " ");
count++;
if (count % 5 == 0) System.out.println();
}
}
public static void main(String args[]){
primeIn100 p = new primeIn100();
p.prime();
}
}
3.2 异常处理简介
8 异常的基本概念
又称为例外,是特殊的运行错误对象,是面向对象规范的一部分,是异常类的对象。Java中声明了很多异常类,每个异常类都代表了一种运行错误,类中包含了该运行错误的信息和处理错误的方法。每当Java程序运行过程中发生一个可识别的运行错误时,即该错误有一个异常类与之相对应时,系统都会产生一个相应的该异常类的对象,即产生一个异常。
9 错误的分类
通常程序中的错误可以分成三类,即编译错误、运行错误和逻辑错误。
Java系统中根据错误的严重程度不同,将错误分为两类:
1) 错误:是致命性的,即程序遇到了非常严重的不正常状态,不能简单地恢复执行。
2) 异常:是非致命的,通过某种修正后程序还能继续执行。
10 异常的抛出和捕获
当程序中发生异常时,称程序产生了一个异常事件,相应地生成异常对象。生成的异常对象传递给Java运行时系统。异常的产生和提交的这一过程称为抛出。
异常发生时,Java运行时系统从生成对象的代码开始,沿方法的调用栈逐层回溯,寻找相应的处理代码,并把异常对象交给该方法处理,这一过程称为捕获。
11 异常的处理
对于检查型异常,Java强迫程序必须进行处理,处理方法有以下两种:
1) 声明抛出异常:不在当前方法内处理异常,而是把异常抛出到调用方法中。
2) 捕获异常:使用try{}catch(){}块,捕获到所发生的异常,并进行相应的处理。
12 用户自定义异常
用户自定义的异常类都必须是Exception的子类。一般的声明方法如下:
public class MyExceptionName extends SuperclassOfMyException {
public MyExceptionName() {
super("Some string explaining the exception");
}
}
3.3 方法的重载
13 方法的重载
方法的重载(overloading)是指在一个类中可以有名字相同的多个方法,但这些方法的参数必须不同,或者是参数个数不同,或者是参数类型不同。返回值可以相同,也可以不同。
第4章 类的重用
4.1 类的继承
1 继承
继承是一种由已有的类创建新类的机制,是面向对象程序设计的基石之一。一个新类可以从现有的类中派生,这个过程称为类继承。派生出的新类称为已有类的子类,已有类称为超类(父类)。
2 属性的隐藏
子类对从父类继承来的属性变量重新加以定义,则从父类继承的属性将被隐藏。
访问被隐藏的父类域有两种方法:
1) 调用从父类继承的方法,则操作的是从父类继承的域;
2) 使用super.域名
3 属性隐藏的例子
class A1
{
int x = 2;
public void setx(int i){
x = i;
}
void printa()
{
System.out.println(x);
}
}
class B1 extends A1
{
int x=100;
void printb()
{
super.x = super.x +10 ;
System.out.println
("super.x= " + super.x +
" x= " + x);
}
}
public class Exam4_4Test
{
public static void main(String[] args)
{
A1 a1 = new A1();
a1.setx(4);
a1.printa();
B1 b1 = new B1();
b1.printb();
b1.printa();
b1.setx(6); // 将继承来的x值设置为6
b1.printb();
b1.printa();
a1.printa();
}
}
运行结果:
4
super.x= 12 x= 100
12
super.x= 16 x= 100
16
4
4 方法的覆盖
如果子类不需要使用从父类继承来的方法的功能,则可以声明自己的方法。在声明的时候,使用相同的方法名及参数表,但执行不同的功能。这种情况称为方法覆盖。
5 需要使用方法覆盖的情况
1) 子类中实现与父类相同的功能,但采用不同的算法或公式。
2) 在名字相同的方法中,要做比父类更多的事情。
6 有继承时的构造方法应遵循的原则
● 子类不能从父类继承构造方法。
● 好的程序设计方法是在子类的构造方法中调用某一个父类构造方法。
● super关键字也可以用于构造方法中,其功能为调用父类的构造方法。
● 如果在子类的构造方法的声明中没有明确调用父类的构造方法,则系统在执行子类的构造方法时会自动调用父类的默认构造方法。
● 如果在子类的构造方法的声明中调用父类的构造方法,则调用语句必须是子类的构造方法的第一条语句。
7 子类将继承父类所有的属性和方法吗?为什么?
8 方法的覆盖与方法的重载有何不同?
9 完成下面父类及子类的声明:
1) 声明Student类
属性包括学号、姓名、英语成绩、数学成绩、计算机成绩和总成绩。
方法包括构造方法、get方法、set方法、toString方法、equals方法、compare方法(比较两个学生的总成绩,结果分为大于、小于、等于)、sum方法(计算总成绩)和testScore方法(计算评测成绩)。
注:评测成绩可以取三门课成绩的平均分,另外任何一门课的成绩的改变都需要对总成绩进行重新计算,因此,在每一个set方法中应调用sum方法计算总成绩。
2) 声明StudentXW(学习委员)类为Student类的子类。
在StudentXW类中增加责任属性,并重写testScore方法(计算评测成绩,评测成绩=三门课的平均分+3)。
3) 声明StudentBZ(班长)类为Student类的子类。
在StudentBZ类中增加责任属性,并重写testScore方法(计算评测成绩,评测成绩=三门课的平均分+5)。
4) 声明测试类,生成若干个Student类、StudentXW类及StudentBZ类对象,并分别计算它们的评测成绩。
4.2 Object类
10 Object类
Object类是Java程序中所有类的直接或间接父类,也是类库中所有类的父类,处在类层次的最高点。
11 相等和同一
如果两个对象具有相同的类型及相同的属性值,则称两个对象相等(equal);如果两个引用变量指向的是同一个对象,则称这两个变量(对象)同一(identical)。
12 Object类中的equals()方法的功能
Object类中的equals()方法的功能是比较接收者对象与参数对象是否是同一个对象,而不是判断两个对象各个属性域的值是否相同。
4.3 终结类与终结方法
13 终结类
如果一个类被final修饰符所修饰和限定,说明这个类不可能有子类,即final类不能有派生类。
被声明为final的类通常是一些有固定作用、用来完成某种标准功能的类,不能被继承以达到修改的目的。在Java程序设计中,当引用一个类或其对象时实际真正引用的既可能确是这个类或其对象本身,也可能是这个类的某个子类及子类对象,即具有一定的不确定性。将一个类声明为final,则可以将它的内容、属性和功能固定下来,与它的类名形成稳定的映射关系,从而保证引用这个类时所实现功能的正确无误。
14 final类存在的两个理由
1) 安全方面:黑客用来搅乱系统的一个手法是建立一个类的派生类,然后用他们的类代替原来的类。为了阻止各种颠覆活动,可以声明此类为final类,从而不能派生任何子类。
2) 设计方面:从面向对象设计方面,如希望声明的类为最终类,则最好或从概念上讲该类不应该有任何派生类。
15 终结方法
final修饰符所修饰的方法是功能和内部语句不能被更改的最终方法,即是不能被当前类的子类重载的方法。这样,就固定了这个方法所对应的具体操作,可以防止子类对父类关键方法的错误重写,增加了代码的安全性和正确性。
将方法声明为final的另一个原因是提高类的运行效率。通常,当Java运行环境运行方法时,它将首先在当前类中查找该方法,接下来在其超类中查找,并一直沿类层次向上查找,直到找到该方法为止。这提供了灵活性和开发工作的容易程度,但代价是速度更低。如果方法是final的,Java编译器可以将该方法可执行的字节码直接放到调用它的程序中。这样当程序调用它们时,执行速度将更快。
4.4 抽象类
16 抽象类
抽象类不能用new方法进行实例化。当一个类被声明抽象类时,要在这个类前加修饰符abstract。
抽象类可以包含常规类能够包含的任何东西,这包括构造方法。抽象类也可以包括抽象方法,这种方法只有方法的声明,而没有方法的实现。抽象类也可以包含非抽象方法,但不能在非抽象类中声明抽象方法。
17 为什么要声明抽象类?
1) 抽象类是类层次中较高层次的概括,抽象类的作用是让其他类来继承它的抽象化特征;
2) 在抽象类中可以包括被它的所有子类共享的公共行为;
3) 抽象类可以包括被它的所有子类共享的公共属性;
4) 在程序中不能用抽象类作为模板来创建对象;
5) 在用户生成实例时强迫用户生成更具体的实例,保证代码的安全性。
18 抽象方法
作为类方法修饰符,abstract声明了一种仅有方法头,而没有具体的方法体和操作实现的抽象方法,为该类的子类声明一个方法的接口标准。
抽象方法体的具体实现是由当前类的不同子类在它们各自的类声明中完成的。
19 抽象方法的优点
1) 抽象方法可以隐藏具体的细节信息,使调用该方法的程序不必过分关注该类和它的子类的内部状况。方法头里实际包含了调用该方法的程序语句所需要了解的全部信息。
2) 抽象方法强迫子类完成指定的行为,抽象类的所有非抽象子类都必须完成其父类中声明的抽象方法,抽象类通常声明抽象方法规定其子类需要用到的“标准”行为。
20 编写一程序计算二维几何形体的面积,所涉及的几何形体至少应包括圆、三角形、矩形。(类层次结构图参见图4-11)
4.5 类的组合
21 组合
Java的类中可以有其它类的对象作为成员,这便是类的组合。可以使用“has a”语句来描述这种关系。
22 组合与继承的比较
“包含”关系用组合来表达
如果想利用新类内部一个现有类的特性,而不想使用它的接口,通常应选择组合,我们需在新类里嵌入现有类的private对象。如果想让类用户直接访问新类的组合成分,需要将成员对象的属性变为public。
“属于”关系用继承来表达
取得一个现成的类,并制作它的一个特殊版本。通常,这意味着我们准备使用一个常规用途的类,并根据特定需求对其进行定制。
23 组合与继承举例
class Plate { //声明盘子
public Plate(int i) {
System.out.println("Plate constructor");
}
}
class DinnerPlate extends Plate { //声明餐盘为盘子的子类
public DinnerPlate(int i) {
super(i);
System.out.println("DinnerPlate constructor");
}
}
class Utensil { //声明器具
Utensil(int i) {
System.out.println("Utensil constructor");
}
}
class Spoon extends Utensil { //声明勺子为器具的子类
public Spoon(int i) {
super(i);
System.out.println("Spoon constructor");
}
}
class Fork extends Utensil { //声明餐叉为器具的子类
public Fork(int i) {
super(i);
System.out.println("Fork constructor");
}
}
class Knife extends Utensil { //声明餐刀为器具的子类
public Knife(int i) {
super(i);
System.out.println("Knife constructor");
}
}
class Custom { // 声明做某事的习惯
public Custom(int i) { System.out.println("Custom constructor");}
}
public class PlaceSetting extends Custom {//声明餐桌的布置
Spoon sp; Fork frk; Knife kn;
DinnerPlate pl;
public PlaceSetting(int i) {
super(i + 1);
sp = new Spoon(i + 2);
frk = new Fork(i + 3);
kn = new Knife(i + 4);
pl = new DinnerPlate(i + 5);
System.out.println("PlaceSetting constructor");
}
public static void main(String[] args) {
PlaceSetting x = new PlaceSetting(9);
}
}
4.6 包的应用
24 包
为了解决类名冲突,Java利用包来组织相关的类,并控制访问权限。
包是一种松散的类的集合。但是由于同一包中的类在默认情况下可以互相访问,所以为了方便编程和管理,通常把需要在一起工作的类放在一个包里。利用包来管理类,可实现类的共享与复用。
25 语言包
语言包java.lang提供了Java语言最基础的类,包括Object类、数据类型包裹类、字符串类(String、StringBuffer)、数学类(Math)、系统和运行时类(System、Runtime)、类操作类(Class、ClassLoader)等
26 实用包
实用包java.util提供了实现各种不同实用功能的类,包括日期类、集合类等。
27 文本包
文本包java.text中的Format、DateFormat、SimpleDateFormat等类提供各种文本或日期格式。
第5章 接口与多态
5.1 接口
1 接口
接口与抽象类一样都是定义多个类的共同属性。接口使抽象的概念更深入了一层,是一个“纯”抽象类,它只提供一种形式,并不提供实现。允许创建者规定方法的基本形式:方法名、参数列表以及返回类型,但不规定方法主体。接口也可以包含基本数据类型的数据成员,但它们都默认为static和final。
2 接口的作用
接口是面向对象的一个重要机制。它的引进是为了实现多继承,同时免除C++中的多继承那样的复杂性。在使用中,接口类的变量可以用来代表任何实现了该接口的类的对象,这就相当于把类根据其实现的功能来分别代表,而不必顾虑它所在的类继承层次。这样可以最大限度地利用动态绑定,隐藏实现细节。接口还可以实现不同类之间的常量共享。
3 声明接口类型的变量,并用它来访问对象举例
interface Shape2D{
double pi=3.14;
double area();
}
class Circle implements Shape2D
{
double radius;
public Circle(double r)
{
radius=r;
}
public double area()
{
return (pi * radius * radius);
}
}
class Rectangle implements Shape2D
{
int width,height;
public Rectangle(int w,int h)
{
width=w;
height=h;
}
public double area()
{
return (width * height);
}
}
public class VariableTester {
public static void main(String []args)
{
Shape2D var1,var2;
var1=new Rectangle(5,6);
System.out.println("Area of var1 = " + var1.area());
var2=new Circle(2.0);
System.out.println("Area of var2 = " + var2.area());
}
}
输出结果
Area of var1 = 30.0
Area of var2 = 12.56
4 多重继承
Java不允许一个类有多个父类,但允许一个类实现多个接口,通过这种机制可以实现多重继承。
5 多重继承举例
interface Shape2D{ //声明Shape2D接口
final double pi=3.14; //数据成员一定要初始化
public abstract double area(); //抽象方法
}
interface Color{
void setColor(String str); //抽象方法
}
class Circle implements Shape2D,Color // 实现Circle类
{
double radius;
String color;
public Circle(double r) //构造方法
{
radius=r;
}
public double area() //定义area()的处理方式
{
return (pi*radius*radius);
}
public void setColor(String str) //定义setColor()的处理方式
{
color=str;
System.out.println("color="+color);
}
}
public class MultiInterfaceTester{
public static void main(String args[]) {
Circle cir;
cir=new Circle(2.0);
cir.setColor("blue");
System.out.println("Area = " + cir.area());
}
}
输出结果
color=blue
Area = 12.56
6 接口的扩展
接口可以通过扩展(extends)技术派生出新的接口,原来的接口称为基本接口(base interface)或父接口(super interface),派生出的接口称为派生接口(derived interface)或子接口(sub interface)。派生接口不仅可以保有父接口的成员,同时也可以加入新的成员以满足实际问题的需要。
7 接口扩展举例
interface Shape{
double pi=3.14;
void setColor(String str);
}
//声明Shape2D接口扩展了Shape接口
interface Shape2D extends Shape {
double area();
}
class Circle implements Shape2D {
double radius;
String color;
public Circle(double r) { radius=r; }
public double area() {
return (pi*radius*radius);
}
public void setColor(String str){
color=str;
System.out.println("color="+color);
}
}
public class ExtendsInterfaceTester{ //测试类
public static void main(String []args) {
Circle cir;
cir=new Circle(2.0);
cir.setColor("blue");
System.out.println("Area = " + cir.area());
}
}
运行结果
color=blue
Area = 12.56
5.2 塑型
8 对象的塑型
类型转换也称为塑型。对象只能被塑型为:
● 任何一个父类类型。任何一个子类的引用变量(或对象)都可以被当成父类引用变量(或对象)来对待;但反过来却不成立。
● 对象所属的类实现的一个接口。虽然不能用接口生成对象,但可以声明接口的引用变量,接口的引用变量可以指向任何实现了此接口的类对象。
● 或者回到它自己所在的类。一个对象被塑型为父类或接口后,还可以再被塑型回到它自己所在的类。
9 在什么情况下,可以对父类对象的引用进行强制类型转换,使其转化成子类对象的引用?
10 塑型的应用场合
塑型主要应用于:
● 赋值转换 赋值号右边的表达式类型或对象转换为左边的类型
● 方法调用转换 实参的类型转换为形参的类型
● 算术表达式转换 算术混合运算时,不同类型的项转换为相同的类型再进行运算
● 字符串转换 字符串连接运算时,如果一个操作数为字符串,一个操作数为数值型,则会自动将数值型转换为字符串
11 实例方法查找举例
类层次如下图:
Manager man = new Manager();
Employee emp1 = new Employee();
Employee emp2 = (Employee)man;
emp1.computePay(); // 调用Employee类中的computePay()方法
man.computePay(); // 调用Manager类中的computePay()方法
emp2.computePay(); // 调用Manager类中的computePay()方法
5.3 多态的概念
12 多态
多态性是指不同的类型的对象可以响应相同的消息。
13 动态绑定举例
class Shape {
void draw() {}
void erase() {}
}
class Circle extends Shape {
void draw()
{ System.out.println("Circle.draw()"); }
void erase()
{ System.out.println("Circle.erase()"); }
}
class Square extends Shape {
void draw()
{ System.out.println("Square.draw()"); }
void erase()
{ System.out.println("Square.erase()"); }
}
class Triangle extends Shape {
void draw()
{ System.out.println("Triangle.draw()"); }
void erase()
{ System.out.println("Triangle.erase()"); }
}
public class BindingTester{
public static void main(String[] args) {
Shape[] s = new Shape[9];
int n;
for(int i = 0; i < s.length; i++) {
n = (int)(Math.random() * 3);
switch(n) {
case 0: s[i] = new Circle(); break;
case 1: s[i] = new Square(); break;
case 2: s[i] = new Triangle();
}
}
for(int i = 0; i < s.length; i++) s[i].draw();
}
}
14 将上例中,如果将Shape改为如下的抽象类,能否得到正确的结果?
abstract class Shape{
abstract void draw();
abstract void erase();
}
5.4 多态的应用
5.5 构造方法与多态
15 构造方法的调用顺序举例
class Meal { //饭类
Meal() { System.out.println("Meal()"); }
}
class Bread { //面包类
Bread() { System.out.println("Bread()"); }
}
class Cheese { //奶酪类
Cheese() { System.out.println("Cheese()"); }
}
class Lettuce { //莴苣类
Lettuce() { System.out.println("Lettuce()"); }
}
class Lunch extends Meal { //午餐类继承自饭类
Lunch() {System.out.println("Lunch()");}
}
class PortableLunch extends Lunch {
PortableLunch() { System.out.println("PortableLunch()"); }
}
public class Sandwich extends PortableLunch {
Bread b = new Bread();
Cheese c = new Cheese();
Lettuce l = new Lettuce();
Sandwich(){System.out.println("Sandwich()");}
public static void main(String[] args) { new Sandwich(); }
}
输出结果:
Meal()
Lunch()
PortableLunch()
Bread()
Cheese()
Lettuce()
Sandwich()
16 构造方法中的多态方法举例
abstract class Glyph {
abstract void draw();
Glyph() {
System.out.println("Glyph() before draw()");
draw();
System.out.println("Glyph() after draw()");
}
}
class RoundGlyph extends Glyph {
int radius = 1;
RoundGlyph(int r) {
radius = r;
System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius);
}
void draw() {
System.out.println("RoundGlyph.draw(), radius = " + radius);
}
}
public class PolyConstructors {
public static void main(String[] args) {
new RoundGlyph(5);
}
}
运行结果
Glyph() before draw()
RoundGlyph.draw(), radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph(), radius = 5
17 定义构造方法时应遵循的原则
● 用尽可能少的动作把对象的状态设置好。
● 如果可以避免,不要调用任何方法。
● 在构造方法内唯一能够安全调用的是在基类中具有final属性的那些方法(也适用于private方法,它们自动具有final属性)。这些方法不能被覆盖,所以不会出现上述潜在的问题。
5.6 内部类
18 内部类
将一个类声明置入另一个类声明中,称这种类为“内部类”。在内部类中可以访问它的外部类的所有数据成员和方法。
19 声明内部类的两种方法
1) 命名的内部类:可在类的内部多次使用
2) 匿名内部类:可在new关键字后声明内部类,并立即创建一个对象
第6章 输入输出流
6.1 概论
1 I/O流的概念
Java将信息的输入与输出过程抽象为输入输出流。输入是指数据流入程序,输出是指数据从程序流出。Java中的输入输出操作通常都是通过输入输出流实现的。一个流就是一个从源流向目的地的数据序列。输入输出流可以与各种数据源和目标相连。
2 输入输出流的分类
从流的方向划分:输入流或输出流
从流的分工划分:节点流或处理流
从流的内容划分:面向字符的流或面向字节的流
3 面向字符的流——Reader和Writer类
针对字符数据的特点进行过优化,提供一些面向字符的有用特性,源或目标通常是文本文件。
4 字节流——InputStream和OutputStream类
是用来处理8位字节流的抽象基类,程序使用这两个类的子类来读写8位的字节信息。
6.2 文件读写
5 写文本文件的例子
在C盘根目录下创建文本文件Hello.txt,并写入若干行文本。
import java.io.*;
class Ex6_2 {
public static void main ( String[] args ) throws IOException {
//main方法中声明抛出IO异常
String fileName = "C:\\Hello.txt";
FileWriter writer = new FileWriter( fileName );
writer.write( "Hello!\n");
writer.write( "This is my first text file,\n" );
writer.write( "You can see how this is done.\n" );
writer.write("输入一行中文也可以\n");
writer.close();
}
}
6 读文本文件的例子
从C盘根目录的文本文件Hello.txt中读取文本并显示在屏幕上。
import java.io.*;
class Ex6_5 {
public static void main ( String[] args ) {
String fileName = "C:/Hello.txt" , line;
try {
BufferedReader in = new BufferedReader(
new FileReader( fileName ) );
line = in.readLine(); //读取一行内容
while ( line != null ) {
System.out.println( line );
line = in.readLine();
}
in.close();
}
catch ( IOException iox ) {
System.out.println("Problem reading " + fileName );
}
}
}
7 写二进制文件的例子
将三个int型整数255,0,-1写入数据文件data1.dat。
import java.io.*;
class Ex6_7{
public static void main ( String[] args ) {
String fileName = "c:/data1.dat" ;
int value0 = 255, value1 = 0, value2 = -1;
try {
DataOutputStream out = new DataOutputStream(
new FileOutputStream( fileName ) );
out.writeInt( value0 );
out.writeInt( value1 );
out.writeInt( value2 );
out.close();
}
catch ( IOException iox ){
System.out.println("Problem writing " + fileName ); }
}
}
8 读二进制文件的例子
读取C盘根目录data1.dat中的三个int型数字,显示相加结果。
import java.io.*;
class Ex6_10 {
public static void main ( String[] args ) {
String fileName = "c:\\data1.dat";
int sum = 0;
try {
DataInputStream instr = new DataInputStream(
new BufferedInputStream(new FileInputStream( fileName )));
sum += instr.readInt();
sum += instr.readInt();
sum += instr.readInt();
System.out.println( "The sum is: " + sum );
instr.close();
}
catch ( IOException iox ) {
System.out.println("Problem reading " + fileName ); }
}
}
}
9 File类使用举例
在C盘创建文件Hello.txt,如果存在则删除旧文件,不存在则直接创建新文件。
import java.io.*;
public class Ex6_13 {
public static void main(String[] args) {
File f=new File("c:"+File.separator+"Hello.txt");
if (f.exists()) f.delete();
else
try{
f.createNewFile();
}
catch(Exception e){
System.out.println(e.getMessage());
}
}
}
10 对象序列化概念
11 对象序列化举例
创建一个书籍对象,并把它输出到一个文件book.dat中,然后再把该对象读出,在屏幕上显示对象信息。
import java.io.*;
public class Ex6_17 {
public static void main(String args[]) throws
IOException,ClassNotFoundException {
Book book=new Book(100032,"Java Programming
Skills","Wang Sir",30);
ObjectOutputStream oos=new ObjectOutputStream(
new FileOutputStream("c:/book.dat"));
oos.writeObject(book);
oos.close();
book=null;
ObjectInputStream ois=new ObjectInputStream(
new FileInputStream("c:/book.dat"));
book=(Book)ois.readObject();
ois.close();
System.out.println("ID is:"+book.id);
System.out.println("name is:"+book.name);
System.out.println("author is:"+book.author);
System.out.println("price is:"+book.price);
}
}
class Book implements Serializable {
int id;
String name;
String author;
float price;
public Book(int id,String name,String author,float price) {
this.id=id;
this.name=name;
this.author=author;
this.price=price;
}
}
运行结果
将生成book.dat文件,并在屏幕显示:
ID is:100032
name is:Java Programming Skills
author is:Wang Sir
price is:30.0
12 FillWriter类和BufferWriter类比较
13 File类
14 DataOutputStream类的方法