第一章 Java与[1]JBuilder概述
Java 历史与概述
l Java概述
– 美国Sun公司开发的一种编程语言与平台
– 世界上第一种具有硬件、操作系统无关性的程序语言
– 在不同硬件、不同操作系统下运行时,不需要重新编译
– 一种“一次编译,到处使用”的语言
– 起源于1991年的绿色计划,原计划用于消费类电子产品
l Java语言的特点
– 简单(Simple)
l 容易编写程序,程序小巧,能够在小型机器,甚至家电、机顶盒、手机上执行
– 面向对象(Object-Oriented)
l 是一种纯粹的面向对象语言,没有全局变量和全局函数,只有类和对象
– 分布式的(Distributed)
l 可以很容易地与TCP/IP通讯协议相配合,方便地实现B/S和C/S以及点对点网络程序结构
l Java语言的特点
– 鲁棒的(Robust)
l Java程序具有相当高的稳定性。Java具有完善的变量类型检查、变量初始化检查、数组下标越界检查、无用变量回收机制,因此能够最大限度地提高程序的鲁棒性
– 安全的(Secure)
l Java拥有多层的互锁(Interlocking)保护措施,能有效地防止病毒的侵入和破坏行为的发生
l Java语言的特点
– 结构中立的(Architecture Neutral)
l Java编译器产生一种结构中立的目标文件格式,可以在多种处理器和操作系统中执行,而不用考虑不同机器的差异
– 可移植的(Portable)
l Java的简单数据类型是不随操作系统变化的。Java程序库所定义的接口也是对所有的操作系统都适用的。这些都使Java具备良好的可移植性
l Java语言的特点
– 解释的(Interpreted)
l Java解释器能直接在任何机器上执行Java二进制码(Bytecodes),这样就省去了在不同机器上编译、连接的时间。这对于缩短程序的开发过程,有极大的帮助
– 高效能的(High Performance)
l Java二进制码能被迅速转换成机器码,Java二进制码的执行效率正在逐渐逼近其它编译语言的执行效率
l Java语言的特点
– 多线程(Multi Threaded)
l Java语言具有多线程的功能,这对于交互式程序以及实时响应程序是很有帮助的
– 动态的(Dynamic)
l Java比C或C++语言更具有动态性,更能适应时刻在变的环境,Java不会因程序库的更新,而必须重新编译程序
l Java编译与运行的特点
– 既是编译语言又是解释语言
– 编译性:将源程序编译成与平台无关的一种中间语言,称为Java二进制码
– 解释性:运行时,Java平台逐句分析解释并运行Java二进制码
l Java平台的概念
– 平台:为程序提供运行环境的硬件和操作系统的总称
– Java平台:纯软件的,为Java提供统一的运行环境
l Java平台的组成结构
– Java虚拟机(JVM)
– Java应用程序界面(Java API)
l Java平台的组成结构
– Java虚拟机:解释并运行Java二进制码
– Java API:由许多软件包组成,这些软件包可以实现很多功能,包括图形界面功能
Java 历史与概述
l Java平台的组成结构
– 最底层是硬件层,表示Java系统运行的硬件和操作系统;
– 第二层是Java虚拟机层,这层是Java平台的核心;
– 第三层是Java程序界面层,这层提供了许多功能软件包;
– 最顶层是应用程序层,也就是在Java平台上运行的应用程序。
l Java应用程序界面的主要内容
– 底层:Java平台目前可以运行的操作系统,如Solaris, Windows, Linux, Mac OS等;
– 中间层:Java API的内容:applet(小程序), math(数学工具),text(文本处理),awt(图形界面),net(网络),util(常用功能),io(输入/输出),swing(Swing图形界面),lang(基本Java语言)等。
– 上层:Java的编译器javac,开发与调试工具
创建第一个Java程序
l 1. 用记事本编写源程序:
创建第一个Java程序
l 源程序:
/*
* 文件名: FirstApp.java
* 功 能: 显示"天天好心情!"
* 编写: 张三
* 编写时间: 2004.06.03
* 修改: 李四
* 修改时间: 2004.08.15
*/
public class FirstApp {
public static void main(String[] args) {
// 显示"天天好心情!"
System.out.println("天天好心情!");
}
}
第二章 Java基本语法
面向对象基础
l 对象的基本特征
– 状态:对象的状态用一个或多个变量表示,这些变量称为成员变量
– 行为:对象的行为用函数或子程序实现,它们称为成员函数
– 一个对象就是一组变量和函数形成的一个软件包
面向对象基础
l 面向对象程序的特点
– 一切都是对象:
– 程序是对象的组合:
– 对象有自主存储空间:
– 对象属于特定的类:
面向对象基础
l Java程序结构
– 对象是全局性的
– Java中没有全局变量和全局函数
– 所有的函数都属于特定的类
– 除少数几种基本变量以外,Java中的所有变量类型都是类
变 量
l 变量定义与变量类型
– 变量:用于保存数据。变量在使用前需要先进行定义和初始化
double resValue = 12.1 + 25.8;
– 变量的定义:给变量设定名字和类型
type name;
– “type”表示变量类型,“name”表示变量名
l 变量类型
– 简单变量的类型
l 变量类型的跨平台性
– 变量的表示范围不随操作系统变化
l int型:32位,范围为-2147483648~2147483647
l float型,32位,IEEE 754规范
l double型:64位,IEEE 754规范
– 字符型变量是16位Unicode字符类型,可以直接表示包括中文在内的各国文字
l 变量名
– 变量名需要满足的条件:
l 变量名必须以字符开头;
l 必须是一串连续的Unicode字符,不能有空格,也不能有减号(否则会与减法相混淆);
l 变量名不能是Java关键字,逻辑值(true或false),以及保留字null;
l 在同一个有效区域里的变量名必须唯一,不同区域(比如不同子程序里)里的变量名可以重复。
l 变量名
– Java关键字列表
l Java变量名的命名规范
– 变量名以小写字母开头,类名以大写字母开头,常量名全部由大写字母组成
– 如果变量名由多个单词组成,则将单词连在一起写,每个单词的首字母大写
l 例:flagDone, totalNum
– 常量:多个单词间以下划线连接
l 例:MAX_INTEGER, MAX_ARRAY_NUM
l 中文变量名
– 对于16位Unicode字符,汉字与英文字母没有区别
– 可以在变量名中使用汉字,也可以混合使用汉字、英文字母,如:
l int 整数 = 5;
l char 汉字 = '文';
l double 费用_Fee = 3.3;
l 常数的类型
– 默认的常数类型:
l 引用变量
– 简单变量仅能存储简单的数据,对于复杂的数据,必须用引用变量来表示
– 引用变量里存储的仅仅是一个指针,它指向真正的对象所在地。例如下面的例子:
l 变量的初始化
– 所谓初始化,就是给变量赋一个初值
– 任何变量,在访问它的值以前,必须先要给它赋一个值,否则结果是不可预料
– 简单变量的初始化:只需赋一个值即可,还可以在变量定义时即将其初始化
int aNum = 0;
double aValue = 0.0;
char aChar = '';
l 变量的初始化
– Java语言对变量初始化的要求非常严格,如果变量存在未初始化的可能,则提示出错,不能继续编译
– 在Delphi语言中,对于变量可能未初始化的问题只会给出一个警告,还可以继续编译
– C/C++语言根本不提示此类问题,完全由程序员自己把握
l 引用变量的初始化
– 用new语句在内存中创建一个对象,再将引用变量指向这个对象
TheClass aClass;
aClass = new TheClass();
– 程序第一行定义了一个引用变量,aClass,此时它还只是一个空的指针;
– 第二行语句在内存中创建了一个TheClass型的对象,再将变量aClass指向该对象
l 最终变量
– 最终变量的值在初始化之后就不能再变了。最终变量相当于常量
– 最终变量的定义:使用final关键字:
final int aConstInteger = 25;
– 最终变量的定义和初始化也可以分开:
final int aConstInteger;
aConstInteger = 25;
– 最终变量在定义之后应当尽快初始化,以免发生多次赋值而出错
运算符
l 什么是运算符
– 运算符对1个、2个或3个参数完成一项函数功能
– 按参数的数量划分:
l 一元运算符、二元运算符和三元运算符
– 按功能划分:
l 可分为5类:算术运算符、关系与条件运算符、移位与逻辑运算符、赋值运算符、其它运算符
l 运算符的形式
– 一元运算符又可分为前缀符号和后缀符号
– 前缀符号的运算符在运算数之前,如“++a”
– 后缀符号的运算符在运算数之后,如“a++”
– 二元运算符只有一种形式,就是运算符在两个运算数之间,例如:“a + b”
– 三元运算符只有一个:“op1 ? op2 : op<chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="False" sourcevalue="3" unitname="”"><font face="Times New Roman">3</font><span style="font-family: "Arial","sans-serif"; mso-bidi-font-family: 'Times New Roman'; mso-ascii-font-family: 'Times New Roman'">”</span></chmetcnv>,它相当于一个简化的条件选择语句
l 算术运算符
– 包括基本的四则运算:加法“+”,减法“-”,乘法“*”,除法“/”,余数“%”
– 算术运算符都支持浮点数和整数运算
l 算术运算符
– 如果两个运算数是相同类型的,则运算的结果也是同样类型
– 如果两个运算数类型不同,Java会先将数值转换为较精确的类型,再进行计算,结果也是较精确的类型
– 数据类型精度的次序:
l byte<short<int<long<float<double
– 例,整数和浮点数相加,首先将整数转换成浮点数,再相加,结果也是浮点数型
l 其它的算术运算符
– 4个一元运算符,其中“++”和“--”运算符各有前缀和后缀两种形式
l 其它的算术运算符
– 最容易混淆的是“op++”和“++op” ,例如:
int a1 = 10;
int a2 = 10;
int b1, b2;
b1 = a1++;
b2 = ++a2;
l 关系运算符
– 比较两个值是否满足某种关系。如果满足,则返回 “true”(真),否则返回 “false”(假)
– 常用的关系运算符:
l 关系运算符
– 在Java中,“=”代表给变量赋值,而用“= =”代表相等,这与传统的习惯不同
– 初学者往往习惯性地用“=”表示相等,从而出现“if (a = b) {...}”的错误
l 条件运算符
– “&&”和“&”的差别:
– “&&” 只有在需要时才计算右边op2的值,如果通过op1就能知道结果时,op2就不会计算
– “&”运算符会计算出两边op1和op2的值,再得到返回值
– 例:if ( (5>7) && (13>2) ) then {...}
– “5>7” 不成立,那么“(5>7) && (13>2)”显然不会成立,因此不再计算“13>2”
l 负数的二进制存储方法
– 在计算机里,负数一般是以二进制“补码”的形式存储的
– 补码是以最大的二进制数减去负数的绝对值再加1而得的
– 例:整数“-14”,计算机中实际存储的值是
“1 0000 0000 0000 0000 - 0000 0000 0000 1110 = 1111 1111 1111 <chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="False" sourcevalue="10" unitname="”">0010”</chmetcnv>
l 移位与按位运算符
– 移位和按位运算符就是对二进制数值进行操作的运算符
– 移位运算符的作用是将二进制数向左或向右移一位。下表列出全部3种移位运算符:
l 移位与按位运算符
– <<:左移,将二进制数左移一位,右边多余的数位填0,相当于乘以2
– >>:有符号右移,将二进制数右移一位,如果二进制数的最高位为0,则左端补0,如果最高位为1,则左端补1,相当于除以2
– >>>:无符号右移,将二进制数向右移一位,左端补0,常用于直接设置二进制位的操作
– 移位运算符常用于直接设置二进制位,此时每一位都有其具体的含义,并不代表一个整数,因此有符号右移就失去了意义,而应当使用无符号右移符“>>>”
– 按位运算符:对二进制数的每一位分别进行逻辑操作,Java提供了4种按位运算符:
l 移位与按位运算符
– “&”运算符对操作数op1和op2的每一位进行“与”操作
– 例:10 & 13,计算过程如下:
1010
& 1101
-------------
1000
– 和1101按位与的结果是将第2位置0,其它位不变,用于将某位数字置零的操作
l 移位与按位运算符
– “|”运算符进行按位的“或”运算,只要有一个操作数的对应位为1,结果的对应位就是1
– 例:计算1010 | 0001的结果:
1010
| 0001
-------------
1011
– 和0001按位“或”之后,右边第1位被置“1”,其它位不变。常用于将某一位置“1”
l 移位与按位运算符
– “^”运算符实现按位异或运算,只有两个操作数的对应位不同,结果才为1
– 例:1010 ^ 1111的结果:
1010
^ 1111
-------------
0101
– 与1111异或的结果是每一位都相反,常用于将某些位颠倒,其它位不变的操作
l 移位与按位运算符
– “~”是按位运算符中唯一的一元运算符,它的作用是将二进制数的每一位取补,例如:
~ 1010
-------------
0101
– 按位运算符在设置逻辑标志时非常有用,通过按位运算符可以方便地设置、修改、访问每个标志位的状态
l 赋值运算符
– “=”:最基本的赋值运算符,将一个变量或常量的值赋给另一个变量。例如:
int a = 5; // a的值为5
a = 8; // 现在a的值为8
– 快捷赋值运算符,用于同时实现算术、移位或按位操作与赋值操作。例如:
i = i + 2;
– 可以用快捷赋值符号“+=”表示:
i += 2;
l 赋值运算符
– 快捷赋值运算符列表:
l 其它运算符
– “? :”,是唯一的一个三元运算符,形式为:
op1 ? op2 : op3
– 首先判断op1,如果op1为真,则返回op2的值;如果op1为假,则返回op3的值
– “(变量类型)”,将变量转换成指定类型:
float b = 3.6;
int c = (int)b * 2;
– b被强制转换成整数,抛弃小数部分以后的值为3,于是c = 6
l 运算符的优先级列表
分支与循环结构
l 分支控制语句
– if语句:是最基本的分支控制语句,使程序根据条件有选择地执行语句
– if语句的形式如下:
if (关系表达式) {
语句
}
– 它的含义是:如果关系表达式为真,则执行后面花括号里的语句,否则就不执行花括号里的语句
分支与循环结构
l 分支控制语句
– 例:对于前面的例子,如果当a不是正数时也需要在屏幕上显示,语句如下:
if (a > 0) {
System.out.println("变量a是正数。");
}
else {
System.out.println("变量a是负数或零。");
}
– 当a不是正数时,执行else内的代码,显示“变量a是负数或零”
l 分支控制语句
– 组合的if ... else 语句:例,要求当a是正数、a是负数、a是零时分别显示:
if (a > 0) {
System.out.println("变量a是正数。");
}
else if (a < 0) {
System.out.println("变量a是负数。");
}
else {
System.out.println("变量a是零。");
}
l 分支控制语句
– 处理多种选择问题的方法:
l 利用多个if ... else结构
l 利用switch语句处理
– switch语句的语法结构:
l switch语句将IntVar的值与每个case语句的整数值比较
l 如果符合,就执行这个case中的语句
l 如果不与任何一个case符合,就执行default中的语句
l 分支控制语句
– switch分支的特点:
– 每个分支均以一个break语句结尾
– 作用是跳出switch结构
– 如果没有break语句,那么程序在执行完这个case的代码后,会接着执行下面一个case的代码
l 例题:不带break语句的switch结构
switch (n) {
case 1:
System.out.println("n的值是1");
case 2:
System.out.println("n的值是2");
case 3:
System.out.println("n的值是3");
case 4:
System.out.println("n的值是4");
case 5:
System.out.println("n的值是5");
default:
System.out.println("n的值不在预设范围内。");
}
l 循环控制语句
– 循环控制语句的作用是反复执行一段代码
– 常用的循环结构:
l while循环
l do ... while循环
l for循环
– 循环结构的组成部分:
l 循环头(控制语句)
l 循环体(代码)
l 循环控制语句
– while循环
while (条件表达式) {
语句
}
– 当条件表达式为真时,反复执行花括号中的语句,直到条件为假,则退出循环
– 例:计算1+2+3+...,一直到结果大于100,求此时加到的最大的数是多少
l 循环控制语句
– 例题程序如下:
int sumx = 0;
int x = 0;
while (sumx <= 100) {
x ++;
sumx += x; }
System.out.println("最大的加数为:" + x + "。");
– 程序的核心是一个while循环结构,当sumx没有超过100的时候,反复执行累加程序
l 循环控制语句
– do...while结构,形式如下:
do {
语句
} while (条件表达式)
– while结构和do...while结构的差异:
l while循环:先判断,再执行。如果一开始循环条件就不满足,则循环内的语句根本不会执行
l do...while循环:先执行,后判断。不管循环条件满不满足,循环内的语句至少会执行一遍
l 循环控制语句
– while和do ... while结构对比例题
// 例题1
int a = 105;
while (a <= 100) {
a += 20; }
System.out.println("a的值是:" + a);
// 例题2
int a = 105;
do { a += 20;
} while (a <= 100)
System.out.println("a的值是:" + a);
l 循环控制语句
– for循环:
for (初值; 终值; 增量) {
语句
}
– for循环一般用于已知循环次数的程序中
– 初值部分用来初始化循环变量,终值部分设定循环的结束条件,增量部分用于在每次循环中改变循环变量的值
l 循环控制语句
– 例题:计算从1加到100的总和
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i; }
System.out.println("1加到100的总和为:" + sum);
l 在for结构中,int i = 1定义了一个整数变量i,并且设它的初值为1;
l i <= 100给出了循环的结束条件,当i <= 100不成立时,循环就会自动跳出;
l i++设定了每个循环中循环变量i的增量,每次循环时i的值都会增加1。
l 中断控制语句
– Java语言支持3种中断语句:
– break:强行退出循环,不执行循环体中剩余的语句和剩余的循环次数
– continue:停止继续执行循环体中下面的语句,跳回循环起始位置开始下一次循环
– return:退出整个函数
第三章 面向对象语言
类与对象
l Java中的类
– 在Java程序中,类是由定义和主体构成的
l 完整的类定义格式:
public
abstract
final
class 类名
extends 父类名
implements 接口名
{
类的主体
}
– 类定义中,必需的部分是 “class”关键字和类的名字,其它部分都是可选的
成员变量与成员函数
l 成员变量
– 成员变量的完整定义形式如下:
accessLevel
static
final
transient
violatile
type name
– 其中用黑体字标的变量类型和变量名是必需项,其它都是可选项
l 成员变量
– name:成员变量名也是必需项,它的命名要求与普通变量名相同
– 在一个类里,你不能定义两个相同名称的成员变量,但允许成员变量和成员函数起相同的名字,例如:
public class Calculator {
public double a, b;
public double add;
public double add() {
return(a + b); } }
l 成员函数
– 成员函数与类相似,是由函数定义和函数主体构成的,如下图所示:
l 函数定义部分包括函数的访问级、返回值的类型、函数名称和参数列表
l 函数主体是花括号里的部分,它包括实现函数功能所需要的代码
l 成员函数
– 能否在函数中改变参数的值
public class TestSum {
public void Sum(double sumx, double x) {
sumx = sumx + x;
}
public static void main(String[] args) {
TestSum aTest = new TestSum();
double sumx, x;
sumx = 0;
x = 3;
aTest.Sum(sumx, x);
System.out.println("累加结果为:" + sumx);
} }
l 成员函数
public class TestStr {
public void ChangeString(StringBuffer OldStr, StringBuffer NewStr)
{ OldStr.append(NewStr); }
public static void main(String[] args) {
TestStr aStr = new TestStr();
StringBuffer Str1 = new StringBuffer("新年");
StringBuffer Str2 = new StringBuffer("快乐!");
aStr.ChangeString(Str1, Str2);
System.out.println(Str1); } }
l 成员函数
– 这种情况常出现于对象的构造函数中:
public class Circle {
public int x, y, radius;
public Circle(int x, int y, int radius) {
this.x = x;
. . . } }
– 构造函数的作用是为对象设定初值,因此函数的参数难免与成员变量重名,此时通过“this”就可以毫不费力地对二者进行区分
l 成员函数
– 一个函数只能有一个返回值:
public double Sum(double x1, double x2) {
double sumx;
sumx = x1 + x2;
return sumx;
}
函数地返回值可以赋给变量:
double y;
y = Sum(5, 3);
l 成员函数
– 对于带返回值的函数,Java语言要求从程序结构上保证函数一定可以返回一个值
– 为便于理解,考虑下面的例子:
public int Test() {
if (7 > 5) { return 1; }
}
– 很明显,“7 > <chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="False" sourcevalue="5" unitname="”">5”</chmetcnv>永远成立,因此“return 1”语句一定会被执行,函数一定有返回值
– 但这段程序在编译时不能通过
l 成员函数
– 成员函数内可以有“this”,“super”对象,它们分别特指函数所属对象本身和它的父类
l 成员函数
– 函数定义的完整形式,其中黑体字的部分为必需项:
accessLevel
static
abstract
final
native
synchronized
returnType methodName
(paramList)
throws exceptions
l 成员变量的初始化
– 对象的成员变量是自动初始化的
– 当用new关键字来产生一个对象时,对象的所有成员变量都自动初始化:
DemoClass aDemo = new DemoClass();
– 初始化的结果:
l 整型、浮点型变量赋值为0
l 字符型赋值为空
l 逻辑型赋值为false
l 引用变量赋值为空,不指向任何一个对象
l 构造函数
– 构造函数是一类特殊的成员函数,它的函数名与类名相同,没有返回值,也不用将返回值类型设为void
– 如果一个类有构造函数,在构造这个类的时候,将会自动调用构造函数
– 可以在构造函数中对指定的变量赋初值,与直接赋初值相比,构造函数灵活许多,并且不受变量先后次序的影响
l 构造函数
– 一个类可以同时拥有几个构造函数,每个构造函数的自变量不同,初始化对象时根据自变量的不同自动选择合适的构造函数
– 例题:源代码见教材,该例题中有三个构造函数:
public Tree() { . . . }
public Tree(double Height) { . . . }
public Tree(int Age) { . . . }
类的封装性
l 封装性与访问级控制
– 类的一个优势在于类可以保护它的成员变量和成员函数不会被其它对象随意访问到
– 在Java程序里,可以为成员变量和函数设定四级访问级:
l private
l protected
l public
l package
类的继承性
l 类的继承的概念
– 一个类可以从另一个类中继承它的成员变量和函数,前者称为子类,后者称为父类。类的这种特点称为继承性
– 类的继承通过extends关键字来说明,extends关键字跟在类名称后面,形式如下:
– class ClassName extends FatherClassName { ... }
– 其中ClassName是子类名,FatherClassName是父类名
l 类的继承性的特点
– 在Java中,一个类只能有一个父类
– Java只支持单继承,而不支持多重继承
– 如果需要多重继承,Java提供了一种接口技术,可以部分地实现多重继承的功能
l 类的继承性的特点
– 在Java中定义的所有类都直接或间接地是Object类的子类。以Object类为根,所有Java类形成一棵类继承树,如下图所示:
l 类的继承性的特点
– 子类可以继承的部分:
l (1) 父类中公开级的成员;
l (2) 父类中保护级的成员;
l (3) 如果子类和父类在同一个包里,则子类继承父类中缺省的包访问级的成员;
– 子类不能继承的部分:
l (1) 父类中私有级的成员;
l (2) 如果不在同一个包里,则缺省级的成员;
l (3) 同名的成员函数或成员变量;
l 继承中的构造函数
– 构造函数是比较特殊的一类
– 在继承时,构造函数不会被继承,也不会被覆盖
– 父类和子类的构造函数依然是独立存在,并且分别发挥着作用
class Drawing {
Drawing() {
System.out.println("Drawing constructor"); }
}
public class Cartoon extends Drawing {
Cartoon() {
System.out.println("Cartoon constructor"); }
public static void main(String[] args) {
Cartoon x = new Cartoon(); }
}
class BoardGame {
BoardGame(int i) {
System.out.println("BoardGame constructor"); }
}
public class Chess extends BoardGame {
Chess() {
super(11);
System.out.println("Chess constructor"); }
public static void main(String[] args) {
Chess x = new Chess(); }
}
类的多态性
l 多态性的作用
– 数据抽象、继承性和多态性是面向对象编程思想的基本特性
– 多态性将函数的功能与实现分开,也就是说,将“做什么”与“怎样做”分开了
l 成员的覆盖
– 在类的继承中,除了继承来的父类成员外,子类也可以有自己的成员
– 如果子类的某个成员变量或成员函数与父类的同名,子类的成员函数或成员变量将隐藏父类的同名成员,这称为成员的覆盖:
class Super {
int aNumber = 10; }
class Subbie extends Super {
double aNumber = 2.87; }
l 成员的覆盖
– 例:BoardGame及其子类的Play函数:
class BoardGame {
public void Play() {
System.out.println("Play a board game."); }
}
public class Chess extends BoardGame {
public void Play() {
System.out.println("Play a chess."); }
}
l 类的多态性
– 如果用父类的变量指向子类,再调用同名的函数,会出现什么情况呢?
BoardGame aBoard = new Chess();
aBoard.Play();
– 运行程序,显示的结果是:
Play a chess.
– 可见,父类的变量指向子类对象,在调用函数时,实际调用的仍然是子类的函数,这就是类的多态性
l 多态性
– 这种调用过程称为“后期绑定”
– 前面涉及到的函数调用都是“前期绑定”,编译时就根据变量类型定好了所调用的函数
– 后期绑定是在执行的时候,再根据变量实际指向的对象类型(不是变量本身的类型)来决定所调用的函数
– 利用后期绑定,一个函数调用语句可能不同类型的函数,这种现象就是多态性
l 函数的重载
– 重载是指一个类的多个成员函数具有相同的名称,但有不同的参数
public void Add(Complex x1, Complex x2) {
realPart = x1.realPart + x2.realPart;
imagPart = x1.imagPart + x2.imagPart;
}
public void Add(Complex x1, double x2) {
realPart = x1.realPart + x2;
imagPart = x1.imagPart;
}
public void Add(double x1, Complex x2) {
realPart = x1 + x2.realPart;
imagPart = x2.imagPart;
}
public void Add(double x1, double x2) {
realPart = x1 + x2;
imagPart = 0;
}
l 覆盖、多态性与重载的区别
– 重载:一个类中有多个函数有相同的名字,但参数不同(严格地说是参数类型列表不同)
– 在调用这些函数时,Java根据调用时给出的参数自动选择适当的函数
– 可以认为,在Java里,两个函数名相同,不代表两个函数相同,只有函数名和参数都相同的时候才是“真正”相同
l 覆盖、多态性与重载的区别
– 覆盖和多态性就涉及到“真正”相同的函数之间的关系
– 覆盖:如果父类和子类的函数相同,当你通过子类调用函数时,你所调用的就只是子类的函数,父类的函数被覆盖了
– 多态性:反过来,当你通过父类调用函数时,如果变量所指向的是一个子类对象,那么所调的仍然是子类函数,这就是多态性
抽象类与抽象函数
l 抽象函数和抽象类的概念
– 抽象函数:仅有定义,没有具体实现的函数
– 抽象类:含有抽象函数的类
– 定义一个抽象类,需要在类的定义前面加上“abstract”关键字
– 定义一个抽象函数,需要在函数定义的前面加上“abstract”关键字
– 一个类如果被定义为抽象类,它就不能实例化,也就是说,不能有自己的对象
l 抽象类的使用
– 抽象化的Game类:
public abstract class Game {
public abstract void Play();
public abstract String GetRule();
}
– 可以定义抽象类的变量,但不能创建对象:
Game myGame;
– 抽象类变量可以指向Game的子类,再利用多态性来调用子类的Play或GetRule函数
l 抽象类的使用
– 抽象函数的意义是没有具体实现的函数
– 它的作用就是被子类的相同函数覆盖,或通过多态性指向子类的相同函数
– 通过抽象函数,可以定义一整套完整的函数功能,再派生出若干子类来实现
– 不同的子类可以以不同的形式实现这些功能,但函数形式是完全一致的
– 抽象类必须有子类,不然就没有意义
l 抽象类的使用
– 抽象类中不仅仅有抽象函数,也可以有普通的成员函数和成员变量
– 但如果一个类中有抽象函数,那么这个类必须定义为抽象类
– 如果一个类继承了父类的几个抽象函数,但没有全部实现(如BoardGame类),那么这个类也必须定义为抽象类
类的静态变量与静态函数
l 静态变量的概念
– 静态变量,又称为类变量,是与对象的成员变量相对的一种变量
– 静态变量不属于具体的对象,系统只为每个类分配一套类变量,而不管这个类产生了多少实例对象
– 静态变量用“static”关键字定义
– ,所有的对象共享一套静态变量
l 静态变量的使用
– 例题:TestClass类:
class TestClass {
public static int testStatic;
}
– 有两种方法访问静态变量testStatic:通过对象访问或用类直接访问:
TestClass aTest1 = new TestClass();
TestClass aTest2 = new TestClass();
aTest1.testStatic = 12;
aTest2.testStatic += 5;
TestClass.testStatic += 7;
l 静态函数的概念
– 实例函数:只能通过对象调用,每个对象都保存着实例函数的指针
– 实例函数有一个“this”指针,它指向对象本身,通过它可以访问到对象的实例变量
– 静态函数:通过类直接调用的成员函数
– 静态函数没有“this”指针,因此不能访问实例变量,只能访问静态变量
l 静态函数的应用
– 最常见的应用是在程序的入口处
– Java程序从类的main成员函数开始执行
– 但开始执行时,还没有建立任何对象,如何调用main函数呢?
– 办法就是把main函数定义为静态函数,这样无需建立对象就可以调用:
class TestClass {
public static void main(String [] args) {
TestClass aTest = new TestClass(); } }
第四章 接口与包
Java 中的接口
l 接口的概念与特点
– 接口是一组特定的函数定义的集合
– 接口中只有函数定义,而没有具体的实现
– 接口中也可以有常量,但不能有变量
– 接口的作用是定义了一组接口协议
– 接口好像一堵墙,将功能与实现彻底分隔开
l 接口和抽象类的相似之处:
– 形式相似,二者都定义了一组抽象的函数,而没有具体实现
l 接口和抽象类的区别:
– 接口不能有任何函数的实现过程,而抽象类可以有函数的实现过程
– 类可以实现很多接口,但只能有一个父类
– 接口不是类层次关系中的一部分,两个彼此无关的类也可以实现同一个接口
l 接口的完整语法形式
– public
– interface InterfaceName
– Extends SuperInterface1, SuperInterface2, ...
– {
– InterfaceBody
– }
– 接口的组成部分:
l 接口定义部分
l 接口主体部分
Java包
l Java中包的概念
– 尽管类具有强大的功能,但仅仅一个类是无法满足应用程序需要的
。
l Java中包的概念
– “包”是是由一组类和接口所组成的具有一定功能的集合。
– 简单地说,将一组功能相关的类和接口打包起来形成的整体,就是包。
– Java包的作用:
l 使类的组织更加合理,避免类的名称冲突
l “包” 具有一定的访问控制能力,可以从更上层的角度进行访问权限控制
l Java中包的概念
– 类在包中的命名空间:
– 每个Java包为类提供了一个命名空间
– 两个类如果名字相同,只要所属的包不同,Java就会认为它们是不同的类
– 这样,在设计类时,就不需要考虑它会不会与现有的类(包括Java系统类)重复,只需要注意别与同一个包里的类重复就可以了
l Java中包的概念
– 使用包的好处:
l (1) 其他编程人员可以轻易地看出这些类和接口是相关的,提高了程序的可读性
l (2) 你编写的类名不会和其它包中的类名相冲突,因为每个包有自己的命名空间
l (3) 你可以让包中的类相互之间有不受限制的访问权限,与此同时包以外的其它类在访问你的类时仍然受到严格限制
用UML浏览类与包
l UML简介
– UML是“The Unified Modeling Language”(统一建模语言)的缩写
– 是一套面向对象系统建模的标准符号体系
– 用图形化的方法来描述元素及其相互关系
– UML为系统设计提供图形表达,它在开发团队成员之间的交流,以及确保架构的稳固性方面起着重要的作用
Java中的注释语句
l 注释语句的作用
– 文档对于程序来说是非常重要的
– 对于中型、大型程序来说,文档是协作开发以及维护的重要依据
– 传统的手工编写文档是一项极为枯燥、繁琐的工作,效率很低
– 在修改程序时,也必须及时更新文档,这一点往往容易被编程人员忽略
l Java注释语言的格式
– Java里有两种类型的注释
– 第一种:传统的C语言风格的注释
– 注释以“/*”起头,随后是注释内容,注释内容可以跨越多行,最后用“*/”结尾
/* MyClass.java
类名:MyClass
功能:这是一个类定义,用以实现……功能
*/
l Java注释语言的格式
– 为了整齐和美观,可以采用下面的格式:
/*
* 类名:MyClass
* 功能:这是一个类定义
*/
– 由于Java编译器忽略从“/*”到“*/”之间的所有内容,因此这两种格式在Java看来毫无区别
l Java注释语言的格式
– 第二种:C++语言风格的注释
– 注释以“//”起头,这一行其后的内容都是注释,但它不能跨行
– 例:
// MyClass.java
// 类名:MyClass
public static void main(String[] args) { // 程序的入口函数
第五章 无用对象回收