2 Java 程序设计 2.JDK1.0 发布 1995 年,Sun 推出的 Java 只是一种语言, 而要想开发复杂的应用程序, 必须要有一个强大的开发库支持才行 Sun 在 1996 年 1 月 23 日发布了 JDK1.0 这个版本包括了两部分: 运行环境 ( 即 JRE) 和开发环境 ( 即

Size: px
Start display at page:

Download "2 Java 程序设计 2.JDK1.0 发布 1995 年,Sun 推出的 Java 只是一种语言, 而要想开发复杂的应用程序, 必须要有一个强大的开发库支持才行 Sun 在 1996 年 1 月 23 日发布了 JDK1.0 这个版本包括了两部分: 运行环境 ( 即 JRE) 和开发环境 ( 即"

Transcription

1 项目 1 Java 语言概述 Java 语言是由 Sun 公司于 1995 年 5 月 23 日正式推出的纯面向对象的程序设计语言 是 当前最流行的一种网络编程语言 它推出不久 就获得了极大的成功 这是因为 Java 与传统 的计算机语言相比 更简洁 更安全 易学易用 并独具特色 本项目主要介绍 Java 语言的 发展史及特点 然后介绍 Java 虚拟机工作原理和 Java 的开发环境搭建 最后介绍面向对象程 序设计的基本概念 了解 Java 的发展与特点 理解 Java 的平台无关性 掌握 JDK 的安装和环境变量的配置 理解面向对象程序设计的基本概念 掌握 Java 程序的开发流程 任务 1 Java 发展史与特点 Java 语言的产生背景 如同它的名字一样 散发着淡淡的咖啡香气 Java 的特点是 简 单 安全 可移植 面向对象 健壮 多线程 结构中立 解释性和高性能 分布式 动态 Java 的起源与发展 1 Java 的起源 1991 年 Sun Microsystem 公司的 Jame Gosling Bill Joe 等人 为了解决家用消费类电子 产品智能化过程中的控制和通信问题 设计出了一个新的软件 这个软件是 Jame 等人在充分 研究了传统计算机语言 尤其是 C/C++语言的特点后 设计出的一种适合开发跨平台嵌入式软 件的语言 当时命名为 Oak 这就是 Java 的前身 但由于智能家用电器并未像预期那样快速 发展 同时 Sun 公司参与投标的一个交互式电视系统的商业项目也未获成功 Oak 面临夭折 但此时 国际互联网的应用却高速发展 Sun 公司看到了 Oak 的跨平台特性在计算机网络应用 方面将大有可为 于是他们对 Oak 系统进行了结构和功能上的改造并加以扩充 将 Oak 重新 命名为 Java 注 印度尼西亚有一个重要的盛产咖啡的岛屿叫 Java 中文译名为爪哇 开发小组在一 次聚会中 从咖啡获得灵感想到了 Java 这个名字

2 2 Java 程序设计 2.JDK1.0 发布 1995 年,Sun 推出的 Java 只是一种语言, 而要想开发复杂的应用程序, 必须要有一个强大的开发库支持才行 Sun 在 1996 年 1 月 23 日发布了 JDK1.0 这个版本包括了两部分: 运行环境 ( 即 JRE) 和开发环境 ( 即 JDK) 在运行环境中包括了核心 API 集成 API 用户界面 API 发布技术 Java 虚拟机 (JVM) 五个部分 而开发环境还包括了编译 Java 程序的编译器 ( 即 javac) 在 JDK1.0 时代,Java 库比较单薄, 不够完善 随着 JDK 的逐步升级, 它为开发人员提供了一个强大的开发支持库 3.Java 2 问世 1998 年 12 月 4 日,Sun 发布了 Java 历史上最重要的一个 JDK 版本 :JDK1.2 这个版本标志着 Java 已经进入 Java 2 时代 这个时期也是 Java 飞速发展的时期 在 Java 2 时代 Sun 对 Java 进行了很多革命性的变化, 而这些革命性的变化一直沿用到现在, 对 Java 的发展形成了深远的影响 JDK1.2 自从被分成了 J2EE J2SE 和 J2ME 三大块, 得到了市场的强烈反响 J2EE 是 Java 2 的企业版 (Java 2 Platform Enterprise Edition), 主要用于构建企业级分布式应用系统, 定义了企业应用 Java 组件 JSP/Servlet JDBC Java 命名和目录服务接口等 这些技术也是 Java 包中的类和接口来实现的, 只是有的包已包含在 J2EE 中, 有的包还要单独下载和安装 J2ME 是 Java 2 微缩版 (Java 2 Platform Micro Edition), 主要适用于 PDA 移动电话 机顶盒设备及无线通信类电子产品中的 Java 应用软件的开发 微缩版的含义表示其构成相对简单 J2SE 是 Java 2 标准版 (Java 2 Platform Standard Edition), 主要用于桌面操作系统环境下开发 Java 程序 除包含微缩版中的三个包以外, 还有 Java.math Java.net 和 Java.text 等三个包, 它们定义了 Java 独立应用程序 Application, 和在浏览器中运行的小应用程序 Applet 的开发过程中可应用的类和接口, 这是 Java 的核心包 2000 年 5 月 8 日,Sun 对 JDK1.2 进行了重大升级, 推出了 JDK 年 2 月 13 日,Sun 发布了 JDK 历史上最为成熟的版本 JDK1.4 4.Java 5.0 发布 2004 年 9 月 30 日,Sun 发布了 JDK1.5, 成为 Java 语言发展史上的又一个里程碑 为了表示该版本的重要性,Sun 将版本号 1.5 改为 5.0, 就是预示着 J2SE 5.0 较以前的 J2SE 版本有着很大的改进 在 Java 5.0 中, 主要包含以下主要新特性 : 泛型 增强 for 语句 可变数目参数 Annotation 注解 自动拆箱和装箱 类型安全的枚举 静态导入

3 项目 1 Java 语言概述 3 5.Java 8.0 发布 2014 年 4 月 24 日,Oracle 的 Java 开发团队终于发布了 Java 8 正式版本 Java 8 版本最大的改进就是 Lambda 表达式, 其目的是使 Java 更易于为多核处理器编写代码 ; 其次, 新加入的 Nashorn 引擎也使得 Java 程序可以和 JavaScript 代码互操作 ; 再者, 新的日期时间 API GC 改进 并发改进也相当令人期待 注 :2009 年 4 月,Oracle 公司以 74 亿美元收购 Sun 公司 Java 的特点 Java 语言是一种跨平台 适合于分布式计算环境的面向对象程序设计语言 具体来说, 它具有平台无关 简单 面向对象 分布式 健壮 多线程 安全及多态性等特点 1. 平台无关性 Java 有一句著名的口号 : 一次编程, 四处应用 Java 可以跨平台使用, 这是因为 Java 编译器把 Java 源程序编译成二进制代码后并不是像其他计算机语言那样生成可执行文件后直接执行, 而是生成了一种后缀名为.class 的字节码文件 运行时, 由一种被称为 Java 虚拟机的运行环境, 针对不同的处理器指令系统, 把字节码转换为不同的具体指令, 然后再执行 所以, Java 程序能够运行安装了 Java 虚拟机的任意类型的操作系统中, 如 Linux Solaris Windows 95/98/2000/NT/XP 等, 实现了平台无关 因而, 特别适合于网络应用 平台无关的特性使 Java 程序可以方便地被移植到网络上的不同机器 同时,Java 的类库中也实现了与不同平台的接口, 使这些类库可以移植 另外,Java 编译器是由 Java 语言实现的, 这使得 Java 系统本身也具有可移植性 2. 简单 Java 语言的设计目标是作为一种小型的嵌入式控制语言以解决智能家用电器设备的控制与通讯, 随着 Internet 的发展,Java 的目标又定位于运行在浏览器中的小程序 (Applet) 所以,Java 语言的出发点就是容易编程, 使用简洁 它与 C/C++ 语言类似, 但却要简单得多 它抛弃了 C/C++ 中的指针 结构体等概念 ; 没有多继承 运算符重载等容易使人迷惑的规则 ; 也没有预处理器 ; 程序员也不必自己释放占用的内存空间, 系统将自动回收 因而 Java 程序设计中不用考虑无用内存的释放和无用对象的删除等, 极大减少了内存冲突的问题 Java 虽然语法简单, 但功能却十分强大 使用它也可以设计出非常复杂的系统 如构建分布式系统的中间件 Jini, 就是完全用 Java 开发的 3. 面向对象 Java 是纯面向对象的语言 面向对象是把现实世界中的物体 ( 对象 ), 用属性和行为来描述, 然后对应到计算机编程中 如汽车, 其颜色 形状等是属性 ; 它的功能如加速 刹车等, 则是行为 ; 整个汽车就是类, 类是抽象的描述, 或者说是泛指, 具有共性 而具体的特指, 如一辆红色面包车则是对象 即对象是类的实例 对应到计算机编程上, 属性就是数据, 常用变量或常量来表达 行为就是能实现一定功能的一段程序代码, 常叫作方法 也就是说, 我们用属性与方法就能够描述一个对象 如果抽象地描述同一类型的对象, 这就是类 定义类, 是 Java 编程的第一步 在定义类时可以继承原有的一个类, 这样新的类就继承

4 4 Java 程序设计 了原有类中的属性与方法, 新类的对象就可以在编程中直接调用原有类中的方法, 从而实现了代码重用 这是面向对象的一大特征, 即继承 封装和多态则是面向对象的另外两大特征 封装就是将对象所具有的描述共性的属性和行为或者说是数据和代码封装于类中, 利用类的优点, 实现程序的简洁性和易维护性 多态则是指一个接口, 可以有多个实现形式, 即完成多个功能 面向对象编程就是定义类与对象, 并利用消息触发机制将事件与方法联系起来, 它支持封装 多态和继承 4. 分布式 Java 允许将其编译后的二进制代码分布于网络上 它提供了大量的系统模块支持基于 TCP/IP 协议的编程, 这使 Java 建立网络连接十分容易 Java 应用程序可以通过 URL 来访问网络资源, 如寻找应用程序所需的类, 这同在本地机上寻找一样方便 快捷 5. 健壮性 Java 在编译和运行程序时, 都要对可能出现的问题进行检查, 以消除错误的产生 它提供自动垃圾收集来进行内存管理, 防止程序员在管理内存时容易产生的错误 通过集成的面向对象的异常处理机制, 在编译时,Java 提示出可能出现但未被处理的异常, 帮助程序员正确地进行选择以防止系统的崩溃 另外,Java 在编译时还可捕获类型声明中的许多常见错误, 防止动态运行时不匹配问题的出现 6. 多线程 Java 支持多线程 线程是程序运行的最小单位 所谓多线程, 即计算机可以同时运行多个程序段, 各自完成不同的任务 这样极大地提高了程序执行的效率和处理能力 Java 提供了有关线程的操作, 线程的创建, 线程的管理, 线程的结束等处理 Java 自身也是多线程的, 它可以利用系统的空闲进行一些常规处理 如 Java 虚拟机启动后, 就一直运行着一个线程, 在后台负责对不在使用的对象的垃圾进行回收工作, 即自动释放内存 当然, 该线程的优先级最低 7. 安全所有 Java 语言对内存进行的访问都是通过对象实例变量实现的 程序运行时, 内存由操作系统分配, 这样可以防止病毒通过指针侵入系统 Java 对程序提供了安全管理器, 使用了公开密钥加密机制, 防止程序的非法访问 8. 动态性 Java 语言的设计目标之一是适应于动态环境的变化 即 Java 程序运行时, 是动态地进行加载, 当程序运行到要找所需的类时, 可在本机上寻找, 也可在网上寻找, 找到后再动态地加载 这保证了对类库支持的一致性, 类库升级也可不必重新编译程序 习题 1.Java 语言的显著特点是什么? 2.Java 平台由哪几部分组成? 各有什么作用

5 项目 1 Java 语言概述 5 任务 2 Java 程序工作原理 Java 虚拟机 Java 虚拟机是软件模拟的计算机, 可以在任何处理器上 ( 无论是在计算机中还是在其他电子设备中 ) 安全并且兼容地执行保存在.class 文件中的字节码 Java 虚拟机的 机器码 保存在.class 文件中, 有时也可以称之为字节码文件 Java 程序的跨平台主要是指字节码文件可以在任何具有 Java 虚拟机的计算机或者电子设备上运行,Java 虚拟机中的 Java 解释器负责将字节码文件解释成为特定的机器码进行运行 Java 源程序需要通过编译器编译成为.class 文件 ( 字节码文件 ),Java 程序的编译和执行过程如图 1-1 所示 解释器 源文件 编译器 字节码文件 图 1-1 Java 程序编译过程 但是,Java 虚拟机的建立需要针对不同的软硬件平台做专门的实现, 既要考虑处理器的型号, 也要考虑操作系统的种类 目前在 SPARC 结构 X86 结构 MIPS 和 PPC 等嵌入式处理芯片上 在 UNIX Linux Windows 和部分实时操作系统上都有 Java 虚拟机的实现, 如图 1-2 所示 应用程序层 Java 应用程序 Java 应用程序 Java 应用程序 Java 平台层 Java 虚拟机 Java 虚拟机 Java 虚拟机 操作系统层 UNIX Linux Windows RTOS 硬件层 SPARC X86 MIPS PPC 图 1-2 不同平台 Java 程序实现过程 在 Java 虚拟机中, 是利用 Java 解释器对编译后的字节码文件进行解释执行的, 由解释器对在不同计算机系统中建立的与内存布局对应的字节码文件进行内存映射 解释器对 Java 字节码文件的解释执行通常分为以下三步 : (1) 装入代码 : 由解释器申请内存空间, 读入字节码文件进行分析, 建立内存布局 其具体功能是由类装载器来完成的 (2) 校验代码 : 由字节码校验器检验内存布局, 发现错误 (3) 运行代码 : 解释读取字节码含义, 执行代码, 完成相应功能 计算机语言的执行方式一般分为解释执行和编译执行两种 Java 虚拟机自推出以来, 一直是对字节码文件采用的解释执行方式, 因而运行速度较慢, 这也曾是 Java 的缺点 但是现在,Sun 公司已经解决了这一问题, 推出了 Java 字节码编译器, 大大提高了 Java 的执行速度

6 6 Java 程序设计 垃圾回收机制在 C++ 中, 对象所占的内存在程序结束运行之前一直被占用, 在明确释放之前不能分配给其他对象 ; 而在 Java 中, 当没有对象引用指向原先分配给某个对象的内存时, 该内存便成为垃圾 JVM 的一个系统级线程会自动释放该内存块 垃圾回收意味着程序不再需要的对象是 无用信息, 这些信息将被丢弃 当一个对象不再被引用的时候, 内存回收它占领的空间, 以便空间被后来的新对象使用 事实上, 除了释放没用的对象, 垃圾回收也可以清除内存记录碎片 由于创建对象和垃圾回收器释放丢弃对象所占的内存空间, 内存会出现碎片 碎片是分配给对象的内存块之间的空闲内存洞 碎片整理将所占用的堆内存移到堆的一端,JVM 将整理出的内存分配给新的对象 垃圾回收能自动释放内存空间, 减轻编程的负担 这使 Java 虚拟机具有一些优点 首先, 它能使编程效率提高 在没有垃圾回收机制的时候, 可能要花许多时间来解决释放无用内存的问题 在用 Java 语言编程的时候, 靠垃圾回收机制可大大缩短时间 其次是它保护程序的完整性, 垃圾回收是 Java 语言安全性策略的一个重要部分 垃圾回收机制是一个系统级线程, 它给程序员带来好处的同时, 也存在着影响系统性能问题, 因为它要必须追踪运行程序中有用的对象, 而且最终释放没用的对象 这一个过程需要花费处理器的时间 其次垃圾回收算法的不完备性, 早先采用的某些垃圾回收算法就不能保证 100% 收集到所有的废弃内存 当然随着垃圾回收算法的不断改进以及软硬件运行效率的不断提升, 这些问题都可以迎刃而解 程序员可以调用 System.gc() 这个方法通知 Java 虚拟机释放无用资源, 但 Java 虚拟机会选择在合适的时候释放无用资源, 具体释放的时间, 不是程序员调用 System.gc() 的时刻, 而是 Java 虚拟机决定的, 程序员不能精确控制和干预 习题 1. 简述 Java 虚拟机的工作原理 2. 请描述什么是 Java 的垃圾回收机制 任务 3 面向对象基础 Java 语言是一个面向对象的程序设计语言, 因此在系统学习 Java 语言之前, 首先需要对面向对象的程序设计思想有一个了解, 在以后学习过程中将不断加深理解并掌握面向对象的方法 什么是面向对象程序设计面向对象程序设计是一种新的程序设计范型 (Paradigm) 程序设计范型是指设计程序的规范 模型和风格, 它是一类程序设计语言的基础 一种程序设计范型体现了一类语言的主要特征, 这些特征能用以支持应用领域所希望的设计风格 不同的设计范型有不同的程序设计技术和方法学 面向过程程序设计范型是使用较广泛的程序设计范型, 这种范型的主要特征是, 程序由

7 项目 1 Java 语言概述 7 过程定义和过程调用组成, 即程序 = 过程 + 调用 基于面向过程程序设计范型的语言称为面向过程性语言, 如 C PASCAL Ada 等都是典型的面向过程性语言 函数式程序设计范型也是较为流行的程序设计范型, 它的主要特征是, 程序被看作 描述输入与输出之间关系 的数学函数 LISP 是支持这种范型的典型语言 除了面向过程程序设计范型和函数式程序设计范型外, 还有许多其他的程序设计范型, 如模块程序设计范型 ( 典型语言是 Modula) 逻辑式程序设计范型 ( 典型的语言是 PROLOG) 进程式程序设计范型 类型系统程序设计范型 事件程序设计范型 数据流程程序设计范型等 面向对象程序设计是一种新型的程序设计范型 这种范型的主要特征是 : 程序 = 对象 + 消息面向对象程序的基本元素是对象, 面向对象程序的主要结构特点是 : 第一, 程序一般由类的定义和类的使用两部分组成, 在程序中定义各对象并规定它们之间传递消息的规律 第二, 程序中的一切操作都是通过向对象发送消息来实现的, 对象接收到消息后, 启动有关方法来完成相应得操作 一个程序中涉及到的类, 可以由程序设计者自己定义, 也可以使用现成的类 ( 包括类库中为用户提供的类和他人已构建好的 ) 尽量使用现成的类, 是面向对象程序设计范型所倡导的程序设计风格 需要说明的是, 某一种程序设计语言不一定与一种程序设计范型相对应 实际上存在有具备两种范型或多种范型的程序设计语言, 即混合型语言 例如 C++ 就不是纯粹的面向对象程序设计范型, 而是面向过程程序设计范型和面向对象程序设计范型的混合型程序设计语言 面向对象的基本概念 1. 对象首先需要搞清楚的第一问题是, 什么是对象? 对象具有两方面含义, 即在现实生活中的含义和在计算机世界中的含义 在我们所生活的现实世界中, 对象 无处不在 在我们身边存在的一切事物都是对象, 例如一粒米 一本书 一个人 一所学校, 甚至一个地球, 这些都是对象 除去这些可以触及的事物是对象外, 还有一些无法整体触及的抽象事件, 例如一次演出 一场球赛 一次借书, 也都是对象 一个对象既可以非常简单, 又可以非常复杂, 复杂的对象往往可以是由若干个简单对象组合而成的 所有这些对象, 除去它们都是现实世界中所存在的事物之外, 它们都还是有各自不同的特征 例如一粒米, 它首先是一粒米这样一个客观存在 再例如一个人, 首先他是一个客观实体, 具有一个名字来标识, 其次它具有性别 年龄 身高 体重等这些体现他自身状态的特征 ; 再其次他还具有一些技能, 例如会说英语 会修电器等 通过上面的这些举例我们可以对 对象 下一个定义, 即对象是现实世界中的一个实体, 它具有如下特性 : 有一个名字以区别于其他对象 有一个状态用来描述它的某些特征 有一组操作, 每一个操作决定对象的一种功能或行为 对象的操作可分为两类 : 一类是自身承受的操作, 一类是施加于其他对象的操作

8 8 Java 程序设计 在面向对象程序设计中, 对象是描述其属性的数据以及对这些数据施加的一组操作封装在一起构成的统一体 对象可以认为是 : 数据 + 操作 对象所能完成的操作表示它的动态行为, 通常也把操作称为方法 为了帮助读者理解对象的概念, 图 1-3 形象地描述了具有 3 个操作的对象 操作 1 操作 2 接口 接口 数据 操作 3 接口 操作 的实现 图 1-3 具有 3 个操作的对象 下面我们用一台录音机比喻一个对象, 通俗地说明对象的某些特点 录音机上有若干按键, 如 Play( 播放 ) Rec( 录音 ) Stop( 停止 ) Rew( 倒带 ) 等, 当人们使用录音机时, 只要根据自己的需要如放音 录音 停止 倒带等按下与之对应的键, 录音机就会完成相应的工作 这些按键安装在录音机的表面, 人们通过它们与录音机交互 我们无法操作录音机的内部电路, 因为它们被装在机壳里, 录音机的内部情况对于用户来说是隐蔽的, 不可见的 一个对象很像一台录音机, 当在软件中使用一个对象的时候, 只能通过对象与外界的接口操作它 对象与外界的接口也就是该对象向公众开放操作 使用对象向公众开放的操作就好像使用录音机的按键, 只需知道该操作的名字 ( 如录音机的键名 ) 和所需要的参数 ( 用于提供附加信息或设置状态, 好像听录音前先装录音带并将录音带转到指定位置 ), 根本无需知道实现这些操作的方法 事实上, 实现对象操作的代码和数据是隐藏在对象内部的, 一个对象好像是一个黑盒子, 表示它内部的数据和实现各个操作的代码, 都被封装在这个黑盒子内部, 在外面是看不见的, 更不能从外面去访问或修改这些数据或代码 使用对象时只需知道它向外界提供的接口形式而无需知道它的内部实现算法, 不仅使得对象的使用变得非常简单 方便, 而且具有很高的安全性和可靠性 可见面向对象程序设计中的对象来源于现实世界, 更接近人们的思维 2. 类在现实世界中, 类 是一组相同属性和行为的对象的抽象 例如, 张三 李四 王五, 虽然每个人的性格 爱好 职业 特长等各不同, 但是他们的基本特征是相似的, 都具有相同的生理构造, 都能吃饭 说话 走路等, 于是把他们统称为 人, 而具体的每一个人是人类的一个实例, 也就是一个对象 类和对象之间的关系是抽象和具体的关系 类是多个对象进行综合抽象的结果, 一个对象是类的一个实例 例如 狗 是一个类, 它是由千千万万个具体的不同狗抽象而来的一般概念 同理, 鸡 鸭 牛 羊等都是类 类在现实世界中并不真正存在 例如, 在地球上并没有抽象的 人, 只有一个个具体的

9 项目 1 Java 语言概述 9 人, 如张三 李四 王五 同样, 世界上没有抽象的 学生, 只有一个个具体的学生 面向对象程序设计中, 类 就是具有相同的数据和相同的操作的一组对象的集合, 即类是对具有相同数据结构和相同操作的一类对象描述 例如, 学生 类可以由学号 姓名 性别 成绩等表示其属性的数据项和对这些数据的录入 修改和显示等操作组成 在面向对象中, 总是先声明类, 再由类生成对象 类是建立对象的 模板, 按照这个模板所建立的一个个具体的对象, 就是类的实际例子, 通常称为实例 比如, 手工制作月饼时, 先雕刻一个有凹下图案的木模, 然后在木模上抹油, 接着将事先揉好的面塞进木模里, 用力挤压后, 将木模反扣在桌上, 一个漂亮的图案就会出现在月饼上了 这样一个接着一个的, 就可以制造出外形一模一样的月饼 这个木模就好比是 类, 制造出来的月饼好比是 对象 3. 消息现实世界中的对象不是孤立存在的实体, 他们之间存在着各种各样的联系, 正是它们之间的相互作用 联系和连接, 才构成了世间各种不同的系统 同样, 在面向对象程序设计中, 对象之间也需要联系, 我们称为对象的交互 面向对象程序设计必须提供一种机制允许一个对象与另一个对象的交互 这种机制叫消息传递 即对象之间进行通信的结构叫做消息 在对象的操作中, 当一个消息发送给某个对象时, 消息包含接收对象去执行某种操作的信息 发送一条消息至少要包括说明接受消息的对象名 发送给该对象的消息名 ( 即对象名 方法名 ) 一般还要对参数加以说明, 参数可以是认识该消息的对象所知道的变量名, 或者是所有对象都知道的全局变量名 在面向对象程序设计中的消息传递实际是对现实世界中的信息传递的直接模拟 以实际生活为例, 我们每一个人可以为他人服务, 也可以要求他人为自己服务 当我们需要别人为自己服务时, 必须告诉他们我们需要的是什么服务, 也就是说, 要向其他对象提出请求, 其他对象接到请求后, 才会提供相应的服务 一般情况下, 我们称发送消息的对象为发送者或请求者, 接收消息的对象为接收者或目标对象 对象中的联系只能通过消息传递来进行 接收对象只有在接收到消息时, 才能被激活, 被激活的对象会根据消息的要求完成相应的功能 消息具有三个性质 : 同一对象可接收不同形式的多个消息, 产生不同的响应 相同形式的消息可以送给不同对象, 所做出的响应可以是截然不同的 消息的发送可以不考虑具体的接收者, 对象可以响应消息, 也可以对消息不予理会, 对消息的响应并不是必须的 在面向对象程序设计中, 消息分为两类 : 即公有消息和私有消息 到底哪些消息是公有消息, 哪些消息是私有消息, 需要有一个明确的规定 若有一批消息同属于一个对象, 其中有一部分是由外界对象直接向它发送的, 称之为公有 (public) 消息 ; 还有一部分则是它自己向本身发送的, 这些消息是不对外开放的, 外界不必了解它, 称之为私有 (private) 消息 4. 方法在面向对象程序设计中, 要求某一个对象做操作时, 就向该对象发送一个相应的消息, 当对象接收到发向它的消息时, 就调用有关方法, 执行相应的操作 方法就是对象所能执行的操作 方法包括界面和方法体两部分 方法的界面也就是消息的模式, 它给出方法的

10 10 Java 程序设计 调用协议 ; 方法则是实现某种操作的一系列计算步骤, 也就是一段程序 消息和方法的关系是 : 对象根据接收到消息, 调用相应的方法 ; 反过来, 有了方法, 对象才能响应相应的消息 所以消息模式与方法界面应该是一致的 同时, 只要方法界面保持不变, 方法体的改动不会影响方法的调用 面向对象编程的特征面向对象的程序设计方法与传统方法相比较有着自身鲜明的特点, 主要概况为抽象 封装 继承和多态四大基本特征 1. 抽象抽象就是忽略一个主题中与当前目标无关的那些方面, 以便更充分地注意与当前目标有关的方面 ( 就是把现实世界中的某一类东西提取出来, 用程序代码表示, 一般叫做类或者接口 ) 抽象并不打算了解全部问题, 而只是选择其中的一部分, 暂时不用部分细节 抽象包括两个方面, 一是数据抽象, 二是过程抽象 数据抽象就是用代码的形式表示现实世界中一类事物的特性, 就是针对对象的属性 比如建立一个鸟这样的类, 鸟都有以下属性 : 一对翅膀 两只脚 羽毛等 抽象出来的类都是鸟的属性, 或者成员变量 过程抽象就是用代码形式表示现实世界中事物的一系列行为, 就是针对对象的行为特征 比如鸟会飞 会叫等 抽象出来的过程一般都是鸟的方法 2. 封装封装性就是尽可能地隐藏对象内部细节, 对外形成一道边界, 只保留有限的接口和方法与外界进行交互 封装的原则是使对象以外的部分不能随意地访问和操作对象的内部属性, 从而避免了外界对对象内部属性的破坏 Java 使用类机制体现其封装性, 可以通过对类的成员设置一定的访问权限, 实现类中成员的信息隐藏 在实际开发中, 类用来构建软件系统的模块, 模块之间只能通过接口进行交互, 使它们的耦合和交叉大大减少, 从而降低开发过程的复杂性, 减少可能的错误, 使得 Java 程序具有良好的可维护性 3. 继承一个类继承另一个类, 继承者可以获得被继承类的所有方法和属性, 并且可以根据实际的需要添加新的方法或者对被继承类中的方法进行覆写, 被继承者称为父类或者超类, 继承者称为子类或导出类, 继承提高了程序代码的可重用性,Java 中一个子类只能继承一个父类, Object 类是所有类的最终父类 一个父类可以被多个子类继承, 此时父类是所有子类的公共属性和方法的集合, 而每个子类则是父类的特殊化, 是在公共属性和方法的基础上的扩展和延伸, 子类是可以定义属于自己的特有的属性和方法 4. 多态对象的多态性是指在父类中定义的属性或方法被子类继承之后, 可以具有不同的数据类型或表现出不同的行为 这使得同一个属性或方法在父类及其各个子类中具有不同的语义 例如 : 几何图形 作为父类有 绘图 方法, 椭圆 和 多边形 都是 几何图形 的子类, 其 绘图 方法功能与父类不同 Java 的多态性体现在两个方面, 一个是由方法重载实现的静态多态性 ( 编译时多态 ), 另

11 项目 1 Java 语言概述 11 一个是方法重写实现的动态多态性 ( 运行时多态 ) 编译时多态 : 在编译阶段, 具体调用哪个被重载的方法, 编译器会根据参数的不同来静态确定调用相应的方法 运行时多态 : 由于子类继承了父类所有的属性 ( 私有的除外 ), 所以子类对象可以作为父类对象使用 程序中凡是使用父类对象的地方, 都可以用子类对象来代替 一个对象可以通过引用子类的实例来调用子类的方法 习题 1. 面向对象编程与面向过程编程有哪些不同? 2. 什么是类? 什么是对象? 两者关系是什么? 3. 面向对象编程四个基本特征是什么? 4. 什么是继承? 什么是多态? 任务 4 Java 的开发和运行环境 如果要开发 Java 应用程序, 必须建立 Java 开发环境 Java 语言自身提供了开发环境 JDK (Java Development Kit,Java 软件开发工具集 ), 当前的最新版本是 JDK 8.0 之前的版本可以使用, 本书使用 JDK 版本相对稳定的 JDK 7.0 版本 JDK 简介 JDK 是一种用于构建 Java 平台上编译和发布 Java 程序的开发和运行环境 它由两部分组成, 下层是处于操作系统层之上的运行环境, 上层由编译工具 调试工具和运行 Java 应用程序所需的工具组成, 其核心 Java API 是一些预定义的类库, 开发人员需要用这些类来访问 Java 语言的预定义的功能 JDK 所包含的工具当中, 主要有以下几种 : javac:java 编译器, 将 Java 源代码转换成字节码 java:java 解释器, 直接从类文件执行 Java 应用程序字节代码 appletviewer: 小程序浏览器, 一种执行 HTML 文件上的 Java 小程序的 Java 浏览器 javadoc: 根据 Java 源码及说明语句生成 HTML 文档 jdb:java 调试器, 可以逐行执行程序, 设置断点和检查变量 javah: 产生可以调用 Java 过程的 C 过程, 或建立能被 Java 程序调用的 C 过程的头文件 javap:java 反汇编器, 显示编译类文件中的可访问功能和数据, 同时显示字节代码含义 JDK 包含以下常用类库 : java.lang: 系统基础类库, 其中包括字符串类 String 等 java.io: 输入输出类库, 例如进行文件读写需要用到 java.net: 网络相关类库, 例如进行网络通信会用到其中的类 java.util: 系统辅助类库, 编程中经常用到的集合属于这个类库

12 12 Java 程序设计 java.sql: 数据库操作类库, 连接数据库 执行 SQL 语句 返回结果需要用到该类库 java.servlet:jsp Servlet 等使用到的类库, 是 Java 后台技术的核心类库 JDK 的安装首先, 在 Oracle 公司的官方网站 ( 下载 JDK 的安装包, 根据自己电脑的操作系统选择正确的版本下载 另外,JDK 的使用也不是版本越高越好 在企业级开发中, 通常一个项目中的开发人员统一使用一个稳定版本的 JDK, 避免因为各版本 JDK 的差异带来问题 JDK 下载结束后, 用鼠标左键双击 JDK 安装包, 系统将显示 JDK 安装向导画面 JDK 的安装过程很简单, 一直单击 下一步 按钮即可 默认情况下,JDK 及其所包含的 JRE 将被安装到 C:\Program Files\Java 文件夹中 将 JDK 安装到默认目录下后, 会形成如图 1-4 所示的目录结构 图 1-4 JDK 目录结构 JDK 目录的主要内容如下 : bin: 存放 javac java appletviewer 等命令程序 db: 包含使用嵌入式数据库 Derby 开发所需要的资源及一些案例 include: 存放与 C 程序相关的头文件 lib: 附加库, 是开发工具所需的其他类库和支持文件 JDK 配置在使用 Java 来编译与运行程序之前, 必须先设置系统环境变量 所谓系统环境变量就是在操作系统中定义的变量, 可供操作系统上的所有应用程序使用 为此, 需要设置三个环境变量 :JAVA_HOME Path 和 CLASSPATH 1.JAVA_JOME 环境变量配置在 Windows 7 系统中, 右击 计算机 图标, 选择 属性 高级系统设置 环境变量 命令, 打开对话框如图 1-5 所示 在 系统变量 (S) 中新建 JAVA_HOME 环境变量, 变量值 输入 C:\Program Files\Java\jdk1.7.0_75, 如图 1-6 所示 2.Path 环境变量配置在图 1-5 所示 环境变量 对话框中的 系统变量 (S) 中找到变量 Path, 然后单击 编辑 按钮, 打开 编辑系统变量 对话框 在 变量值 编辑框最前面加上 %JAVA_HOME%\ bin;%java_ HOME%\jre\bin;, 如图 1-7 所示

13 项目 1 Java 语言概述 13 图 1-5 环境变量对话框 图 1-6 新建环境变量图 1-7 编辑环境变量 3.CLASSPATH 环境变量配置与新建 JAVA_HOME 环境变量类似, 新建 CLASSPATH 环境变量, 变量值 输入.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar ( 注意最前面有一点号 ), 如图 1-8 所示 图 1-8 新建环境变量 4. 验证 JDK 是否成功单击 开始 按钮, 在 搜索程序和文件 框中输入 cmd, 按 Enter 健, 打开仿真 DOS 窗口, 输入 java version 命令, 如果能看到如图 1-9 所示的显示版本信息, 则说明安装和配置成功 图 1-9 JDK 显示版本信息

14 14 Java 程序设计 第一个 Java 程序 1. 编辑 Java 程序 JDK 中没有提供 Java 编辑器, 需要使用者自己选择一个方便易用的编辑器或集成开发工 具 Java 程序的源程序是以.java 为后缀的文本文件 作为初学者, 可以使用记事本 EditPlus UltraEdit 作为 Java 编辑器, 编写第一个 Java 程序 下面以记事本为例, 使用它编写 HelloWorld 程序 打开 记事本, 按照示例程序 1.1 输入代码 ( 注意大小写和缩进结构 ), 完成后将其保存 为 HelloWorld.java 文件 ( 注意如果保存为 HelloWorld.txt 要修改后缀名为.java) 例 1.1 第一个 Java 程序 HelloWorld public class HelloWorld public static void main(string args[]) System.out.println( 欢迎来到 Java 世界 ); 说明 : (1) public class HelloWorld 表明要建立一个类, 类名为 HelloWorld 定义类必须使用 关键字 class Java 应用程序必须以类的形式出现, 一个程序中可以定义若干个类 public 表明 该类是公共类, 可以被所有类访问 虽然一个程序文件中可以定义多个类, 但只能有一个 public 类 如果一个文件中包含一个 public 类, 文件名字必须和该类名相同 类的内容, 即类中的属性和方法在后面的一对花括号中列出 类的属性用变量描述, 称 为成员变量 对应地, 类中的方法也称为成员方法 (2) public static void main(string args[]) 建立一个名为 main 的方法 一个应用程序中 可以有多个方法, 但只能有一个 main() 方法 main() 方法是程序入口点, 若无此方法, 程序无 法运行 public 表明该方法是一个公共方法, 所有的类都可以调用该方法 static 表明该方法可以 通过类名 HelloWorld 访问 void 表明该方法没有返回值 String args[] 表明该方法有一个字符 串数组类型的参数 (3)main() 方法中包含一条语句 System.out.println(" 欢迎来到 Java 世界 ");, 其功能是 输出括号中字符串的内容, 即输出 欢迎来到 Java 世界 其中 System 是 Java 类库中的一个类, 利用此类可以获得 Java 运行环境的有关信息和输 入 输出信息 out 是 System 类中的一个对象 println 是 out 对象的一个方法, 其功能是向标 准设备即显示器输出括号中的字符串 初次接触 Java 程序, 可能觉得程序结果比较复杂, 但读者很快就会发现, 对于一般的 Java 程序, 其基本框架和这里给出的结构是相同的, 所不同的只是类中的属性 ( 变量 ) 和方法 2. 编译 Java 源文件 打开一个命令提示符窗口, 进入保存 HelloWorld.java 的目录, 输入如下命令开始编译源 程序,Java 源程序必须带上扩展名.java, 否则编译程序会提示出错

15 项目 1 Java 语言概述 15 javac HelloWorld.java Java 源程序经过编译后得到的字节码文件为.class 文件, 一个源程序可以编译成一个或 多个字节码文件, 每个字节码文件对应源程序中定义的一个类, 文件名与对应的类名相同 例 1.1 源程序编译后生成的文件为 HelloWorld.class, 该文件被自动保存在源程序所在的目录下 3. 运行 class 文件 运行编译后的 Java 字节码文件需要调用 Java 的解释器 java.exe 在编译后使用如下命令 运行已生成的 HelloWorld.class 文件 注意 :Java 字节码文件的扩展名.class 在运行时不必列出 运行结果如图 1-10 所示 java HelloWorld 图 1-10 编译和运行 Java 程序 习题 1. 简述 J2SE 的下载及安装步骤 2. 运行一个 Java 应用程序需要哪些步骤? 具体如何实现? 3. 参照例 1.1 编写一个简单的 Java 程序, 在屏幕上打印出自己的名字 4. 上网搜索 Java 有哪些常用的集成开发环境, 分析它们各自的优点 项目总结 1. 介绍 Java 的起源与发展史,Java 语言的主要特点 2.Java 语言程序的运行机制, 虚拟机工作原理及垃圾回收机制 3. 面向对象程序设计基本概念及四个基本特征 4.Java 运行环境的安装与配置,Java 程序编辑 编译和运行过程

16 项目 2 Java 语言基础 本章主要讲述 Java 编程语言的基本语法知识 如 Java 语言的基本组成 数据类型 运算 符和表达式 控制语句 数组及字符串的处理 这些内容是任何一门程序设计语言都必须包含 的部分 也是我们编程的基础 通过本章的学习 同学们可以编写简单的 Java 程序 熟练掌握 Java 的标识符 关键字和数据类型的使用 掌握通过命令行进行数据输入输出的方法 熟练掌握运算符 表达式和各种语句的使用 熟练掌握 Java 数组的使用方法 掌握 Java 方法的声明和调用 掌握 Java 字符串处理 任务 1 Java 语言的基本组成 Java 语言主要由以下五种元素组成 标识符 关键字 数据 运算符和分隔符 这五种 元素有着不同的语法含义和组成规则 它们互相配合 共同完成 Java 语言的语意表达 标识符 在 Java 程序中 标识符是用来命名变量 符号常量 方法 类 接口和包等元素的 在 Java 中对标识符的规定如下 1 标识符由字母 数字 下划线 美元符 $ 组成 并且首字符不能是数字 2 Java 关键字 保留字等不能作为标识符 3 标识符对大小写敏感 4 标识符没有长度限制 Java 源程序使用的编码不是我们常见的 ASCII 码 而是 Unicode 码 Unicode 码用 16 位 二进制位表示一个字符 Unicode 编码把汉字和英文字母统一处理 所以 标识符也可以包含 汉字 在 Java 程序设计中 对标识符通常还有如下约定 1 变量名 对象名 方法名 包名等标识符全部采用小写字母 如果标识符由多个单 词构成 则首字母小写 其后单词的首字母大写 其余字母小写 如 getage

17 项目 2 Java 语言基础 17 (2) 类名首字母大写 (3) 常量名全部大写 合法的标识符示例 : area group_7 opendoor boolean_1 面积 不合法的标识符示例 : area# // 含有非法字符 7group // 数字不能作为第一个字符 boolean //Java 关键字 关键字 关键字是由 Java 语言定义的 具有特殊含义的字符序列 每一个关键字都有一个特定的 含义, 不能将关键字作为普通标识符使用 Java 关键字如表 2-1 所示 表 2-1 Java 的关键字 abstract do implements private throw boolean double import protected throws break else instanceof public transient byte extends int return true case false interface short try catch final long static void char finally native super volatile class float new switch while continue for null synchronized default if package this 变量和常量 数据的不同表现形式, 也就是不同的数据类型,Java 的数据类型有 : 整型 浮点型 布 尔型 字符型 字符串型, 关于每种数据类型的定义和使用后面详细叙述 1. 变量 变量是指在程序运行过程中其值可以改变的量 变量有四个基本要素, 即名字 类型 值和使用范围 变量名是用户定义的标识符 每个变量都属于一种数据类型, 可以是基本数据 类型, 也可以是引用数据类型 在程序运行过程中, 变量的值可以改变, 但其数据类型不能改 变 每个变量都有其可被使用的范围 Java 中变量必须先声明, 后使用 变量的声明方式如下 : [ 修饰符 ] 类型标识符变量名 [= 常量 ] 说明 : (1) 修饰符是表示该变量使用范围的权限修饰, 如 public private protected 等 (2) 类型标识符可以是任意的基本数据类型或引用数据类型, 如 int long float double 等

18 18 Java 程序设计 决定 (3) 声明一个变量时系统必须为变量分配内存单元 分配的内存单元大小由类型标识符 (4) 如果声明中包含 = 常量 部分, 常量的数据类型必须与类型标识符的类型相匹配, 系统将此常量的值赋予变量, 作为变量的初始值 ; 否则变量没有初始值 (5) 可以同时声明同一数据类型的多个变量, 各变量之间用逗号分隔 声明变量举例, 例如 : int num=10,total; double v,r,h=123.45; 2. 常量 常量是指在程序运行过程中其值始终保持不变的量 Java 中的常量有整型 浮点数型 字符型 布尔型和字符串型 如 'a' true "student" 分别是整数型 浮点数型 字 符型 布尔型和字符串型常量, 这种表示方式称为直接常量 在 Java 中, 常量除了使用直接常量方式表示外, 还可以用标识符表示常量, 称为符号常 量 符号常量有四个基本要素, 即名字 类型 值和使用范围 常量名字是用户定义的标识符, 每个符号常量都属于一种基本数据类型, 每个符号常量都有其可被使用的范围 符号常量必须先声明, 后使用 符号常量的声明方式如下 : final [ 修饰符 ] 类型标识符常量名 = 常量 ; 说明 : (1) 修饰符是表示该常量使用范围的权限修饰符, 如 public private protected 或缺省 符号 [] 表示其中内容可以省略 的原则 (2) 类型标识符可以是任意的基本数据类型, 如 int long float double 等 (3) 常量名必须符合标识符的规定, 并习惯采用大写字母 取名时最好符合 见名知意 (4) = 右边的常量类型必须和类型标识符的类型相匹配 声明常量举例, 例如 : final int PRICE=30; final double PI= ; 运算符 任何语言都有自己的运算符,Java 语言也不例外, 如 + - * / 等都是运算 符, 运算符的作用是与一定的运算数据组成表达式来完成相应的运算 对不同的数据类型, 有 着不同的运算符, 本书将在后面章节中对不同类型的运算符分别讨论 分隔符 分隔符是一些用来区分 Java 源程序中的基本成分如标识符 关键字 运算符和常量等的 特定符号 分隔符分为注释符 空白符和普通分隔符三种 1. 注释符 注释是为源程序增加必要的解释说明的内容, 其目的是提高程序的可读性, 书写注释是 编写程序的良好习惯 Java 中有三种形式的注释符 :

19 项目 2 Java 语言基础 19 (1) // : 一行的注释符 表示从 // 开始到这一行的结尾的内容均为注释内容 (2) /* */ : 一行或多行的注释起止符 表示起止符之间的一行或几行的内容都是 注释内容 (3) /** */ : 文档注释起止符 表示该段注释内容包含在由 javadoc 命令生成的 HTML 文件中 注释是非执行语句, 即在程序中是不被编译执行的 2. 空白符 空白符包括空格 回车 换行和制表符 (Tab 键 ) 等符号 各种 Java 基本成分之间的多 个空白符与一个空白符的作用相同 3. 普通分隔符 普通分隔符具有确定的语法含义, 不能用错 表 2-2 列出了常用的普通分隔符及其用途 表 2-2 Java 的普通分隔符 符号 名称 用途 花括号 用来定义类体 方法体 复合语句和数组的初始化 ; 分号 语句结束标志, 逗号 分隔方法的参数, 分隔变量说明的各个变量 : 冒号 说明语句标号 [ ] 方括号 用于声明数组, 及引用数组的元素值 () 圆括号 用于在方法定义和访问中将参数表括起来, 或在表达式中定义运算的先后顺序, 或在控制语句中将表达式和类型转换括起来. 句号 ( 点 ) 用于分隔包 子包和类或分隔引用变量中的变量和方法 习题 1. 什么是标识符? 标识符的命名规则有哪些?true 是否可以作为标识符? 2. 什么是关键字? 请列举 10 个 Java 关键字 3. 选择题 (1) 下列属于合法标识符的是 A.&abr B.(stati) C.8ADDF D.$341 (2) 下列属于合法标识符的是 A.*AQabr B.!stati C.A_DEF D.41 (3) 下列属于合法标识符的是 A.?DDDD B.stati C.static D.341 (4) 下列属于合法标识符的是 A._436abr B.=read C.extends D.abstract (5) 下列属于合法标识符的是 A.default B.#W23 C.@adef D.$_341 (6) 下列哪一项不属于变量的组成部分

20 20 Java 程序设计 A. 变量名 B. 变量属性 C. 变量初值 D. 变量大小 (7) 下列哪一个不是 Java 语言中保留字 A.if B.sizeof C.private D.null (8) 下列关于变量作用域的描述中, 不正确的一项是 A. 变量属性是用来描述变量作用域的 B. 局部变量作用域只能是它所在的方法的代码段 C. 类变量能在类的方法中声明 D. 类变量的作用域是整个类 任务 2 Java 基本数据类型 Java 数据类型概述数据类型在计算机语言里面, 是对内存位置的一个抽象表达方式, 可以理解为针对内存的一种抽象的表达方式 不管学习何种计算机语言, 都会存在数据类型的认识, 有复杂的 简单的, 各种数据类型都需要在学习初期去了解,Java 是强类型语言, 所以 Java 对于数据类型的规范会相对严格 Java 数据类型分为两大类, 即基本类型和引用数据类型, 如图 2-1 所示 其中引用数据类型又分为类 接口和数组, 不是本章介绍的重点, 在后面的课程中会详细介绍 基本数据类型 整数类型 (byte,short,int,long) 数值型浮点类型 (float,double) 字符型 (char) 数据类型 布尔型 (boolean) 类 (class) 引用数据类型 接口 (interface) 数组 (array) 图 2-1 Java 数据类型 整数类型整数类型数据值有负整数 零和正整数, 其含义与数学中相同 在 Java 中, 整数类型又细分为四种, 如表 2-3 所示 不同整数类型的差别在于占用内存空间和数据取值范围的不同 一个整数的默认类型为 int 要表示一个整数为 long 型, 在其后加后缀 L 或 l, 如 567L Java 提供了三种进制的整数表示形式 : (1) 十进制数 用 0~9 之间的数字表示的数, 其首位不能为 0, 如 等 (2) 八进制数 用 0~7 之间的数字表示的数, 以 0 为前缀, 如 等

21 项目 2 Java 语言基础 21 (3) 十六进制数 用 0~9 之间数字或 a~f,a~f 之间的字母表示的数, 以 0x 或 0X 为 前缀, 如 0xA3 0X1b 等 其中 a~f 或 A~F 分别表示十进制的 10~15 表 2-3 Java 整数类型 类型 数据位 范围 字节型 byte 到 127 短整整 short 到 整型 int 到 长整型 long 到 浮点类型浮点数类型表示数学中的实数, 即带小数点的数 浮点数类型有两种表示形式 (1) 标准记数法 : 由整数部分 小数点和小数部分组成, 如 等 (2) 科学记数法 : 由尾数 E 或 e 及阶码组成, 也称为指数形式, 如 1.2E4 表示数学中 , 其中 1.2 称为尾数,4 称为阶码 尾数可以是浮点数, 但阶码必须是整数 再如 2.5E-4 表示数学中的 在 Java 中, 有两种浮点数类型 :float( 单精度浮点数 ) 和 double( 双精度浮点数 ), 取值范围及所占用的内存大小如表 2-4 所示 表 2-4 Java 浮点数类型类型数据位范围 单精度浮点 float 32 双精度浮点 double 64 负数取值范围为 E+38 ~ E-45, 正数取值范围为 E-45 ~ E+38 负值取值范围为 E+308 ~ E-324, 正值取值范围为 E-324 ~ E+308 在 Java 中, 一个浮点数默认类型为 double 要表示一个浮点数为 float 型, 在其后加后缀 F 或 f, 如 f 逻辑类型逻辑类型又称布尔类型 逻辑常量值只有两个 : 真 和 假, 在 Java 中, 分别用 true 和 false 来表示 逻辑型变量的类型的关键字为 boolean, 如表 2-5 所示 表 2-5 Java 逻辑类型类型数据位范围 boolean 8 true 或 false

22 22 Java 程序设计 字符类型 字符型 (char) 表示 Unicode 字符, 一个字符占 16 位 字符类型数据有三种表示法 : (1) 用单引号括起来的单个字符, 如 'A' 'a' 等 (2) 用 Unicode 码表示, 前缀是 \u, 如 \u0043 表示 C (3)Unicode 字符集中的控制字符 ( 如回车符 ) 不能通过键盘输入, 需要通过转义字符 表示 转义字符和对应的 Unicode 码如表 2-6 所示 表 2-6 转义字符 转义字符 功能 Unicode 码 \b 退格 \u0008 \t 水平制表 \u0009 \n 换行 \u000a \f 换页 \u000c \r 回车 \u000d 由多个字符组成的字符串序列称为字符串, 字符串用双引号括起来, 如 "red" 就是一个字符串, 关于字符串的详细使用将在后面章节介绍 习题 1.Java 基本数据类型有哪些? 如何进行相互转换? 2. 选择题 (1) 下列描述中正确的一项是 A. 标识符首字符的后面可以跟数字 B. 标识符不区分大小写 C. 复合数据类型变量包括布尔型 字符型 浮点型 D. 数组属于基本数据类型 (2) 下列选项中, 哪一项不属于 Java 语言的简单数据类型 A. 整数型 B. 数组 C. 字符型 D. 浮点型 (3) 下列关于基本数据类型的说法中, 不正确的一项是 A.boolean 是 Java 特殊的内置值, 或者为真或者为假 B.float 是带符号的 32 位浮点数 C.double 是带符号的 64 位浮点数 D.char 是 8 位的 Unicode 字符 (4) 下列关于溢出的说法中, 正确的一项是 A. 一个整型的数据达到整型能表示的最大数值后, 再加 1, 则机器产生上溢, 结果为整型的最大值 B. 一个整型的数据达到整型能表示的最小数值后, 再减 1, 则机器产生下溢, 结果为

23 项目 2 Java 语言基础 23 整型的最小值 C. 实型变量在运算中不会产生溢出的问题 D. 实型变量在运算中和整型一样会产生溢出问题 (5) 下列关于 char 类型的数据说法中, 正确的一项是 A.'\r' 表示换行符 B.'/n' 表示回车符 C.char 类型在内存中占 16 位 D. 凡是 Unicode 的字符都可以用单引号括起来作为 char 类型常量 (6) 下列关于变量作用域的说法中, 正确的一项是 A. 方法参数的作用域是方法外部代码段 B. 异常处理参数的作用域是方法外部代码段 C. 局部变量的作用域是整个类 D. 类变量的作用域是类的某个方法 (7) 下列关于基本数据类型的取值范围描述中, 正确的是 A.byte 类型范围是 -128~128 B.boolean 类型范围是真或者假 C.char 类型范围是 0~65536 D.short 类型范围是 ~32767 (8) 下列关于 Java 语言简单数据类型的说法中, 哪个是正确的 A. 以 0 开头的整数代表 8 进制常量 B. 以 0x 或者 0X 开头的整数代表 8 进制整型常量 C.boolean 类型的数据作为类成员变量的时候, 系统默认值初始为 true D.double 类型的数据占计算机存储的 32 位 (9) 下列关于自动类型转型的说法中, 哪个正确 A.char 类型数据可以自动转换为任何简单的数据类型的数据 B.char 类型数据只能自动转换为 int 类型数据 C.char 类型数据不能自动转换为 boolean 类型数据 D.char 类型不能做自动类型转换 (10) 下列语法中不正确的一个是 A.float a=1.1f; B.byte d=128; C.double c=1.1/0.0; D.char b=1.1f; (11) 下列哪一项属于 Java 语言的复合数据类型 A. 无符号整数类型 B. 整型 C. 联合类型 D. 接口 (12) 下列对整型常量定义的解释中, 正确的是 A.034 代表八进制的数 1C B.034 代表八进制的数 34 C.034 代表十六进制的数 28 D.34L 代表 34 的 64 位长整数 (13) 下列关于整型类型的说法中, 正确的是 A.short 类型的数据存储顺序先低后高 B.Integer.MAX_VALUE 表示整型最大值 C.Long.MIN_VALUE 表示整型最大值

24 24 Java 程序设计 D.long 类型表示数据范围和 int 类型一样 (14) 下列语句中, 不正确的是 A.float e=1.1f; B.char f=1.1f; C.double g=1.1f; D.byte h=1; (15) 下列哪一个不属于 Java 语言的数据类型 A. 指针类型 B. 类 C. 数组 D. 浮点类型 任务 3 Java 运算符与表达式 对数据进行加工和处理称为运算, 表示各种运算的符号称为运算符, 参与运算的数据称为操作数 运算符与操作数的数据类型必须匹配才能进行相应运算, 否则将产生语法错误 根据操作数的个数, 可以将运算符分为单目 双目和多目运算符 单目运算符只对一个操作数运算, 出现在操作数的左边或右边 单目运算符也称为一元运算符 双目运算符对两个操作数运算, 出现在两个操作数的中间 双目运算也称为二元运算符 算术运算符算术运算符完成数学上的加 减 乘 除四则运算 算术运算符包括双目运算和单目运算符 Java 中各种算术运算符的含义如表 2-7 所示 表 2-7 算术运算符运算符含义使用说明 + 加 - 减 ( 或取负 ) 双目运算时表示减运算, 单目运算时表示取负 * 乘 / 除 两个整数相除得到的是商的整数部分, 两个浮点数相除得到的是浮点数的商 % 取模 也叫取余运算符, 得到两个数相除后的余数 ++ 加 1 减 1 可以用在变量的左边, 也可以用在变量的右边 执行 x++ 和 ++x 后 x 的值一样, 都等于 x+1; 但是如果处在表达式中,x++ 表示先将整个 x 的值返回, 再执行增 1 的操作 ;++x 先执行增 1 的操作, 再将整个 x 的值返回 可以用在变量的左边, 也可以用在变量的右边 执行 x 和 x 后 x 的值一样, 都等于 x-1; 但是如果处在表达式中,x 表示先将整个 x 的值返回, 再执行减 1 的操作 ; x 先执行减 1 的操作, 再将整个 x 的值返回 算术运算符应用举例如下 : , 结果为 , 结果为 7 16*3, 结果为 48 36/3, 结果为 12 23/2, 结果为 11 27%4, 结果为 3 依次执行 int i=8;int j=++i; 后 i 的值是 9,j 的值是 9

25 项目 2 Java 语言基础 25 依次执行 int i=8;int j=i++; 后 i 的值是 9, 但 j 的值是 赋值运算符与赋值表达式 1. 赋值运算符 当需要为各种不同的变量赋值时, 就必须使用赋值运算符 =, 这里的 = 不是 等于 的意思, 而是 赋值 的意思, 例如 : a1=3; 这个语句的作用是将整数 3 赋值给变量 a1 使变量 a1 此时拥有值为 3 再看下面的语句 : a1=a1+1; 大家知道, 这种表示方法在数学上是行不通的, 但作为赋值语句, 在以后的程序设计中 是经常用到的, 把 a1 加 1 后的结果再赋值给变量 a1 存放, 若此语句执行前 a1 的值为 3, 则 本语句执行后,a1 的值将变为 4 赋值表达式由变量 赋值运算符和表达式组成, 其定义的格式为 : 变量 = 表达式 ; 其中, 表达式的值的类型应与左侧的变量类型一致或者可以转换为左侧的变量类型 2. 复合赋值运算符 在赋值运算符前加上其他运算符, 就构成了复合赋值运算符, 例如 : int a; a+=8; 第二句等价于 : a=a+8; 同样, 还有其他复合赋值运算符, 即 -= *= /= %= >>= <<= >>>= = = &= 赋值运算符应用举例如下 : a=b=c=d=3; // 表达式以及 a b c d 的值都是 3 a=3+(b=10); // 表达式的值是 13,a 的值是 13,b 的值是 10 a=(b=14)/(c=7); // 表达式的值是 2,a 的值是 2,b 的值是 14,c 的值是 7 a+=a-=a*a; // 等效于 a=a+(a=a-a*a); 关系运算符关系运算符是两个操作数之间的比较运算 关系运算符有 6 种 :>( 大于 ) <( 小于 ) >=( 大于等于 ) <=( 小于等于 ) ==( 等于 ) 和!=( 不等于 ) 6 种关系运算符都可以用于整数 浮点数及字符类型操作数, == 和!= 还可用于布尔类型及字符串类型操作数 字符类型操作数的比较依据是其 Unicode 值, 字符串从左向右以此对每个字符比较 a~z 的 26 个小写字母中, 后面一个字母比其前一个字母的 Unicode 的值大 1,A~Z 的 26 个大写字母中, 后面一个字母比其前一个字母的 Unicode 值大 1,0~9 的 10 个数字中, 后面一个数字比其前一个数字的 Unicode 值大 1 所有小写字母的 Unicode 值大于所有大写字母的 Unicode 值, 所有大写字母的 Unicode 值大于所有数字字符的 Unicode 值 关系运算符的运算结果是布尔类型值 如果关系成立, 结果的值为 true; 否则, 结果的值

26 26 Java 程序设计 为 false 关系运算符的使用方法及功能如表 2-8 所示 表 2-8 关系运算符 运算符 用例 功能 > a>b 如果 a>b 成立, 结果为 true; 否则, 结果为 false >= a>=b 如果 a b 成立, 结果为 true; 否则, 结果为 false < a<b 如果 a<b 成立, 结果为 true; 否则, 结果为 false <= a<=b 如果 a b 成立, 结果为 true; 否则, 结果为 false == a==b 如果 a==b 成立, 结果为 true; 否则, 结果为 false!= a!=b 如果 a b 成立, 结果为 true; 否则, 结果为 false 关系运算符应用举例如下 : int x=4,y=9; boolean a,b,c; a=0>3; b=(x==y); c=(x+2)<=9; 逻辑运算符 //a 的值为 false //b 的值为 false //c 的值为 true 逻辑运算符是对布尔类型操作数进行的与 或 非 异或等运算, 运算结果仍然是布尔 类型值 逻辑运算也称为布尔运算 逻辑运算符有 6 个 :&( 与 ) ( 或 )!( 非 ) ^( 异或 ) &&( 条件与 ) ( 条件或 ) 其中! 是单目运算符, 其他 5 个都是双目运算符 逻辑运算 符真值表如表 2-9 所示 表 2-9 逻辑运算真值表 a b!a a&b,a&&b a b,a b a^b false false true false false false false true true false true true true false false false true true true true false true true false 注 :&& 和 操作符通常也称为短路操作符 它们运算结果分别和 & 与 相同, 但运算规则不同 进行运算时, 先计算运算符左侧表达式的值, 如果使用该值就能得到整个表达式的值, 则跳过运算符右侧表达式的计算, 否则计算运算符右侧表达式, 并得到整个表达式的值 逻辑运算符应用举例如下 : int x=12,y=1; boolean z; z=(x<30)&&(y<0) //z 的值为 flase 假设 x 初值为 67,a 的初值为 0, 则执行 : (1) (x==67) (a++==1) 后,a 的值为 0, 整个表达式的值为 true (2) (x==67) (a++==1) 后,a 的值为 1, 整个表达式的值为 true

27 项目 2 Java 语言基础 位运算符 位运算符是对二进制位进行操作的特定符号, 位运算将数值按照二进制的每一位进行运 算 位运算只能对整型和字符型数据进行操作, 运算结果是整型 位运算有位逻辑运算和移位 运算两种 Java 中各种位运算符的含义如表 2-10 所示 : 表 2-10 逻辑位运算符 运算符 含义 使用说明 ~ 按位取反 每一位二进制位取反,0 取 1,1 取 0 & 按位与 对应位均为 1, 结果的对应位才为 1 按位或 对应位均为 0, 结果的对应位才为 0 ^ 按位异或 对应位相异, 结果的对应位才为 1 << 位左移 按位向左移位, 每一位均向左移位, 右边移出的空位用 0 填充 >>> 无符号右移 也称逻辑右移, 按位向右移位, 每一位向右移位, 左边移出的空位用 0 填充 >> 位右移 也称算术右移, 按位向右移位, 用最高位填充左边移出的空位 位运算符应用举例如下 : int x=5,y=6; boolean z; z=(x&4) && (6 0); // 执行后,z 的值为 true x=x&y; // 执行后,x 的值为 4 说明 : 进行位逻辑运算时, 必须先将数值数据转换为二进制再按位进行运算 int x=16,y,z; y=(x<<2); // 运算结果为 16*22 即 64 y=(128>>1) // 运算结果为 128/2 1 即 64 z=(128<<1) // 运算结果为 128*2 1 即 256 注 :Java 提供两种右移, 算术右移 (>>) 结果为每移一位, 第一操作数被 2 整除一次, 移动的次数由第二个操作数确定 逻辑右移只对位进行操作, 没有算术含义 算术右移不改变 原数的符号, 而逻辑右移不确定 移位运算在运算时, 总是将它们右侧的运算数与左侧运算数的存储长度取余后, 再进行 移位运算 条件运算符 条件运算符为?: 是三目运算符 条件运算的格式为 : 表达式 1? 表达式 2: 表达式 3 运算规则 : 如果表达式 1 的值为真, 则运算结果为表达式 2 的值 ; 如果表达式 1 的值为 假, 则运算结果为表达式 3 的值 达式 注 : 表达式 2 和表达式 3 返回值的类型要相同 ; 表达式 1 通常为逻辑表达式或关系表

28 28 Java 程序设计 条件运算符应用举例如下 : int x=5,y=10,a,b; a=x>y?x+1:x-1; // 该语句执行之后 a 的值为 (x-1) 即 4 b=x<y?y+1:y-1; // 该语句执行之后 b 的值为 (y+1) 即 其他运算符 1. 括号运算符 ( ) 在 Java 语言中, 括号运算符 () 的运算优先级最高, 在表达式中使用时, 用于改变运算符的 运算顺序 2. 下标运算符 [ ] [ ] 是数组下标运算符, 定义数组时用于声明数组元素的个数, 引用数组元素时用于提供数 组的下标值 3. 对象运算符 instanceof 对象运算符 instanceof 用于测试一个指定的对象是否是指定类 ( 或它的子类 ) 的一个实例 化的对象, 若是则返回 true, 否则返回 fale 例如 : String s="123456"; boolean b=s instanceof String; 由于 s 是字符串类 String 的一个实例对象, 所以 b 的值是 true 4. 内存分配运算符 new Java 语言使用 new 运算符为数组和对象分配内存空间 运算符的优先级 当表达式中有多个运算符参与运算, 必须为每种运算符规定一个优先级, 以决定运算符 在表达式中的运算次序 优先级高的先运算, 优先级低的后运算, 优先级相同的由结合性确定 其计算次序 运算符的优先级及结合性如表 2-11 所示 其中, 优先级值越小, 优先级越高 表 2-11 运算符的优先级 优先级运算符结合性 1 [] () 左到右 ~! -( 取负 ) 右到左 3 * / % 左到右 左到右 5 << >> >>> 左到右 6 < > <= >= instanceof 左到右 7 ==!= 左到右 8 & 左到右 9 ^ 左到右 10 左到右 11 && 左到右

29 项目 2 Java 语言基础 29 续表 优先级 运算符 结合性 12 左到右 13?: 右到左 14 = *= /= %= += -= <<= >>= >>>= &= ^= = 右到左 习题 1. 计算下列表达式值 (1)6+4<10+5 (2)4%4+4*4+4/4 (3)(2+1)*2+12/4+5 (4)7>0&&6<6&&12<13 (5)7+7<15 2. 已知 x=5,y=9,f=true, 计算下列各式中变量 z 的值 (1)z=y*x+1 (2)z=x>y&&f (3)z=y+++x (4)z=y+x++ (5)z=~x (6)z=x<y!f (7)z=x^y 3. 选择题 (1) 设 int x=15,int y=27,int z=0; 则 z=x+++--y 运算后,x,y,z 的值各为 A.15,26,41 B.16,26,42 C.15,27,43 D.16,26,41 (2) 设 int x=3,int y=9,int z=0; 则表达式 z=--x*y++-13 运算后,x,y,z 值各为 A.3,10,17 B.3,9,14 C.2,10,5 D.2,9,5 (3) 设 a=3, 则表达式 (--a)<<a 的值是 A.16 B.8 C.24 D.12 (4) 设 x=1,y=2,z=3, 则表达式 y+=z--/++x 的值是 A.3 B.3.5 C.4 D.5 (5) 若 a 的值为 3, 下列程序段被执行后,C 的值是 C=1;if(a>0) if(a>3) C=2; else C=3; else C=4; A.1 B.2 C.3 D.4 (6) 下列语句中不正确的一个是 A.float f = 1.1f; B.byte b = 128; C.double d = 1.1/0.0; D.char c = (char)1.1f; (7) 已知 y=2, z=3, n=4, 则经过 n=n+ -y*z/n 运算后 n 的值为 A.3 B.-1 C.-12 D.-3 (8) 已知 a=2, b=3, 则表达式 a%b*4%b 的值为 A.2 B.1 C.-1 D.-2 (9) 已知 x=2, y=3, z=4, 则经过 z- = --y-x-- 运算后,z 的值为 A.1 B.2 C.3 D.4 (10) 表达式 (12==0) && (1/0 < 1) 的值为 A.true B.false C.0 D. 运行时抛出异常 (11) 下列关于运算符优先级的说法中, 不正确的一个是 A. 运算符按照优先级顺序表进行运算

30 30 Java 程序设计 B. 同一优先级的运算符在表达式中都是按照从左到右的顺序进行运算的 C. 同一优先级的运算符在表达式中都是按照从右到左的顺序进行运算的 D. 括号可以改变运算的优先次序 (12) 在编写 Java 程序时, 如果不为类的成员变量定义初始值,Java 会给出它们的默认值, 下列说法中不正确的一个是 A.byte 的默认值是 0 B.boolean 的默认值是 false C.char 类型的默认值是 '\0' D.long 类型的默认值是 0.0L 任务 4 Java 语句和方法 语句 语句是 Java 最小执行单位, 以分号作为结束符和分隔符 语句决定着程序中运算数据的 顺序和方法, 即语句指示计算机完成特定操作, 并在该操作完成后控制转给另一语句 (1) 空语句 : 单独的一个分号被看作一个空语句, 空语句不做任何事情 (2) 复合语句 : 也称作块语句, 是包含在一对大括号中的任意语句序列 与其他语句用 分号作结束符不同, 复合语句右边的花括号后面不需要分号 从语法上讲, 一条复合语句也是 一条简单语句 在复合语句内可以定义数据, 但被定义的数据仅在定义它的复合语句内起作用 在复合语句中, 可以包含另外的复合语句, 称为复合语句的嵌套, 复合语句可嵌套多层 (3) 表达式语句 : 表达式可当作一个值, 也可当作语句 在 Java 中, 方法调用通常返回 一个值, 一般用在表达式中, 有的方法调用可直接当作语句 (4) 控制语句 : 控制语句完成一定的控制功能, 包括选择语句 循环语句和转移语句 在程序中实现人机交互的功能是通过输入输出操作完成的 程序中需要用户提供的信息 可以通过输入操作来完成, 通过输出操作则可以把计算机处理的结果呈现给用户 标准输入输 出是指通过标准输入 输出设备进行数据的输入和输出操作 键盘和显示器分别是计算机系统 中的标准输入设备和输出设备 下面介绍常用的基本输入输出语句 : 1.System.out.println() 语句和 System.out.print() 语句 说明 : Syetem.out 是系统对象, 代表系统标准输出 print() 和 println() 是 System.out 拥有的输 出方法 Syetem.out.println() 和 System.out.print() 的功能都是输出括号内的字符串, 不同是, print() 方法在输出括号内的字符串后不输出回车, 光标停在字符串最后一个字符的右 边, 而 println() 输完括号内字符串后输出回车, 即光标停在下一行的起始位置 例 2.1 基本输出语句功能示例 public class Outprt public static void main(string args[]) System.out.println( My name is: ); System.out.println( Tom! );

31 项目 2 Java 语言基础 31 该程序输出结果如下 : My name is: Tom! 2.System.in.read() 语句 System.in 也是系统对象, 代表系统的标准输入 read() 是该对象内的字符输入方法 System.in.read() 语句读入字符后返回的是 int 型的变量, 需要用类型转换符 (char) 强制转 换成 char 类型才能赋值给字符型变量 当程序执行到该语句时, 会等待用户输入字符, 当用 户输入字符并按回车后程序才能接收到字符 例 2.2 基本输入语句功能示例 public class Input public static void main(string args[]) char ch; ch=(char)system.in.read(); System.out.println(ch); 该程序功能是将用户从键盘上读入的字符重新输出到显示器上 方法 方法 (method), 在面向过程的语言中称作函数 (function), 在汇编语言中称作子程序, 是一个代码功能块, 实现某个特定的功能 在实际的程序开发中, 方法是一种基础的组织代码 的方式 本部分就介绍方法相关的概念 语法以及实际使用时需要注意的问题 1. 方法声明 在 Java 中, 方法声明格式如下 : [ 修饰符 ] 返回值类型方法名 ([ 参数表 ]) 声明部分语句部分 说明 : (1) 修饰符可以是公共访问控制符 public 私有访问控制符 private 保护访问控制符 protected 等 (2) 返回值类型是指方法功能实现以后需要得到的结果类型, 该类型可以是 Java 语言中 的任意数据类型, 包括基本数据类型和复合数据类型 如果方法功能实现以后不需要反馈结果, 则返回值类型书写为 void (3) 方法名称是一个标识符, 用来代表该功能块, 在方法调用时, 需要方法名称来确定 调用的内容 在 Java 编码规范中, 要求方法的首字母小写, 而方法名称中单词和单词间隔的 第一个字母大写, 例如 bubblesort

32 32 Java 程序设计 (4) 参数列表是声明方法需要从外部传入的数据类型以及个数, 例如求 int 类型绝对值 的方法, 每次需要从外部传入一个 int 类型的值, 这就需要在参数列表部分进行声明, 语法格 式为 : 数据类型参数名 1, 数据类型参数名 2, 分隔 声明参数时, 类型在前, 名称在后, 如果有多个参数时, 参数和参数之间使用逗号进行 参数的值在方法调用时进行指定, 而在方法内部, 可以把参数看作是已经初始化完成的 变量, 直接进行使用 (5) 方法体是方法的功能实现代码 方法体部分在逻辑上实现了方法的功能, 该部分都 是具体的实现代码, 不同的逻辑实现代码区别会比较大 在方法体部分, 如果需要返回结果的值, 则可以使用 return 语句, 其语法格式为 : return 结果的值 ; 或无结果返回时 : return; 如果方法的返回值类型不是 void, 则可以使用 return 返回结果的值, 要求结果值的类型和 方法声明时返回值类型必须一致 如果返回值类型是 void 时, 可以使用 return 语句实现方法 返回, 而不需要返回值 当代码执行到 return 语句时, 方法结束, 所以 return 语句的后续书写 的代码将不会执行, 例如 : return 0; int n = 0;// 语法错误, 永远无法执行到该语句 另外, 如果返回值类型不是 void 时, 需要保证有值返回, 例如下面的方法就有语法错误 : public int test(int a) if(a < 0) return 0; 则该方法的声明代码中, 当 a 的值大于等于零时, 则没有返回值, 这语法上称作返回值丢 失, 这个也是在书写 return 语句时需要特别注意的问题 例 2.3 定义计算平方值的方法 static int square(int x) int s; s=x*x; return (s); 程序分析 : 方法 square() 的返回值类型是 int, 只有一个类型参数 x 方法体的声明部分只 声明了一个 int 类型变量 s, 语句部分包含计算平方的语句和返回结果的语句 2. 方法调用 方法调用, 即执行该方法 调用方法的形式如下 : 方法名 ([ 实际参数表 ]);

33 项目 2 Java 语言基础 33 说明 : (1) 实际参数表是传递给该方法的诸参数, 实际参数简称为实参 实参可以是常量 变 量或表达式 相邻的两个实参之间用逗号隔开 实参的个数 顺序 类型和形参要一一对应 (2) 调用的执行过程是, 首先将实参传递给形参, 然后执行方法体, 当方法体运行结束 后, 从调用该方法的语句的下一句处继续执行 Java 的方法主要有两种调用形式, 一是对于有返回值的方法可以作为表达式或表达式的 一部分调用, 二是可以作为独立语句的方式调用 例 2.4 以方法表达式方式调用方法 public class SquareM static int square(int x) int s; s=x*x; return (s); public static void main(string args[]) int n=5; int result=square(n); System.out.println(result); 程序分析 : 在 main() 方法中声明了变量 n, 并给其赋整型值 5 通过 square(n) 调用方法 square(), 实际参数 n 执行方法 square() 时, 首先将实参 n 的值传递给形参 x, 所以形参 x 的 值为 5, 然后执行 square() 的方法体 当执行到 return 语句时, 方法 square() 结束, 返回的值为 25, 并回到调用该方法体的赋值语句, 通过赋值语句将返回值 25 赋给变量 result 最后执行输 出语句, 输出变量 result 的值 该程序执行的输出结果为 : 25 例 2.5 以方法语句方式调用方法 class areaf static void area(int a,int b) int s; s=a*b; System.out.println(s); public static void main(string args[]) int x=5; int y=3;

34 34 Java 程序设计 area(x,y); 程序分析 : 在 main() 方法中, 首先为变量 x 和 y 分别赋值 5 和 3, 接着以 x 和 y 为实参, 调用方法 rear() 当执行到 println() 时, 方法运行结束, 返回到调用该方法的 main() 方法中 该程序的结果为 : 方法重载 在 Java 中, 同一个类中的 2 个或 2 个以上的方法可以有同一个名字, 只要它们的参数声 明不同即可 在这种情况下, 该方法就被称为重载 (overloaded), 这个过程称为方法重载 (method overloading) 重载方法的要求 : (1) 有相同的方法名, 不同的参数类型或个数 (2) 有相同的方法名, 相同的参数类型但不同的参数类型排列 注意 : 如果方法除返回类型不同外其他完全相同的话, 这不是方法的重载 例如, 以下是正确的方法的重载 : public void a(int a) public int a() public void a(int a,string s) 在以上示例方法中, 方法的名称都是 a, 而参数列表却各不相同, 这些方法实现了重载的 概念 但是仔细观察可以发现, 这些重载的方法的返回值不尽相同, 因为返回值类型和方法的 重载无关 以下的方法不是正确的方法的重载 : public int volume(int a,int b) public void volume(int x,int y) 因为两个方法虽然返回值类型和参数名不同, 但参数个数 类型和顺序完全相同, 即具 有相同的参数表 重载的实现原理 : 编译器根据函数不同的参数表, 对同名函数的名称做修饰, 然后这些 同名函数就成了不同的函数 所以相同函数经过编译后就成了不同的函数 重载方法的调用是 在编译的时候决定的 编译器根据参数的类型决定调用哪个重载方法 4. 参数传递 在方法调用时, 需要根据方法声明传入适当的参数, 通过每次调用方法时传递参数, 极 大地增强了方法的统一性, 避免了方法内部功能代码的重复 但是在实际传递参数时, 如果在 方法内部修改了参数的值, 则调用时使用的变量是否发生改变呢? 例 2.6 参数传递示例 public class TransferValueDemo public static void main(string[] args) int m = 10; int[] a = 1,2,34; test(m,a); System.out.println(m);

35 项目 2 Java 语言基础 35 System.out.println(a[0]); public static void test(int n,int[] t) n = 0; t[0] = 123; 程序运行的结果是 : 则在调用 test 方法时, 同样都是传入参数, 为什么变量 m 的值未改变, 而 a[0] 的值发生了改 变呢? 下面就来说明该问题 在 Java 语言中参数传递存在如下两种规则 : 按值传递 适用范围 :8 种基本数据类型 String 对象 特点 : 在内存中复制一份数据, 把复制后的数据传递到方法内部 作用 : 在方法内部改变参数的值, 外部数据不会跟着发生改变 按址传递 ( 按引用传递 ) 适用范围 : 除 String 以外的所有复合数据类型, 包括数组 类和接口 特点 : 将对象的地址传递到方法内部 作用 : 在方法内部修改对象的内容, 外部数据也会跟着发生改变 按照上面的规则, 则上面的代码中变量 m 的类型是 int, 属于按值传递, 所以在方法内部 修改参数的值时 m 的值不发生改变, 而 a 的类型是数组, 属于按址传递, 所以在方法内部修 改参数的值时, 原始的值发生了改变 以上特性是 Java 语言中的规定, 在语法上无法指定参数传递是按值传递还是按址传递, 但是可以通过下面的变换实现 : 对于按值传递的参数, 如果需要在方法调用以后修改参数的值, 可以利用返回值来实现 对于按值传递的参数, 如果需要在方法内部修改时原来的参数不改变, 则可以在方法 习题 内部重新创建该对象实现 1. 简述 Java 的方法 (method) 2.Java 中什么是控制语句? 它包含哪些语句? 3. 用输入语句输入三种不同类型的变量, 然后把它们输出到显示器上 4. 定义一个求三个整型数中最大值的方法 5. 什么是方法的重载? 为什么除了返回类型不同, 其他都相同不是方法的重载? 任务 5 控制语句 Java 中的程序控制语句分为三类 : 选择语言 循环语句和跳转语句 有了这些控制语句, Java 程序能更加灵活地实现用户所需要的各种功能

36 36 Java 程序设计 选择语句 选择语句提供了这样一种控制机制, 它根据条件或表达式值的不同选择执行不同的语句 序列 当条件成立时, 执行一些程序段 ; 当条件不成立时, 执行另一些程序段, 或不执行 选 择语句有两种 :if 语句和 switch 语句 1.if 语句 if 语句通常都与 else 语句配套使用, 所以我们一般都把它叫做 if-else 语句, 它的语法结构 如下 : if ( 条件表达式 ) 语句 1 [else] 语句 2 说明 : (1) 如果表达式取值为真, 执行语句 1; 否则执行语句 2 其中 else 子句是可选, 如果 没有 else 子句, 在表达式取值为假时, 什么也不执行, 形成单分支选择 (2) 语句 1 和语句 2 可以是一条简单语句, 也可以是复合语句或其他构造语句 if 语句的执行流程图如图 2-2 所示 (a) 单分支 图 2-2 if 语句流程图 (b) 二分支 例 2.7 找出三个整数中的最大值和最小值 public class Max3if public static void main(string args[]) int a=1,b=2,c=3,max,min; if (a>b) max = a; else max = b; if (c>max) max = c; System.out.println("max = "+max); min = a<b?a : b; min = c<min?c : min; System.out.println("min = "+min);

37 项目 2 Java 语言基础 37 程序运行结果 : max=3 min=1 从两种情况中选择其一可以使用一个 if 语句, 而从三种情况中选择其一可以使用两个 if 语句 本例使用了两个并列的 if 语句, 其中第二个 if 语句没有 else 子句 除此之外, 本例还 使用了另一种方法 ( 条件运算符?:) 实现同样的问题 注意 : (1)else 子句不能单独作为语句使用, 它必须和 if 语句配对使用 (2)if 语句的分支中的语句还可以出现 if 语句, 即 if 语句也可以嵌套使用 下面是一个 用 if-else 语句构造多分支程序的例子 : if(a>5)b=1; else if(a>4)b=2; else if(a>3)b=3;... else b=-1; 2.switch 语句 当程序中需要对一个变量的多种不同的值分别执行不同的语句时, 尽管由嵌套的 if 语句 可以实现, 但程序的可读性较差, 这时可使用 switch 语句来实现 switch 语句的定义形式为 : switch ( 表达式 ) 说明 : case 常量 1: 语句 1; [break;] case 常量 2: 语句 2; [break;] case 常量 n: 语句 n; [break;] [default: 语句 n+1;] (1) 表达式的数据类型可以是 byte char short 和 int 类型, 不允许是浮点类型和 long 类型 break 语句和 default 子句是可选项 (2)switch 语句首先计算表达式的值, 如果表达式的值和某个 case 后面的常量值相等, 就执行该 case 子句中的语句序列, 直到遇到 break 语句为止 如果某个 case 子句中没有 break 语句, 一旦表达式与该 case 后面的常量值相等, 在执行完该 case 子句中语句序列后, 继续执 行后继的 case 子句序列, 直到遇到 break 语句为止 如果没有一个常量值与表达式的值相等,

38 38 Java 程序设计 则执行 default 子句中的语句序列 ; 如果没有 default 子句,switch 语句不执行任何操作 例 2.8 显示星期对应的英文字符串 用 week 表示星期, 可用 switch 语句将 week 转换成 对应英文字符串 程序如下 : public class Week1 public static void main(string args[]) int week=1; System.out.print("week = "+week+" "); switch (week) case 0: System.out.println("Sunday"); break; case 1: System.out.println("Monday"); break; case 2: System.out.println("Tuesday"); break; case 3: System.out.println("Wednesday");break; case 4: System.out.println("Thursday"); break; case 5: System.out.println("Friday"); break; case 6: System.out.println("Saturday"); break; default: System.out.println("Data Error!"); 程序运行结果 : week=1 Monday 循环语句 在程序中如果满足一定条件, 需要反复执行某段程序, 这种操作可以通过循环语句实现 Java 的循环语句共有三种 :for 语句 while 语句和 do-while 语句 1.while 语句 while 语句的语法如下 : while( 条件表达式 ) 循环体 说明 : (1) 条件表达式表示循环执行的条件 (2) 循环体可以是一条简单语句, 也可以是复合语句 (3)while 语句的执行过程是 : 计算条件表达式的值, 如果其值是真, 执行循环体 ; 再计 算条件表达式的值, 如果其值是真, 再执行循环体, 形成循环, 直到条件表达式的值变为假, 结束循环, 执行 while 语句的下一条语句 while 语句的流程控制如图 2-3 所示 while 语句的特点是 : 先判断, 后执行

39 项目 2 Java 语言基础 39 图 2-3 while 语句流程图 例 2.9 利用 while 语句计算 1~100 间所有偶数之和 public class WhileExample public static void main(string args[]) int s=0; int i=2; while(i<=100) s=s+i; i=i+2; System.out.println("s="+s); 程序运行结果 : s=2550 注意 : 在 while 语句循环体中, 应有改变循环控制变量的语句, 否则, 若条件表达式永远 为真,while 循环将永远不会结束, 程序会陷入死循环 例如本例中循环继续的条件是 i<=100, 因此在循环体中应该有使 i 增值以最终导致 i>100 成立的语句, 而 i=i+2; 便达到了这个 目的 2.do-while 语句 do-while 语句和 while 语句类似, 不同之处在于 do 语句先将循环体执行一次, 然后再检 测条件, 再决定是否继续执行循环体 do-while 语句的语法如下 : do 循环体 while( 条件表达式 ); 说明 : (1) 条件表达式表示循环执行的条件 (2) 循环体可以是一条语句, 也可以是语句序列 (3)do-while 语句的执行过程是 : 执行循环体, 计算条件表达式的值, 如果其值是真, 再执行循环体, 形成循环, 直到条件表达式的值变为假, 结束循环, 执行 do-while 语句的下 一条语句 do-while 语句的控制流程如图 2-4 所示 do-while 语句的特点是 : 先执行, 后判断 所以

40 40 Java 程序设计 do-while 语句循环体至少执行一次 图 2-4 do-while 语句流程图 例 2.10 计算 1~50 之间的所有奇数和偶数之和 public class DoWhileExample public static void main(string args[]) int i=1,oddsum=0,evensum=0; do if(i%2==0) evensum+=i; else oddsum+=i; i++; while(i<=50); System.out.println("Odd sum="+oddsum); System.out.println("Even sum="+evensum); 程序运行结果如下 : Odd sum=625 Even sum=650 3.for 语句 for 语句是使用比较频繁的一种循环语句, 其语法如下 : for( 表达式 1; 表达式 2; 表达式 3) 循环体 ; 说明 : (1) 表达式 1 的作用是给循环控制变量 ( 及其他变量 ) 赋初值 ; 表达式 2 为条件表达式, 给出循环条件 ; 表达式 3 给出循环变量的变化规律, 通常是递增或递减的 (2) 循环体可以是一条简单语句, 也可以是复合语句 (3)for 语句的执行过程是 : 执行表达式 1 给循环控制变量 ( 及其他变量 ) 赋初值 ; 计算 表达式 2 的值, 如果其值是真, 执行循环体 ; 执行表达式 3, 改变循环控制变量的值 ; 再计算

41 项目 2 Java 语言基础 41 表达式 2 的值, 如果其值是真, 再执行循环体, 形成循环, 直到表达式 2 的值变为假, 结束循 环, 执行 for 语句的下一条语句 for 语句控制流程图如图 2-5 所示 图 2-5 do-while 语句流程图 例 2.11 按 5 度的增量打印出一个从摄氏度到华氏度的转换表 public class TempConversion public static void main (String args[]) int fahr,cels; System.out.println("Celsius Fahrenheit"); for(cels=0;cels<=100;cels+=5) fahr=cels*9/5+32; System.out.println(cels+" "+fahr); 跳转语句 跳转语句可以用来实现执行过程中流程的转移 在前面章节中介绍的 switch 语句中 break 语句就是跳转语句 Java 中实现程序的跳转语句有三个 :continue 语句 break 语句和 return 语句 1.continue 语句 continue 语句只能用在循环语句中, 其语法格式如下 : continue [ 标号 ]; 说明 : (1) 不带标号的 continue 语句的功能是 : 终止当前这一次循环, 跳过本次剩余的语句, 直接进入循环体的下一次循环 在 while 或 do-while 循环中, 不带标号的 continue 语句会使流 程直接跳转至条件表达式 ; 在 for 循环中, 不带标号的 continue 语句会跳转到表达式 3, 计算

42 42 Java 程序设计 修改循环控制变量后再判断条件 (2) 带标号的 continue 语句的功能是 : 使程序的流程直接转到标号注明的循环层次, 一 般用在多层循环结构中, 而标号名应该定义在程序中外层循环语句的前面, 用来标志这个循环 结构 例 2.12 打印整数 1~30 之间能被 9 整除的数 ( 不带标号的 continue 语句的使用 ) public class ContExample1 public static void main(string args[]) for(int i=1;i<30;i++) if(i%9!=0)continue;//next iteration System.out.println(i); 程序运行结果 : 例 2.13 查找整数 1~100 之间的素数 ( 带标号的 continue 语句的使用 ) public class ContExample2 public static void main(string args[]) int i,j; loop1:for(i=1;i<100;i++) for(j=2;j<i;j++) if(i%j==0)continue loop1; System.out.println(i); 该程序的功能是查找 1~100 之间的素数 如果找到整数 i 的一个因子 j, 则说明这个 i 不 是素数, 程序将跳过本次循环剩余语句直接进入下一次循环, 继续检查下一数是否是素数 2.break 语句 break 语句用来跳出 switch 语句的分支语句或跳出循环体结构, 使用格式为 : break [ 标号 ]; 不带标号的 break 语句从它所在的 switch 结构分支语句或最内层的循环体跳出, 带标号的 break 语句用来跳出标号所标识的这层循环体 例 2.14 break 语句的使用 public class BreakExample public static void main(string arg[]) boolean p=true; int i=0; while(p) System.out.println("i="+i); i++;

43 项目 2 Java 语言基础 43 if(i>=10)break; 执行这个程序时, 尽管 while 条件表达式始终为真, 但实际上只循环了 10 次, 原因是当 i 等于 10 时遇到了 break 语句, 使流程跳出了循环 3.return 语句 return 语句用于方法的返回, 当程序执行到 return 语句时, 终止当前方法的执行, 返回到 它的调用者 return 语句的使用格式为 : return [ 表达式 ]; return 语句通常位于一个方法体的最后一行, 格式中的表达式为可选项 当程序执行到带表达式的 return 语句时, 首先计算 表达式 的值, 然后将该值返回调用 该方法的语句 返回值的数据类型必须与方法声明中的返回值类型一致, 如果不一致可以使用 强制类型转换来使类型一致 当方法用 void 声明时, 说明不需要返回值 ( 即返回类型为空 ), 应使用不带表达式的 return 语句, 也可以省略 例 2.15 return 语句的使用 public class ReturnExample final static double PI= ; public static void main(string arg[]) double r1=4.5,r2=10.0; System.out.println(" 半径为 "+r1+" 的面积 ="+area(r1)); System.out.println(" 半径为 "+r2+" 的面积 ="+area(r2)); static double area(double r) return (PI*r*r); 程序中定义了一个 area 方法, 该方法的类型为双精度型,return 语句带有一个双精度数据 类型的返回值 在 main 方法中, 引用了这个方法, 输出了圆的面积 area 方法的功能和有些 语言中的函数一样, 引用时给定一个值, 它相应地会返回一个值 程序运行结果 : 半径为 4.5 的面积 = 半径为 10.0 的面积 = 习题 1. 选择题 (1) 下列关于 for 循环和 while 循环的说法中哪个是正确的 A.while 循环能实现的操作,for 循环也都能实现 B.while 循环判断条件一般是程序结果,for 循环判断条件一般是非程序结果 C. 两种循环任何时候都可替换 D. 两种循环结构中都必须有循环体, 循环体不能为空

44 44 Java 程序设计 (2)Java 程序经常用到 递归, 递归 的基本思想是 A. 让别人反复调用自己 B. 自己反复调用别人 C. 自己反复调用自己 D. 以上说法都不对 (3) 下面哪些选项是正确的 main 方法说明? A.public main(string args[]) B.public static void main(string args[]) C.private static void main(string args[]) D.void main() (4) 下列代码哪几行会出错 : 1) public void modify() 2) int I, j, k; 3) I=100; 4) while(i>0) 5) j= I * 2; 6) System.out.println (" The value of j is " + j ); 7) k=k + 1; 8) I--; 9) 10) A.line 1 B.line 6 C.line 7 D.line 8 (5) 下列 不属于 Java 语言流程控制结构 A. 分支语句 B. 跳转语句 C. 循环语句 D. 赋值语句 (6) 假设 a 是 int 类型的变量, 并初始化为 1, 则下列 是合法的条件语句 A.if(a) B.if(a<<=3) C.if(a=2) D.if(true) (7) 下列说法中, 不正确的一个是 A.switch 语句的功能可以由 if else if 语句来实现 B. 若用于比较的数据类型为 double 型, 则不可以用 switch 语句来实现 C.if else if 语句的执行效率总是比 switch 语句高 D.case 子句中可以有多个语句, 并且不需要大括号 括起来 (8) 设 a b 为 long 型变量,x y 为 float 型变量,ch 为 char 类型变量且它们均已被赋 值, 则下列语句中正确的是 A.switch(x+y) B.switch(ch+1) C.switch ch D.switch(a+b); (9) 下列循环体执行的次数是 int y=2, x=4; while(--x!= x/y) A.1 B.2 C.3 D.4 (10) 下列循环体执行的次数是 int x=10, y=30; do y -= x; x++; while(x++<y--); A.1 B.2 C.3 D.4

45 项目 2 Java 语言基础 判断题 (1) 若循环变量在 for 语句前面已经有定义并具有循环初值, 则初始语句可以为空 ( 分 号不可省略 ) ( ) (2)do while 循环的执行过程是无条件执行循环体一次, 在根据判断条件决定是否继续 执行循环体 ( ) (3)Java 语言的任何类型, 包括整型 浮点型 字符和布尔型都可以使用运算符 == 来比较是否相等, 用运算符!= 来判断是否不等 ( ) (4)Java 语言提供了三个专门的循环控制语句 :for 语句 while 语句和 do while 语句 ( ) (5) 程序中的 break 语句是用于退出 switch 的, 若无则程序将不再比较而是依次执行所 有语句 ( ) (6) 标号提供了一种简单的 break 语句所不能实现的控制循环的方法, 当在循环语句中 遇到 break 后面有标号时, 不管其他控制变量如何, 都会终止标号的循环体 ( ) 3. 参照例 2.1 编写一个简单的 Java 程序, 在屏幕上打印出自己的名字 4. 上网搜索 Java 有哪些常用的集成开发环境, 分析它们各自的优点 5. 编写程序, 计算出 1~100 中所有偶数的和 6. 编写程序, 要求输出成绩等级 A B C D E 在一百分制成绩中的分数段, A 为 90 分以上, B 为 80~90 分, C 为 70~79 分, D 为 60~69 分, E 为 60 分以下 7. 编写一个程序, 随机产生 10 个小写字母, 并判断是元音字母 半元音字母还是复音 字母 任务 6 数组 数组类型属于复合数据类型, 数组是一组相同类型变量数据的集合, 集合中的变量按照 一定的顺序排列, 被称作是数组的元素, 而且这些元素均是由一个统一的数组名和唯一的下 标来确定的 数组中包含的元素的个数, 称为数组的长度 下标的范围是从零到该数组的长 度减 一维数组 使用数组时, 需要声明 创建 初始化和引用这几个步骤 1. 数组的声明 声明一个数组就是要确定数组名, 数组的维数和数组元素的数据类型 数据类型数组名 []; 或数据类型 [] 数组名 ; 说明 : (1) 数组名的命名方法同简单变量, 可以是任意合法的标识符 取名时最好符合 见名 知意 的原则 (2) 数据类型可以是任意的基本类型, 如 int long float double, 也可以是类和接口

46 46 Java 程序设计 (3) 声明数组的时候不可以指定数组长度, 这一点与其他高级语言不同 声明数组就是告诉计算机, 该数组中元素是什么类型的, 例如 : int number[]; double score[]; String[] str1; Point[] P; 2. 创建数组 所谓创建数组, 就是要为数组分配内存空间, 不分配内存是不能存放数组元素, 创建数 组就是在内存中划分出几个连续的空间用于依次存储数组中的数据元素, 其语法形式如下 : 数组名 =new 元素类型 [ 元素个数 ]; 可以把数组声明和数组创建合并, 其语法形式为 : 数据类型 [] 数组名 =new 数据类型 [ 数组长度 ]; 其中数组长度就是数组中存放的元素的个数, 必须是整数 例如 : int []number=new int[5]; String []engname=new String[10]; 可以在一条声明语句中创建多个数组, 例如 : String []s1=new String[3],s2=new String[8],s3=new String[10]; 3. 数组的初始化 数组声明后必须经过初始化才能引用, 也就是数组的元素要被赋予初始值 Java 语言中 数组初始化有下面两种方法 : 用 new 初始化数组 当使用 new 创建数组对象时, 将自动为它的所有元素赋予初始值 基本类型数值数据, 默认的初始值 : 整型为 0 实型为 0.0f( 或 0.0d) 字符型为 '\0' boolean 型为 false 引用类型 元素为 null 例如 : int []number=new int[5]; 这条语句给声明的整型数组 number 开辟了 5 个元素的空间, 并且每个元素的值均被自动 置为 0 赋初值初始化数组 如果在声明数组名时给出了数组的初始值, 程序便会利用数组初始化值创建数组并对它 的各个元素进行初始化 其语法形式如下 : 类型标识符数组名 []= 初值表 ; 初值表用逗号隔开的初始值 用这种方法声明数组时, 无须说明数组长度, 按顺序举出 数组中的全部元素即可, 编译器会通过计算列表中的初始值的个数来确定数组元素的个数 例如, 初始化一个整型数组 : int a[]=11,22,33,44; 上述语句创建了一个包含四个元素的数组 a, 四个元素分别是 11,22,33, 和 数组的引用 声明并初始化数组之后, 就可以在程序中引用数组中的每一个元素了 数组中元素引用 方式为 : 数组名 [ 下标 ] 数组下标必须是 int short byte 或者 char 类型中的一种, 并且从 0 开始计数 元素的个

47 项目 2 Java 语言基础 47 数为数组的长度 (length), 可以通过属性 length 获得 其格式为 : 数组名.length 异常 因此元素下标的最大值为 数组名.length-1, 一旦下标超过最大值, 将会产生数组越界 例 2.16 采用冒泡排序算法将 10 个整数按照从小到大的顺序排列 public class MyArray2 public static void main(string args []) int[] array=11,9,3,5,7,2,8,10,4,15; System.out.print(" 数组排序前的顺序 :"); for(int i=0;i<array.length;i++) int temp; System.out.print(array[i]+" "); for(int i=0;i<array.length-2;i++) for(int j=0;j<array.length-i-1;j++) if(array[j]>array[j+1]) temp=array[j]; array[j]=array[j+1]; array[j+1]=temp; System.out.println(); System.out.print(" 数组排序后的顺序 :"); for(int i=0;i<array.length;i++) System.out.print(array[i]+" "); 运行结果 : 数组排序前的顺序 : 数组排序后的顺序 : 二维数组在 Java 语言中, 多维数组被看做数组的数组 例如, 二维数组就是一个特殊的一维数组, 它的每个元素是一个一维数组 将这个概念进行推广, 就可以得到多维数组 由于在 Java 中, 需要为数组元素分配相应的空间, 分配空间可以在声明数组的同时进行, 也可以用 new 操作符为数组元素分配内存, 这就使得多维数组中每维数组的长度可以不同, 数组空间也不是连续分配的 ( 当然一维数组的空间仍然是连续分配的 )

48 48 Java 程序设计 1. 二维数组声明 二维数组声明的格式如下 : 数据类型数组名 [][]; 或数据类型 [][] 数组名 ; 说明 : (1) 数组名的命名方法同简单变量, 可以是任意合法的标识符 取名最好符合 见名知 意 的原则 (2) 数据类型可以是任意的基本类型, 如 int long float double, 也可以是类和接口 (3) 对于多维数组, 只需在数组名或数据类型的后面放置多对方括号 2. 二维数组的创建 创建二维数组要定义多少行多少列, 创建格式为 : 数组名 =new 类型标识符 [ 行数 ][ 列数 ]; 与一维数组不同的是, 二维数组在分配内存时, 必须告诉编译器二维数组行与列的个数 例如 : int x[][]; x=new [2][3]; // 声明整型二维数组 x // 分配内存给数组供 2 行 3 列的整型数组 x 使用 同样也可以在声明数组的同时给数组规定空间, 将上面的两个语句合并为 : int x[][]=new int[2][3]; 以上语句表示定义了 2 行 3 列的二维数组 x, 数组的每个元素为一个整数 数组中各元素 通过其下标来区分, 每个下标的最小值为 0, 最大值比行数或列数少 1 数组 x 包括 6 个元素, 分别是 :x[0][0],x[0][1],x[0][2],x[1][0],x[1][1],x[1][2], 相当于一个 m 行 n 列的规则矩阵 编译上面的语句后, 内存的分配情况, 如图 2-6 所示 图 2-6 规则矩阵的二维数组内存分配 需要注意的是由于 Java 语言把多维数组看作是数组的数组, 所以并不要求多维数组中的 每一维数组的大小都相同, 也就是说 Java 的多维数组不一定是规则的矩阵形式 例如 : int x[][]; x=new [3][]; 这两句代码表示数组 x 有 3 个元素, 每个元素都是 int 类型的一维数组, 相当于定义了 3 个数组变量, 分别为 :x[0][],x[1][],x[2][] 在这里可以把 x[0],x[1],x[2] 看作变量名, 通 过 new 运算符使其指向它们的数组对象, 如 : x[0]=new int[3]; x[1]=new int[2]; 由此可以看出, 二维数组 x 的每行长度可以是不一样的 编译上面的语句后, 内存的分 配情况, 如图 2-7 所示

49 项目 2 Java 语言基础 49 图 2-7 不规则矩阵的二维数组内存分配 若要取得二维数组的行数, 只要在数组名后加上.length 属性即可, 若要得到数组中某 行元素的个数, 则需要在数组名后加上该行的下标, 再加上.length, 例如 : a.length; // 得到数组 a 的行数 a[0].length; // 得到数组 a 的第 1 行元素个数 3. 二维数组的初始化 同一维数组一样, 二维数组声明后必须经过初始化才能引用 二维数组的初始化有下面 两种方法 : (1) 用 new 初始化数组 当使用 new 创建数组对象时, 将自动为它的所有元素赋予初始值 其规则与一维数组相同 (2) 赋初值初始化数组 可以在声明二维数组的同时, 给数组元素赋初值 通过初值的组数和每组的个数决定二 维数组的行数和每行的列数 其格式如下 : 类型标识符数组名 [][]= 第 1 行初值表, 第 2 行初始值表,, 第 n+1 行初始值表 ; 后面 每个初值表是用逗号隔开的初始值 数组名后的一对方括号也可以移到类型标识符的 例如 : int a[][]=1,2,3,4,5,6; //2 行 3 列的数组 char spring[][]=" 春 "," 夏 "," 秋 "," 冬 ";//2 行分别为 1 列 3 列的数组 需要注意的是同一维数组一样, 在声明二维数组时不能指定其长度, 否则会出现错误 如 : int a[2][3]=1,2,3,4,5,6;// 编译时该行报错 4. 二维数组的引用 与一维数组一样, 二维数组也用数组名和下标值来确定引用的数组元素, 语法格式如下 : 数组名 [ 行号 ][ 列号 ] 数组下标必须是 int short byte 或者 char 类型中的一种, 并且从 0 开始计数 下面这个 例题通过二维数组来打印杨辉三角形 例 2.17 打印杨辉三角 class MyArray1 public static void main(string args[]) int i,j,level=7; int Yang[][]=new int[level][];// 声明二维数组存放杨辉三角形 System.out.println(" 杨辉三角形 "); for(i=0;i<yang.length;i++) Yang[i]=new int[i+1];// 定义二维数组的第 i 行有 i+1 列 Yang[0][0]=1; // 第一个元素为 1

50 50 Java 程序设计 for(i=1;i<yang.length;i++)// 计算杨辉三角形 Yang[i][0]=1; // 杨辉三角形左边都为 1 Yang[i][Yang[i].length-1]=1; // 杨辉三角形右边都为 1 for(j=1;j<yang[i].length-1;j++) Yang[i][j]=Yang[i-1][j-1]+Yang[i-1][j]; for(i=0;i<yang.length;i++)// 输出杨辉三角形 for(j=0;j<yang[i].length;j++) System.out.print(Yang[i][j]+" "); System.out.println(); 运行结果 : 杨辉三角形 习题 1. 从键盘输入 10 个整数, 放入一个一维数组, 然后将前 5 个元素与后 5 个元素对换, 即将第一个元素与第十个元素互换, 将第二个元素与第九个元素互换, 以此类推 位置 2. 建立一个 m 行 n 列的矩阵, 找出其中最小元素所在的行和列, 并输出该值及其行 列 3. 求一个 10 行 10 列整型方阵对角线上元素之积 4. 编写程序, 将字符串 "sbc" "fds" "des" 按字母的升序输出 项目总结 1. 介绍 Java 语言的基本组成, 包括标识符 关键字 变量和常量 运算符和分隔符 2.Java 数据类型包括基本数据类型和引用数据类型, 其中基本数据类型包括整数 浮点 逻辑和字符类型 3. 表达式是由运算符和操作数组成的符号序列, 对一个表达式进行运算时, 要按运算符的优先顺序从高向低进行, 同级的运算符则按结合性方向进行 4.Java 中 if 语句和 switch 语句构成了选择语句,while 语句 do-while 语句和 for 语句构成了各种循环结构, 无论是选择语句还是循环结构都可以嵌套使用 5. 数组是一种常见的数据组织形式, 组成数组的任意数据称为数据的元素 在计算机系统中, 一个数组在内存中占有一段连续的存储空间

51 项目 3 Java 面向对象程序设计 Java 是一门纯面向对象的编程语言 它提供了用来支持面向对象程序设计所需要的一切 要素 本章主要讲述 Java 面向对象程序设计的基础 包括类和对象基本概念 类的声明与组 织 对象的生存与销毁 类的继承 多态性概念及实现方法 抽象类和最终类介绍 面向对象编程具有封装 继承以及多态的特点 类和对象充分体现了抽象性和封装性 而继承性则实现了代码的重用 多态性使程序增加新功能变得更为容易 Java 语言提供了异 常处理机制来帮助程序员处理错误 从而提高程序的质量 加快开发速度 熟练掌握类和对象的定义方法 掌握 static 关键字和 this 关键字的使用方法 熟练掌握包的使用方法及各种访问权限的关系 熟练掌握继承的实现形式 理解 abstract 关键字的作用和意义 熟练掌握接口的定义和使用方法 理解内部类和匿名类的意义 掌握 Java 异常处理机制 任务 1 类与对象 在 Java 语言中 类和对象是面向对象程序设计中的基本概念 也是面向对象程序设计中 编程方法的基础 因此 熟练地创建类 并且熟练地重用或继承系统中已经存在的类 对于 Java 编程人员来说 是最基本的工作 类 类是 Java 的核心 是整个 Java 语言的基本单元 事实上 一个 Java 程序是由多个类组 成的 简单地说 类是一种具有特定功能的抽象的模板 它包含两部分内容 数据和对数据的 操作 1 类的声明 Java 是一种纯面向对象的程序设计语言 每个程序中至少包含一个类 所有数据和操作 都封装在类中 类要先声明 后使用

52 52 Java 程序设计 在 Java 语言中, 类的声明形式如下 : [ 修饰符 ]class 类名 [extends 父类名 ][implements 接口名列表 ] 说明 : 成员变量声明部分成员方法声明部分 (1) 类修饰符可选, 用于指定访问权限, 可用值为 public abstract 和 final 每个修饰符 的基本含义如下 : public: 把一个类声明为公有类, 这意味着该类可以被任意的对象使用或扩展, 无须考虑 它所在的包 需要注意的是,Java 语言规定包含 main() 方法的类必须是公共类 final: 说明为最终类, 即不能再定义子类的类 abstract: 定义一个抽象类, 即不能被实例化的对象的类 (2) 类名一般情况下, 要求首字母要大写 (3) 父类名可选, 用于指定要定义的类继承于哪个父类 (4) 接口名列表可选, 用于指定该类实现的是那些接口 2. 类体 类体是类的主体内容, 类声明之后的一对大括号以及它们之间的内容称作类体 类体的 内容由两部分构成 : 一部分是成员变量的定义, 用来描述属性 ; 一部分是方法的定义, 用来描 述功能 类的成员变量是指那些在类中方法体之外的变量, 其定义形式为 : [ 修饰符 ] 变量类型变量名列表 [= 初值 ]; 说明 : (1) 修饰符可选, 主要是 public protected private static final, 其中 public protected private 三个最多只能出现其中之一, 可以与 static final 组合起来修饰成员变量 (2) 变量类型可以是 Java 中的任意一种类型 既可以是前面讲过的各种简单类型, 也可 以是 Java 中的引用类型, 如类和接口 达式 (3) 变量名列表是一组用逗号隔开的变量名, 其中每个变量都可以带有自己的初始化表 类的成员方法也属于方法, 其定义形式为 : [ 修饰符 ] 返回值类型方法名 ([ 参数表 ]) 关于方法的描述在 节中已经介绍, 这里不在详述 这里需要强调的是类的成员方法 修饰符更丰富, 主要包含 public protected private static final abstract, 其中 public protected private 三个最多只能出现其中之一 ;final 和 abstract 最多只能出现其中之一, 它们可以与 static 组合起来修饰方法 例 3.1 定义一个表示二维平面上点的类 class Point// 类首 private float x,y; public void setpoint(float a,float b) // 类成员变量 // 类成员函数

53 项目 3 Java 面向对象程序设计 53 x=a;y=b; public float getx() return x; public float gety() return y; public String tostring() // 类的成员函数 // 类的成员函数 // 类的成员函数 return "["+x+","+y+"]"; 在 Point 类中, 声明了 x 和 y 两个成员变量, 声明了 setpoint(float,float),getx(),gety() 和 tostring() 几个成员方法 例 3.2 定义一个求立方体体积的类 public class Box // 声明 Box 类 private double width; // 定义成员变量 private double height; // 定义成员变量 private double depth; // 定义成员变量 public void setdemo(double w,double h,double d) // 定义成员方法, 为 Box 对象赋值 width=w; height=h; depth=d; public double volume() // 定义成员方法, 计算 Box 对象的体积 return width*height*depth; 说明 : 此例中定义了一个公共类 Box, 因此本程序存储时的文件名应为 Box.java 在 Box 类中, 定义了三个私有成员变量 width height 和 depth, 用以存储立方体的宽 高和深 ; 定义了一个 公共的无返回值方法 setdemo, 完成对三个成员变量的赋值 ; 定义了一个返回值为 double 类 型的公共方法 volume, 其返回值为立方体的体积 3. 构造方法 每创建一个类的实例都去初始化它的所有变量是乏味的 如果一个对象在被创建时就完 成了所有的初始工作, 将是简单的和简洁的 因此,Java 在类里提供了一个特殊的成员方法, 叫做构造方法 (Constructor) 一个构造方法是对象被创建时初始对象的成员方法 它具有和它所在的类完全一样的名 字 一旦定义好一个构造方法, 创建对象时就会自动调用它 构造方法没有返回类型, 即使是 void 类型也没有 这是因为一个类的构造方法的返回值的类型就是这个类本身 构造方法的 任务是初始化一个对象的内部状态, 所以用 new 操作符创建一个实例后, 立刻就会得到一个 清楚 可用的对象 构造方法是一种特殊的方法, 具有以下特点 (1) 构造方法的方法名必须与类名相同 (2) 构造方法没有返回类型, 也不能定义为 void, 在方法名前面不声明方法类型

54 54 Java 程序设计 象的域 (3) 构造方法的主要作用是完成对象的初始化工作, 它能够把定义对象时的参数传给对 (4) 构造方法不能由编程人员调用, 而要系统调用 (5) 一个类可以定义多个构造方法, 如果在定义类时没有定义构造方法, 则编译系统会 自动插入一个无参数的默认构造方法, 这个构造方法不执行任何代码 默认的构造方法创建的 对象其成员变量的初始化值决定于 Java 的默认值 (6) 构造方法可以重载, 以参数的个数, 类型或排列顺序区分 例 3.3 将例 3.1 中 Point 类用构造函数初始化 class Point private float x,y; public Point(float a,float b) x=a; y=b; public float getx() return x; public float gety() return y; public String tostring() return "["+x+","+y+"]"; 对象 在 Java 中, 对象是用类来创建的,Java 中的一个对象就是类的一个实例 一个对象的生 命周期包括三个阶段 : 生成 使用和销毁 1. 对象创建 类就像 int char 等数据类型一样, 是对事物的抽象 在程序中必须创建类的实例, 即对 象 对象的创建包括声明 实例化和初始化三项工作 对象的声明实际上是给对象命名, 对象声明的一般格式为 : 类名对象名 ; 创建对象的形式如下 : 对象名 =new 类名 ( 参数表 ); 也可以将以上两个语句进行合并, 使用下面的格式创建对象 类名对象名 =new 类名 ( 参数表 ); 说明 : (1) 声明一个对象, 并不会分配一个完整的对象实体所需要的内存空间, 只是将对象名 所代表的变量看成是一个引用变量, 并为它分配所需内存空间, 该变量已有一个特殊的 Java 值 null, 它所占用的空间远远小于一个类的对象实体所需要的空间 (2) 实例化对象就是创建一个对象实体, 给对象实体分配必要的存储空间, 用来保存对 象的数据和代码 实例化后的每个对象实体均占有自己的一块内存区域, 实例化时, 每个对象 实体分配有一个 引用 保存到对象中 引用 实际上是一个指针, 此指针指向对象实体所 占有的内存区域 (3) 当一个对象实体完成了它的使命不再被使用时, 可将该对象实体的引用变量明确赋 值为 null,java 虚拟机将自动回收那些不被任何变量引用的对象实体所占据的内存空间, 以便

55 项目 3 Java 面向对象程序设计 55 节约资源 (4) 同类型的对象之间可以赋值 例如, 创建例 3.2 中 Box 类的一个对象可以写成 : Box mybox=new Box(); 这行代码创建一个 Box 实例, 也被称为 Box 对象实体, 这个 Box 对象实体被赋值给对象 mybox,mybox 也被称为引用变量 注意 : 这行代码在内存中开辟两块存储区 : 一个是给对象 mybox, 一个是给对象实体 Box 对象是引用类型 引用类型是指该类型的变量名表示的是一片连续内存地址的首地址 对象声明后系统将给对象分配一个内存单元, 用以存放对象实体在内存中的首地址, 初始的时 候它的值为 null 创建对象时, 首先在内存中为这个对象实体分配内存空间, 然后将这段内存 的首地址赋值给对象 上述创建 Box 类的具体对象 mybox 的过程可以用图 3-1 表示 (a) 声明对象 (b) 分配空间 (c) 指向首地址 图 3-1 创建对象过程 需要注意的是, 对象作为一种引用类型, 尽管存放的是对象的地址, 但是不能使用该地 址直接操作对象的实际内存, 这与 C/C++ 中的指针不同, 是 Java 语言保证安全性的一种机制 2. 对象的使用 对象的使用就是通过某种手段使一个对象产生它的效能 有两种方式使用对象 : 一是通 过使用对象的成员变量来使用对象 ; 二是通过调用对象的方法来使用对象 两种使用方式的一 般格式和语法如下 : 对象名. 成员变量名 ; // 使用对象的变量对象名. 成员方法名 ([ 参数列表 ]); // 使用对象的方法 例 3.4 创建日期类 Date 并输出 public class Date private int day; private int month; private int year; public void setdate(int d,int m,int y) day=d; month=m; year=y; public void printdate () System.out.println(" 今天是 "+year+" 年 "+month+" 月 "+day+" 日 "); public static void main(string [] args) Date today=new Date(); today.setdate(12,3,2015);

56 56 Java 程序设计 Date thisday; thisday=today; thisday.printdate(); 运行结果 : 今天是 2015 年 3 月 12 日 说明 : 首先定义了 Date 类, 然后在 main 方法中创建了 Date 类的对象 today, 利用对象 today 访 问其成员方法 setdate(), 为其成员变量 day month year 赋值, 最后, 声明了 Date 类的另一 个对象 thisday, 并将 today 对象赋值给 thisday 对象, 使 today 和 thisday 指向同一个对象实体, 调用 thisday 的 printdate() 方法, 输出了同样的日期 3. 对象销毁 在 Java 中, 清除对象的操作是由系统自动完成的, 它提供了一种垃圾回收机制用来清除 无用的对象 Java 垃圾收集器是 Java 语言的一大优点, 它使程序设计人员不必跟踪每个对象, 并在不需要它们时显式释放, 从而使程序设计人员可以全心投入到编程中, 不必过多考虑内存 管理, 同时也避免了在管理内存时由于错误的操作而造成系统的崩溃 如果需要主动释放对象, 或在释放对象时需要执行特定操作, 则在类中可以定义 finalize() 方法 finalize() 方法也称析构方法 当系统销毁对象时, 将自动执行 finalize() 方法 对象也可 以调用 finalize() 方法销毁自己 finalize() 方法没有参数, 也没有返回值 一个类只能有一个 finalize() 方法, 其基本格式如下 : public void finalize() 方法体 ; 4. 对象初始化 当使用 new 运算符实例化一个对象时, 系统自动调用构造方法初始化该对象 如果在类 中没有定义构造函数, 则调用默认的构造方法去初始化该对象, 使用默认值初始化该对象的成 员变量 另外, 还有两种方法可以给成员变量初始化, 一是在定义类时, 给成员变量赋初值 ; 二是可以通过初始化块来初始化成员变量 初始化块就是在类中用一对大括号括起来的代码块, 语法格式如下 : 代码块 使用初始化块可以用来初始化类的成员变量, 其执行顺序是在默认初始化成员变量以及 成员变量声明时赋初值之后, 在使用构造方法初始化之前 因此, 在使用 new 创建并初始化 对象的过程中, 具体执行顺序为以下四步 (1) 给对象实体分配空间, 用默认值初始化该对象的成员变量 (2) 用定义类时给定的初值来初始化成员变量 (3) 初始化块来初始化 (4) 构造方法初始化

57 项目 3 Java 面向对象程序设计 57 其中第二步在声明变量时可以省略初值, 第三步可以省略不写 例 3.5 对象初始化执行顺序演示 public class Student private String stuname=" 王五 "; private int stuage=18; private char stusex='m'; private int stugrade=1; // 使用初始化块初始化 System.out.println(" 使用初始化块初始化 "); stuname=" 张三 "; stuage=18; stusex='m'; stugrade=4; public Student() System.out.println(" 使用无参构造函数初始化 "); // 带形参构造初始化成员变量 public Student(String name,int age,char sex,int grade) System.out.println(" 使用有参构造函数初始化 "); stuname=name; stuage=age; stusex=sex; stugrade=grade; String getstuname()return stuname; int getstugrade()return stugrade; public static void main(string[] args) Student temp=new Student(); System.out.println(temp.getStuName()+" 正在读大学 "+temp.getstugrade()+" 年级 "); Student stu=new Student(" 李四 ",20,'m',3); System.out.println(stu.getStuName()+" 正在读大学 "+stu.getstugrade()+" 年级 "); 程序运行结果 : 使用初始化块初始化使用无参构造函数初始化张三正在读大学 4 年级使用初始化块初始化使用有参构造函数初始化李四正在读大学 3 年级 如果将初始化块全部删除, 程序运行结果 : 使用无参构造函数初始化王五正在读大学 1 年级

58 58 Java 程序设计 使用有参构造函数初始化李四正在读大学 3 年级 如果将初始化块全部删除, 并且全部删除成员变量的初值, 程序运行结果 : 使用无参构造函数初始化 null 正在读大学 0 年级使用有参构造函数初始化李四正在读大学 3 年级 说明 : (1) 初始化块在两种情况下都执行, 对于无参构造方法显示是初始化块中的值, 实际上 是对默认值的覆盖, 对于有参构造方法对初始化块中的值进行覆盖 (2) 在没有初始化块的情况下, 对于无参构造方法显示的是成员变量的初值, 对于有参 构造方法相当于对成员变量的初值进行覆盖 (3) 在既没有初始化块, 又没有初值的情况下, 对于无参构造方法显示的是成员变量的 默认值, 对于有参的构造方法则是对默认值进行覆盖 静态成员 static 关键字的中文意思是静态的, 该修饰符可以修饰成员变量, 成员常量和成员方法 使用该关键字修饰的内容, 在面向对象中 static 修饰的内容是隶属于类, 而不是直接隶属于对 象的, 所以 static 修饰的成员变量一般称作类变量, 而 static 修饰的方法一般称作类方法 另 外,static 还可以修饰代码块, 下面进行详细的介绍 1. 静态成员变量 static 修饰的成员变量也叫静态成员变量, 在类被加载时 static 修饰的成员变量被初始化, 与类关联, 只要类存在, 静态成员变量就存在 一个静态成员变量单独划分一块存储空间, 不 与具体的对象绑定在一起, 该存储空间被类的各个对象所共享 也就是说当声明一个对象时, 并不产生静态成员变量的拷贝, 而是该类所有的实例对象共用同一个静态成员变量 静态成员 变量能在创建任何实例对象之前被访问, 而不必引用任何对象, 也就是说不管创建多少对象, 静态成员变量只占有一块内存 例 3.6 静态成员变量的使用 class Test2 public int x = 1; public static int y = 1; public class Test_static public static void main(string[] args) Test2 t1 = new Test2(); Test2 t2 = new Test2(); Test2.y += 1; t1.x += 1; t1.y += 1; t2.x += 2;

59 项目 3 Java 面向对象程序设计 59 t2.y += 2; System.out.println("t1.x:" + t1.x + ",t1.y:" + t1.y+",test2.y:"+test2.y); System.out.println("t2.x:" + t2.x + ",t2.y:" + t2.y+",test2.y:"+test2.y); 程序运行结果 : t1.x:2,t1.y:5,test2.y:5 t2.x:3,t2.y:5,test2.y:5 说明 : (1)Test2 类中的 x 是一般成员变量, 而 y 是静态成员变量 当 t1 和 t2 生成时,t1 中有 一个独立的变量 x,t2 中也有一个独立的变量 x, 所以 t1.x 和 t2.x 是不同的, 而 t1 和 t2 中的 y 是相同的, 它们是同一个 y, 所以 t1.y 和 t2.y 是相同的 另外, 由于 y 是静态成员变量, 所以 也可以使用 Test2.y 的形式来使用变量 y 每次对 t1.x 和 t2.x 进行的操作是互不影响的, 而对 t1.y 和 t2.y 以及 Test2.y 的操作都使用同一个 y (2) 静态成员变量既可以通过类名来访问, 又可以通过对象名来访问, 但是一般不推荐 使用对象名来访问, 因为这样可能在读代码时造成误解, 以为类变量是实例变量 (3)static 关键字不能修饰成员方法或构造方法内部的变量 2. 静态成员方法 static 修饰的成员方法称作静态成员方法 静态成员方法和一般的成员方法相比, 不同的 地方有两个 : 一是调用起来比较方便, 二是静态成员方法内部只能使用静态的成员变量 所以 一般静态方法都是类内部的独立的功能方法 例 3.7 静态成员变量和方法的使用 class StaticDemo static int a = 42; static int b = 99; static void callme() System.out.println("a = "+a); class Test_static1 public static void main(string[] args) StaticDemo.callme(); System.out.println("b = "+StaticDemo.b); 程序运行结果 : a = 42 b = 99 说明 : (1) 静态方法在类的外部进行调用时不需要创建对象, 可以直接通过类名来访问, 访问 语法为 : 类名. 静态方法名 ( 参数列表...)

60 60 Java 程序设计 或对象名. 静态方法名 ( 参数列表...) (2) 静态方法中不能用 this 和 super 关键字, 不能直接访问所属类的实例变量和实例方 法, 只能访问所属类的静态成员变量和成员方法 3. 静态代码块 static 修饰的代码块也叫静态代码块, 是在类中独立于类成员的 static 语句块, 可以有多个, 位置可以随便放, 它不在任何的方法体内,JVM 加载类时会执行这些静态的代码块, 如果 static 代码块有多个,JVM 将按照它们在类中出现的先后顺序依次执行它们, 每个代码块只会被执 行一次 例 3.8 静态代码块使用 public class Test_static2 private static int a; private int b; static Test_static2.a=3; System.out.println(a); Test_static2 t=new Test_static2(); t.f(); t.b=1000; System.out.println(t.b); static Test_static2.a=4; System.out.println(a); public static void main(string[] args) System.out.println(123); static Test_static2.a=5; System.out.println(a); public void f() System.out.println("hhahhahah"); 程序运行结果 : 3 hhahhahah 说明 :

61 项目 3 Java 面向对象程序设计 61 (1) 在该程序中有 4 个静态代码块,JVM 将按照它们出现的先后顺序分别执行, 其中 public static void main(string[] args) 方法是程序入口, 该方法只有一条语句, 打印输出 123 最后一个静态代码块虽然在 main() 后面, 在运行过程中也要在 main() 方法前执行 (2) 利用静态代码块可以对一些 static 变量进行赋值, 我们回顾一下前面我们举的例子 都有一个 static 的 main 方法, 这样 Java 在运行 main 方法的时候可以直接调用而不用创建实例 4.main() 方法 在 Java 中,main() 方法是 Java 应用程序的入口方法, 也就是说, 程序在运行的时候, 第 一个执行的方法就是 main() 方法, 这个方法和其他的方法有很大的不同, 比如方法的名字必须 是 main, 方法必须是 public static void 类型的, 方法必须接收一个字符串数组的参数等等 main 方法的完整定义语法 : public static void main(string[] args) 说明 : (1)main 方法是个特殊的方法, 它是 JDK/JRE 约定的运行应用程序的启动入口方法, 如果你用 java.exe 运行一个 class, 它就会在这个 class 里面寻找这个方法, 并调用它, 等它返 回了, 程序也就结束了 (2)main 方法必须是一个静态方法, 这样可以在不需要构造类实例的前提下, 直接运行 应用程序 (3) 返回值声明必须为 void, 而不能没有定义或定义为其他类型 (4) 当缺少 String[] args 时, 虽然编译可以通过, 但是运行时无法获取有效的 main 方法, 运行时候会报错 其中方法参数名字 args 可以改变 (5)main 方法只作为 Java 应用程序的入口 Applet 程序不需要 main() 方法, 一般由浏览 器不同方式启动, 其入口程序一般为 init() 方法 final this 和 null 1.final final 关键字是最终的 最后的意思, 在程序中可以用来修饰类 成员变量和方法的声明, 由该关键字修饰的内容都是不可变的 (1)final 数据 final 修饰的数据是常量, 常量既可以出现在类的内部, 也可以出现在方法或构造方法的 内部 在程序中常量只能赋值一次 在程序中, 一般类内部的成员常量为了方便调用, 一般都使用 static 修饰符进行修饰 示 例代码如下 : public class Student int sex; public final static int MALE = 0; public final static int FEMALE = 1; (2)final 方法

62 62 Java 程序设计 final 关键字也可以修饰方法,final 修饰的方法称作最终方法, 最终方法不能被覆盖, 也 就是不能在子类的内部重写该方法 使用 final 修饰方法, 可以在一定程度上提高该方法的执行速度, 因此在调用该方法时, 就不需要进行覆盖的判断了 (3)final 类 final 关键字也可以修饰类,final 修饰的类称作最终类, 最终类不能被继承, 也就是该类 不能有子类 final 类内部的每个方法都是 final 方法 2.this this 关键字代表自身, 在程序中主要的使用用途有以下几个方面 : 使用 this 关键字引用成员变量 使用 this 关键字在自身构造方法内部引用其他构造方法 使用 this 关键字代表自身类的对象 使用 this 关键字引用成员方法 (1) 引用成员变量 在一个类的方法或构造方法内部, 可以使用 this. 成员变量名 这样的格式来引用成员变 量名, 有些时候可以省略, 有些时候不能省略 首先看一下下面的代码 : 例 3.9 使用 this 引用成员变量 public class ReferenceVariable private int a; public ReferenceVariable(int a) this.a = a; public int geta() return a; public void seta(int a) this.a = a; 在该代码的构造方法和 seta 方法内部, 都是用 this.a 引用类的成员变量 因为无论在构造 方法还是 seta 方法内部, 都包含 2 个变量名为 a 的变量, 一个是参数 a, 另外一个是成员变量 a 按照 Java 语言的变量作用范围规定, 参数 a 的作用范围为构造方法或方法内部, 成员变量 a 的作用范围是类的内部, 这样在构造方法和 seta 方法内部就存在了变量 a 的冲突,Java 语言 规定当变量作用范围重叠时, 作用域小的变量覆盖作用域大的变量 所以在构造方法和 seta 方法内部, 参数 a 起作用 这样需要访问成员变量 a 则必须使用 this 进行引用 当然, 如果变量名不发生重叠, 则 this 可以省略 但是为了增强代码的可读性, 一般将参数的名称和成员变量的名称保持一致, 所以 this 的使用频率在规范的代码内部应该很多

63 项目 3 Java 面向对象程序设计 63 (2) 引用构造方法 在一个类的构造方法内部, 也可以使用 this 关键字引用其他的构造方法, 这样可以降低代 码的重复, 也可以使所有的构造方法保持统一, 这样方便以后的代码修改和维护, 也方便代码 的阅读 下面是一个简单的示例 : 例 3.10 使用 this 关键字引用构造方法 public class ReferenceConstructor int a; public ReferenceConstructor() this(0); public ReferenceConstructor(int a) this.a = a; 这里在不带参数的构造方法内部, 使用 this 调用了另外一个构造方法, 其中 0 是根据需要 传递的参数的值, 当一个类内部的构造方法比较多时, 可以只书写一个构造方法的内部功能代 码, 然后其他的构造方法都通过调用该构造方法实现, 这样既保证了所有的构造是统一的, 也 降低了代码的重复 在实际使用时, 需要注意的是, 在构造方法内部使用 this 关键字调用其他的构造方法时, 调用的代码只能出现在构造方法内部的第一行可执行代码 这样, 在构造方法内部使用 this 关键字调用构造方法最多会出现一次 (3) 代表自身对象 在一个类的内部, 也可以使用 this 代表自身类的对象, 或者换句话说, 每个类内部都有一 个隐含的成员变量, 该成员变量的类型是该类的类型, 该成员变量的名称是 this, 实际使用 this 代表自身类的对象的示例代码如下 : 例 3.11 使用 this 代表自身类对象 public class ReferenceObject ReferenceObject instance; public ReferenceObject() instance = this; public void test() System.out.println(this); 在构造方法内部, 将对象 this 的值赋值给 instance, 在 test 方法内部, 输出对象 this 的内 容, 这里的 this 都代表自身类型的对象 (4) 引用成员方法 在一个类的内部, 成员方法之间的互相调用时也可以使用 this. 方法名 ( 参数 ) 来进行 引用, 只是所有这样的引用中 this 都可以省略, 所以这里就不详细介绍了 3.null null 是 Java 中的一个直接量 ( 也称常量 ), 表示类类型 ( 也称引用 ) 变量为空的状态 其

64 64 Java 程序设计 主要用途是 : 一方面 null 作为类类型的实例变量的默认值 ; 另一方面就是在对象清除中, 将 null 赋值给一个对象, 则可 手动 清除该对象 其实,null 还有一个经常使用的地方, 那就 是通常用来作为类类型变量的初始值 如 String str=null, 以这样的形式为成员变量赋初值时, 可使程序醒目, 增强了程序的可读性 另外, 当这样的形式用于局部变量时, 在通常情况下都 是赋初始值所要求的 包 包是类和接口的集合, 是 Java 中组织程序文件的一种树形结构 ; 包的概念与其他程序设 计语言中的函数库和类库比较相似 用户可将功能相似的多个类或接口放在同一包中, 同时也 可在某一个包中声明一个子包, 从而形成了一个包的树形结构 Java 系统提供了很多标准包, 其中包含了大量的类和接口, 用户在编辑时可直接使用它们 1. 包的概念 包是 Java 提供的文件组织方式 一个包对应一个文件夹, 一个包中可以包括很多类文件, 包中还可以有子包, 形成包等级 Java 把类文件放在不同等级的包中 这样一个类文件就会 有两个名字 : 一个是类文件的短名字, 另外一个是类文件的全限定名 短名字就是类文件本身 的名字, 全限定名则是在类文件的名字前面加上包的名字 例如, 把 Hello 这个类放在名为 mypackage 的包中, 则 Hello 这个类的短名字为 Hello.class, 全限定名为 mypackage.hello.class 使用包不仅方便了类文件的管理, 而且扩大了 Java 命名空间 不同的程序员可以创建相 同名称的类, 只要把它们放在不同的包中, 就可以方便地区分, 不会引发冲突 Java 规定, 同一个包中的文件名必须唯一, 不同包中的文件名可以相同 Java 语言中的 这种包等级和 Windows 中用文件夹管理文件的方式完全相同, 差别只是表示方法不同 2. 创建包 要创建一个包, 只需要包含一个 package 命令作为 Java 源文件的第一句即可 在该文件 中定义的任何类将属于指定的包 package 语句定义了一个存储类的名字空间 下面是 package 声明的通用形式 : package 包名 ; 其中 package 是关键字, 包名是包的标识符 package 语句使得其所在文件中的所有的类 都属于指定的包 例如 : package mypackage; 只要将该语句作为源文件的第一句, 就创建了一个名为 mypackage 的包 也可以创建包的层次 为做到这点, 只要将每个包名与它的上层包名用点号. 分隔开 就可以了 一个多级包的声明的通用形式如下 : package 包名 [. 子包名 [. 子子包名 ]]; 例如, 下面的声明在名为 mypackage 的包中创建了它的子包 secondpackage package mypackage.secondpackage; 需要注意的是, 在一个 Java 文件中, 只允许出现一句 package 语句, 因为不可能将某个 类放在两个不同的包中 当多个 Java 源文件中都有 package 语句, 且 package 语句后的包名相 同时, 则表明这些类同属于一个包

65 项目 3 Java 面向对象程序设计 65 例 3.12 将 HelloWorld 程序放入自己定义的包 mypackage 中 package mypackage; public class HelloWorld public static void main(string args[]) System.out.println(" 欢迎来到 Java 世界 "); 将以上代码用文件名 HelloWorld.java 保存在当前路径下 在程序编译时输入 : javac d. HelloWorld.java Java 编译器会在当前目录下生成名为 mypackage 的文件夹, 并且把 HelloWorld.class 文件 放在该文件夹下 例 3.13 创建自己的包 package myfirstpackage; public class Class1 Class2 c=new Class2(); public void prt() System.out.println("myFirstPackage 包中的类 Class1"); System.out.println("Class1 中的对象 c 调用 Class2 中的 prt()"); c.prt(); class Class2 public void prt() System.out.println("myFirstPackage 包中的类 Class2"); 除了上述方法外, 也可以在当前路径下创建一个文件夹 myfirstpackage, 将以上代码用文 件名 Class1.java 保存在 myfirstpackage 文件夹中 这样, 包 myfirstpackage 就产生了 例 3.14 子包的定义 package myfirstpackage.subpackage; public class Class3 public void prt() System.out.println("myFirstPackage.subPackage 包中的类 Class3"); 这里 myfirstpackage 是例 3.13 中定义的包 具体操作时, 在例 3.13 所创建的文件夹 myfirstpackage 中, 创建一个名为 subpackage 的文件夹, 将例 3.14 中的代码以 Class3.java 为 文件名保存在 subpackage 文件夹中

66 66 Java 程序设计 这样就形成了一个包的层次结构,subPackage 是 myfirstpackage 的子包 3. 导入包 将类用包组织起来的另一个目的是为了更好地利用包中的类 对于同一个包中的类, 可 以直接通过类名访问, 但是如果需要使用其他包中的类, 则必须通过以下几种方式 : (1) 全限定名方式访问包中的类 全限定名方式访问类, 即指在访问某个类时, 应写出这个类所属的各层次的包名 若对 应目录结构来说, 也可叫全路径访问 所谓各层次的包, 是因为包可能还有子包, 这就会出现 包的层次 若要访问某个包中的类时, 就需在类名前再加上其所属的各级包名作前缀 例如 : java.awt.graphics 这表示访问 java.awt 包含的类 Graphics 注意 :Java 类库中包名与类名是以首字母大写或小写来区分的, 即包名都是以小写字母 打头, 类名则以大写字母打头 这样, 就可以容易地区分包名与类名 对于自定义包和自定义 类的名称, 也可以按首字母包名小写, 类名大写来区分命名 当然, 这只是一种习惯, 不是语 法规定 如果要使用 java.awt 中的类 Graphics 声明对象 a, 可以写成下面格式 : java.awt.grphics a; 例 3.12 中, 在 mypackage 包中创建了一个名为 HelloWorld 的类 当你试图运行 HelloWorld 时,java 解释器会报告一个 不能发现 HelloWorld 类 的错误消息 这是因为该类现在被保 存在 mypackage 包中 不再能简单用 HelloWorld 来引用 在当前路径下, 该类现在必须叫做 mypackage. HelloWorld 即需要按照下面的格式运行该程序 : java mypackage. HelloWorld (2)import 导入包 包的存在方便了类的管理, 但在使用一个类时必须加上类所在包的包名, 也就是要使用 类的全限定名, 这会带来编程的不便 为此,Java 使用 import 语句将需要使用的类甚至是整 个包导入到当前程序中, 这样在程序中用到这个类时, 只需使用类名就可以了 下面是 import 声明的通用形式 : import 包名 1[. 包名 2. 包名 3 ].( 类名 *); 如果使用一个星号 (*) 指明要引入这个包中所有的 public 类 例如 : import java.util.date;// 引入 java.util.date 类 import java.io.*;// 引入 java.io 包中的所有 public 类 例 3.15 包的导入 import myfirstpackage.*; import myfirstpackage.subpackage.class3; public class PackageTest public static void main(string [] args) Class1 c=new Class1(); Class3 c3=new Class3(); c.prt(); c3.prt();

67 项目 3 Java 面向对象程序设计 67 程序运行结果 : myfirstpackage 包中的类 Class1 Class1 中的对象 c 调用 Class2 中的 prt() myfirstpackage 包中的类 Class2 myfirstpackage.subpackage 包中的类 Class3 (3) 常用系统包简介 Java 提供了强大的应用程序接口, 即 Java 类库 它包含大量已经设计好的工具类, 帮助 程序员进行字符串处理 绘图 数学计算和网络应用等方面的工作 下面简单介绍 Java 核心 类库中常用的组建包 1)java.lang java.lang 是 Java 语言的核心类库, 包含了运行 Java 程序必不可少的系统类, 如 : 基本数据类型 基本数学函数 字符串处理 线程管理和异常处理类等 运行 Java 程序 时, 系统会自动加载 java.lang 包, 即这个包的加载是默认的 2)java.io java.io 提供了对计算机中存储的文件进行相关操作的类, 如文件类 (File) 文件输入流类 (FileInputStream) 文件输出流类 (FileOutputStream) 等, 它需要通过 import 语句完成引入 ( 以下的各类库同样如此 ) 3)java.util java.util 包含了 Java 语言中一些很有用的工具类, 包括向量类 (Vector) 日 期类 (Calendar) 时间类 (Date) 堆栈类 (Stack) 等 4)java.awt java.awt 是 Java 语言用来构建图形用户界面 (GUI) 的类库, 包括许多界面 元素和资源, 如图形类 (Graphics) 窗体类 (Frame) 按钮类 (Button) 等 5)java.applet java.applet 是用来实现运行于 Internet 浏览器中的 Java Applet 的工具类库, 它仅包含一个非常有用的类 :java.applet.applet 6)java.net java.net 是 Java 语言原来实现网络功能的类库, 由支持底层 Internet 编程和实 现 WWW/HTML 应用的类组成 利用 java.net 类库中的类, 开发者可以方便地编写具有网络 功能的程序 7)javax.swing javax.swing 也是构建图形用户界面 (GUI) 的类库, 与 java.awt 相比, 同样包含相关界面元素和资源, 但用 javax.swing 类库形成的界面不会产生同位体, 因此, 它 们具有平台无关性的特点 访问性 访问属性控制 面向对象程序设计提供了访问属性来实现数据的隐藏 不同的访问属性标志着不同的可 通过前面章节的描述, 我们知道 Java 提供了四种访问控制属性, 分别为 : 默认的 ( 缺省 的 ) 公有的 私有的和保护的访问控制属性 这四种访问属性的可访问范围, 如表 3-1 所示 表 3-1 类的访问规则 修饰符同一类同一个包中不同类不同包中的子类不同包中非子类 默认可以可以不可以不可以 public 可以可以可以可以 private 可以不可以不可以不可以 protected 可以可以可以不可以

68 68 Java 程序设计 规则 关于访问控制属性须作如下几点说明 : 没有指定任何访问控制属性时即为默认访问控制属性 这四种访问控制属性都可以用来修饰成员变量 成员方法和内部类 修饰类和接口时只能使用公有或默认访问控制属性, 不能使用私有和保护 构造方法的定义一般使用公有或默认访问控制属性来修饰, 当用私有修饰时在别的类 中将无法用 new 调用相应的构造方法 这四种访问控制修饰属性有且仅有一个出现, 当同时出现两个或多个修饰时编译出错 下面讨论这四种访问属性的访问规则 主要讨论它们用来修饰成员变量和成员方法时的 1. 默认访问属性 在定义类 接口 成员变量 成员方法 构造方法以及内部类时, 若没有指定任何访问 属性控制符, 则它们的访问控制属性即为默认访问控制属性 默认访问控制属性的可访问范围 为同一包, 即具有默认访问控制属性的类 成员变量 成员方法等只能被同一包中的其他类 成员方法等访问, 因此也称默认访问控制属性为包属性 在前面几章中, 成员变量和成员方法的修饰使用了默认访问控制属性, 在此不再举例 2.public 用 public 修饰的类 接口 成员变量 成员方法等具有最宽的可访问范围, 它们可以被 任何包中的任何类所访问, 所以,public 具有最好的开放性 下面是一个关于复数类的例子 一个复数 a+bi 的形式表示, 故定义两个变量 a 和 b 此 处关于复数的运算仅给出加法运算 例 3.16 定义复数类并完成两个复数相加 import complexpackage.complex; public class ComplexTest public static void main(string args[]) Complex c1=new Complex(1,1),c2=new Complex(2,2),c3; c3=c1.add(c2); System.out.println("c1="+c1.a+"+"+c1.b+"i"); System.out.println("c2="+c2.a+"+"+c2.b+"i"); System.out.println("c3="+c3.a+"+"+c3.b+"i"); 将以上代码作为一个文件存储起来, 文件名为 ComplexTest.java package complexpackage; public class Complex public float a,b; public Complex()a=b=0.0f; public Complex(float aa,float bb)a=aa;b=bb; public Complex(Complex c)a=c.a;b=c.b; public Complex add(float aa,float bb)

69 项目 3 Java 面向对象程序设计 69 Complex c=new Complex(); c.a=a+aa; c.b=b+bb; return c; public Complex add(complex cc) Complex c=new Complex(); c.a=a+cc.a; c.b=b+cc.b; return c; 在当前位置创建一个文件名为 complexpackage 的文件夹, 这就是所谓的 complexpackage 包 将以上代码作为一个文件存储在该文件夹中, 文件名为 Complex.java 编译 ComplexTest 类, 然后运行, 可得如下结果 : c1= i c2= i c3= i 由以上的程序可以看出,ComplexTest 类和 Complex 类不在同一包中, 但在 ComplexTest 类中可以直接引用 Complex 中定义的 public 成员变量 a,b 以及 public 成员方法 add() 由于 public 的开放性, 常将成员方法声明为 public 的访问控制属性, 这是封装的对象向 外界提供接口的本质要求 3.private 成员变量和成员方法用 private 修饰时具有很好的封闭性, 这是实现信息隐藏的最好方法 被 private 修饰的成员变量和成员方法只能在同一类中可被访问, 在不同包的类中不可访问, 在同一包的不同类中也不可访问 对于不同包中的情况, 只需将例 3.16 中 Complex 类中的语句 :public float a,b; 改为 :private a,b; 则编译时报错, 系统会给出提示, 在 ComplexTest 类中的几个语句 System.out.println("c1="+c1.a+"+"+c1.b+"i"); System.out.println("c2="+c2.a+"+"+c2.b+"i"); System.out.println("c3="+c3.a+"+"+c3.b+"i"); 直接引用 Complex 类中的 private 成员 a,b 对于同一包中的情况, 将例 3.16 中的两个类合写在一个文件中, 将测试类名改为 Complex- Test2, 再以 ComplexTest2.java 为文件名保存 例 3.17 定义复数类并完成两个复数相加 public class ComplexTest2 public static void main(string args[]) Complex c1=new Complex(1,1),c2=new Complex(2,2),c3; c3=c1.add(c2);

70 70 Java 程序设计 System.out.println("c1="+c1.a+"+"+c1.b+"i"); System.out.println("c2="+c2.a+"+"+c2.b+"i"); System.out.println("c3="+c3.a+"+"+c3.b+"i"); class Complex private float a,b; public Complex()a=b=0.0f; public Complex(float aa,float bb)a=aa;b=bb; public Complex(Complex c)a=c.a;b=c.b; public Complex add(float aa,float bb) Complex c=new Complex(); c.a=a+aa; c.b=b+bb; return c; private Complex add(complex cc) Complex c=new Complex(); c.a=a+cc.a; c.b=b+cc.b; return c; public float geta()return a; public float getb()return b; 编译以上代码, 一开始出现 7 个错误, 分别提示在 ComplexTest2 类中如下几行 : c3=c1.add(c2); System.out.println("c1="+c1.a+"+"+c1.b+"i"); System.out.println("c2="+c2.a+"+"+c2.b+"i"); System.out.println("c3="+c3.a+"+"+c3.b+"i"); 错误都显示出不能访问 Complex 类中的 private 成员 虽然这两个类都在同一包中, 但由于 Complex 中的 a,b 和第二个 add() 方法被声明为 private 的访问控制属性, 故它们在别的类中不可访问 此时, 若做如下改动 : public Complex add(complex cc); System.out.println("c1="+c1.getA()+"+"+c1.getB()+"i"); System.out.println("c2="+c2.getA()+"+"+c2.getB()+"i"); System.out.println("c3="+c3.getA()+"+"+c3.getB()+"i"); 程序运行结果 : c1= i c2= i c3= i

71 项目 3 Java 面向对象程序设计 71 说明 : 在 Complex 类的第二个 add() 方法中, 虽然有直接引用 Complex 类中的 private 成员 a 和 b, 但它们在同一个类中, 因此可以引用 而在 ComplexTest2 类中, 直接引用 Complex 类中的 private 的成员变量 a,b 和 add() 方法是不允许的, 因为它们不在同一类中,private 成员只能被同一类 中成员引用 一般地, 成员变量都声明为 private, 以保持其信息的隐蔽性 而对于成员变量的存取一 般像上面所提供的 geta() 和 getb() 方法一样, 通过添加 public 的公有方法向外界提供访问对象 内部实例变量的统一接口, 这有利于信息安全, 也是面向对象中封装性的其中一个直接目的 所以, 一般将成员方法定义为 public 的访问控制属性, 而将成员变量定义为 private 的访 问控制属性 4.protected protected 访问控制局限在同一个包的其他类以及子类 ( 可以属于其他包 ) 中 protected 与默认访问权限相比要宽松一些, 它的访问范围介于默认属性和 public 属性之间 protected 将访问范围扩展到其子类, 子类与父类关系更紧密一些, 所以在访问控制权限上, 子类可以比 其他不相干的类享有更多的权利 在类的继承一节中将对 protected 的使用规则作详细讨论 习题 1. 类及类成员的访问控制符有哪些? 2. 为什么说构造方法是一种特殊的方法? 特殊在哪里? 构造方法什么时候执行? 3. 静态变量成员有什么特点? 类对象可以访问或修改静态变量成员吗? 4. 对象初始化有哪几种方法? 写出它们的执行顺序 5. 为什么定义 fianl 变量成员时往往声明为 static 的? 6. 选择题 (1) 类成员修饰符修饰的变量只能在本类中被访问 A.protected C.default B.public D.private (2) 在 Java 语言中, 包中的类是自动导入的 A.java.lang C.java.io (3) 给出下面的程序代码 : B.java.awt D.java.applet public class X4_1_3 private float a; public static void m ( ) 如何使成员变量 a 被方法 m( ) 访问? A. 将 private float a 改为 protected float a B. 将 private float a 改为 public float a C. 将 private float a 改为 static float a D. 将 private float a 改为 float a (4) 有一个类 B, 下面为其构造方法的声明, 正确的是 A.void B(int x) B.B(int x) C.b(int x) D.void b(int x)

72 72 Java 程序设计 (5) 下面关于类的说法, 不正确的是 A. 类是同种对象的集合和抽象 B. 类属于 Java 语言中的复合数据类型 C. 类就是对象 D. 对象是 Java 语言中的基本结构单位 (6) 下面关于方法的说法, 不正确的是 A.Java 中的构造方法名必须和类名相同 B. 方法体是对方法的实现, 包括变量声明和合法语句 C. 如果一个类定义了构造方法, 也可以用该类的默认构造方法 D. 类的私有方法不能被其他类直接访问 (7) 定义外部类时不能用到的关键字是 A.final B.public C.protected D.abstract (8) 为 AB 类定义一个无返回值的方法 f, 使得使用类名就可以访问该方法, 该方法头的形式为 A.abstract void f() B.public void f() C.final void f() D.static void f() (9)Java 中访问限定符不包括 A.public B.private C.default D.final (10) 构造方法何时被调用 A. 类定义时 B. 创建对象时 C. 调用对象方法时 D. 使用对象的变量时 7. 填空题 (1) 是对事物的抽象, 而 是对对象的抽象和归纳 (2) 从用户的角度看,Java 源程序中的类分为两种 : 和 (3) 一个类主要包含两个要素 和 (4) 创建包时需要使用关键字 (5) 类中的 方法是一个特殊的方法, 该方法的方法名和类名相同 (6) 如果用户在一个自定义类中未定义该类的构造方法, 系统将为这个类定义一个 构造方法 这个方法没有, 也没有任何, 不能完成任何操作 (7) 静态数据成员被保存在类的内存区的 单元中, 而不是保存在某个对象的内存区中 因此, 一个类的任何对象访问它时, 存取到的都是 ( 相同 / 不同 ) 的数值 (8) 静态数据成员既可以通过 来访问, 也可以通过 直接访问它 (9) 定义常量时要用关键字, 同时需要说明常量的 并指出常量的 (10) 方法体内定义变量时, 变量前不能加 ; 局部变量在使用前必须, 否则编译时会出错 ; 而类变量在使用前可以不用赋值, 它们都有一个 的值 (11)static 方法中只能引用 类型的数据成员和 类型的成员方法 ; 而非 static 类型的方法中既可以引用 类型的数据成员和成员方法, 也可以引用非 类型的数据成员和成员方法 (12) 引用 static 类型的方法时, 可以使用 做前缀, 也可以使用 做前缀

73 项目 3 Java 面向对象程序设计 73 任务 2 类的继承和多态 类的继承继承性是面向对象的核心特征之一, 继承是由已有类创建新类的机制 利用继承机制, 可以创建一个具有共性的一般类, 根据该一般类再创建具有特殊性的新类, 新类继承一般类的数据属性和行为, 并根据需要增加它自己的新属性和新行为 类的继承机制是面向对象程序设计中实现软件可重用性的最重要手段 1. 继承的概念继承是在已存在的类的基础上扩展产生新的类 继承所表达的就是一种对象类之间的相交关系, 它使得某类对象可以继承另外一类对象的数据成员和成员方法 若类 B 继承类 A, 则属于 B 的对象便具有类 A 的全部或部分性质 ( 数据属性 ) 和功能 ( 操作 ), 我们称被继承的类 A 为基类 父类或超类, 而称继承类 B 为 A 的派生类或子类 例如水果和苹果的关系, 苹果继承了水果, 苹果是水果的子类, 则苹果是一种特殊的水果 现欲定义人 (Person) 类和学生 (Student) 类, 拟设立的属性 方法如图 3-2 所示 其中 Student 类的很多属性 方法与 Person 类相同, 新增的部分较少 ( 斜体部分 ) 在已声明 Person 类的情况下, 定义 Student 类时是否需要将其所有属性 方法重写一次? // 功能要增强 图 3-2 Person 类 Student 类的属性和方法有了继承机制, 我们可以让 Student 类继承 Person 类 这样,Student 类就可以利用 Person 已有属性 方法, 定义时只要增加新的属性与方法即可, 具体如图 3-3 所示 面向对象程序设计语言在继承特性中, 有单重继承和多重继承 所谓单重继承, 是指任何一个类都只有一个单一的直接父类 ; 而多重继承是指一个类可以有一个以上的直接父类 C++ 是开发人员熟悉的支持多重继承的面向对象的编程语言, 而 Java 语言出于安全考虑, 仅支持单继承 Java 中是通过接口来实现多重继承的, 关于接口的知识将在后面进行介绍

74 74 Java 程序设计 // 功能要增强 2. 继承的实现 继承的语法形式如下 : [ 修饰符 ] class 子类名 extends 父类名 例如 : // 新增属性 方法, 或改写父类原有方法 class Student extends Person 说明 : 图 3-3 Student 类继承 Person 类的过程 (1) 修饰符说明类的访问权限 (public) 是否为抽象类 (abstract) 或最终类 (final) (2) 如果没有用 extends 指明父类, 则默认继承 Object 根类,Object 类是所有类的直接 父类或间接父类, 有关 Object 类的内容稍后介绍 (3)Java 只支持单一继承, 一个类只能继承一个直接父类 例 3.18 继承于点类的圆类 class Point protected int x, y; public Point() public Point(int xx,int yy)setpoint(xx,yy); public void setpoint(int m,int n)x=m;y=n; public int getx()return x; public int gety()return y; class Circle extends Point private double radius; public Circle(int x,int y,double r)this.x=x;this.y=y;setradius(r); public void setradius(double r)radius=r; public double getradius()return radius; public double getarea()return *radius*radius;

75 项目 3 Java 面向对象程序设计 75 public String tostring() return "Poistion("+x+","+y+")Radius="+radius; public class CircleTest public static void main(string args[]) Circle c=new Circle(50,50,10); System.out.println(c.toString()); c.setpoint(100,100); c.setradius(20); System.out.println(c.toString()); 程序运行结果 : Poistion(50,50)Radius=10.0 Poistion(100,100)Radius=20.0 通过继承, 在 Circle 类中除了有其自身所定义的属性和行为外, 也拥有了从父类继承下来 的属性和行为, 此时,Circle 其实具有了如下一些属性变量和行为方法 :x,y,radius,setpoint(), getx(),gety(),setradius(),getradius(),getarea(),tostring() 等 可见, 通过继承, 子类继 承了父类的特性 ; 同时, 在子类中也可扩展父类中没有的特性, 这既有利于代码的重用, 也不 失灵活性 3. 继承的规则与访问权限 子类继承父类时应遵循以下规则 (1) 子类能继承除父类构造方法以外的所有成员 ( 包括成员变量和成员方法 ) (2) 一个子类只能有一个直接父类, 一个父类可以派生出多个子类 如果 B 继承 A,C 继承 B, 那么 C 是 B 的子类也是 A 的子类,A 和 B 分别是 C 的间接父类和直接父类 (3) 子类可以声明自己的成员变量和成员方法, 也可以重新定义父类成员 在例 3.18 中, 定义了三个类 Point Circle 和 CircleTest Circle 继承于 Point 类,Point 类 是父类,Circle 是子类, 它们的成员列表如表 4-1 所示 而 CircleTest 类是用于对 Circle 作一 简单测试 从表 3-1 中可以看到子类 Circle 除了父类的构造方法外, 其他成员全部继承 表 3-1 例 3.18 中各类的成员列表 类拥有的成员类拥有的成员 Point 属性 : int x,y 方法 : Point() Point(int,int) void setpoint(int,int) int getx() int gety() Circle 属性 : int x,y // 继承父类 Point double radius 方法 : void setpoint(int,int)// 继承父类 Point int getx()// 继承父类 Point int gety()// 继承父类 Point Circle(int,int,double) void setradius(double) double getradius() double getarea() String tostring()

76 76 Java 程序设计 子类虽然继承了父类的成员变量和成员方法, 但并不是对父类所有成员变量和成员方法 都具有访问权限, 即并不是在自己声明的方法中能够访问父类所有成员变量或成员方法 Java 中子类访问父类成员的权限如下 : (1) 父类和子类在同一个包 子类对父类的 private 成员没有访问权限 子类对父类的缺省 protected 和 public 成员具有访问权限 (2) 父类和子类不在同一个包 子类对父类的缺省 private 成员没有访问权限 子类对父类的 proteded 和 public 修饰的成员具有访问权限 子类继承父类时同样要求能够找到父类, 因此父类必须有 package 语句, 子类必须有 import 语句, 父类还必须有 public 修饰符 4. 隐藏与覆盖 子类不仅可以继承父类的所有成员, 还可以对父类的成员变量及方法进行重新定义, 分 别称为成员变量的隐藏和方法覆盖 (1) 成员变量的隐藏 若子类声明了与父类同名的成员变量, 则父类中同名的成员变量将被隐藏起来 所谓隐 藏是指子类拥有了两个名字相同的变量, 一个继承自父类, 另一个由自己定义, 当子类执行继 承自父类的方法时, 处理的是继承自父类的成员变量, 而当子类执行它自己定义的方法时, 其 操作对象则是它自己定义的变量, 而把来自父类的变量 隐藏 起来 例 3.19 计算三个整型数之和 class sum_3 int sum,num1,num2; static int num3; sum_3() num1=num2=num3=0; class sub_sum3 extends sum_3 int sum,num1,num2; static int num3; void sum(int i,int j,int k) num1=i; num2=j; num3=k; sum=num1+num2+num3; public class computing

77 项目 3 Java 面向对象程序设计 77 public static void main(string[] args) sub_sum3 m=new sub_sum3(); m.sum(1,2,3); System.out.println("sum="+m.num1+"+"+m.num2+"+"+m.num3+"="+m.sum); 程序运行结果 : sum=1+2+3=6 在该程序中, 子类中定义了与父类同名的成员变量, 将来自父类的变量隐藏起来, 而计 算出正确的结果 (2) 方法的覆盖 子类可以继承父类中的成员方法, 同时也可以在子类中定义一些新的成员方法, 这些新 添加的成员方法有如下三种情况 在子类中所添加的成员方法是全新的成员方法, 它与父类中的成员方法不存在同名冲 突问题 在子类中, 定义了与父类同名的成员方法, 但参数不一样, 这种情况称为成员方法的 重载 只不过它的重载不是在同一类中来完成的, 而是在子类和父类中共同完成 在子类中定义了与父类中同名同参的成员方法, 这种情况被称为成员方法的覆盖, 这 种情况就像成员变量的隐藏一样 如果在子类中或通过子类的对象直接引用同名的方 法, 则引用的是子类中所定义的成员方法 ; 若要引用父类中的同名方法, 也可以通过 关键字 super 来完成 例 3.20 成员变量隐藏和方法覆盖实例 class A int x=1234; void show() System.out.println("class A:"); class B extends A double x=567.89; void show() super.show(); System.out.println("calss B:"); class C extends B char x='c'; void showabc() System.out.println(super.x);

78 78 Java 程序设计 System.out.println(x); super.show(); show(); void show() System.out.println("class C:"); class OverTest public static void main(string args[]) C cc=new C(); cc.showabc(); 程序运行结果 : c class A: calss B: class C: 说明 : 子类对父类成员变量的隐藏, 对相同变量名重新定义, 类型可以不同 ; 子类对父类成员 方法的覆盖要求方法名称 参数列表 返回值类型必须相同 5.super super 是用来引用父类的成分的关键字 当用 new 创建子类的实例对象的时候, 这个子类 对象里面会有一个父类对象 怎么去引用里面的父类对象呢? 使用 super 来引用 它被广泛地 用于父类构造方法调用 被隐藏的成员变量和被重写方法的访问中 super 的使用格式如下 : super. 成员变量 // 引用父类成员变量 super. 成员方法 ( 参数表 ) // 引用父类成员方法 super( 参数表 ) // 引用父类的构造函数 (1) 调用父类的构造方法 在子类的构造方法中, 可以使用 super 调用指定的直接父类的构造方法 例 3.21 super 关键字调用父类构造方法 class Student public int stu_no; public String stu_name; public String specialty; Student() System.out.print("create a student"); Student(String name)

79 项目 3 Java 面向对象程序设计 79 this(); System.out.print(":"+name); class GraduateStudent extends Student public String tutor; public String spec_direction; GraduateStudent(String name) super(name); System.out.println(",he is a graduatestudent"); GraduateStudent(String name,string tutor,string spec) this(name); System.out.println("his tutor:"+tutor+",his specialty direction:"+spec); public class SuperTest public static void main(string[] args) GraduateStudent stu1=new GraduateStudent("John"); GraduateStudent stu2=new GraduateStudent("Tom","Bill","computer"); 程序运行结果 : create a student:john,he is a graduatestudent create a student:tom,he is a graduatestudent his tutor:bill,his specialty direction:computer 说明 : super() 表示当前类的父类的构造方法,this() 表示当前类的构造方法, 无论是 super() 还是 this(), 如果出现在构造方法中, 必须是第一条语句 (2) 调用被隐藏的父类成员变量 对于父类中被子类隐藏的成员变量, 同样可以使用 super 访问 例 3.22 super 关键字访问被隐藏的变量 class Student public int stu_no=1001; public String stu_name; public String specialty; class GraduateStudent extends Student public int stu_no=1002; // 隐藏父类成员变量 public String tutor;

80 80 Java 程序设计 public String spec_direction; void printstu_no(int stu_no) // 形参变量隐藏成员变量 System.out.println("stu_no is "+stu_no); // 形参变量 System.out.println("stu_no of Student is "+super.stu_no); System.out.println("stu_no of GraduateStudent is "+this.stu_no); public class SuperTest1 public static void main(string[] args) int stu_no=1003; // 局部变量隐藏成员变量 System.out.println("stu_no is"+stu_no); GraduateStudent stu1=new GraduateStudent(); stu1.printstu_no(1004); 程序运行结果 : stu_no is1003 stu_no is 1004 stu_no of Student is 1001 stu_no of GraduateStudent is 1002 说明 : 如果要访问父类被隐藏的成员变量 stu_no, 用 super.stu_no 表示 ; 同一类中, 成员变量被 局部变量隐藏时用 this.stu_no 访问 (3) 调用被覆盖的父类方法 当子类的某个方法覆盖了父类的一个方法, 在子类的范围内, 父类的方法不可见, 此时 如果需要访问父类的方法, 可以通过 super 关键字来实现, 而通过 this 关键字则可以访问当前 类中的方法, 但常常被省略 例 3.23 super 关键字访问被覆盖的方法 class Student public int stu_no=1001; public String stu_name; public String specialty; void printstu_no() System.out.println("stu_no is "+stu_no); class GraduateStudent extends Student public int stu_no=1002; public String tutor; public String spec_direction; void printstu_no()

81 项目 3 Java 面向对象程序设计 81 super.printstu_no();// 调用父类被覆盖的方法 System.out.println("stu_no of GraduateStudent is "+this.stu_no); void printstu_no(int stu_no) this.printstu_no();// 调用同类中的方法 System.out.println("stu_no of GraduateStudent is "+stu_no); public class SuperTest2 public static void main(string[] args) GraduateStudent stu1=new GraduateStudent(); stu1.printstu_no(1003); 程序运行结果 : stu_no is 1001 stu_no of GraduateStudent is 1002 stu_no of GraduateStudent is 1003 说明 : (1) 如果父类中的成员变量和方法被定义为 private 类型 那么子类永远无法访问它们, 如果试图采用 super 访问父类的 private 的成员变量和方法, 就会导致编译错误 (2)super 不能多重使用, 除了直接父类, 其他祖先类的成员不可以越级使用 例如, super.super() 就是非法操作 6. 子类构造方法 子类的构造方法主要是用来完成对该类中的所有成员变量进行初始化, 子类中的变量包 含从父类继承下来的变量和自己新增加的变量 这些成员变量的初始化进行分工, 其中父类构 造方法负责父类成员变量的初始化工作, 子类构造方法负责子类中新增变量的初始化工作 为 了实现这种分工, 在子类的构造方法中可以用 super 关键字来调用父类的构造方法, 格式如下 : 子类构造方法 ([ 参数表 ]) super([ 父类构造方法参数 ]); // 调用父类构造方法其他 ;// 对本类新增变量进行初始化或进行其他处理 方法 例 3.21 中在子类 GraduateStudent 的构造方法中, 使用 super 直接调用父类 Student 的构造 继承条件下构造方法的调用规则如下 : 子类没有定义构造方法, 创建对象时系统将先自动调用父类的无参构造方法, 再调用 子类默认的无参构造方法 子类的构造方法中没有通过 super 显式调用父类构造方法, 则系统会自动调用父类的 无参构造方法 子类的构造方法中通过 super 显式调用父类的构造方法, 那将执行父类相应构造方法, 且该调用语句必须是子类构造方法的第一个可执行语句

82 82 Java 程序设计 子类构造方法可利用 this 调用本类其他的构造方法, 此时 this 应是除 super 外的第一 个可执行语句 现在回过头看例 3.18, 在子类 Circle 的构造方法中没有显式调用父类的构造方法, 根据规则 在创建子类对象时会自动调用父类的无参构造方法, 因此, 在 Point 类中定义一个无参构造方法 public Point(), 虽然它什么也没有干, 也必须保留 如果删除这个无参构造方法, 则编译报错 例 3.24 继承条件下的子类构造方法调用顺序 public class A extends F5 private F1 f1 = new F1(); private F2 f2 = new F2(); A() System.out.println("A"); public static void main(string args[]) A a = new A(); class F1 F1() System.out.println("F1"); class F2 F2() System.out.println("F2"); class F3 F3() System.out.println("F3"); class F4 extends F3 F4() System.out.println("F4"); class F5 extends F4 F5() System.out.println("F5"); 程序运行结果 : F3 F4 F5 F1 F2 A 说明 : (1) 有父类的话先调用父类的构造方法, 如果父类之上还有父类, 则继续向上调用, 直 到最顶层父类为止, 再一层一层向下调用其他父类的构造方法 (2) 当调用完最靠近子类的父类的构造方法时, 执行本子类成员对象的构造方法 (3) 最后执行子类的构造方法 多态 多态是面向对象程序设计方法的重要特征之一, 其基本含义是 拥有多种形态 所谓多态

83 项目 3 Java 面向对象程序设计 83 性, 具体指在程序中用相同的名称来表示不同的含义 例如 : 用同一方法名来表示不同的操作 Java 语言的多态可以分为静态多态性和动态多态性两类 静态多态又称为编译时多态, 主要体现为方法的重载, 即编译时根据对象类型和参数决定调用哪一个方法 ; 动态多态性又称 为运行时多态, 在编译时不能确定调用方法, 只有在运行时才能确定调用方法 静态多态性即 方法的重载在前面已经介绍, 这里不再赘述, 下面主要介绍动态多态性 1. 对象的转型 在例 3.23 程序中,GraduateStudent 和 Student 类虽然存在继承关系, 但从数据类型的角度 看, 它们是两种不同的数据类型, 而下面的语句将父类引用指向子类对象实例 : Student s=new GraduateStudent(); 这个是允许的, 由 Java 的类型转换机制决定的 Java 的类型转换分为两种, 其一是基本 数据类型的转换, 分为自动转换和强制转换 ; 其二是对象转型机制, 分为向下转型和向上转型 把父类的引用可以指向子类的对象, 称为向上转型 向上转型机制自觉遵守对象转换机 制, 不需要显式声明 例如 : Student s=new GraduateStudent(); 这容易理解, 研究生是学生中一种, 或者说一个研究生也是一个学生 把子类的引用指向父类的对象, 称为向下转型 例如 : GraduateStudent g=new Student(); 这样的语句编译时会报错, 因为这种转换不会自动进行 实际中我们通常可以说, 一个研究生 也是一个学生, 但一个学生不一定是一个研究生 当需要子类引用指向父类对象时, 需要作强 制类型转化 例如 : Student s=new Student(); GraduateStudent g=(graduatestudent)s; 例 3.25 对象转型实例 class TestCasting public static void main(string[] args) Animal a = new Animal("a"); Cat c = new Cat("c","cEyesColor"); Dog d = new Dog("d","dForlorColor"); System.out.println(a instanceof Animal); //true System.out.println(c instanceof Animal); //true System.out.println(d instanceof Animal); //true System.out.println(a instanceof Dog); //false a = new Dog("d2","d2ForlorColor"); // 父类引用指向子类对象, 向上转型 System.out.println(a.name); // 可以访问 //System.out.println(a.folorColor); //!error 不可以访问超出 Animal 自身的任何属性 System.out.println(a instanceof Animal); // 是一只动物 System.out.println(a instanceof Dog); // 是一只狗, 但是不能访问狗里面的属性 Dog d2 = (Dog)a; // 强制转换 System.out.println(d2.folorColor); // 将 a 强制转换之后, 就可以访问狗里面的属性了

84 84 Java 程序设计 class Animal public String name; public Animal(String name) this.name = name; class Dog extends Animal public String folorcolor; public Dog(String name,string folorcolor) super(name); this.folorcolor = folorcolor; class Cat extends Animal public String eyescolor; public Cat(String name,string eyescolor) super(name); this.eyescolor = eyescolor; 程序运行结果 : true true true false d2 true true d2forlorcolor 说明 : (1) 一个基类的引用不可以访问其子类对象新增加的成员 ( 属性和方法 ) (2) 可以使用 引用变量 instanceof 类名 来判断该引用型变量所 指向 的对象是否 属于该类或该类的子类 2. 动态多态性 由于类之间的继承关系, 导致父类和子类中存在同名的方法, 对于子类对象, 在运行时 应该选择哪个方法来执行呢? 此时的规则是 : 由对象本身的实际类型决定 在 Java 语言中, 实现运行时多态技术的条件 : 多态性出现在继承关系中 子类覆盖父类的成员方法 通过父类的引用对子类对象进行调用 例 3.26 运行时多态实例 // 父类 Shape 建立了一个通用接口 也就是说, 所有 ( 几何 ) 形状都可以描绘和删除 class Shape void draw()

85 项目 3 Java 面向对象程序设计 85 void erase() // 子类覆盖了 draw 方法, 为每种特殊类型的几何形状都提供了独一无二的行为 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 BindingTest 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();break; for(int i=0;i<s.length;i++)s[i].draw(); 程序运行结果 : Circle.draw() Square.draw() Square.draw() Circle.draw() Triangle.draw() Circle.draw() Triangle.draw()

86 86 Java 程序设计 Square.draw() Circle.draw() 说明 : 在 main() 方法的循环体中, 每次根据运行时产生的随机数生成指向一个 Circle Square 或 Triangle 的引用, 而在编译的时侯是无法知道 s 数组元素的具体类型到底是什么, 根据声明只 知道获得一个单纯的 Shape 的引用 当然, 由于几何形状是每次随机选择的, 所以每次运行都 可以有不同的结果 引进多态技术之后, 尽管子类的对象千差万别, 但都可以采用 父类引用. 方法名 ([ 参数 ]) 统一方式来调用, 在程序运行时能根据子对象的不同得到不同的结果 这种 以不变应万变 的形式可以规范 简化程序设计, 符合软件工程的 一个接口, 多种方法 思想 抽象类 假设 鸟 是一个类, 它可以派生出若干个子类如 鸽子 燕子 麻雀 天鹅 等, 那么是否存在一只实实在在的鸟, 它既不是鸽子, 也不是燕子或麻雀, 更不是天鹅, 它不是任 何一种具体种类的鸟, 而仅仅是一只抽象的 鸟 呢? 答案很明显, 没有 鸟 仅仅作为一 个抽象的概念存在着, 它代表了所有鸟的共同属性, 任何一只具体的鸟儿都同时是由 鸟 经 过特殊化形成的某个子类的对象 这样的类就是 Java 中的抽象类 既然抽象类没有具体的对象, 定义它又有什么作用呢? 仍然以 鸟 的概念为例 : 假设 需要向别人描述 天鹅 是什么, 通常都会这样说 : 天鹅是一种脖子长长, 姿态优美的候鸟 ; 若是描述 燕子, 可能会说 : 燕子是一种长着剪刀似的尾巴, 喜在屋檐下筑窝的鸟 ; 可见 定义是建筑在假设对方已经知道了什么是 鸟 的前提之上, 只有在被进一步问及 鸟 是什 么时, 才会具体解释说 : 鸟是一种长着翅膀和羽毛的卵生动物, 而不会在一开始就把 天鹅 描述成 是一种脖子长长, 姿态优美, 长着翅膀和羽毛的卵生动物 这实际是一种经过优化 了的概念组织方式 : 把所有鸟的共同特点抽象出来, 概括形成 鸟 的概念 ; 其后在描述和处 理某一种具体的鸟时, 就只需要简单地描述出它与其他鸟类所不同的特殊之处, 而不必再重复 它与其他鸟类相同的特点 这种组织方式使得所有的概念层次分明, 非常符合人们的思维习惯 Java 中定义抽象类是出于相同的考虑 由于抽象类是它的所有子类的公共属性的集合, 所以使用抽象类的一大优点就是可以充分利用这些公共属性来提高开发和维护程序的效率 在了解抽象类之前, 先来了解一下抽象方法 抽象方法是一种特殊的方法 : 它只有声明, 而没有具体的实现 抽象方法的声明格式为 : abstract void fun(); 在 Java 中, 凡是用 abstract 修饰符修饰的类称为抽象类 它和一般的类不同之处在于 : (1) 如果一个类中含有未实现的抽象方法, 那么这个类就必须通过关键字 abstract 进行 标记声明为抽象类 (2) 抽象类中可以包含抽象方法, 但不是一定要包含抽象方法 它也可以包含非抽象方 法和成员变量, 就像一般类一样 (3) 抽象类是没有具体对象的概念类, 也就是说抽象类不能实例化为对象 (4) 抽象类必须被继承 子类为它们父类中的所有抽象方法提供实现, 否则它们还是 抽象类

87 项目 3 Java 面向对象程序设计 87 定义一个抽象类的格式如下 : abstract class ClassName...// 类的主体部分 下面我们通过例子, 学习抽象方法的使用 例 3.27 使用抽象类 abstract class fatherclass abstract void abstractmethod(); void printmethod() System.out.println("fatherClass function! "); class childclass extends fatherclass void abstractmethod() System.out.println("childClass function! "); public class mainclass public static void main(string args[]) childclass obj=new childclass(); obj.printmethod(); obj.abstractmethod(); 程序运行结果 : fatherclass function! childclass function! 习题 1. 什么是抽象类? 什么是抽象方法? 为什么 abstract 修饰符和 final 修饰符不能同时修饰 同一个类 2. 什么是类的继承性? 什么是类的多态性? 3. 选择题 (1) 子类对象能否直接向其父类赋值? 父类对象能否向其子类赋值? A. 能, 能 B. 能, 不能 C. 不能, 能 D. 不能, 不能 (2)Java 语言类间的继承关系是 A. 单继承 B. 多重继承 C. 不能继承 D. 不一定

88 88 Java 程序设计 (3) 如果局部变量和成员变量同名, 如何在局部变量作用域内引用成员变量? A. 不能引用, 必须改名, 使它们的名称不相同 B. 在成员变量前加 this, 使用 this 访问该成员变量 C. 在成员变量前加 super, 使用 super 访问该成员变量 D. 不影响, 系统可以自己区分 (4) 下面说法不正确的是 A. 抽象类既可以做父类, 也可以做子类 B.abstract 和 final 能同时修饰一个类 C. 抽象类中可以没有抽象方法, 有抽象方法的类一定是抽象类或接口 D. 声明为 final 类型的方法不能在其子类中重新定义 (5) 区分类中重载方法的依据是 A. 形参列表的类型和顺序 B. 不同的形参名称 C. 返回值的类型不同 D. 访问权限不同 4. 编写一个类, 求出三个变量 a,b,c 的最大值 5. 编写一个 学生 类 该类拥有属性 : 校名 学号 姓名 性别 出生日期 方法包含设置姓名和成绩 6. 在上题的基础上, 编写学生的子类 : 大学生 大学生 类除了拥有学生的属性和方法以外, 还有其自己的属性和方法 属性包括系 专业 ; 方法包含设置系别和专业 7. 设计一个圆类 Circle, 具有属性 : 圆心坐标 x 和 y 及圆半径 r, 除具有设置及获取属性的方法外, 还具有计算周长的方法 Perimeter() 和计算面积的方法 Area() 再设计一个圆柱体类 Cylinder,Cylinder 继承自 Circle, 增加了属性 : 高度 h, 增加了设置和获取 h 的方法 计算表面积的方法 Area() 和计算体积的方法 Volume() 创建 Cylinder 的类对象, 显示其所有属性, 计算并显示其面积和体积 任务 3 接口与内部类 接口抽象类可以有抽象方法, 也可以有普通方法, 但是有抽象方法的类必须是抽象类 如果抽象类中的方法都是抽象方法, 那么这些抽象方法组成的特殊的抽象类就是所说的接口 1. 接口的概念在 Java 中, 出于简化程序结构的考虑, 不支持类间的多重继承而只支持单重继承, 即一个类至多只能有一个直接父类 然而在解决实际问题的过程中, 在很多情况下仅仅依靠单重继承不能完整地表述问题的复杂性, 因此需要其他机制作为辅助 为例既能实现多重继承的功能, 又不希望引入多重继承的复杂性和低效率,Java 提供了接口 (interface) 接口是用来实现多重继承功能的一种结构, 它在语法上与类相似, 接口中有属性和方法, 接口可以形成继承关系, 但接口中只包含常量和方法的声明, 没有变量和方法的实现 接口是一种概念性的模型, 有助于类的层次结构的设计

89 项目 3 Java 面向对象程序设计 89 接口与类相似点 : 一个接口可以有多个方法 接口文件保存在.java 结尾的文件中, 文件名使用接口名 接口的字节码文件保存在.class 结尾的文件中 接口相应的字节码文件必须在与包名称相匹配的目录结构中 接口与类的区别 : 接口不能用于实例化对象 接口没有构造方法 接口中所有的方法必须是抽象方法 接口不能包含成员变量, 除了 static 和 final 变量 接口不是被类继承了, 而是要被类实现 接口支持多重继承 现实中也有很多接口的实例, 比如说串口电脑硬盘,Serial ATA 委员会指定了 Serial ATA 2.0 规范, 这种规范就是接口 Serial ATA 委员会不负责生产硬盘, 只是指定通用的规范 希 捷 日立 三星等生产厂家会按照规范生产符合接口的硬盘, 这些硬盘就可以实现通用化, 如 果正在用一块 160G 日立的串口硬盘, 现在要升级了, 可以购买一块 320G 的希捷串口硬盘, 安装上去就可以继续使用了 2. 接口的定义 接口的声明语法格式如下 : [public] interface 接口名 [extends < 父接口列表 >] [public] [static] [final] 常量 ; [public] [abstract] 方法 ; 说明 : (1)public 修饰的接口可以被所有的类使用, 被所有接口继承 没有 public 修饰的接口 只能被同一个包中的其他类使用, 被同一个包中的接口继承 (2) 接口中所有的常量默认使用 public static final, 所以在定义常量时可以省略不写 (3) 接口中所有的方法默认为 public abstract, 在声明时也可以省略不写 例 3.28 定义接口 interface Pet public abstract void speak(); 3. 接口的实现 类实现接口的语法形式如下 : [ 修饰符 ] class < 类名 > [extends < 父类名 >] [implements < 接口列表 >] 类体 其中, 接口列表可以包括多个接口名称, 各名称间用逗号分隔 要实现一个接口的非抽 象类必须重写接口中定义的方法并添加方法体, 同时实现接口的非抽象类可以使用接口中定义 的任何常量

90 90 Java 程序设计 下面看一个简单的例 3.29, 它定义了接口 Pet 图 3-4 描述了接口 Pet 和实现接口的类之 间的关系, 带箭头的虚线表示实现关系 图 3-4 接口 Pet 例 3.29 接口 Pet interface Pet public abstract void speak(); class Dog implements Pet public void speak() System.out.println("Woof"); class Cat implements Pet public void speak() System.out.println("Meow"); class Bird implements Pet public void speak() System.out.println("Tweedle"); public class TestAnimals2 public static void main(string args[]) Pet mypets[] = new Pet[4]; mypets[0] = new Bird(); mypets[1] = new Cat(); mypets[2] = new Bird(); mypets[3] = new Dog(); for (int index=0; index<4; index++) mypets[index].speak(); 程序运行结果 : Tweedle Meow

91 项目 3 Java 面向对象程序设计 91 Tweedle Woof 在这个例子中类 Bird Cat 和 Dog 与 Pet 不具有父子关系, 而分别是实现了接口 Pet 的类 一个接口的定义由关键字 interface 开始, 包括一套 public abstract 方法, 接口也可以包 含 public final static 数据 一个期望使用接口的类必须将自己声明为 implements 接口, 并重写接口定义的每个方法, 保证参数列表及返回类型与接口中所定义的相一致 简单说, 强 制要求实现接口的类覆盖接口的方法 如果一个类没有覆盖接口定义中的全部方法, 那么这个类就成为抽象类, 必须被声明为 abstract 4. 接口的应用 下面是一个完整实例程序例 3.30, 图 3-5 描述了程序中的类关系 其中符号 + 表示 public 属性, 符号 - 表示 private 属性, 符号 # 表示 protected 属性, 用 protected 标示的类或 成员的可访问性介于 public 和 private 之间 如果 Cat 希望同时具有 Animal 和 Pet 的特性, 却因为不能同时具有两个父类而无法实现 现在 Pet 表现为接口,Cat 可以在继承 Animal 的同时, 实现接口接口 Pet Bird 同时具有 Pet 和 Flyer 的行为特征,Bird 类可以同时实现接口 Pet 和 Flyer, 并且继承 Animal 类 Spider 类 从 Animal 类中继承过来 这些类的关系图如图 3-5 所示 其中虚线表示实现接口, 实线表示 继承关系 图 3-5 父类 Animal 和子类 Cat Bird Spider 及接口 Pet Flyer 的关系 例 3.30 两个接口的实现 abstract class Animal protected int legs; protected Animal(int legs) this.legs = legs; abstract void eat();

92 92 Java 程序设计 public void walk() System.out.println("This animal walks on " + legs + " legs."); interface Pet public abstract void setname(string name); public abstract String getname(); public abstract void speak(); public abstract void play(); interface Flyer void takeoff(); void land(); void fly(); class Bird extends Animal implements Pet,Flyer private String name; public Bird() super(2); // 这行是必需的 public void setname(string name) this.name = name; public String getname() return name; public void speak() System.out.println("speak:Tweedle"); public void play() System.out.println("play:Birds fly on the sky all day."); public void takeoff() System.out.println("Birds take off at 6 o'clock."); public void land() System.out.println("Birds landing every evening."); public void fly() System.out.println("Birds fly on the sky all day."); public void walk() //Bird 覆盖父类 Animal 的方法 walk() System.out.println("walk:Birds, of course, can't walk; they fly."); public void eat() //Bird 覆盖父类 Animal 的抽象方法 eat() System.out.println("eat:Birds eat corn."); public void buildnest()//bird 创建自己的方法 buildnest() System.out.println("buildNest:Birds buid the nest within the branches."); public void layeggs() //Bird 创建自己的方法 layeggs() System.out.println("layEggs: Birds lay eggs."); class Cat extends Animal implements Pet private String name; public Cat(String name) super(4); this.name = name;

93 项目 3 Java 面向对象程序设计 93 public Cat() this(""); public void setname(string name) this.name = name; public String getname() return name; public void speak() System.out.println("speak:Meow"); public void play() System.out.println("play:"+name+" likes to play with string."); public void eat() //Cat 覆盖父类 Animal 的抽象方法 eat() System.out.println("eat:Cats like to eat spiders and mice."); class Spider extends Animal public Spider() super(8); public void eat() //Spider 覆盖父类 Animal 的抽象方法 eat() System.out.println("eat:Spiders catch flies in their webs to eat."); public class TestAnimals3 public static void main(string[] args) Bird b = new Bird(); Cat c = new Cat("Fluffy"); Animal ab = new Bird(); Animal as = new Spider(); Pet p = new Cat(); // 允许定义接口类型的引用 // 示例对接口的不同实现 b.speak(); // 调用 Bird 类对接口 Pet 的实现 b.play(); // 调用 Bird 类对接口 Pet 的实现 b.eat(); b.walk(); p.speak(); // 通过接口类型的变量 p 访问 Cat 实现的接口方法 c.play(); // 调用 Cat 类对接口 Pet 的实现 c.eat(); c.walk(); as.eat(); as.walk(); ((Bird)ab).speak(); ab.walk(); 程序运行结果 : speak:tweedle play:birds fly on the sky all day. eat:birds eat corn. walk:birds, of course, can't walk; they fly.

94 94 Java 程序设计 speak:meow play:fluffy likes to play with string. eat:cats like to eat spiders and mice. This animal walks on 4 legs. eat:spiders catch flies in their webs to eat. This animal walks on 8 legs. speak:tweedle walk:birds, of course, can't walk; they fly. 说明 : (1) 接口是抽象的, 不允许有实体方法, 也不允许实例化 (2) 可以定义接口类型的变量, 其值可以是任何实现该接口的类的实例, 可以通过该变 量访问该类所实现的接口方法 在例 3.30 中 Pet p = new Cat(); 定义了接口引用, 并指向了 一个实现该接口的 Cat 类对象实例 如下 : (3) 一个类可以实现多个接口 在例 3.30 中 Bird 类实现了两个接口, 分别为 Pet 和 Flyer (4) 接口可以继承其他接口, 实现接口合并的功能 例如, 在例 3.30 中修改 Flyer 接口 interface Flyer extends Pet void takeoff(); void land(); void fly(); 将 Bird 类的定义修改为 : class Bird extends Animal implements Flyer 回调 接口回调是多态的另一种体现 接口回调是指 : 可以把使用某一个接口的类创建的对象 的引用赋给该接口声明的接口变量中, 那么该接口变量就可以调用被类实现的接口中的方法 当接口变量调用被类实现的接口中的方法时, 就是通知相应的对象调用接口的方法, 成为接口 回调 不同的类在使用同一接口时, 可能具有不同的功能体现 即接口的方法体不必相同, 因 此接口回调可能产生不同的行为 例 3.31 接口回调的实现 interface ShowMessage void Show(String s); class TV implements ShowMessage public void Show(String s) System.out.println(s); class PC implements ShowMessage

95 项目 3 Java 面向对象程序设计 95 public void Show(String s) System.out.println(s); public class Test12 public static void main(string[] args) ShowMessage sm; sm=new TV(); sm.show(" 夏普电视!"); sm=new PC(); sm.show("lenovo ThinkCenter!"); 程序运行结果 : 夏普电视! Lenovo ThinkCenter! 现在有两个类, 一个是 A, 一个是 B, 回调就是 A 去调用 B 中的某个方法, 然后 B 又回 调 A 中的某个方法, 这个方法就是回调方法, 回调的机制是与接口分不开的 也就是 A 类实 现一个接口, 并实现接口的方法 ( 回调方法 ),B 类得到这个回调方法的引用, 然后去调用这 个回调方法 例 3.32 Java 回调的实现 // 回调接口, 相当于老师的办公室地址 interface CallBack public void callback(string homework); // 老师作为上层应用身份出现 class Teacher implements CallBack public void callback(string homework) System.out.println(" 老师, 这是我的作业..."+homework); // 学生作为下层身份出现 class Student // 相当于老师的办公室 private CallBack callback = null; public void setcallback(callback callback) this.callback = callback; public void dohomework() System.out.println(" 写作业...( 半个小时后 ) 作业写完了 "); // 告诉老师, 作业写完了并且交到了办公室 callback.callback(" 数学卷子一张 ");

96 96 Java 程序设计 public class Test13 public static void main(string[] args) Teacher t = new Teacher(); Student s = new Student(); // 传入回调对象老师 s.setcallback(t); s.dohomework(); 程序运行结果 : 写作业...( 半个小时后 ) 作业写完了老师, 这是我的作业... 数学卷子一张 例 3.32 就是回调的一个例子 CallBack 是一个回调接口, 就是办公室地址, 老师是处于 上层的, 它的学生是不知道也不了解的, 于是老师要想让学生将作业交到办公室, 也就得告诉 学生办公室的地址, 所以老师这个类必须去实现这个回调接口 在老师的这个类里, 也可以去调用学生类的某个方法, 当然在这个例子中, 并没有去调 用学生类中的方法, 而仅仅是实现了回调函数, 告诉学生他的作业收到了 内部类 1. 内部类的概念和使用 类与类除了继承关系, 还存在嵌套关系, 即一个类可以包含另一个类 被包含的类称为 内部类 (Inner class), 包含内部类的类称为外部类或顶层类 内部类是在一个类的内部嵌套定 义的类, 它可以是其他类的成员, 也可以在一个语句块的内部定义, 还可以在表达式内部匿名 定义 例如, 可以创建如下内部类 : class OuterClass class InnerClass 在类 OuterClass 中定义了一个类 InnerClass, 这是个内部类 编译时会生成两个字节码文 件, 分别是 OuterClass.class 和 OuterClass$InnerClass.class 为什么需要内部类? 典型的情况是, 内部类继承自某个类或实现某个接口, 内部类的代 码操作创建其的外部类的对象 所以你可以认为内部类提供了某种进入其外部类的窗口 使用内部类最吸引人的原因是 : 每个内部类都能独立地继承自一个 ( 接口的 ) 实现, 所 以无论外部类是否已经继承了某个 ( 接口的 ) 实现, 对于内部类都没有影响 如果没有内部类 提供的可以继承多个具体的或抽象的类的能力, 一些设计与编程问题就很难解决 从这个角度 看, 内部类使得多重继承的解决方案变得完整 接口解决了部分问题, 而内部类有效地实现了 多重继承 内部类与外部类一样, 具有相同的定义格式 由于内部类被看作是类的成员, 因此内部 类的修饰符与类成员的修饰符相似, 可以用 private protected 等修饰 内部类的类修饰还可以 是关键字 static, 此时内部类称为静态内部类

97 项目 3 Java 面向对象程序设计 97 在外部类的类体中使用内部类时, 可以像使用普通类那样进行声明 定义和创建该内部 类的对象, 其格式为 : 内部类名. 对象名 =new 内部类名 ( 构造方法参数列表 ); 如果在外部类之外的其他类的类体中使用该外部类的内部类, 则声明 定义及创建该内 部类的对象格式为 : 外部类. 内部类对象名 =new 外部类名 ( 参数列表 ).new 内部类名 ( 参数列表 ); 或外部类. 内部类对象名 = 外部类对象名.new 内部类名 ( 参数列表 ); 访问内部类的静态成员属性和静态方法格式为 : 外部类. 内部类. 静态属性名 ; 外部类. 内部类. 静态方法名 ( 参数列表 ); 外部类对象访问内部类非静态成员属性和方法格式为 : 外部类对象名. 内部类. 静态属性名 ; 外部类对象名. 内部类. 静态方法名 ( 参数列表 ); 在 Java 中, 内部类一般来说包括这四种 : 成员内部类 局部内部类 匿名内部类和静态 内部类 下面就先来了解一下这四种内部类的用法 如下 : 2. 成员内部类 成员内部类, 就是作为外部类的一个成员存在, 与外部类的属性和方法并列 其特性 (1) 成员内部类可以无条件访问外部类的所有成员属性和成员方法 ( 包括 private 成员和 静态成员 ) (2) 当成员内部类拥有和外部类同名的成员变量或者方法时, 会发生隐藏现象, 即默认情 况下访问的是成员内部类的成员 如果要访问外部类的同名成员, 需要以下面的形式进行访问 : 外部类.this. 成员变量外部类.this. 成员方法 (3) 在外部类中如果要访问成员内部类的成员, 必须先创建一个成员内部类的对象, 再 通过指向这个对象的引用来访问 (4) 成员内部类不能含有 static 的变量和方法 因为成员内部类需要先创建了外部类, 才能创建它自己的 (5) 成员内部类用 private 修饰, 则只能在外部类的内部访问, 如果用 public 修饰, 则任 何地方都能访问 ; 如果用 protected 修饰, 则只能在同一个包下或者继承外部类的情况下访问 ; 如果是默认访问权限, 则只能在同一个包下访问 例 3.33 成员内部类使用 class Circle// 外部类 private double radius = 0; public static int count =1; public Circle(double radius) this.radius = radius; getdrawinstance().drawsahpe();// 必须先创建成员内部类的对象, 再进行访问 System.out.println(" "); // 建议使用 getxxx() 来获取成员内部类, 尤其是该内部类的构造函数无参数时

98 98 Java 程序设计 public Draw getdrawinstance() return new Draw(); class Draw // 内部类 private double radius=5.0;// 内部类成员 public void drawsahpe() System.out.println(radius); // 内部类的 private 成员 System.out.println(Circle.this.radius); // 外部类的 private 成员 System.out.println(count); // 外部类的静态成员 public class TestInner public static void main(string[] args) // 第一种方式 : Circle outter = new Circle(10); Circle.Draw inner = outter.new Draw(); // 必须通过 Outter 对象来创建 inner.drawsahpe(); // 第二种方式 : Circle.Draw inner1 = outter.getdrawinstance();// 外部类成员方法创建 程序运行结果 : 局部内部类 局部内部类是定义在一个方法或者一个代码块中的类, 它和成员内部类的区别在于局部 内部类的访问仅限于方法内或者代码块中 其特性为 : (1) 局部内部类前面不能有访问修饰符 (2) 它有它自己的作用域, 超出了这个范围就无效 ( 无法使用 ) (3) 它可以访问外部类的所有成员 ( 因为外部类的成员对该方法可见 ) (4) 定义在方法体中的内部类只能访问该方法的 final 变量 (5) 定义在代码块中的内部类只能使用在该代码块中定义的 final 变量 例 3.34 局部内部类使用 public class Outer1 private int s = 100; private int out_i = 1; public void f(final int k)

99 项目 3 Java 面向对象程序设计 99 final int s = 200; int i = 1; final int j = 10; class Inner// 定义在方法内部 int s = 300; // 可以定义与外部类同名的变量 //static int m = 20; // 不可以定义静态变量 Inner(int k) inner_f(k); int inner_i = 100; void inner_f(int k) // 如果内部类没有与外部类同名的变量, 在内部类中可以直接访问外部类的实例变量 System.out.println(out_i); // 可以访问外部类的局部变量 ( 即方法内的变量 ), 但是变量必须是 final 的 System.out.println(j); //System.out.println(i); // 如果内部类中有与外部类同名的变量, 直接用变量名访问的是内部类的变量 System.out.println(s); // 用 this. 变量名访问的也是内部类变量 System.out.println(this.s); // 用外部类名.this. 内部类变量名访问的是外部类变量 System.out.println(Outer1.this.s); new Inner(k); public static void main(string[] args) // 访问局部内部类必须先有外部类对象 Outer1 out = new Outer1(); out.f(3); 程序运行结果 : 静态内部类 静态内部类也是定义在另一个类里面的类, 只不过在类的前面多了一个关键字 static 静 态内部类是不需要依赖于外部类的, 这点和类的静态成员属性有点类似 其特性如下 : (1) 要创建静态内部类的对象, 并不需要其外部类的对象 (2) 创建静态内部类对象 : 外部类. 静态内部类对象名 = 外部类. 静态内部类 ( 构造方法参数列表 ); (3) 静态内部类可以有非静态成员 (4) 静态内部类只能访问外部类的静态成员

100 100 Java 程序设计 例 3.35 静态内部类使用 public class Outer2 private static int i= 1; private int j = 10; public static void outer_f1() System.out.println(" 外部类的静态方法 outer_f1()"); public void outer_f2() System.out.println(" 外部类的一般方法 outer_f2()"); // 静态内部类可以用 public,protected,private 修饰 // 静态内部类中可以定义静态或者非静态的成员 static class Inner static int inner_i = 100; int inner_j = 200; static void inner_f1() // 静态内部类只能访问外部类的静态成员 ( 包括静态变量和静态方法 ) System.out.println("Outer2.i=" + i); outer_f1(); void inner_f2() // 静态内部类不能访问外部类的非静态成员 ( 包括非静态变量和非静态方法 ) //System.out.println("Outer2.i="+j); 错误 //outer_f2(); 错误 System.out.println(" 静态内部类的一般方法 inner_f2()"); public void outer_f3() // 外部类访问内部类的静态成员 : 内部类. 静态成员 System.out.println(Inner.inner_i); Inner.inner_f1(); // 外部类访问内部类的非静态成员 : 实例化内部类即可 Inner inner = new Inner(); inner.inner_f2(); public static void main(string[] args) new Outer2().outer_f3(); 程序运行结果 : 100 Outer2.i=1 外部类的静态方法 outer_f1() 静态内部类的一般方法 inner_f2() 5. 匿名内部类 在某些情况下, 我们只需要内部类的一个对象, 而不需要该类的名字时使用匿名内部类 简单地说 : 匿名内部类就是没有名字的内部类

101 项目 3 Java 面向对象程序设计 101 在使用匿名内部类时, 要记住以下几个原则 : (1) 匿名内部类不能有构造方法 (2) 匿名内部类不能定义任何静态成员 方法和类 (3) 匿名内部类不能是 public,protected,private,static (4) 只能创建匿名内部类的一个实例 (5) 一个匿名内部类一定是在 new 的后面, 用其隐含实现一个接口或实现一个类 (6) 因匿名内部类为局部内部类, 所以局部内部类的所有限制都对其生效 例 3.36 匿名内部类使用 public class Main public static void main(string[] args) InnerTest inner = new InnerTest(); Test t = inner.get(3); System.out.println(t.getI()); class Test // 父类 private int i; public Test(int i) this.i = i; public int geti() return i; class InnerTest // 用于内部类的测试 public Test get(int x) return new Test(x) // 创建匿名内部类, public int geti() return super.geti() * 10; ; 程序运行结果 : 30 习题 1. 接口与抽象类有何相同之处和不同之处? 2. 定义接口的意义是什么? 对实现接口的类有什么要求? 3. 什么是回调? 4.Comparable 接口和 Comparator 接口在功能和使用上有什么不同?

102 102 Java 程序设计 5. 选择题 (1) 下面关于接口的说法中不正确的是 A. 接口中所有的方法都是抽象的 B. 接口中所有的方法都是 public 访问权限 C. 子接口继承父接口所用的关键字是 implements D. 接口是 Java 中的特殊类, 包含常量和抽象方法 (2)Java 语言接口间的继承关系是 A. 单继承 B. 多重继承 C. 不能继承 D. 不一定 (3) 一个类实现接口的情况是 A. 一次可以实现多个接口 B. 一次只能实现一个接口 C. 不能实现接口 D. 不一定 6. 判断题 (1) 内部类可以访问外部类的任何变量, 包括私有的 ( ) (2) 匿名类可以有构造方法, 声明时候不能带参数 ( ) (3) 抽象方法可以是 static 的 ( ) (4) 构造部没有任何返回类型, 哪怕是 void 也不行 ( ) (5) 只要类中显式地定义一个构造方法, 那么 Java 不会再为你定义一个默认的构造方法 ( ) (6) 构造方法可以被继承 ( ) (7) 方法的参数变量不能是 final 的 ( ) (8) 一个 Java 类可以有多个父类, 这称作多继承 ( ) 任务 4 Java 异常处理 异常概述程序设计中有两种错误 : 一种是语法错误, 也称为编译错误 语法错误在编译时会检测出来, 在编程阶段解决, 并不会生成运行代码 另一种是运行错误, 它在运行时才会发生 异常通常指的是运行错误, 即程序运行过程中出现的影响语句正常运行顺序的意外或特殊事件 当程序违反了 Java 语言的语义约束时,Java 虚拟机 JVM 将错误以异常的形式发送给应用程序 导致异常的有多种情况, 大致可以分为以下几类 1. 代码逻辑错误用整数除以 0 试图访问超过数组界限的数组元素 数据格式不正确 访问一个空对象等都可能导致代码逻辑错误 2. 用户输入错误用户输入一个不存在的网址, 或要求输入一个数字, 而用户偏偏输入一个字母, 都会导致用户输入错误 3. 硬件设备错误内存空间不够时要求分配内存, 而硬盘物理空间不够, 打印机没纸了, 软驱坏了等, 这

103 项目 3 Java 面向对象程序设计 103 些都属于硬件设备错误 异常分类当异常状态出现在 Java 语言的方法中时, 方法就产生一个异常对象, 它包含异常的类型和错误出现时程序的状态信息, 并将产生的异常对象交给运行系统处理 在 Java 术语中, 产生一个异常对象以及把它转交给运行系统称为抛出异常 (Throw Exception) Java 语言中的每个异常都是一个对象, 它是 Throwable 类或其子类的对象 异常对象将信息从出现异常的位置传递到捕获并处理异常的位置 想要知道异常的分类, 了解 Java 异常的继承关系, 了解 Java 异常的层次结构, 如图 3-6 所示 图 3-6 Java 异常层次结构图 所有异常都继承自 java.lang.throwable 类,Throwable 类有两个直接子类,Error 类和 Exception 类 Error 类是 Throwable 类的子类, 是 Java 应用程序本身无法恢复的严重错误, 应用程序不需要捕获 处理这些严重错误 当程序发生这种严重错误, 通常的做法是通知用户并中止程序的执行 Exception 类是 Java 应用程序中可预测 可恢复的例外, 我们称为异常 异常可分为运行时异常 (RuntimeException) 和检查时异常 (CheckedException) 两种 RuntimeException 类, 运行时异常即程序运行时抛出的异常, 不要求程序员在编程时必须对这些异常进行处理, 也能编译通过 前面数组下标异常和除数为 0 的异常都是运行时异常 CheckedException 类, 检查时异常又称为非运行时异常, 这样的异常要求程序员必须在编译时进行处理, 否则就会编译不通过 例如我们在前面的学习过程中, 经常在编译的时候发生类找不到的情况, 这就是一个典型的检查时异常 Java 编译器要求 Java 程序必须捕获或声明所有的非运行时异常, 但对运行时异常可以不做处理 常见的异常如表 4-2 所示 表 4-2 系统定义的常见异常 异常名称 错误描述 ClassNotFondException ArrayIndexOutOfBoundsException FileNotFoundException IOException NullPointerException InterruptedException 未找到欲装载使用的类数组越界使用未找到指定的文件或目录输入输出异常引用空的尚无内存空间的对象线程在睡眠或其他原因暂停时被其他线程打断 AirthmeticException 算术异常, 如除数为 0 UnknowHostException 无法确定主机的 IP 地址

104 104 Java 程序设计 异常处理机制 Java 中的每个异常类都代表了一种运行错误, 类中包含了该运行错误的相关信息和处理 错误的方法等 当 Java 程序在运行过程中产生了可预知的运行错误, 意味着该错误必定有一 个异常类的对象与之对应, 系统都会创建一个相应的该异常类的对象, 或者说产生了异常 一 旦异常类对象产生, 系统中必须有相应的机制来处理它, 这样保证不会死机 死循环或破坏操 作系统, 从而保证整个程序的安全运行 在 Java 中, 对于 Error 类错误或运行时异常, 程序只需要简单忽略该类错误, 因为系统会 把该异常对象交给默认的异常处理程序来处理 当一个方法中产生了非运行时异常, 在处理方 式上有以下两种解决方案 1. 结构化异常处理 当一个方法发生错误时, 系统运行时就会寻找可以处理异常的代码块, 这个代码块被称 为异常处理器 下面介绍如何使用 try-catch-finally 语句编写异常处理器 在 Java 程序里, 针对每一种需要捕获的异常类, 都必须有一个相应的异常处理器 异常 处理器是紧接在 try 语句块后面 以 catch 为标志的异常处理语句块, 其格式如下 : try // 在此区域内或能发生异常 ; catch( 异常类 1 e 1 ) // 处理异常 1; catch( 异常类 n e n ) // 处理异常 n; finally // 不论异常是否发生, 都要执行的部分 ; try 语句块, 表示要尝试运行代码,try 语句块中代码受异常监控, 其中代码发生异常时, 会抛出异常对象 catch 语句的参数类似于方法的参数, 包括一个异常类型和一个异常对象 异常类型必须 为 Exception 类的子类, 它指明了 catch 语句所处理的异常类型, 在程序运行时, 当 try 语句块 中产生异常时,catch 会捕获到发生的异常, 并和自己的异常类型匹配, 若匹配, 则执行 catch 块中代码, 并将 catch 块参数指向所抛的异常对象 catch 语句可以有多个, 分别处理不同类型异常 Java 程序运行时, 系统从上到下分别对 每个 catch 语句处理的异常类型进行检测, 直到找到类型相匹配的 catch 语句为止 注意异常 类型匹配是指 catch 所处理的异常类型与生成的异常对象的类型完全一致或者是它的父类 因 此,catch 语句的排列顺序应该是从特殊到一般 也可以用一个 catch 语句处理多个异常类型, 这时它的异常类型参数应该是这些异常类型 的父类, 程序设计中要根据具体的情况来选择相应的 catch 语句进行异常处理 finally 语句块是紧跟 catch 语句后的语句块, 这个语句块总是会在方法返回前执行, 而不 管 try 语句块是否发生异常 finally 语句块是可选的, 在 finally 块中常进行关闭资源的操作, 如关闭文件, 因为无论是否出现异常, 关闭文件的操作都是必须执行的

105 项目 3 Java 面向对象程序设计 105 例 3.37 结构化异常处理 class Try_Catch_Exp1 public static void main(string[] args) int d=0,a; try System.out.println("Before throw Exception"); a=5/d; System.out.println("the Exception is throw,the statement is't run"); catch(arithmeticexception e) System.out.println(" 处理算术异常的 catch 语句块捕获了异常!"); System.out.println(" 捕获的异常为 "+e); catch(arrayindexoutofboundsexception e) System.out.println(" 处理数组下标越异常的 catch 语句块捕获了异常!"); System.out.println(" 捕获的异常为 "+e); finally System.out.println(" 这是所有 catch 块的共有部分!"); System.out.println("try-catch-finally 块后面的语句 "); 出现运行结果 : 处理算术异常的 catch 语句块捕获了异常! 捕获的异常为 java.lang.arithmeticexception: / by zero 这是所有 catch 块的共有部分! try-catch-finally 块后面的语句 2. 抛出异常 Java 抛出异常有三种形式, 一是 throws, 一个 throw, 还有一种系统自动抛出异常 在方 法中使用 try-catch-finally 处理异常, 此时出现异常是系统自动抛出 在有些情况下, 一个方法并不需要处理它所产生的异常, 而是向上传递, 由调用它的方 法来处理异常, 这时就要用到 throws 语句 throws 语句包含在方法的声明中, 其格式如下 : 返回类型方法名 ( 参数 ) throws 异常类 1, 异常类 2, 方法体 例 3.38 throws 抛出异常 class TestThrows static void func(int i) throws ArrayIndexOutOfBoundsException System.out.println(" 状况 "+i); if(i==0) System.out.println(" 没有异常情况出现 "); return; else if(i==1) int i_array[]=new int[6];

106 106 Java 程序设计 i_array[9]=15; public static void main(string[] args) try func(0); func(1); catch(arrayindexoutofboundsexception e) System.out.println(" 捕获 "+e); 程序运行结果 : 状况 0 没有异常情况出现状况 1 捕获 java.lang.arrayindexoutofboundsexception: 9 在此例中 func() 方法中生成的异常通过调用栈传递给 main() 方法, 由 main() 方法进行处理 在 Java 语言中, 如果需要在方法中抛出异常, 可以使用 throw 语句实现, 即显式抛出异 常 其格式为 : throw ThrowableObject; 其中 ThrowableObject 必须为 Throwable 类或其子类的对象 例如用下面语句可以抛出一 个算术异常 throw new AirthmeticException(); 执行 throw 语句后没运行流程立即停止,throw 的下一条语句将暂停执行, 系统转向内层 的一个 try 语句块检查是否有 catch 子句能匹配的 Throwable 实例 如果找到相匹配的实例, 系统转向该子句 如果没有找到, 则转向上一层的语句, 这样逐层向上, 直到最外层的异常处 理程序中止程序并打印出调用栈状况 例 3.39 throw 抛出异常 class TestThrow static void proc() try throw new NullPointerException("deno"); catch(nullpointerexception e) System.out.println(" 捕获 throw 抛出异常 "); throw e; public static void main(string[] args) try

107 项目 3 Java 面向对象程序设计 107 proc(); catch(nullpointerexception e) System.out.println(" 重新捕获 :"+e); 程序运行结果 : 捕获 throw 抛出异常重新捕获 :java.lang.nullpointerexception: deno 在此例中 proc() 方法抛出异常 NullPointerException 实例, 并被 proc() 方法中的 catch 捕获 后打印捕捉信息, 随后抛出另外一个异常, 而在 main() 方法中 catch 捕获了这个异常, 随后打 印出重新捕获信息 自定义异常 尽管 Java 提供了很多异常类, 但用户还是可以根据需要定义自己的异常类, 即创建自定 义异常类 创建用户自定义异常时, 一般需要完成如下的工作 : (1) 定义一个新的异常类, 这个类必须是 Throwable 类或 Exception 类的子类 定义格式 如下 : class 自定义异常类名 extends Exception 异常类体 ; (2) 自定义的异常类, 一般只要声明两个构造方法, 一个是不用参数的, 另一个以字符 串为参数 作为构造方法参数的字符串应当反映异常的信息 例如, 定义一个 MyException 的异常类如下 : class MyException extrends Exception public MyException() public MyException(String msg) super(msg); Exception 类从父类 Throwable 那里继承了很多方法, 其中最常用的有以下三种 : String getmessage(): 获得异常对象的描述信息 String tostring(): 返回描述当前 Exception 类信息的字符串 void printstacktrace(): 向标准输出设备打印出当前异常对象的调用堆栈路径 注意 : 用户定义的异常同样要用 try-catch 捕获, 但必须由用户自己抛出 throw new MyException() 例 3.40 自定义异常 class Exception_exp public static void main(string[] args)

108 108 Java 程序设计 try System.out.println("2+3="+add(2,3)); System.out.println("-8+10="+add(-8,10)); catch (Exception e) System.out.println("Exception is "+e); static int add(int n,int m) throws UserException if (n<0 m<0) throw new UserException(); return n+m; class UserException extends Exception UserException() super(" 数据为负数 "); 程序运行结果为 : 2+3=5 Exception is UserException: 数据为负数 习题 1. 选择题 (1) 关于异常的含义, 下列描述中最正确的一个是 A. 程序编译错误 B. 程序语法错误 C. 程序自定义的异常事件 D. 程序编译或运行时发生的异常事件 (2) 自定义异常时, 可以通过对下列哪一项进行继承 A.Error 类 C.Exception 类及其子类 B.Applet 类 D.AssertionError 类 (3) 对应 try 和 catch 子句的排列方式, 下列哪一项是正确的? A. 子类异常在前, 父类异常在后 B. 父类异常在前, 子类异常在后 C. 只能有子类异常 D. 父类和子类不能同时出现在 try 语句块中 (4) 运行下面程序时, 会产生什么异常? public class X7_1_4 public static void main(string[] args) int x=0; int y=5/x; int []z=1,2,3,4; int p=z[4];

109 A.ArithmeticException 项目 3 Java 面向对象程序设计 109 B.NumberFormatException C.ArrayIndexOutOfBoundsException D.IOException (5) 运行下面程序时, 会产生什么异常? public class X7_1_5 public static void main(string[] args) int[] z=1,2,3,4; int p=z[4]; int x=0; int y=5/x; A.ArithmeticException B.NumberFormatException C.ArrayIndexOutOfBoundsException D.IOException (6) 下列程序执行的结果是 public class X7_1_6 public static void main(string[] args) try return; finally System.out.println("Finally"); A. 程序正常运行, 但不输出任何结果 B. 程序正常运行, 并输出 Finally C. 编译通过但运行时出现异常 D. 因为没有 catch 子句, 因此不能通过编译 (7) 下列代码中给出正确的在方法体内抛出异常的是 A.new throw Exception(" "); B.throw new Exception(" "); C.throws IOException(); D.throws IOException; (8) 下列描述了 Java 语言通过面向对象的方法进行异常处理的好处, 请选出不在这些好 处范围之内的一项 A. 把各种不同的异常事件进行分类, 体现了良好的继承性 B. 把错误处理代码从常规代码中分离出来 C. 可以利用异常处理机制代替传统的控制流程 D. 这种机制对具有动态运行特性的复杂程序提供了强有力的支持 2. 编写一个方法, 比较两个字符串 假如其中一个字符串为空对象, 会产生 NullPointer- Exception 异常, 在方法声明中通告该异常, 并在适当时候触发异常, 然后编写一个程序捕获 该异常 3. 假如要从命令行获得两个参数, 自定义两个异常类来描述可能发生的异常 :Parameter- NumberException( 参数个数异常 ),ParameterFormatException( 参数格式异常 ), 在 main 方法 中通告这两个异常并在相应的情况下触发异常, 然后捕获异常, 对它们进行处理

110 110 Java 程序设计 项目总结 1. 类是 Java 程序的基本单元,Java 程序的编写过程就是定义类的过程 类由数据和方法组成 类和类成员都具有访问权限, 它规定了何时以及何种方式访问类和类成员是合法的 2. 对象是类的实例, 类是对象的模板, 类为对象提供了方法 数据结构以及按照类创建对象机制 3. 包是类的组织结构 创建包使用 import 语句, 并要作为源文件的第一句 包主要用来管理 Java 中的类并提供访问控制 4.Java 通过继承使子类拥有父类的所有非私有成员 ;Java 语言多态性可以分编译时多态和运行时多态 ; 用 abstract 修饰符修饰的类称为抽象类, 抽象类不能实例化为对象 5.Java 语言用接口来实现多重继承 接口类似于抽象类, 只包含常量和方法定义, 且其所有方法都是抽象的, 这些方法由实现这一接口的不同类具体完成 内部类是指在一个类的内部嵌套定义的类 6.Java 语言对异常的处理方法分为两类, 一类是将异常的处理递交给当前方法的调用者去处理, 称为 throws a Exception; 另一类是在方法中使用 try-catch-finally 语句自己处理异常

111 项目 4 Java API 常用类库 Java 的类库是 Java 语言提供的已经实现的标准类的集合 是 Java 编程的 API Application Program Interface 它可以帮助开发者方便 快捷地开发 Java 程序 这些类根据实现的功能 不同 可以划分为不同的集合 每个集合组成一个包 称为类库 Java 类库中大部分都是由 Sun 公司提供的 这些类库称为基础类库 Java 语言中提供了大量的类库供程序开发者来使用 了解类库的结构可以帮助开发者节 省大量的编程时间 而且能够使编写的程序更简单更实用 Java 中丰富的类库资源也是 Java 语言的一大特色 是 Java 程序设计的基础 了解 Java 语言公共类库的使用 熟练掌握 String 和 StringBuffer 的使用 了解基本数据类型的封装 掌握常用实用类的相关操作 掌握正则表达式的定义及使用 任务 1 Object 与 System Object 类 Object 类是 Java 程序中所有类的直接或间接父类 也是类库中所有类的父类 任何一个 类都是由 Object 类派生出来的 它是继承树上的根节点 所以它含有的属性和方法将被所有 的类继承 下面是 Object 类源码中的部分方法的定义 public class Object public final native Class getclass(); public native int hashcode(); public Boolean equals(object obj)return (this==obj); protected native Object clone()throws CloneNotSupportedException; public String tostring() return +Integer.toHexString(hashCode()); protected void finalize()throws Throwable

112 112 Java 程序设计 public Object(): 构造方法, 创建一个 Object 对象 public final Class getclass(): 返回一个 Class, 该对象提供了调用 getclass() 方法的对象的类的相关信息 public native int hashcode(): 返回一个哈希码值, 不同的对象有不同的哈希码值 public boolean equals(object obj): 比较两个对象是否相同, 更精确地说是两个引用变量是否是对同一对象的引用, 即堆内存的内容是否相同 说明 : (1) == 运算符用于基本数据类型的比较和引用数据类型的比较, 如果变量指向数组或者对象, 那这个变量就是引用数据类型 == 比较的是引用数据类型里存放的地址 (2)equals() 方法在 Object 中默认定义的也是比较对象的地址 但是我们可以重写 equals() 方法, 使它可以用来比较对象的内容 例 4.1 重写 equals() 方法比较对象内容 public class MyEquals String name; int n; public boolean equals(object obj) // 如果比较的内容是自身 if(obj == this) return true; if(!(obj instanceof MyEquals))// 对象类型不同 return false; MyEquals m = (MyEquals)obj;// 转换成当前类类型 /* 依次比较对象中每个变量 */ if(!name.equals(m.name)) //name 属性不同 return false; if(!(n == m.n))//n 属性不同 return false; return true;// 如果都相同, 则返回 true public static void main(string[] args) MyEquals m1=new MyEquals(); m1.name="zhangshan";m1.n=22; MyEquals m2=new MyEquals(); m2.name="lisi";m2.n=22; if(m1.equals(m2)) System.out.println("Two objects are equal."); else System.out.println("Two objects are not equal.");

113 项目 4 Java API 常用类库 113 程序运行结果为 : Two objects are not equal. protected Object clone()throws CloneNotSupportedException 生成当前对象的一个复制, 并返回这个复制的对象, 该对象类型为 Object, 但所有需要使 用该方法的类都必须实现接口 cloneable, 否则, 运行时将抛出 CloneNotSupportedException 类 的例外 例 4.2 clone() 方法的使用 import java.awt.point; public class CloneTest public static void main(string[] args)//todo Auto-generated method stub Point p=new Point(4,6); System.out.println(p); Point p1=(point)p.clone();// 就是完成一个复制操作 System.out.println(p1); System.out.println(p==p1);// 但是它们不是同一个对象, 这就是 clone 的作用 程序运行结果为 : java.awt.point[x=4,y=6] java.awt.point[x=4,y=6] false public String tostring() 用来返回当前对象本身的有关信息, 默认的是返回对象所在的类名和哈希码的十六进制 表示 几乎每个类都会覆盖该方法, 以便打印对该对象当前状态的表示 大多数 ( 非全部 ) tostring() 方法都遵循如下格式 : 类名 [ 字段名 = 值, 字段名 = 值...], 当然, 子类应该定义自己 的 tostring() 方法 例如 : public String tostring() reurn "Employee[name="+name+",salary="+salary+",hireDay="+hireDay+"]"; tostring() 方法是非常重要的调试工具, 很多标准类库中的类都定义了 tostring() 方法, 以 便程序员获得有用的调试信息 System 类 System 类是一个特殊类, 它是一个公共最终类, 不能被继承, 也不能被实例化, 即不能 创建 System 类的对象 System 类功能强大, 与 Runtime 一起可以访问许多有用的系统功能 标准的输入 输出 和 Java 运行时的错误输出存储在变量 in,out 和 err 中 System 类中所有的变量和方法都是静态 的, 使用时以 System 作为前缀, 即形如 System. 变量名 和 System. 方法名 1. 标准的输入输出 public static final InputStream in 标准输入流对象, 此对象可以通过 read 方法接收 从键盘输入的内容 public static final PrintStream out 标准输出流对象, 此对象可以通过 println 或 print

114 114 Java 程序设计 方法将内容输出到控制台显示 public static final PrintStream err 标准错误输出流对象, 此对象可以通过 println 或 print 方法将内容输出到控制台显示 out 与 err 使用上的不同是 :out 的输出一般需要 缓存 ; err 不需要缓存, 快速显示紧急信息 2.System 类的常用方法 public static long currenttimemillis() 返回自从 1970 年 1 月 1 日午夜起到现在的时间, 时间单位是毫秒 例 4.3 估计执行某个循环所占用的时间 class System_test public static void main(string[] args) long starttime=system.currenttimemillis();// 记录循环开始时间 int sum=0; for(int i=0;i< ;i++) sum+=i; long endtime=system.currenttimemillis();// 记录循环结束时间 System.out.println("time: "+(endtime-starttime)+ "milliseconds. "); 程序运行结果 : time: 15milliseconds. 注意 : 虽然使用 currenttimemillis() 方法可以计算出当前的日期和时间, 但是获取当前日 期和时间最好使用 java.util 中的 Date 类 public static void arraycopy(a1,int sourcestart,a2,int targetstart,int size) 将数组 a1 从下标 sourcestart 开始, 长度为 size 的元素依次复制到数组 a2 的以 targetstart 为起始的单元中 例 4.4 用 arraycopy() 方法复制两个数组 class CopyArray static byte array1[ ]=97,98,99,100,101; static byte array2[ ]=102,102,102,102,102; public static void main(string[] args) System.out.println(" array1="+new String(array1)); System.out.println(" array2="+new String(array2)); System.arraycopy(array1,0,array2,0,array1.length); System.out.println(" array1="+new String(array1)); System.out.println(" array2="+new String(array2)); System.arraycopy(array1,0,array1,1,array1.length-1); System.arraycopy(array2,1,array2,0,array2.length-1); System.out.println(" array1="+new String(array1)); System.out.println(" array2="+new String(array2));

115 项目 4 Java API 常用类库 115 程序运行结果 : array1=abcde array2=fffff array1=abcde array2=abcde array1=aabcd array2=bcdee public static void exit(int status) 在用户的程序还未执行完之前, 强制关闭 Java 虚拟机的方法, 并把状态信息 status 返回 给运行虚拟机的操作系统 status 通常取 0, 表示正常终止, 结束程序 非零的状态信息表示 异常终止 public static void gc() 垃圾收集器一般情况下运行于后台, 并自动地收集已不使用的内存 使用 gc() 方法可强制 垃圾收集器启动 习题 1.Object 类主要功能是什么? 它有哪些主要的方法? 2.Object 类中的 equals 方法和 hashcode 方法主要功能是什么? 3.System 类主要功能是什么? 它有哪些主要的方法? 4. 选择题 下面代码运行后的输出结果为 public class X6_1_5 public static void main(string[] args) AB aa=new AB(); AB bb;bb=aa; System.out.println(bb.equals(aa)); class AB int x = 100; A.true B.false C. 编译错误 D 填空题 (1) 是所有类的直接或间接父类, 它在 包中 (2)System 类是一个功能强大 非常有用的特殊的类, 它提供了 系统 信息等重要工具 这个类不能, 即不能创建 System 类的对象, 所以它所有的属性和方 法都是 类型, 引用时以类名 System 为前缀即可 任务 2 String 与 StringBuffer Java 提供了两个用于字符串操作的类, 一个是经常用到的 String, 另一个是 StringBuffer 字符串类提供了丰富的字符串操作方法, 程序员可以方便地使用这些常用的算法和操作, 而不

116 116 Java 程序设计 需要自己再重复编写, 这就是面向对象的好处 String 类用于处理那些值不会发生改变的字符串, 以前程序中的 String 变量, 全都是其取值没有发生过变化的字符串 而 StringBuffer 类则用于那些可能发生变化的字符串的处理 例如, 在程序中拼接字符串 从文件中读取字符串等等 由于 String 类对象都是常量, 它的处理效率要比 StringBuffer 类对象高得多, 因此, 读者在编程时尽可能使用 String 类 String 类 Java 语言中的字符串属于 String 类 虽然有其他方法表示字符串 ( 如字符数组 ), 但 Java 使用 String 类作为字符串的标准格式 Java 编译器把字符串转换成 String 对象 String 对象一旦被创建了, 就不能被改变 如果需要进行大量的字符串操作, 应该使用 StringBuffer 类或者字符数组, 最终结果可以被转换成 String 格式 1. 字符串创建创建字符串的方法有多种方式, 通常我们用 String 类的构造方法来建立字符串 表 4-3 列出了 String 类的构造方法及使用说明 表 4-3 String 类构造方法及使用说明 构造方法 使用说明 String( ) String(char[ ] value ) String(char[ ] ch, int sta,int count) String(String value ) String(String Bufferbuffer ) 初始化一个新的 String 对象, 使其包含一个空字符串分配一个新的 String 对象, 使它代表字符数组参数包含的字符序列分配一个新的 String 对象, 使它包含来自字符数组参数中子数组的字符初始化一个新的 String 对象, 使其包含和参数字符串相同的字符序列初始化一个新的 String 对象, 它包含字符串缓冲区参数中的字符序列 例 4.5 使用多种方法创建一个字符串并输出字符串内容 public class StrOutput public static void main(string[] args) // 将字符串常量作为 String 对象对待, 实际上是将一个 String 对象赋值给另一个 String s1="hello,jcut!"; String s2;// 声明一个字符串, 然后为其赋值 s2 = "Hello,java!"; // 使用 String 类的构造器中的一个 创建一个空字符串, 然后赋值给它 String s3 = new String(); s3 = "Hello,jcut!"; // 将字符串直接传递给 String 类构造器来创建新的字符串 String s4 = new String("Hello,jcut!"); // 使用 String 类的构造器中的一个 // 通过创建字符数组传递给 String 类构造器来创建新的字符串 char c1[] = 'a','b','c','d','e','f','g'; String s5 = new String(c1); // 将字符数组子集传递给 String 类构造器来创建新的字符串 String s6 = new String(c1,0,2);

117 项目 4 Java API 常用类库 117 System.out.println(s1); System.out.println(s2); System.out.println(s3); System.out.println(s4); System.out.println(s5); System.out.println(s6); 程序运行结果为 : Hello,jcut! Hello,java! Hello,jcut! Hello,jcut! abcdefg ab 2. 字符串的常用方法 字符串的常用方法一共分为 6 类, 下面分别介绍 (1) 字符串比较 public boolean equals(object anobject): 在区分大小写的前提下, 判断当前字符串对象 和另一个对象 anobject 的内容是否相等 若相等返回 true, 否则为 false public boolean equalsignorecase(sting anotherstring): 在忽略大小写的前提下, 判断当 前的字符串对象和参数字符串对象 anotherstring 的内容是否相等 若相等返回 true, 否则为 false public boolean regionmatches(boolean ignorecase,int toffset,string s,int ooffset,int len): 如果当前字符串对象和参数字符串对象 s 的子串相等, 返回 true, 否则为 false 其中, ignorecase 为忽略大小写设置 (true 为忽略大小写,false 为不忽略大小写 ),toffset 确 定当前字符串对象的起始偏移量,ooffset 确定 s 的起始偏移量,len 确定子串的长度 public int compareto(string anotherstring): 按照字典的方式比较两个字符串, 忽略大 小写 如果当前字符串对象小于参数字符串对象 anotherstring, 则返回小于 0 的值 ; 如果当前字符串对象等于 anotherstring, 则返回 0; 如果当前字符串对象大于 anotherstring, 则返回大于 0 的值 例 4.6 比较字符串 public class StrCompare public static void main(string[] args) String s1="aaaa"; String s2="aaaa"; String s3="aaaa"; String s4="bcd"; if(s1.equals(s2)) System.out.println("s1==s2"); else System.out.println("s1!=s2");

118 118 Java 程序设计 if(s1.equalsignorecase(s3)) System.out.println("s1==s3 when ignoring case"); else System.out.println("s1!=s3 when ignoring case"); if(s1.regionmatches(true,0,s3,1,3)) System.out.println("s1==s3 when ignoring case"); else System.out.println("s1!=s3 when ignoring case"); if (s1.regionmatches(false,0,s3,1,3)) System.out.println("s1= =s3 when not ignoring case"); else System.out.println("s1!=s3 when not ignoring case"); if(s1.compareto(s4)<0) System.out.println("s1<s4"); else if(s1.compareto(s4)==0) System.out.println("s1==s4"); else System.out.println("s1>s4"); 程序运行结果为 : s1==s2 s1==s3 when ignoring case s1==s3 when ignoring case s1!=s3 when not ignoring case s1<s4 (2) 求字符串长度 public int length( ) 返回当前字符串对象的长度, 即字符串中所含字符的个数 例 4.7 求字符串长度 public class Strlen public static void main(string[] args) String s1="hello,jcut!"; String s2=" 中国是一个伟大的国家 "; int i=s1.length();

119 项目 4 Java API 常用类库 119 int j=s2.length(); System.out.println(" 字符串 s1 长度为 "+i); System.out.println(" 字符串 s2 长度为 "+j); 程序运行结果为 : 字符串 s1 长度为 11 字符串 s2 长度为 10 (3) 字符串的连接 可以使用两种方法将字符串连接起来 : + 操作符和用 String 类的 concat 方法 public String concat(string str) 将给定的字符串 str 连接到当前字符串的末尾 注意 : 参数字符串对象可以是字符串常量 例 4.8 字符串连接 public class StrConcat public static void main(string[] args) String s1="hello"; String s2=s1+","; String s3=s2.concat(" Java"); String s4=new String("! "); String s5=s3.concat(s4); System.out.println(" 连接而成的字符串是 "+s5); 程序运行结果为 : 连接而成的字符串是 Hello, Java! (4) 字符串的复制 public static String copyvalueof(char[ ] data): 创建一个和给定字符数组 data 内容相同 的 String 对象 public static String copyvalueof(char[ ]data,int offset,int count): 创建一个与数组 data 中 以 offset 起始, 长度为 count 的内容相同的 String 对象 public void getchars(int strbegin,int strend,char[ ] data,int offset): 将当前字符串中的全 部或部分内容拷贝到数组 data 中 其中,strbegin 为字符的起始,strend 为字符的终 止 ( 不包括 strend),offset 为字符数组的起始 public char[ ] tochararray( ): 将当前字符串中的全部内容拷贝到目标字符数组中 public String substring(int strbegin): 将当前字符串中以 strbegin 起始的内容拷贝到目 标字符串中 public String substring(int strbegin,int strend): 将当前字符串中以 strbegin 起始, 以 strend 结束 ( 不包括 strend) 之间的内容拷贝到目标字符串中 例 4.9 字符串复制 public class StrCopy public static void main(string[] args) String s1=new String();

120 120 Java 程序设计 char data[]= 'a', 'b', 'c', 'd', 'e', 'f'; s1=s1.copyvalueof(data); System.out.println("s1="+s1); s1=s1.copyvalueof(data,2,3); System.out.println("s1="+s1); s1.getchars(1,2,data,0); System.out.print("data="); System.out.println(data); data=s1.tochararray(); System.out.print("data="); System.out.println(data); String s2=new String(); String s3=new String(); s2=s1.substring(0); System.out.println("s2="+s2); s3= s1.substring(1,2); System.out.println("s3="+s3); 程序运行结果 : s1=abcdef s1=cde data=dbcdef data=cde s2=cde s3=d (5) 字符串检索 public char charat(int index): 获取当前字符串中第 index 个位置的字符 public int indexof(int char): 在当前字符串中检索参数字符 char 第一次出现的位置 如果检索不到, 返回 -1 public int indexof(string str): 在当前字符串中检索参数字符串 str 第一次出现的位置 如果检索不到, 返回 -1 public int lastindexof (int char): 在当前字符串中检索参数字符 char 最后一次出现的位 置 如果检索不到, 返回 -1 public int lastindexof (String str): 在当前字符串中检索参数字符串 str 最后一次出现的 位置 如果检索不到, 返回 -1 例 4.10 字符串检索 public class StrSearch public static void main(string[] args) String s1="javav"; char c=s1.charat(2); System.out.println("c="+c); int i=s1.indexof('a'); System.out.println("fistchar="+i);

121 项目 4 Java API 常用类库 121 int j=s1.lastindexof('a'); System.out.println("lastchar="+j); i= s1.indexof("av"); System.out.println("fiststring="+i); j=s1.lastindexof("av"); System.out.println("laststring="+j); 程序运行结果为 : c=v fistchar=1 lastchar=3 fiststring=1 laststring=3 (6) 字符串替换 public String replace(char oldchar,char newchar): 将当前字符串中的 oldchar 字符转换 为 newchar 字符后, 创建一个新的对象 若指定字符不存在, 则不替代 public String tolowercase( ): 将这个 String 对象中的所有字符变为小写后, 创建一个 新的对象 public String touppercase( ): 将这个 String 对象中的所有字符变为大写后, 创建一个 新的对象 public String trim( ): 去掉当前字符串开头和结尾的空格后, 创建一个新的对象 例 4.11 字符串替换 public class StrModify public static void main(string[] args) String s1="java"; s1=s1.replace('a','b'); System.out.println("s1="+s1); String s2=s1.tolowercase(); String s3=s1.touppercase(); System.out.println("s2="+s2); System.out.println("s3="+s3); s1=" "+s1+" "; System.out.println("s1="+s1); s2=s1.trim(); System.out.println("s2="+s2); 程序运行结果为 : s1=jbvb s2=jbvb s3=jbvb s1= Jbvb s2=jbvb

122 122 Java 程序设计 StringBuffer 类缓冲字符串类 StringBuffer 与 String 类相似, 它具有 String 类的很多功能, 甚至更丰富 它们主要的区别是 StringBuffer 对象可以方便地在缓冲区内被修改, 如增加 替换字符或子串 当完成了缓冲字符串数据操作后, 可以通过调用其方法 StringBuffer.toString() 或 String 构造方法把它们有效地转换回标准字符串格式 1.StringBuffer 类的构造方法可以使用 StringBuffer 类的构造方法来创建 StringBuffer 对象 表 4-4 是 StringBuffer 的构造方法及其使用说明 表 4-4 StringBuffer 类构造方法及使用说明 构造方法 StringBuffer( ) StringBuffer(int length) StringBuffer(String str) 使用说明构造一个空的缓冲字符串, 其中没有字符, 初始长度为 16 个字符的空间构造一个长度为 length 的空缓冲字符串用参数 str 对象创建一个 StringBuffer 对象 例 4.12 用多种方法创建 StringBuffer 对象 public class StrBufferSet public static void main(string[] args) StringBuffer s1=new StringBuffer(); s1.append("hello,java!"); System.out.println("s1="+s1); StringBuffer s2=new StringBuffer(10); s2.insert(0,"hello,java!"); System.out.println("s2="+s2); StringBuffer s3=new StringBuffer("Hello,Java!"); System.out.println("s3="+s3); 程序运行结果 : s1=hello,java! s2=hello,java! s3=hello,java! 2.StringBuffer 类常用方法 StringBuffer 类是可变字符串, 因此它的操作主要集中在对字符串的改变上 (1) 为 StringBuffer 的对象插入和追加字符串 public StringBuffer append(string str): 将参数字符串 str 追加到当前 StringBuffer 对象 的后面 public StringBuffer insert(int offset,string str): 在当前 StringBuffer 对象的参数 offset 位 置上, 插入一个字符串 str 关于 append 和 insert 方法的使用见例 4.12

123 项目 4 Java API 常用类库 123 (2) 获取和设置 StringBuffer 对象的长度和容量 public int length(): 返回当前 StringBuffer 对象中, 字符串的长度 public int capacity(): 返回当前 StringBuffer 对象的总空间, 而非字符串的长度 public void setlength(int newlength): 重新设置 StringBuffer 对象中字符串的长度, 如 果 newlength 小于当前的字符串长度, 将截去多余的字符 例 4.13 显示确定字符串的长度和容量, 并改变字符串的长度 public class StrLength public static void main(string[] args) StringBuffer s1=new StringBuffer("Hello,Java!"); System.out.println("The length is "+s1.length()); System.out.println("The allocated length is "+s1.capacity()); s1.setlength(100); System.out.println("The new length is "+s1.length()); 程序运行结果 : The length is 11 The allocated length is 27 The new length is 100 (3) 读取和改变 StringBuffer 对象中的字符 public void setcharat(int index, char ch): 设置当前 StringBuffer 对象中索引号 index 的 字符为 ch public StringBuffer replace(int start, int end, String str): 将当前 StringBuffer 对象中从参 数 start 开始到参数 end-1 之间的内容替换为参数字符串 str public StringBuffer delete(int start, int end): 将当前 StringBuffer 对象中从参数 start 开 始到参数 end-1 之间的内容删除 public StringBuffer deletecharat(int index): 删除当前 StringBuffer 对象中索引号为 index 的字符 例 4.14 改变字符串的内容 public class StrChange public static void main(string[] args) StringBuffer s1=new StringBuffer("Hallo,Java!"); s1.setcharat(1,'e'); System.out.println(s1); s1.replace(1,5,"i"); System.out.println(s1); s1.delete(0,3); System.out.println(s1); s1.deletecharat(4); System.out.println(s1); 程序运行结果为 :

124 124 Java 程序设计 Hello,Java! Hi,Java! Java! Java 习题 1. 选择题 (1) 已知有定义 :String s="i love", 下面哪个表达式正确? A.s += "you"; C.int len = s.length; (2) 对于下列代码 : String str1="java"; String str2="java"; String str3=new String("java"); StringBuffer str4=new StringBuffer("java"); 以下表达式的值为 true 的是 B.char c = s[1]; D.String s = s.tolowercase(); A.str1==str2; B.str1==str4; C.str2==str3; D.str3==str4; (3) 以下程序段输出结果的是 public class Test public static void main(string args[]) String str="abcde"; str.substring(3); str.concat("xyz"); System.out.print(str); A.DE B.DEXYZ C.ABCDE D.CDEXYZ (4) 在 Java 语言中, 下列语句中正确的是哪个? A.String temp[]=new String"a" "b" "c"; B.String temp[]="a","b","c"; C.String temp="a" "b" "c"; D.String temp[]="a","b","c"; (5) 对于以下声明 : String s1="hello"; String s2="world"; String s3; 下面的操作合法的是 A.s3=s1+s2; B.s3=s1-s2; C.s3=s1&s2; D.s3=s1&&s2; (6) 下面程序段的输出结果是 StringBuffer buf1=new StringBuffer(20); System.out.println(buf1.length()+','+buf1,capacity()); A.0,20 B.0,null C.20,20 D.0,0 2.String 类和 StringBuffer 类有什么区别?

125 项目 4 Java API 常用类库 使用 StringBuffer 类编写一个程序, 将一个字符按字典序插入到一个字符串中 如, 字符 'c' 插入到字符串 "Hello" 的第一个位置 4. 使用 String 类的 public String concat(string str) 方法可以把调用当前字符串与参数字符串连接, 获得一个新的串 5. 编写程序删除一个字符串中的全部数字字符, 例如, 将 "abc123xyz" 的数字字符全部删除, 得到字符串 "abcxyz" 任务 3 Java 包装类 Java 语言是一个面向对象的语言, 但是 Java 中的基本数据类型却不是面向对象的, 这在 实际使用时存在很多的不便, 为了解决这个不足, 在设计类时为每个基本数据类型设计了一个 对应的类进行代表, 这样八个基本数据类型对应的类统称为包装类 (Wrapper Class) 每个基 本数据类型对应的包装类见表 4-5 所示 表 4-5 基本数据类型与包装类的对应 类别 基本数据类型 包装类 包装类中的常量 int Integer MAX_VALUE(2 31-1)/MIN_VALUE(-2 31 ) 整型 byte Byte MAX_VALUE(2 7-1)/MIN_VALUE(-2 7 ) short Short MAX_VALUE(2 15-1)/MIN_VALUE(-2 15 ) long Long MAX_VALUE(2 63-1)/MIN_VALUE(-2 63 ) 浮点型 float Float MAX_VALUE(( ) )/MIN_VALUE( ) double Double MAX_VALUE(( ) )/MIN_VALUE( ) 字符型 char Character MAX_VALUE('\uFFFF')/MIN_VALUE('\u0000') 布尔型 boolean Boolean TRUE/FALSE 说明 : (1)Integer Byte Float Double Short Long 都属于 Number 类的子类,Number 类本身提供了一系列的返回以上六种基本数据类型的操作 (2)Character 属于 Object 的直接子类 (3)Boolean 属于 Object 的直接子类 基本数据类型与包装类型的转换 1. 基本数据类型转换为包装类型对象把基本数据类型变量转换成包装类型对象是通过对应包装类型的构造方法来实现的, 具体见表 4-6 所示 2. 包装类型对象转换为基本数据类型每个包装类都提供了方法将包装类型对象转换为基本数据类型, 具体见表 4-7 所示

126 126 Java 程序设计 表 4-6 基本数据类型转换成对应包装类对象 包装类 方法 含义 Integer public Integer(int value) 用整型数据 value 构造整型对象 Byte public Byte(byte value) 用字节数据 value 构造字节对象 Short public Short(short value) 用短整型数据 value 构造短整型对象 Long public Long(long value) 用长整型数据 value 构造长整型对象 Float public Float(float value) 用单精度浮点数据 value 构造单精度浮点对象 Double public Double(double value) 用双精度浮点数据 value 构造双精度浮点对象 Character public Character(char c) 用字符数据 c 构造字符类型对象 Boolean public Boolean(boolean b) 用布尔变量 b 构造布尔类型对象 表 4-7 包装类对象转换为基本数据类型 包装类 方法 含义 Integer public intvalue() 将当前整型对象中包装的数据转换为整型数据 Byte public bytevalue() 将当前字节型对象中包装的数据转换为字节数据 Short public shortvalue() 将当前短整型对象中包装的数据转换为短整型数据 Long public longvalue() 将当前长整型对象中包装的数据转换为长整型数据 Float public floatvalue() 将当前浮点对象中包装的数据转换为单精度浮点数据 Double public doublevalue() 将当前浮点对象中包装的数据转换为双精度浮点数据 Character public charvaluer() 将当前字符对象中包装的数据转换为字符数据 Boolean public booleanvalue() 将当前布尔对象中包装的数据转换为布尔数据 例 4.15 基本数据类型与对应的包装类相互转换 public class Primitive2Wrapper public static void main(string[] args) boolean b1 = true;// 通过构造器把 b1 基本类型变量包装成包装类对象 Boolean blobj = new Boolean(b1); int it =5;// 通过构造器把 it 基本类型变量包装成包装类对象 Integer itobj = new Integer(it); float f=(float)123.45; Float fobj=new Float(f); char ch='a'; Character cobj=new Character(ch); // 下面程序运行时将出现 java.lang.numberformatexception 异常 //Long lobj = new Long("ddd"); boolean b=blobj.booleanvalue(); System.out.println(b); int i=itobj.intvalue(); System.out.println(i); f1=fobj.floatvalue();

127 项目 4 Java API 常用类库 127 float System.out.println(f1); char c1=cobj.charvalue(); System.out.println(c1); 出现运行结果为 : true a 字符串与数值类型的相互转换 1. 字符串转换为数值类型 包装类在实际中用得最多的还在于字符串变为基本数据类型的操作上, 例如 : 将一个全 由数字组成的字符串变为一个 int 或 float 类型的数据 常用的字符串转换数值类型的方法如表 4-8 所示 表 4-8 字符串转换为基本数据类型的方法 包装类方法含义 Integer public static int parseint(string str) 将字符串 str 转换为基本整型十进制数 Byte public static int parsebyte(string str) 将字符串 str 转换为字节型十进制数 Short public static int parseshort(string str) 将字符串 str 转换为短整型十进制数 Long public static int parselong(string str) 将字符串 str 转换为长整型十进制数 Float public static int parsefloat(string str) 将字符串 str 转换为单精度实型十进制数 Double public static int parsedouble(string str) 将字符串 str 转换为双精度实型十进制数 例 4.16 字符串转换为数值类型 public class Str2Pri public static void main(string[] args) String intstr = "123"; // 把一个特定字符串转换成 int 变量 int it = Integer.parseInt(intStr); System.out.println("it 的结果是 :"+it); String floatstr = "4.56"; // 把一个特定字符串转换成 float 变量 float ft = Float.parseFloat(floatStr); System.out.println(ft); // 把一个 double 变量转换成 String 变量 String dbstr = String.valueOf(3.344); System.out.println(dbStr); // 把一个整型变量转换成 String 变量 String intstring=string.valueof(it); System.out.println(intstring);

128 128 Java 程序设计 // 把一个 boolean 变量转换成 String 变量 String boolstr = String.valueOf(true); System.out.println(boolStr.toUpperCase()); 程序运行结果为 : it 的结果是 : TRUE 2. 数值类型转换为字符串 数值类型转换为字符串的方法主要有两种, 一种是将数值与空字符串进行连接, 完成数 值字符串的组合, 例如 : String s=234+""; 另一种是使用字符串类中定义的类方法 valueof, 具体见表 4-9 所示 关于数值类型转换 成字符串的应用见例 4.16 所示, 其中 String boolstr = String.valueOf(true) 是 boolean 变量转 换成字符串 表 4-9 String 类将数值转换成字符串的方法 方法 public static String valueof(byte n) public static String valueof(int n) public static String valueof(long n) public static String valueof(float n) public static String valueof(double n) 使用说明将参数字节变量 n 转换为字符串将参数整型变量 n 转换为字符串将参数长整型变量 n 转换为字符串将参数单精度变量 n 转换为字符串将参数双精度变量 n 转换为字符串 包装类自动装箱和拆箱 所谓装箱, 就是把基本类型用它们相对应的引用类型包起来, 使它们可以具有对象的特 质, 如我们可以把 int 型包装成 Integer 类的对象, 或者把 double 包装成 Double 等等 所谓拆 箱, 就是跟装箱的方向相反, 将 Integer 及 Double 这样的引用类型的对象重新简化为值类型的 数据 JDK1.5 之前使用手动方式进行装箱和拆箱的操作, 具体见例 4.17 例 4.17 包装类的手动装箱和拆箱 public class WrapperDemo public static void main(string[] args) int m=500; Integer obj=new Integer(m);// 手动装箱 int n=obj.intvalue();// 手动拆箱 System.out.println("n = "+n); Integer obj1=new Integer(500);

129 项目 4 Java API 常用类库 129 System.out.println("obj 等价于 obj1?"+obj.equals(obj1)); 程序运行结果为 : n = 500 obj 等价于 obj1?true JDK1.5 之后可以自动拆箱装箱, 也就是在进行基本数据类型和对应的包装类转换时, 系 统将自动进行, 这将大大方便程序员的代码书写, 具体见例 4.18 例 4.18 包装类的自动装箱和拆箱 public class WrapperDemo1 public static void main(string[] args) int m=500; Integer obj=m;// 自动装箱 int n=obj;// 自动拆箱 System.out.println("n = "+n); Integer obj1=500; System.out.println("obj 等价于 obj1?"+obj.equals(obj1)); 程序运行结果为 : n = 500 obj 等价于 obj1?true 习题 1. 包装类包括哪些类? 有什么作用? 2. 什么是自动装箱和拆箱? 使用该特征有哪些注意事项? 3.int 和 Integer 有什么区别? 4. 创建 Integer 类对象, 并以 int 类型将 Integer 的值返回 5. 创建两个 Character 对象, 通过 equals() 方法比较它们是否相等 ; 之后将这两个对象分 别转换成小写形式, 再通过 equals() 方法比较两个 Character 对象是否相等 6. 编写程序, 实现通过字符型变量创建 boolean 值, 再将其转换成字符串输出, 观察输 出后的字符串与创建 Boolean 对象时给定的参数是否相同 任务 4 Java 数字处理类 在解决实际问题时, 对数字的处理是非常普遍的, 如数学问题 随机问题等, 为了应对以上问题,Java 提供了处理相关问题的类, 包括 Math 类 ( 为各种数学计算提供了工具方法 ) Random 类 ( 为 Java 处理随机数问题提供了各种方法 ) BigInteger 类与 BigDecimal 类 ( 为所有的大数字处理提供了相应的数学运算操作方法 ) Math 类在 Java 语言中提供了一个执行数字基本运算的 Math 类, 该类包括常用的数学运算方法,

130 130 Java 程序设计 如三角函数法 指数函数法等一些常用数学函数, 这些方法都是静态方法, 可以不用创建实例, 直接通过类名调用 除此之外还提供了两个类常量 E 和 PI, 它们分别代表了数学中的数学常数和圆周率, 其值分别是 和 Math 类常用方法如表 4-10 所示 表 4-10 Math 类的常用方法 方法 含义 public static double sin(double a) public static double asin(double a) public static double log(double a) public static double sqrt(double a) public static double pow (double a,double b) public static double random( ) public static intabs(int a) public static intmax(int a,int b) public static intmin(int a,int b) 返回 a 的正弦值返回 a 的反正弦值返回 a 的对数值 (lna) 返回 a 的平方根返回以 a 为底数, 以 b 为指数的幂值返回一个伪随机数, 其值介于 0 和 1 之间返回 a 的绝对值返回 a 和 b 的最大值返回 a 和 b 的最小值 例 4.19 Math 类应用举例 import java.lang.math; public class MyMath public static void main(string[] args) int x=9; int y=16; System.out.println(Math.sqrt(x)); // 计算 x 的算术平方根 System.out.println(Math.abs(x)); // 计算 x 的绝对值 System.out.println(Math.max(x, y)); // 计算 x y 的最大值 System.out.println(Math.min(x, y)); // 计算 x y 的最小值 程序运行结果为 : Random 类 Random 类中实现的随机算法是伪随机, 也就是有规则的随机 在进行随机时, 随机算法的 起源数字称为种子数 (seed), 在种子数的基础上进行一定的变换, 从而产生需要的随机数字 相同种子数的 Random 对象, 相同次数生成的随机数字是完全相同的 也就是说, 两个种 子数相同的 Random 对象, 第一次生成的随机数字完全相同, 第二次生成的随机数字也完全相 同 这点在生成多个随机数字时需要特别注意

131 项目 4 Java API 常用类库 Random 对象的生成 Random 类包含两个构造方法, 下面依次进行介绍 : (1)public Random() 该构造方法使用一个和当前系统时间对应的相对时间有关的数字作为种子数, 然后使用 这个种子数构造 Random 对象 (2)public Random(long seed) 该构造方法可以通过制定一个种子数进行创建 例如 : Random r = new Random(); Random r1 = new Random(10); 2.Random 常用方法 Random 类提供了获取各种数据类型随机数的方法, 其常用方法见表 4-11 所示 表 4-11 Random 类的常用方法 方法 public boolean nextboolean() public double nextdouble() 含义 生成一个随机的 boolean 值, 生成 true 和 false 的值几率相等 生成一个随机的 double 值, 数值介于 [0,1.0) 之间 public int nextint() 生成一个随机的 int 值, 该值在 到 之间 public int nextint(int n) public void setseed(long seed) 生成一个随机的 int 值, 该值介于 [0,n) 的区间 重新设置 Random 对象中的种子数, 等同 new Random(long seed) 例 4.20 Random 类应用举例 import java.util.random; class RandomDemo public static void main(string[] args) Random r=new Random(); System.out.println(" 随机产生一个数 :"+r.nextint()); System.out.println(" 随机产生一个布尔型的值 :"+r.nextboolean()); Random r1=new Random(50); System.out.println(" 随机产生一个浮点数 :"+r1.nextdouble()); System.out.println(" 随机产生一个 0-10 之间的数 :"+r1.nextint(10)); 程序运行结果为 : 随机产生一个数 : 随机产生一个布尔型的值 :true 随机产生一个浮点数 : 随机产生一个 0-10 之间的数 : 大数操作类 在 Java 中提供了大数字操作类,java.math.BigInteger 类与 java.math.bigdecimal 类, 这两 个类用于高精度计算,BigInteger 类是用于大整数的处理类,BigDecimal 类是用于大小数的处

132 132 Java 程序设计 理类 1.BigInteger 类 (1)BigInteger 对象创建 可以使用 BigInteger 类的构造方法创建一个十进制的 BigInteger 类对象, 语法格式如下 : public BigInteger(String val) 其中,val 是十进制字符串 (2)BigInteger 类的常用方法 如表 4-12 所示 表 4-12 BigInteger 类的常用方法 方法 public BigInteger abs() public BigInteger add(biginteger val) public BigInteger subtract(biginteger val) public BigInteger multiply(biginteger val) public BigInteger divide(biginteger val) public BigInteger remainder(biginteger val) public BigInteger pow(int exponent) public int compareto(biginteger val) 含义返回此 BigInteger 的绝对值返回当前 BigInteger 与 val 相加的值返回当前 BigInteger 与 val 相减的值返回当前 BigInteger 与 val 相乘的值返回当前 BigInteger 与 val 相除的值返回当前 BigInteger 取 val 余数的值返回以当前 BigInteger 为底的 exponent 次幂值返回当前 BigInteger 与 val 进行比较的值 例 4.21 BigInteger 类应用举例 import java.math.biginteger; class BigIntegerDemo public static void main(string[] args) BigInteger bigint=new BigInteger(" "); // 取该数加 2 的操作 System.out.println(" 加法操作 :"+bigint.add(new BigInteger("2"))); // 取该数减去 2 的操作 System.out.println(" 减法操作 :"+bigint.subtract(new BigInteger("2"))); // 取该数乘以 2 的操作 System.out.println(" 乘法操作 :"+bigint.multiply(new BigInteger("2"))); // 取该数 2 次方操作 System.out.println(" 平方操作 :"+bigint.pow(2)); 程序运行结果为 : 加法操作 : 减法操作 : 乘法操作 : 平方操作 : BigDecimal 类 (1)BigDecimal 对象的创建

133 项目 4 Java API 常用类库 133 public BigDecimal (double val): 创建一个具有参数所指定双精度值的对象 public BigDecimal (String val): 创建一个具有参数所指定以字符串表示的数值的对象 public BigDecimal(int): 创建一个具有参数所指定整数值的对象 public BigDecimal(long): 创建一个具有参数所指定长整数值的对象 (2)BigDecimal 类的常用方法如表 4-13 所示 表 4-13 BigDecimal 类的常用方法 方法 public BigDecimal add(bigdecimal val) public BigDecimal subtract(bigdecimal val) public BigDecimal multiply(bigdecimal val) public BigDecimal divide(bigdecimal divior,int scale, int mode) 含义 返回当前 BigDecimal 与 val 相加的值 返回当前 BigDecimal 与 val 相减的值 返回当前 BigDecimal 与 val 相乘的值 除法操作, 三个参数分别为除数 商的小数点后的位数 舍入模式 BigDecimal 类中的 divide() 方法有多种设置, 用于返回商末位小数点的处理, 这些模式的 含义和名称如表 4-14 所示 表 4-14 BigDecimal 类 divide() 方法舍入模式 方法 BigDecimal. ROUND_UP BigDecimal. ROUND_DOWN BigDecimal. ROUND_CEILING BigDecimal. ROUND_FLOOR BigDecimal. ROUND_HALF_UP BigDecimal. ROUND_HALF_DOWN BigDecimal. ROUND_HALF_EVEN 含义 在丢弃非零部分之前始终增加数字 ( 始终对非零舍弃部分前面的数字加 1) 在丢弃某部分之前始终不增加数字 ( 从不对舍弃部分前面的数字加 1, 即截短 ) 如果 BigDecimal 为正, 则舍入行为与 ROUND_UP 相同 ; 如果为负, 则舍入行为与 ROUND_DOWN 相同 如果 BigDecimal 为正, 则舍入行为与 ROUND_DOWN 相同 ; 如果为负, 则舍入行为与 ROUND_UP 相同 如果舍弃部分 >= 0.5, 则舍入行为与 ROUND_UP 相同 ; 否则舍入行为与 ROUND_DOWN 相同 ( 四舍五入 ) 如果舍弃部分 > 0.5, 则舍入行为与 ROUND_UP 相同 ; 否则舍入行为与 ROUND_DOWN 相同 ( 五舍六入 ) 如果舍弃部分左边的数字为奇数, 则舍入行为与 ROUND_HALF_ UP 相同 ; 如果为偶数, 则舍入行为与 ROUND_HALF_DOWN 相同 ( 银行家舍入法 ) 例 4.22 BigDecimal 类应用举例 import java.math.bigdecimal; class MyMath public static double add(double d1, double d2)// 进行加法运算 BigDecimal b1 = new BigDecimal(d1); BigDecimal b2 = new BigDecimal(d2);

134 134 Java 程序设计 return b1.add(b2).doublevalue(); public static double sub(double d1, double d2)// 进行减法运算 BigDecimal b1 = new BigDecimal(d1); BigDecimal b2 = new BigDecimal(d2); return b1.subtract(b2).doublevalue(); public static double mul(double d1, double d2)// 进行乘法运算 BigDecimal b1 = new BigDecimal(d1); BigDecimal b2 = new BigDecimal(d2); return b1.multiply(b2).doublevalue(); public static double div(double d1,double d2,int len)// 进行除法运算 BigDecimal b1 = new BigDecimal(d1); BigDecimal b2 = new BigDecimal(d2); return b1.divide(b2,len,bigdecimal.round_half_up).doublevalue(); public static double round(double d,int len)// 进行四舍五入操作 BigDecimal b1 = new BigDecimal(d); BigDecimal b2 = new BigDecimal(1); // 任何一个数字除以 1 都是原数字 //ROUND_HALF_UP 是 BigDecimal 的一个常量, 表示进行四舍五入的操作 return b1.divide(b2,len,bigdecimal.round_half_up).doublevalue(); public class BigDecimalDemo public static void main(string[] args) System.out.println(" 加法运算 :"+MyMath.round(MyMath.add(10.345,3.333),1)); System.out.println(" 乘法运算 :"+MyMath.round(MyMath.mul(10.345,3.333),3)); System.out.println(" 除法运算 :"+MyMath.div(10.345, 3.333, 3)); System.out.println(" 减法运算 :"+MyMath.round(MyMath.sub(10.345,3.333),3)); 程序运行结果为 : 加法运算 :13.7 乘法运算 :34.48 除法运算 :3.104 减法运算 :7.012 习题 1. 选择题 (1) 要产生 [20,999] 之间的随机整数使用哪个表达式? A.(int)(20+Math.random()*97) C.(int)Math.random()*999 (2) 下列程序运行的结果为 : B.20+(int)(Math.random()*980) D.20+(int)Math.random()*980

135 项目 4 Java API 常用类库 135 public class test public static void main(string args[]) int i; float f = 2.3f; double d = 2.7; i=((int)math.ceil(f))*((int)math.round(d)); System.out.println(i); A.4 B.5 C.6 D.6.1 E. 9 (3) 如果以下条件成立, 则用到 java.lang.math 类中哪个方法? method( -4.4 ) == -4; A.round() B.min() C.trunc() D.abs() E.floor() F.ceil() (4) 关于 Math.PI 说法正确的是 A. 静态常量 B. 被 final 修饰 C.Math.PI 每次运行结果不一样 (5) 可以获取绝对值的方法是 D. 以上的说法都正确 A.Math.ceil() B.Math.floor() C.Math.pow() D.Math.abs() (6) Math.random() 说法正确的是 A. 返回一个不确定的整数 B. 返回 0 或是 1 C. 返回一个随机的 double 类型数, 该数大于等于 0.0 小于 1.0 D. 返回一个随机的 int 类型数, 该数大于等于 0.0 小于 1.0 (7)Math.ceil(-12.5) 运行结果是 A.-13 B.-11 C.-12 D (8)Math.floor(15.6) 运行结果是 A.15.0 B.15 C.16.0 D.16.6 (9) 在 Random 类中, 可以生成 100 以内正整数随机数的方法是 A.nextDouble() B.nextFloat() C.nextInt(100) D.nextInt() (10) 表达式 (int)math.random() + 1 的值是 A.0 B.1 C.2 D. 大于等于 1.0 且小于 2.0 的 double 型随机数 2. 调用类 java.lang.math 的成员方法 public static double random() 运算下面表达式 次, 统计其中生成的整数 0,1,2,,20 的个数分别是多少, 并输出统计结果 (int)(math.random()*20+0.5) 任务 5 Java 日期类 Date 类和 SimpleDateFormat 类 Date 类中封装了有关日期和时间的信息, 用户还可以通过调用相应的方法来获取系统时 间或设置日期和时间 Date 类在 java.util 类库中, 因此, 在使用它之前, 应先进行该类库的引

136 136 Java 程序设计 入, 例如 : import java.util.*; 1.Date 对象的创建 public Date(): 创建一个保存了当前系统时间的 Date 对象 public Date(long date): 创建一个 Date 对象并初始化它代表指定的毫秒数 date 以毫 秒为单位, 从 1970 年 1 月 1 日 0 时 0 分 0 秒开始 2.SimpleDateFormat 对象的创建 如果想用自己习惯的格式定义时间可以使用 SimpleDateFormat 类来实现日期的格式化 该类在 java.text 类包当中, 它有一个常用构造方法 : public SimpleDateFormat (String pattern): 创建一个用参数 pattern 指定的格式的对象, 该对象调用 format(date date) 方法格式化时间对象 date pattern 中应当含有一些有效 的字符序列, 如表 4-15 所示 表 4-15 pattern 有效字符序列 字符序列含义字符序列含义 y 或 yy 2 位数字输出年份 M 或 MM 2 位数字输出月份 d 或 dd 2 位数字输出日期 H 或 HH 2 位数字输出小时 m 或 mm 2 位数字输出分 s 或 ss 2 位数字输出秒 E 输出星期 S 输出毫秒 D 一年中的第几天 F 一个月中第几个星期几 w 一年中第几个星期 W 一个月中第几个星期 a 上午 / 下午标记 z 时区 k 24 小时制表示时间 K 12 小时制表示时间 例 4.23 日期类应用举例 import java.util.date; import java.text.simpledateformat; public class FormatDateTime public static void main(string[] args) SimpleDateFormat myfmt=new SimpleDateFormat("yyyy 年 MM 月 dd 日 HH 时 mm 分 ss 秒 "); SimpleDateFormat myfmt1=new SimpleDateFormat("yy/MM/dd HH:mm"); SimpleDateFormat myfmt2=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 等价于 now.tolocalestring() SimpleDateFormat myfmt3=new SimpleDateFormat("yyyy 年 MM 月 dd 日 HH 时 mm 分 ss 秒 E "); SimpleDateFormat myfmt4=new SimpleDateFormat( " 一年中的第 D 天一年中第 w 个星期一月中第 W 个星期在一天中 k 时 z 时区 "); Date now=new Date(); System.out.println(myFmt.format(now)); System.out.println(myFmt1.format(now)); System.out.println(myFmt2.format(now)); System.out.println(myFmt3.format(now)); System.out.println(myFmt4.format(now));

137 项目 4 Java API 常用类库 137 System.out.println(now.toGMTString()); System.out.println(now.toLocaleString()); System.out.println(now.toString()); 程序运行结果为 : 2016 年 07 月 27 日 10 时 46 分 36 秒 16/07/27 10: :46: 年 07 月 27 日 10 时 46 分 36 秒星期三一年中的第 209 天一年中第 31 个星期一月中第 5 个星期在一天中 10 时 CST 时区 27 Jul :46:36 GMT :46:36 Wed Jul 27 10:46:36 CST Calendar 类 在 JDK1.0 中,Date 类是唯一的一个代表时间的类, 但是由于 Date 类不便于实现国际化, 所以从 JDK1.1 版本开始, 推荐使用 Calendar 类进行时间和日期的处理, 其位于 java.util 包中 在设计上,Calendar 类的功能要比 Date 类强大很多, 而且在实现方式上也比 Date 类要复杂一 些, 下面就介绍一下 Calendar 类的使用 1.Calendar 对象的创建 Calendar 类是一个抽象类, 在实际使用时实现特定的子类的对象, 创建对象的过程对程序 员来说是透明的, 只需要使用 getinstance 方法创建即可 public static Calendar.getInstance() 创建一个由当前日期和时间初始化的 Calendar 对象 2.Calendar 类常用方法 public final void set(int year,int month,int date) 按参数中的 year month date 设置指定的日期, 其中年份的数值直接书写, 月份的值为 实际的月份值减 1, 日期的值就是实际的日期值 例如 : 设置的日期为 2010 年 3 月 9 日的代 码如下 : Calendar c1 = Calendar.getInstance(); c1.set(2010, 3-1,9); public void set(int field,int value) 只对日期中某个指定字段设置其值 参数 field 代表要设置的字段的类型, 常见类型如下 : Calendar.YEAR 年份 ; Calendar.MONTH 月份 ; Calendar.DATE 日期 ; Calendar.DAY_OF_MONTH 日期, 和上面的字段完全相同 ; Calendar.HOUR 12 小时制的小时数 ; Calendar.HOUR_OF_DAY 24 小时制的小时数 ;

138 138 Java 程序设计 Calendar.MINUTE 分钟 ; Calendar.SECOND 秒 ; Calendar.DAY_OF_WEEK 星期几 public int get(int field) 返回给定日历字段的值 参数 field 与 set() 方法中的含义相同 public abstract void add(int field,int amount) 该方法的作用是在 Calendar 对象中的某个字段上增加或减少一定的数值, 增加时 amount 的值为正, 减少时 amount 的值为负 public boolean after(object when) 该方法的作用是判断当前日期对象是否在 when 对象的后面, 如果在 when 对象的后面则 返回 true, 否则返回 false public final Date gettime() 该方法的作用是将 Calendar 类型的对象转换为对应的 Date 类对象, 两者代表相同的时间点 public final void settime(date date) 该方法的作用是将 Date 对象转换为对应的 Calendar 对象, 两者代表相同的时间点 public long gettimeinmillis() 返回 Calendar 对象距 1970 年 1 月 1 日 0 时 0 分 0 秒的时间值, 单位为毫秒 public void settimeinmillis(long millis) 用给定的 long 值设置此 Calendar 的当前时间值 例 4.24 计算两个日期之间相差的天数 import java.util.*; public class DateExample1 public static void main(string[] args) // 设置两个日期 // 日期 :2008 年 5 月 11 号 Calendar c1=calendar.getinstance(); c1.set(2008,5-1, 11); // 日期 :2011 年 6 月 1 号 Calendar c2=calendar.getinstance(); c2.set(2011,6-1, 1); // 转换为相对时间 long t1=c1.gettimeinmillis(); long t2=c2.gettimeinmillis(); // 计算天数 long days=(t2-t1)/(24 * 60 * 60 * 1000); System.out.println(days); 出现运行结果为 : 1116 例 4.25 输出当前月的日历 import java.util.*;

139 项目 4 Java API 常用类库 139 public class DateExample2 public static void main(string[] args) Calendar c=calendar.getinstance();// 获得当前时间 c.set(calendar.date,1);// 设置代表的日期为 1 号 int start=c.get(calendar.day_of_week);// 获得 1 号是星期几 // 获得当前月的最大日期数 int maxday = c.getactualmaximum(calendar.date); // 输出标题 System.out.println(" 星期日星期一星期二星期三星期四星期五星期六 "); for(int i=1;i<start;i++)// 输出开始的空格 System.out.print(" "); for(int i=1;i<=maxday;i++)// 输出该月中的所有日期 System.out.print(" " + i);// 输出日期数字 System.out.print(" ");// 输出分隔空格 if(i<10) System.out.print(' '); if((start + i - 1) % 7 == 0)// 判断是否换行 System.out.println(); System.out.println();// 换行 程序运行结果为 : 星期日星期一星期二星期三星期四星期五星期六 习题 1. 选择题 (1)DateFormat 类中的 parse() 方法描述正确的是 A. 将毫秒值转成日期对象 B. 格式化日期对象 C. 将字符串转成日期对象 D. 将日期对象转成字符串 (2) 可以获取当前日期毫秒值的方法是 A.Date d = new Date(); d.gettime(); B.long start = System.currentTimeMillis(); C.Calendar 中的 gettime(); D.DateFormat 中的 gettime()

140 140 Java 程序设计 (3) 哪个方法可以将毫秒值转成日期对象 A.Date 类中的构造方法 B.Date 类中的 settime 方法 C.Date 类中的 gettime 方法 D.SimpleDateFormat 类中的 format 方法 (4)Date 类中, 哪个方法可以返回当前日期对象的毫秒值 A.getSeconds() B.getTime() C.getDay() D.getDate() (5)Calendar 类中的 Day_OF_WEEK 可以获取到 A. 年中的某一天 B. 月中的某一天 C. 星期中的某一天 D. 月中的最后一天 2.Java 程序定义一个日期类, 包含年月日三个数据成员, 包含读取和设置各数据成员的 方法, 包含两个不同的构造函数, 包含一个判断是否为闰年的函数, 包含一个测试类 3. 设计一个日期类, 可以输入年月日作为构造时的参数, 如果不使用参数, 则设定为 1900 年 1 月 1 日 ; 编写一个方法 equals 判断两个日期是否相等 ; 另一个方法 compareto 可以 进行日期之间的比较, 返回两个日期之间的相差的天数 任务 6 正则表达式 正则表达式是一种可以用于模式匹配和替换的规范, 一个正则表达式就是由普通的字符 ( 例如字符 a 到 z) 以及特殊字符 ( 元字符 ) 组成的文字模式, 它用以描述在查找文字主体时 待匹配的一个或多个字符串 正则表达式作为一个模板, 将某个字符模式与所搜索的字符串进 行匹配 字符串对象 (String) 调用 matches() 方法可以判断当前字符串对象是否和参数 regex 指定 的正则表达式匹配 : 所示 public boolean matches(string regex) 1. 正则表达式 因为正则表达式是一个很庞杂的体系, 在此仅列出常用的元字符及其含义, 具体如表 4-16 表 4-16 元字符使用说明 元字符 含义. 匹配除 "\r\n" 之外的任何单个字符, 否则请使用 "[\s\s]" 之类的模式 \d 数字字符匹配, 等效于 [0-9] \D 非数字字符匹配, 等效于 [^0-9] \s 匹配任何空白字符, 包括空格 制表符 换页符等, 与 [ \f\n\r\t\v] 等效 \S 匹配任何非空白字符, 与 [^ \f\n\r\t\v] 等效 \w 匹配包括下线划的任何单词字符 (Unicode 字符集 ), 与 "[A-Za-z0-9_]" 等效 在正则表达式中可以使用方括号, 在里面包含若干个字符来表示一个元字符, 该元字符 代表方括号中的任何一个字符 另外, 方括号里允许嵌套中括号, 进行交 并 补等运算 方 括号元字符的含义如表 4-17 所示

141 项目 4 Java API 常用类库 141 表 4-17 方括号元字符使用说明 元字符 含义 [xyz] [^xyz] 匹配包含的任一字符 例如,"[abc]" 匹配 "plain" 中的 "a" 匹配未包含的任何字符 例如,"[^abc]" 匹配 "plain" 中 "p","l","i","n" [a-za-z] a 到 z 或 A 到 Z, 两头的字母包括在内 ( 范围 ) [a-d[m-p]] a 到 d 或 m 到 p, 相当于 [a-dm-p]( 并集 ) [a-z&&[def]] d e 或 f( 交集 ) [a-z&&[^bc]] a 到 z, 除了 b 和 c, 相当于 [ad-z]( 减去 ) [a-z&&[^m-p]] a 到 z, 而非 m 到 p, 相当于 [a-lq-z]( 减去 ) 在正则表达式中同样可以使用限定修饰符, 表 4-18 给出了常用限定修饰符的使用方法 表 4-18 限定修饰符使用说明 元字符 含义 X? X, 一次或一次没有 X* X, 零次或多次 X+ X, 一次或多次 Xn Xn, Xn,m X, 恰好 n 次 X, 至少 n 次 X, 至少 n 次, 但是不超过 m 次 2.Pattern 和 Matcher 的用法 String 上可使用正则表达式的操作, 实际上是利用了 java.util.regex.pattern 与 java.util.regex.matcher 的功能 当调用 String 的 matches() 方法时, 实际上是调用 Pattern 的静态方法 matches(), 这个方法会返回 boolean 值, 表示字符串是否符合正则表达式 如果想要将正则表达式视为一个对象来重复使用, 可以使用 Pattern 的静态方法 compile() 进行编译 compile() 方法会返回一个 Pattern 的实例, 这个实例代表正则表达式, 之后就可以重复使用 Pattern 实例的 matcher() 方法来返回一个 Matcher 的实例, 代表符合正则式的实例, 这个实例上有一些寻找符合正则表达式条件的方法可供操作 (1)Pattern 类常用方法 public static Pattern compile(string regex): 将给定的正则表达式编译并赋予给 Pattern 类 public Matcher matcher(charsequence input): 生成一个给定命名的 Matcher 对象 public String pattern(): 返回该 Pattern 对象所编译的正则表达式 (2)Matcher 类常用方法 public boolean find(): 尝试在目标字符串里查找下一个匹配子串 public boolean find(int start): 重设 Matcher 对象, 并且尝试在目标字符串里从指定的位置开始查找下一个匹配的子串 public boolean matches(): 尝试对整个目标字符展开匹配检测, 也就是只有整个目标字符串完全匹配时才返回真值

142 142 Java 程序设计 public Pattern pattern(): 返回该 Matcher 对象的现有匹配模式, 也就是对应的 Pattern 对象 public Matcher appendreplacement(stringbuffer sb, String replacement): 将当前匹配子 串替换为指定字符串, 并且将替换后的子串及其之前到上次匹配子串之后的字符串段 添加到一个 StringBuffer 对象里 public StringBuffer appendtail(stringbuffer sb): 将最后一次匹配工作后剩余的字符串 添加到一个 StringBuffer 对象里 例 4.26 把句子里 aght 改为 ight import java.util.regex.*; public class MatcherTest public static void main(string[] args) throws Exception// 生成 Pattern 对象并且编译一个简单的正则表达式 "Kelvin" Pattern p=pattern.compile("aght"); // 用 Pattern 类的 matcher() 方法生成一个 Matcher 对象 Matcher m=p.matcher("no need to laght a naght laght on a laght naght like tonaght"); StringBuffer sb=new StringBuffer(); int i=0; boolean result=m.find();// 使用 find() 方法查找第一个匹配的对象 while(result)// 使用循环将句子里所有的 aght 找出并替换再将内容加到 sb 里 i++; m.appendreplacement(sb,"ight"); System.out.println(" 第 "+i+" 次匹配后 sb 的内容是 :"+sb); result = m.find();// 继续查找下一个匹配对象 m.appendtail(sb);// 最后调用 appendtail() 方法将最后一次匹配后的剩余字符串加到 sb 里 System.out.println(" 调用 m.appendtail(sb) 后 sb 的最终内容是 :"+sb.tostring()); 程序运行结果为 : 第 1 次匹配后 sb 的内容是 :no need to light 第 2 次匹配后 sb 的内容是 :no need to light a night 第 3 次匹配后 sb 的内容是 :no need to light a night light 第 4 次匹配后 sb 的内容是 :no need to light a night light on a light 第 5 次匹配后 sb 的内容是 :no need to light a night light on a light night 第 6 次匹配后 sb 的内容是 :no need to light a night light on a light night like tonight 调用 m.appendtail(sb) 后 sb 的最终内容是 :no need to light a night light on a light night like tonight 习题 1. 选择题 (1) 正则表达式中可以表示所有的单词字符信息的是下面哪个规则 : A.\W B.\w C.[a-zA-Z] D.[a-zA-Z_0-9] (2) 能够完全匹配字符串 "(010) " 和字符串 " " 的正则表达式包括

143 项目 4 Java API 常用类库 143 A."\(?\d3\)?-?\d8" B."[0-9()-]+" C."[0-9(-)]*\d*" D."[(]?\d*[)-]*\d*" (3) 能够完全匹配字符串 "c:\rapidminer\lib\plugs" 的正则表达式包括 A."c:\rapidminer\lib\plugs" B."c:\\rapidminer\\lib\\plugs" C."(?i)C:\\RapidMiner\\Lib\\Plugs" D."(?s)C:\\RapidMiner\\Lib\\Plugs" (4) 能够完全匹配字符串 "back" 和 "back-end" 的正则表达式包括 A."\w4-\w3 \w4" B."\w4 \w4-\w3" C."\S+-\S+ \S+" D."\w*\b-\b\w* \w*" (5) 能够完全匹配字符串 "go go" 和 "kitty kitty", 但不能完全匹配 "go kitty" 的正则表达式 包括 A."\b(\w+)\b\s+\1\b" B."\w2,5\s*\1" C."(\S+) \s+\1" D."(\S2,5)\s1,\1" (6) 能够在字符串 "aabaaabaaaab" 中匹配 "aab", 而不能匹配 "aaab" 和 "aaaab" 的正则表达式 包括 A."a*?b" B."a,2b" C."aa??b" D."aaa??b" 2.Pattern 和 Matcher 方法的主要功能是什么? 项目总结 1.Object 类是 Java 中所有类的父类, 无论是系统类还是自定义类, 都由 Object 类派生出来 System 类主要用于对系统实施一些操作, 如调用 exit() 方法关闭虚拟机等 2. 字符串是程序设计中组织字符的基本数据结构,Java 语言把字符串当作对象来处理, 并提供了一系列的方法对整个字符串进行操作 StringBuffer 类是对 String 类的功能补充, 它能创建可修改的字符串序列, 便于存放一个可变的字符序列 3. 基本数据类型的封装提供了另外一种使用数据类型的方法, 尤其是在涉及到以对象形式传递数据的场合, 必须把基本数据类型封装成相应对象, 以符合相关要求 4.Java 中 Math 类为常用的数学运算提供了许多方便 快捷的方法,BigInteger 类提供了大整数的处理方法,Date 类和 Calender 类用于实现对日期以及日历的相应操作 5. 正则表达式是含有一些具有特殊意义的字符的字符串, 字符串对象调用 matches() 方法可以判断当前字符串对象是否和参数 regex 指定的正则表达式匹配

144 项目 5 Java 泛型与集合框架 编写程序时经常需要与各种数据打交道 为了处理这些数据所选的数据结构对于程序的 运行效率非常重要 数据结构可以通过具体的算法来实现 Java 提供了一些实现常见数据机 构的类和接口 它们统称为 Java 集合框架 在 JDK1.5 后 Java 集合框架开始支持泛型 理解泛型的概念 熟练掌握泛型的定义及应用 掌握 List Set 和 Map 接口的使用 掌握 Collections 类常用方法 掌握 Iterator Enumeration 的用法 任务 1 泛型 泛型是 Java SE 1.5 的新特性 泛型的本质是参数化类型 也就是说所操作的数据类型被 指定为一个参数 这种参数类型可以用在类 接口和方法的创建中 分别称为泛型类 泛型接 口 泛型方法 泛型类 1 泛型类的声明 泛型类的声明格式如下 class 类名<泛型列表> 例如 class A<E> 其中 A 是泛型类的名称 E 是其泛型列表中的一个类型参数 它可以是任何类或接口 但不能是基本数据类型 泛型声明时 E 可以作为类的成员变量的类型 方法的类型以及局部 变量的类型 泛型列表也可以有多个 必须用逗号隔开 格式如下 class A<E,F> 其中 F 与 E 的含义相同 即没有指定 F 是何种类型数据 它可以是任何类或接口 注意 类型参数使用大写形式 而且比较短 在 Java 类库中使用参数类型 E 表示集合元 素类型 K 表示关键字类型 V 表示值的类型 T 表示任意类型

145 项目 5 Java 泛型与集合框架 145 泛型类的结构和普通类完全类似, 由成员变量和方法构成 例如 : 我们声明一个泛型类 Pair, 代码如下 : class Pair<T> private T first; private T second; public Pair()first=null; second=null; public Pair(T first,t second)this.first=first; this.second=second; public T getfirst()return first; public T getsecond()return second; public void setfirst(t newvalue)first=newvalue; public void setsecond(t newvalue)second=newvalue; 2. 使用泛型类声明对象 与普通类不同的是, 使用泛型类型声明和创建对象时, 类名后多了一对 < > 符号 而 且要用具体的类型替换 < > 符号中的泛型 如 : Pair<String> pair; // 声明一个泛型对象 pair= new Pair<String>("Hello","Java");// 利用泛型类创建泛型对象 例 5.1 泛型类的应用 public class PairTest public static void main(string[] args) Pair<String> pair=new Pair<String>("Hello","Java"); System.out.println("first="+pair.getFirst()); System.out.println("second="+pair.getSecond()); 程序运行结果为 : first=hello second=java 泛型方法 前面已经介绍了如何定义一个泛型类 实际上, 还可以定义一个带有参数类型的方法即 泛型方法 泛型方法使得该方法能够独立于类而产生变化, 泛型方法所在的类可以是泛型类, 也可以不是泛型类 创建一个泛型方法常用的形式如下 : [ 访问修饰符 ] [static] [final] < 泛型列表 > 返回值方法名 ( [ 形式参数列表 ] ) 下面例题中在 GenericMethod 类当中声明一个 f() 泛型方法, 用于返回调用该方法时, 所 传入的参数类型的类名 例 5.2 泛型方法的应用 class GenericMethod public <T> void f(t x) System.out.println(x.getClass().getName()); public class GenericMethodTest

146 146 Java 程序设计 public static void main(string[] args) GenericMethod gm=new GenericMethod(); gm.f(" "); gm.f(1); gm.f(1.0f); gm.f('c'); gm.f(gm); 程序运行结果为 : java.lang.string java.lang.integer java.lang.float java.lang.character GenericMethod 注意 : 当使用泛型类时, 必须在创建对象的时候指定类型参数的值, 而使用泛型方法的时候, 通常不必指明参数类型, 因为编译器会为我们找出具体的类型 这称为类型参数推断 因此我们 可以像调用普通方法一样调用 f(), 编译器会根据调用 f() 时传入的参数类型与泛型类型进行匹配 泛型接口 除了泛型类和泛型方法, 还可以使用泛型接口 泛型接口的定义与泛型类非常相似, 它 的声明形式如下 : interface 接口名 < 泛型列表 > 码如下 : 下面我们创建了一个名为 MinMax 的接口, 用来返回某个对象集的最小值或最大值 代 interface MinMax<T extends Comparable<T>> T min(); T max(); MinMax 接口类型参数 T 是有界类型, 它必须是 Comparable 的子类 注意到 Comparable 本身也是一个泛型类, 它是由系统定义在类库中的, 可以用来比较两个对象的大小 接下来我 们定义一个类 MyClass 来实现这个接口, 代码如下 : class MyClass<T extends Comparable<T>> implements MinMax<T> T[] vals; MyClass(T[] ob) vals = ob; public T min() T val = vals[0]; for(int i=1; i<vals.length; ++i) if(vals[i].compareto(val)<0)val=vals[i]; return val; public T max()

147 项目 5 Java 泛型与集合框架 147 T val = vals[0]; for(int i=1;i<vals.length;++i) if (vals[i].compareto(val)>0)val=vals[i]; return val; 注意 :MyClass 的声明部分中, 它的类型参数 T 必须和要实现的接口中的声明完全一样 反而是接口 MinMax 的类型参数 T 最初是写成有界形式的, 现在已经不再需要重写一遍 ( 对 于有界类型在这里不需要重复再写 ) 例 5.3 泛型接口的应用 public class MyClassTest public static void main(string args[]) Integer inums[] = 56,47,23,45,85,12,55; Character chs[] = 'x','w','z','y','b','o','p'; MyClass<Integer> iob = new MyClass<Integer>(inums); MyClass<Character> cob = new MyClass<Character>(chs); System.out.println("Max value in inums: "+iob.max()); System.out.println("Min value in inums: "+iob.min()); System.out.println("Max value in chs: "+cob.max()); System.out.println("Min value in chs: "+cob.min()); 出现运行结果为 : Max value in inums: 85 Min value in inums: 12 Max value in chs: z Min value in chs: b 习题 1.Java 中的泛型是什么? 使用泛型有什么好处? 2.Java 的泛型是如何工作的? 什么是类型擦除? 3. 什么是泛型中的限定通配符和非限定通配符? 4. 如何编写一个泛型方法, 让它接受泛型参数并返回泛型类型? 5.Java 中如何使用泛型编写带有参数的类? 6.Array 中可以用泛型吗? 任务 2 泛型集合类 Java 泛型的一个重要优点是在使用泛型类建立数据结构时, 不必强制性类型转换, 即不要求运行时检查类型 JDK1.5 是支持泛型的编译器, 它将运行时类型检查提前到编译时执行, 使代码更安全 Java 推出泛型的主要目的是为了建立具有类型安全的数据结构, 如链表 散列映射等 集合可理解为一个容器, 该容器主要指映射 (map) 集合 (set) 列表 (list) 散列表

148 148 Java 程序设计 (hashtable) 等抽象数据结构 容器可以包含有多个元素, 这些元素通常是一些 Java 对象 针对上述抽象数据结构所定义的一些标准编程接口称之为集合框架 我们把 JDK1.5 及以后版本中支持泛型的集合类称之为 泛型集合类, 作为对集合接口的实现 Collection<E> 接口 Java 集合框架由两种类型构成, 一个是 Collection, 另一个是 Map Collection 对象用于存放一组对象,Map 对象用于存放一组关键字 / 值的对象 Collection 和 Map 是最基本的接口, 它们又有子接口, 这些接口都位于 java.util 包中, 其层次关系如图 5-1 所示 图 5-1 集合框架层次关系图 Collection 接口是构造集合框架的基础 它声明所有集合类都将拥有的核心方法, 这些方法的定义如表 5-1 所示 因为所有集合框架类均实现了 Collection 接口, 所以掌握这些方法的使用对于理解集合框架是十分必要的 表 5-1 Collection 定义的方法 方法 描述 int size() boolean isempty() boolean contains(object o) boolean containsall(collection c) Iterator iterator() Object[] toarray() Object[] toarray(object a[]) boolean add(object o) boolean addall(collection c) boolean remove(object o) boolean removeall(collection c) boolean retainall(collection c) void clear() boolean equals(object o) int hashcode() 返回调用类集中元素的个数如果调用类集是空, 则返回 true; 否则返回 false 判断类集是否包含 o 元素判断类集是否包含 c 中的所有元素返回调用类集的迭代程序将当前类集中的元素转换成 Object 类型的数组将当前类集中的元素转换成指定类型的对象数组将 o 加入到调用类集中将 c 中的所有元素都加入到调用类集中从调用类集中删除 o 的一个实例从调用类集中删除 c 的所有元素删除调用类集中除了包含在 c 中的元素之外的所有元素从调用类集中删除所有元素判断类集与 o 是否相等返回调用类的散列码

149 项目 5 Java 泛型与集合框架 Set<E> 接口 集合是指一个不包含重复元素的对象集合, 是数学上 集合 概念的一种抽象 其本身 是无序的 Set<E> 接口继承 Collection<E> 接口, 它不允许集合中存在重复项, 每个具体的 Set 实现类依赖添加的对象的 equals() 方法来检查独一性 另外,Set<E> 接口没有引入新方法, Set<E> 就是一个 Collection<E>, 只不过其行为不同 在集合框架中,HashSet 类和 TreeSet 类实现了 Set 接口 这两个类定义在 java.util 包中 一般情况下, 采用 HashSet 类创建一个无序的集合对象, 采用 TreeSet 类创建有序的集合对象 这里我们只介绍比较常用的 HashSet 类 HashSet 类所创建的类集使用散列表进行存储 关键字到其散列码的转换是自动执行的, 即看不到散列码本身, 程序代码也不能直接索引散列表 散列法的优点在于即使对于大集合, 其基本操作, 如 add(),contains(),remove() 和 size() 等方法运行消耗的时间保持不变 HashSet 类的构造方法如下 : HashSet(): 创建一个空的哈希集合, 装填因子 (load factor) 是 0.75 HashSet(Collection c): 用指定的集合 c 的元素创建一个哈希集合 HashSet(int initialcapacity): 创建一个哈希集合, 并指定集合初始容量 HashSet(int initialcapacity, float loadfactor): 创建一个哈希集合, 并指定集合初始容量 和装填因子 例 5.4 从命令行输入若干英文单词, 输出每个重复的单词, 不同单词的个数及消除重复 单词后的列表 import java.util.*; public class FindDups public static void main(string args[]) Set<String> hs = new HashSet<String>(); for(string a : args) if (!hs.add(a)) System.out.println("Duplicate: " + a); System.out.println(hs.size()+" distinct words: "+hs); 如果使用下面命令运行程序 : C:\>java FindDups you came you saw you left 程序运行结果为 : Duplicate: you Duplicate: you 4 distinct words: [left, came, saw, you] 注意 : 由于上面程序中使用的实现类为 HashSet, 它并不保证集合中元素的顺序 List<E> 接口 List<E> 接口也是 Collection<E> 接口的子接口, 它实现一种顺序表的数据结构, 有时也称

150 150 Java 程序设计 为序列 存放在 List 中的所有元素都有一个下标 ( 下标从 0 开始 ), 可以通过下标访问 List 中的元素 List 中可以包含重复元素 List 接口及其实现类的层次结构如图 5-2 所示 : 图 5-2 List 接口及实现类的层次结构 在列表接口中, 除了在集合中所定义的方法外, 还有一系列针对列表中特定位置的方法 列表 List<E> 接口定义在 java.util 包中, 这些方法如表 5-2 所示 表 5-2 List 定义的方法 方法 void add(int index, E element) boolean addall(int index, Collection c) Object get(int index) boolean containsall(collection c) int indexof(object o) int lastindexof(object o) Object remove(int index) boolean add(object o) boolean addall(collection c) boolean remove(object o) Object set(int index, Object o) List sublist(int start,int end) 描述在列表的指定位置索引为 index 处插入指定元素 element 将指定 Collection c 中的所有元素都插入到列表中的指定位置返回存储在调用类集内指定下标处的对象判断类集是否包含 c 中的所有元素返回调用列表中 o 的第一个实例下标返回调用列表中 o 的最后一个实例下标删除调用列表中 index 位置的元素并返回删除的元素将 o 加入到调用类集中将 c 中的所有元素都加入到调用类集中从调用类集中删除 o 的一个实例用 o 对调用列表内由 index 指定位置进行赋值返回调用列表中从 start 到 end 之间的元素列表 List 接口中只是声明了一些列表元素存取的方法, 而没有具体的实现, 要真正实现对列表的操作还需要通过其实现类, 下面列出了几种 List 接口的实现类 1.ArrayList 类 ArrayList 类继承 AbstractList 并实现 List 接口 它提供了一种数组列表的数据结构 ArrayList 类实际上实现了一个变长的对象数组, 其元素可以动态地增加和删除 它的定位访问时间是常量时间 ArrayList 的构造方法如下 : ArrayList(): 创建一个空的数组列表对象 ArrayList(Collection c): 用集合 c 中的元素创建一个数组列表对象 ArrayList(int initialcapacity): 创建一个空的数组列表对象, 并指定初始容量

151 项目 5 Java 泛型与集合框架 151 例 5.5 ArrayList 类的应用 import java.util.*; public class ListDemo public static void main(string args[]) Collection c=new ArrayList(); String weekday[]=new String[] "Sunday","Monday","Tuesday","Wednesday", "Thursday","Friday","Saturday"; for(int i=0;i<weekday.length;i++) c.add(weekday[i]); System.out.println(c); String weekend[]=new String[]"Saturday","Sunday"; Collection c1=new ArrayList(); Collection c2=new ArrayList(); c1.add(weekend[0]); c1.add(weekend[1]); System.out.println(c1); c.removeall(c1); c2=new ArrayList(c); //c2=c; System.out.println(c); System.out.println("c.containsAll(c1)="+c.containsAll(c1)); System.out.println(c2); c.addall(c1); System.out.println(c); System.out.println("c.containsAll(c1)="+c.containsAll(c1)); c.retainall(c2); System.out.println(c); 程序运行结果为 : [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday] [Saturday, Sunday] [Monday, Tuesday, Wednesday, Thursday, Friday] c.containsall(c1)=false [Monday, Tuesday, Wednesday, Thursday, Friday] [Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday] c.containsall(c1)=true [Monday, Tuesday, Wednesday, Thursday, Friday] 2.LinkedList 类 ArrayList 类是通过数组方式来实现的, 相当于可变长度的数组 通过数据结构的学习, 我们知道 ArraryList 在插入或删除数据元素时, 需要批量移动数据元素, 故性能较差 ; 但查询 数据元素时, 可以通过下标进行访问, 所以遍历元素或随机访问元素时效率高,LinkedList 类 则是通过链表结构来实现, 情况正好相反 一般说来, 若对一个列表结构的开始和结束处有频 繁的添加和删除操作时, 一般选用 LinkedList 类所实例化的对象表示该列表 LinkedList 类扩展了 AbstractSequentialList 类并实现 List 接口, 其构造方法如下 :

152 152 Java 程序设计 LinkedList(): 创建一个空的链表 LinkedList(Collection c): 用集合 c 中的元素创建一个链表 通常利用 LinkedList 对象表示一个堆栈 (stack) 或队列 (queue) 对此 LinkedList 类中特 别定义了一些方法, 而这是 ArrayList 类所不具备的 这些方法用于在列表的开始和结束处添 加和删除元素, 其方法定义如下 : public void addfirst(object o): 将指定元素插入此列表的开头 public void addlast(object o): 将指定元素添加到此列表的结尾 public Obejct removefirst(): 移除并返回此列表的第一个元素 public Object removelast(): 移除并返回此列表的最后一个元素 public Obejct getfirst(): 获得列表的第一个元素 public Object getlast(): 获得列表的最后一个元素 例 5.6 LinkedList 类的应用 import java.util.linkedlist; public class LinkedListDemo public static void main(string[] args) LinkedList<String> queue=new LinkedList<String>(); queue.addfirst("set"); queue.addlast("hashset"); queue.addlast("treeset"); queue.addfirst("list"); queue.addlast("arraylist"); queue.addlast("linkedlist"); queue.addlast("map"); queue.addfirst("collection"); System.out.println(queue); queue.removelast(); queue.removefirst(); System.out.println(queue); 程序运行结果为 : [collection, List, set, HashSet, TreeSet, ArrayList, LinkedList, map] [List, set, HashSet, TreeSet, ArrayList, LinkedList] Map<K,E> 接口 1.Map<K,E> 接口 映射接口是与 Collection 接口相并列的一个接口, 该接口描述了不重复的键到值的映射 一个映射对象中不能包含重复的键, 且每个键只能映射到一个值 在映射中 键 值 对所描 述的对象称为条目 (Entry) Map<K, V> 定义在 java.util 包中, 其主要方法见表 5-3 所示 2.HashMap 类 HashMap 类和 TreeMap 类是集合框架提供的 Map 接口的实现类 定义在 java.util 包中 在 Map 中插入 删除和定位元素,HashMap 是最好的选择 如果需要按顺序遍历键, 则选择

153 项目 5 Java 泛型与集合框架 153 TreeMap 这里我们主要介绍 HashMap 类的使用 表 5-3 Map 定义的方法 方法 描述 V put(k key, V value); V get(object key); V remove(object key); boolean containskey(object key); boolean containsvalue(object value); boolean isempty(); void putall(map<?extends K,?extends V> t); void clear(); public Set<K> keyset(); public Collection<V> values(); 将指定的值与此映射中的指定键关联 返回指定键所映射的值 ; 如果此映射不包含该键的映射关系, 则返回 null 如果存在一个键的映射关系, 则将其从此映射中移除 如果此映射包含指定键的映射关系, 则返回 true 如果此映射将一个或多个键映射到指定值, 则返回 true 判断集合是否为空 从指定映射中将所有映射关系复制到此映射中 从 Map 对象中清除所有的映射 返回此映射中包含的键的 Set 视图 返回此映射中包含的值的 Collection 视图 HashMap 类的构造方法如下 : HashMap(): 创建一个空的映射对象, 使用缺省的装填因子 (0.75) HashMap(int initialcapacity): 用指定的初始容量和缺省的装填因子 (0.75) 创建一个 映射对象 HashMap(int initialcapacity, float loadfactor): 用指定的初始容量和指定的装填因子创 建一个映射对象 HashMap(Map t): 用指定的映射对象创建一个新的映射对象 例 5.7 HashMap 类的应用 import java.util.*; public class Frequency public static void main(string args[]) Map<String, Integer> m = new HashMap<String, Integer>(); // 由命令行参数初始化单词频率表 for (String a : args) Integer freq = m.get(a); m.put(a, (freq==null?1:freq+1)); System.out.println(m.size() + " distinct words:"); System.out.println(m); 使用下面的命令行运行该程序 : C:\>java Frequency no need to light a night light on a light night like tonight. 程序运行结果为 : 9 distinct words:

154 154 Java 程序设计 no=1, a=2, need=1, light=3, like=1, night=2, tonight.=1, to=1, on=1 习题 1. 选择题 (1) 在 Java 中, 对象可以使用 键 - 值 的形式保存数据 A.ArrayList B.HashSet C.HashMap D.LinkedList (2) 创建一个类, 来存储唯一的对象元素, 元素不需要保持特定顺序, 但是必须唯一 最能满足这种要求的接口是 : ( 多选 ) A.Set B.List C.Map D.Array E.HashSet (3) 以下不属于 ArrayList 的方法的是 A.add() B.addAll() C.addFirst() D.size() (4) 下列声明语句错误的是 A.List list=new ArrayList(); B.List<String> list=new LinkedList(); C.ArrayList al= new List(); D.Set set=(set)new ArrayList(); (5) 欲构造 ArrayList 类的一个实例, 此类继承了 List 接口, 下列哪个方法是正确的 A.ArrayList mylist=new Object( ); B.List mylist=new ArrayList( ); C.ArrayList mylist=new List( ); D.List mylist=new List( ); 2. 判断题 (1)ArrayList 和 LinkedList 都实现 Cloneable 接口, 都提供了两个构造函数, 一个无 参的, 一个接收另一个 Collection ( ) (2)Map 接口不是 Collection 接口的继承 ( ) 3.Java 集合框架是什么? 说出一些集合框架的优点? 4. 集合框架中的泛型有什么优点? 5.Java 集合框架的基础接口有哪些? 6. 为何 Collection 不从 Cloneable 和 Serializable 接口继承? 7. 为何 Map 接口不继承 Collection 接口? 8. 遍历一个 List 有哪些不同的方式? 9. 我们能否使用任何类作为 Map 的 key? 任务 3 工具类 本次任务将会介绍 Collections 工具类和 Arrays 工具类的使用, 这两个类都位于 java.util 包中, 它们都有一个共同的特点是类中的方法都是静止的, 不需要创建对象, 直接使用类名调

155 项目 5 Java 泛型与集合框架 155 用即可 Collections 工具类 Collections 工具类, 是集合对象的工具类, 提供了操作集合的工具方法, 例如排序, 复制 和反转排序等方法 Collections 工具类常用的方法如下 : public static void sort(list list): 根据数据元素的自然顺序对指定类集按升序进行排序 public static void shuffle(list list): 对指定类集进行随机排序 public static void reverse(list list): 反转指定类集中数据元素的顺序 public static Object max(collection coll): 根据数据元素的自然顺序, 返回给定 Collection 的最大元素 public static Object min(collection coll): 根据数据元素的自然顺序, 返回给定 Collection 的最小元素 public static int binarysearch(list list,object key): 使用二分搜索法搜索指定类集, 以获 得指定 key 的对象 public static int indexofsublist(list source,list target): 返回指定源类集中第一次出现 指定目标类集的起始位置, 如果没有返回 -1 public static int lastindexofsublist(list source,list target): 返回指定源类集中最后一次 出现指定目标类集的起始位置, 如果没有返回 -1 public static void copy(list source,list target): 将所有数据元素从一个类集 source 复制 到另一个类集 target public static void fill(list list,object obj): 使用指定数据元素 obj 替换指定类集中的所 有数据元素 public booleanreplaceall (List list,object old,object new): 使用指定的新数据元素替换 集合中出现的所有指定的原始数据元素 public static void swap(list list,int i,int j): 在指定类集的指定位置处交换数据元素 例 5.8 Collections 工具类应用 import java.util.*; public class TestCollections public static void main(string[] args) List list=new ArrayList(); list.add("w"); list.add("o"); list.add("r"); list.add("l"); list.add("d"); System.out.println(" 排序前 :"+list); System.out.println(" 该集合中的最大值 :"+Collections.max(list)); System.out.println(" 该集合中的最小值 :"+Collections.min(list)); Collections.sort(list);

156 156 Java 程序设计 System.out.println(" 排序后 :"+list); // 二分查找, 要求查找前集合是自然有序排列 System.out.println("r 在集合中的索引为 :"+Collections.binarySearch(list,"r")); Collections.shuffle(list); System.out.println(" 随机排序结果 :"+list); Collections.reverse(list); System.out.println(" 逆序结果 :"+list); Collections.swap(list,1,4); System.out.println(" 索引号为 1 4 的元素交换后 :"+list); Collections.replaceAll(list,"w","d"); System.out.println(" 把 w 都换成 d 后结果 :"+list); Collections.fill(list,"s"); System.out.println(" 全部填充为 s 后的结果 :"+list); 出现运行结果为 : 排序前 :[w, o, r, l, d] 该集合中的最大值 :w 该集合中的最小值 :d 排序后 :[d, l, o, r, w] r 在集合中的索引为 :3 随机排序结果 :[w, d, r, l, o] 逆序结果 :[o, l, r, d, w] 索引号为 1 4 的元素交换后 :[o, w, r, d, l] 把 w 都换成 d 后结果 :[o, d, r, d, l] 全部填充为 s 后的结果 :[s, s, s, s, s] Arrays 工具类 Arrays 工具类, 是数组的工具类, 提供了对数组的工具方法, 例如排序, 二分查找等 Arrays 工具类常用方法如下 : public static void fill (int[] a, int val): 用指定的 val 值填充数组中的每个元素 public static void fill (int[] a, int fromindex, int toindex, int val): 用指定的 val 值填充数 组中的下标从 fromindex 开始到 toindex 为止的每个元素 public static void fill (Object[] a, Object val): 用指定的 val 值填充对象数组中的每个元素 public static void fill (Object[] a, int fromindex, int toindex, Object val): 用指定的 val 值 填充对象数组中的下标从 fromindex 开始到 toindex 为止的每个元素 public static void sort(object[] a): 对数组 a 按自然顺序排序 public static void sort(object[] a, int fromindex, int toindex): 对数组 a 中的元素从起始 下标 fromindex 到终止下标 toindex 之间的元素排序 public static int binarysearch (int[] a, int key): 对已排序的 int 类型数组使用二分搜索法 搜索, 如果找到指定的值就返回其所在的索引位置, 否则返回负值 public static int binarysearch (Object[] a, Object key): 对已排序的对象数组使用二分搜 索法搜索, 如果找到指定的值就返回其所在的索引位置, 否则返回负值

157 项目 5 Java 泛型与集合框架 157 public static boolean equals(boolean[] a, boolean[] b): 比较布尔型数组 a 与 b 是否相等 public static boolean equals(object[] a, Object[] b): 比较对象数组 a 与 b 是否相等 public static <T> List<T> aslist(t... a): 返回一个受指定数组支持的固定大小的列表 该方法提供了一个方便的从多个元素创建 List 对象的途径 例 5.9 fill() 方法的使用 import java.util.*; public class FillTest public static void main(string args[]) int size = 6; boolean[] a1 = new boolean[size]; byte [] a2 = new byte[size]; float [] a3 = new float[size]; String [] a4 = new String[size]; Arrays.fill(a1,true); Arrays.fill(a2, (byte)11); Arrays.fill(a3, (float)3.14); Arrays.fill(a4, "Hello"); Arrays.fill(a4, 2, 4, "World"); for(int i = 0; i<a3.length; i++) System.out.print(a4[i]+" "); 程序运行结果为 : Hello Hello World World Hello Hello 例 5.10 数组的排序 import java.util.*; public class SortDemo public static void main(string args[])string[] s=new String []"China", "England", "France","America","Russia",; for(int i=0;i<s.length;i++) System.out.print(s[i]+" "); System.out.println(); Arrays.sort(s);// 对数组 s 排序 for(int i=0;i<s.length;i++) System.out.print(s[i]+" "); System.out.println(); 程序运行结果为 : China England France America Russia America China England France Russia 例 5.11 数组的比较 import java.util.*; public class EqualsTest public static void main(string[] args)

158 158 Java 程序设计 int[] a1 = new int[10]; int[] a2 = new int[10]; Arrays.fill(a1, 47); Arrays.fill(a2, 47); System.out.println(Arrays.equals(a1, a2)); // 输出 true System.out.println(a1.equals( a2)); // 输出 false a2[3] = 11; System.out.println(Arrays.equals(a1, a2)); // 输出 false String[] s1 = new String[5]; Arrays.fill(s1, "Hi"); String[] s2 = "Hi", "Hi", "Hi", "Hi", "Hi"; System.out.println(Arrays.equals(s1, s2)); // 输出 true 程序运行结果为 : true false false true 例 5.12 数组转换为 List 对象 import java.util.*; public class AsListTest public static void main(string[]args) List stooges = Arrays.asList("Larry", "Moe", "Curly"); System.out.println(stooges); 程序运行结果为 : [Larry, Moe, Curly] 习题 1.Collection 和 Collections 的区别? 2.Collections 工具类中的 sort() 方法如何比较元素? 3. 选择题 (1)Collections 工具类中的 binarysearch() 方法描述正确的是 A.binarySearch() 方法只能操作 Set 集合 B.binarySearch() 方法只能操作 List 集合 C.binarySearch() 方法只能操作 Map 集合 D.binarySearch() 可以操作所有的集合 (2) 下面类或者接口中, 不属于集合体系的是 A.java.util.Collections C.java.util.Vector B.java.util.Map D.java.util.Hashtable (3) 下面关于 Collection 和 Collections 的区别错误的是

159 项目 5 Java 泛型与集合框架 159 A.Collections 是集合顶层接口 B.Collection 是针对 Collections 集合操作的工具类 C.List Set Map 都继承自 Collection 接口 D.Collections 是针对 Collection 集合操作的工具类 (4)Collections 类对集合对象不能进行 操作 A. 只读 B. 同步 C. 排序 D. 删除 (5)Java 语言的集合框架类定义在 包中 A.java.util B.java.lang C.java.array D.java.collections 项目总结 1. 泛型的本质是参数化类型, 也就是说所操作的数据类型被指定为一个参数 这种参数类型可以用在类 接口和方法的创建中, 分别称为泛型类 泛型接口 泛型方法 2. 集合框架是一些常见的数据结构和接口总称, 泛型是集合框架的一种定义形式, 使用泛型的主要目的是建立具有类型安全的数据结构 Collection<E> 接口是所有构造泛型的基础, 所有泛型接口和泛型类型都继承或实现 Collection<E> 接口, 常用的泛型接口有 List<E> 接口 Set<E> 接口和 Map<E> 接口 3. 在 java.util 包中提供了 Arrays 类和 Collections 类, 这两个类提供了数组和集合对象的算法功能 这两个类提供了若干 static 方法实现有关操作

160 项目 6 Java 的 I/O 流和文件 在程序设计过程中经常需要完成从键盘或文件读取数据 或者向文件中写入数据等输入 输出操作 Java 语言对这些操作统一进行处理 把不同类型的输入 输出源抽象为流 Stream 用统一的接口来表示 从而简化程序的数据读写操作 理解流的概念 掌握文件类对象的构造和使用 掌握字节流和字符流的使用 熟练掌握文件的顺序读写和随机读写 理解 I/O 其他流的定义和使用 任务 1 I/O 流类简介 流的基本概念 在 Java 语言中程序所完成的输入和输出操作是以流的形式来实现的 所谓流 是指在计 算机的输入与输出之间运动的数据序列 流序列中的数据既可以是未加工的原始的二进制数 据 也可以是经过一定编码处理后符合某种格式规定的特定数据 通常 Java 程序的输入/输出操作主要针对三种不同的类型 1 控制台 指的是屏幕输出 在 Windows 系统下指的是 DOS 窗口 和键盘输入 2 文件 针对磁盘文件的读/写操作 3 网络 通过网络套接字所实现的数据访问 有了流 程序和外界的数据交换都可通过流实现 当程序要从数据源获得数据时 必须 在程序和数据源之间建立输入流 如图 6-1 所示 当程序要把结果输送到数据宿时 必须在 程序和数据宿之间建立输出流 如图 6-2 所示 图 6-1 输入流的图示 图 6-2 输出流的图示

161 项目 6 Java 的 I/O 流和文件 161 无论涉及输入 / 输出的数据源和数据宿是什么, 只要在程序和数据源 / 数据宿之间建立了流, 用户就不需要再关心数据来自何方或送向何处, 程序中输入 / 输出操作的复杂性就大大降低了 所有输入 / 输出操作都转换为对流的操作 在所有的流中, 不管输入 / 输出数据的类型是什么, 在程序中读和写的过程都是相同的 流按照处理数据的单位, 可以分为字节流和字符流 字节流的处理单位是字节, 通常用来处理二进制文件, 例如音乐 图片文件等 而字符流的处理单位是字符, 因为 Java 采用 Unicode 编码,Java 字符流处理的即为 Unicode 字符, 所以在操作汉字 国际化等方面, 字符流具有优势 常用输入输出类在 Java 语言中数据的输入输出处理都通过对数据流的操作来完成, 完成这些操作的类都定义在 java.io 包中 图 6-3 所示是 java.io 包中提供的一些处理数据流和文件的类, 它们都是 Object 类的直接子类 图 6-3 常用的 Java 输入输出类层次结构 File 类是专门用来处理文件的类, RandomAccessFile 类实现了文件的随机读写 ; InputStream 和 OutputStream 类及其子类都是处理以字节为基本单位的字节流类 ;Reader 和 Writer 类及其子类是专门处理字符流的类 ;InputStream 和 OutputStream 类 Reader 和 Writer 类都是抽象类, 提供了所有子类共有的一些读写字节或字符数据的操作, 其派生的子类在此基础上通过实现某些接口, 添加一些特殊的方法, 来满足不同的读写需要, 从而提高读写数据的效率或增强读写数据的功能 习题 1. 什么叫流? 简述流的分类? 2. 选择题 (1) 下列数据流中, 属于输入流的一项是 A. 从内存流向硬盘的数据流 B. 从键盘流向内存的数据流 C. 从键盘流向显示器的数据流 D. 从网络流向显示器的数据流 (2)Java 语言提供处理不同类型流的类所在的包是

162 162 Java 程序设计 A.java.sql B.java.util C.java.net D.java.io (3) 不属于 java.io 包中的接口的是 A.DataInput B.DataOutput C.DataInputStream D.ObjectInput 任务 2 File 类 文件是用来保存数据的, 目录是管理文件的特殊机制, 同类文件保存在同一个目录下可 以简化文件管理 因此, 掌握文件和目录的操作对于编程人员是必需的 文件对象创建 每个 File 类的对象表示一个磁盘文件或目录, 其对象属性中包含了文件或目录的相关信 息, 如名称 长度和所含文件个数等, 调用它的方法则可以完成对文件或目录的常用管理操作, 如创建 删除等 Java 提供了三种构造方法来创建一个文件对象 File(String path): 字符串参数 path 指明了新创建的 File 对象对应的磁盘文件或目录名 及其路径名 path 参数也可以对应磁盘上的某个目录, 如 D:\java\temp 或 java\temp File(String path,string name): 第一个参数 path 表示所对应的文件或目录的绝对或相 对路径, 第二个参数 name 表示文件或目录名 这里将路径与名称分开的好处是相同 路径的文件或目录可共享同一个路径字符串, 管理 修改都比较方便 File(File dir,string name): 这种构造方法使用另一个已经存在的代表某磁盘目录的 例如 : File 对象作为第一个参数, 表示文件或目录的路径, 第二个字符串参数表述文件或目 录名 File f1 = new File("c:\\java\\"); File f2 = new File("c:\\java\\","autoexec.bat"); File f3 = new File(f1,"autoexec.bat"); 注意 : 在 Windows/DOS 下路径使用反斜线 (\) 的约定, 你需要在字符串内使用它的转义 序列 (\\) File 类构造方法只是生成一个文件对象, 但没有生成真正的文件, 如果要生成真正的文件, 就需要使用 File 类的 createnewfile() 方法 例 6.1 创建文件对象 import java.io.*; public class FileCreate public static void main(string[] args) File f1=new File("d:\\java\\FileDemo.java"); File f2 = new File("d:\\java\\"); File f3=new File(f2,"FileDemo1.java"); System.out.println(f1); System.out.println(f3);

163 项目 6 Java 的 I/O 流和文件 163 try f1.createnewfile(); f3.createnewfile(); catch(ioexception e) 程序运行结果为 : d:\java\filedemo.java d:\java\filedemo1.java 文件和目录属性 一个对应于某磁盘文件或目录的 File 对象一经创建, 就可以通过调用它的方法来获得该 文件或目录的属性 其中, 较常用的方法见表 6-1 所示 表 6-1 获取文件或目录属性常用方法 方法 public Boolean exists() public Boolean isfile() public Boolean isdirectory() public String getname() public String getpath() public String getabsolutepath() public String getparent() public long length() public Boolean canread() public Boolean canwrite() public String[] list() public Boolean equals(file f) public ishudden() long lastmodified() public boolean isabsolute() 描述若文件或目录存在, 则返回 true, 否则返回 false 若对象代表有效文件, 则返回 true, 否则返回 false 若对象代表有效目录, 则返回 true, 否则返回 false 返回文件或目录名返回文件或目录的路径返回文件或目录的绝对路径返回文件或目录的双亲的路径返回文件的字节数若文件为可读文件, 则返回 true, 否则返回 false 若文件为可写文件, 则返回 true, 否则返回 false 将目录中所有文件名保存在字符串数组中返回若两个 File 对象相同, 则返回 true, 否则返回 false 若文件或目录具有隐藏属性, 则返回 true, 否则返回 false 文件最后修改的时间如果路径是绝对路径返回为 true, 否则返回 false 例 6.2 获取文件和目录的属性 import java.io.*; class FileDemo static void p(string s) System.out.println(s); public static void main(string args[]) File f1 = new File("/java/COPYRIGHT");

164 164 Java 程序设计 try f1.createnewfile(); catch(ioexception e) p("file Name: " + f1.getname());// 获取文件名 p("path: " + f1.getpath());// 获取文件相对路径 p("abs Path: " + f1.getabsolutepath());// 获取文件的绝对路径 p("parent: " + f1.getparent());// 获取文件的双亲路径 p(f1.exists()?"exists" : "does not exist");// 文件是否存在 p(f1.canwrite()?"is writeable" : "is not writeable");// 文件是否可写 p(f1.canread()?"is readable" : "is not readable");// 文件是否可读 p("is " + (f1.isdirectory()?"" : "not" + " a directory"));// 该对象是否是目录 p(f1.isfile()?"is normal file" : "might be a named pipe");// 该对象是否是文件 p(f1.isabsolute()?"is absolute" : "is not absolute");// 文件路径是否是绝对路径 p("file last modified: " + f1.lastmodified());// 文件最后修改的时间 p("file size: " + f1.length() + " Bytes");// 文件的大小 程序运行结果为 : File Name: COPYRIGHT Path: \java\copyright Abs Path: D:\java\COPYRIGHT Parent: \java exists is writeable is readable is not a directory is normal file is not absolute File last modified: File size: 0 Bytes 注意 : 在 Windows 环境下,Java 的路径也可以用斜线 (/) 表示, 路径处理依然正确 文件和目录操作 File 类中还定义了一些对文件或目录进行管理 操作的方法, 常用的有如下几种 public boolean renameto(file newfile): 将文件重命名成 newfile 对应的文件名 public void delete(): 将当前文件删除 public boolean mkdir();: 创建当前目录的子目录 public String[] list(): 用字符串形式返回目录下的全部文件 public File[] listfiles(): 用 File 对象形式返回目录下的全部文件 public String[] list(filenamefilter obj): 用字符串形式返回目录下由 obj 指定类型的所 有文件 public File[] listfiles(filenamefilter obj): 用 File 对象返回目录下由 obj 指定类型的所 有文件

165 项目 6 Java 的 I/O 流和文件 165 其中, 参数类型 FilenameFilter 是一个接口, 该接口声明了一个方法用于检查文件是否符 合指定的目录和文件类型的要求, 方法格式如下所示 : public boolean accept(file path,string name) 例 6.3 显示当前路径下的文件和目录信息 import java.io.*; public class FileDemo1 public static void main(string[] args) File fldir=new File("."); // 实例化 File 对象, 其中. 表示当前目录 System.out.println("File in:"+fldir.getabsolutepath()); String strfiles[] = fldir.list(); // 引用 File 类的方法 list(), 将当前目录下的所有文件名和子目录名填入字符串数组 int intdircount = 0, intfilecount = 0; // 变量 intdircount 和 intfilecount 分别用来记录子目录的个数和文件的个数 long lngsize=0; // 变量 lngsize 用来记录所有文件的长度 for(int i=0;i<strfiles.length;i++) File fltemp=new File(strFiles[i]); if (fltemp.exists()) if(fltemp.isfile()) // 判断是否普通文件 System.out.println(strFiles[i]+"\t"+flTemp.length()); // 列出当前目录下的所有文件和文件长度 intfilecount++; lngsize=lngsize+fltemp.length(); if(fltemp.isdirectory())// 判断是否为目录 System.out.println(strFiles[i]+"\t<DIR>");// 列出当前目录下的所有子目录 intdircount++; System.out.println(intFileCount +"file(s)\t" + lngsize + "bytes"); System.out.println(intDirCount + "dir(s)"); 出现运行结果为 : File in:d:\java\. actionevent.java 1178 complexpackage <DIR> ComplexTest.java 330 database <DIR> myfirstpackage <DIR> mypackage <DIR>

166 166 Java 程序设计 xt.java file(s) bytes 4dir(s) 例 6.4 列出 c:\java 目录下的所有 java 文件 import java.io.*; class FileAccept implements FilenameFilter String str=null; FileAccept(String s)// 构造方法 str="."+s; public boolean accept(file path,string name)// 实现 accept 方法 return name.endswith(str); // 判断 name 字符串末尾是否以 str 字符串结束 public class FileDemo2 public static void main(string[] args) File path=new File("c:\\java"); FileAccept acceptmode=new FileAccept("java");// 设置文件的后缀为.java String filelist[]=path.list(acceptmode);// 将满足条件的文件名写入字符串数组 System.out.println(" 目录下有 "+filelist.length+" 个文件 "); for(int i=0;i<filelist.length;i++) System.out.println(filelist[i]);// 循环输出指定目录下满足条件的所有文件 程序运行结果为 : 目录下有 6 个文件 FileCreate.java FileDemo.java FileDemo1.java FileDemo2.java FindDups.java Frequency.java 可执行文件运行 当要执行一个本地机器上的可执行文件时, 可以使用 java.lang 包中的 Runtime 类 首先 使用 Runtime 类声明一个对象, 然后使用该类的静态方法 getruntime() 创建这个对象 例如 : Runtime ex; ex=runtime.getruntime(); ex 可以调用 exec(string command) 方法打开本地可执行文件或执行一个操作 例 6.5 利用 Runtime 对象打开 Windows 平台上的记事本 import java.io.*; public class FileDemo3

167 项目 6 Java 的 I/O 流和文件 167 public static void main(string[] args) try Runtime ex=runtime.getruntime();// 创建 Runtimedx 对象 File file=new File("c:\\windows","Notepad.exe");// 创建文件对象 ex.exec(file.getabsolutepath());// 执行文件 catch(exception e) 程序运行结果如图 6-4 所示 : 图 6-4 例 6.5 运行结果 习题 1. 选择题 (1) 下列程序实现了在当前包 dir815 下新建一个目录 subdir815, 选择正确的一项填入 程序的横线处, 使程序符合要求 package dir815; import java.io.*; public class X8_1_5 public static void main(string[] args) char ch; try File path = ; if(path.mkdir()) System.out.println("successful!"); catch(exception e) e.printstacktrace(); A.new File("subDir815"); B.new File("dir815.subDir815"); C.new File("dir815\subDir815"); D.new File("dir815/subDir815"); (2) 下列关于流类和 File 类的说法中错误的一项是 A.File 类可以重命名文件 B.File 类可以修改文件内容 C. 流类可以修改文件内容 D. 流类不可以新建目录 (3) 若要删除一个文件, 应该使用下列哪个类的实例? A.RandomAccessFile C.FileOutputStream B.File D.FileReader

168 168 Java 程序设计 2. 编写一个测试文件一般属性 ( 如显示文件的路径 绝对路径 显示文件是否可写 显 示文件是否可读 显示文件的大小等属性 ) 的程序 任务 3 字节流与字符流 字节流处理单元为 1 个字节, 如操作字节和字节数组, 而字符流处理的单元为 2 个字节的 Unicode 字符, 分别为操作字符 字符数组或字符串 字节流可用于任何类型的对象, 包括二进制对象, 例如音频 图片文件等, 但它不能直接处理 Unicode 字符, 而字符流只能处理字符或者字符串, 如果是关系到中文 ( 文本 ) 的, 用字符流好点 所有文件的存储都是字节 (byte) 的存储, 在磁盘上保留的并不是文件的字符而是先把字符编码成字节, 再存储这些字节到磁盘 在读取文件 ( 特别是文本文件 ) 时, 也是一个字节一个字节地读取以形成字节序列 字节流在 Java 语言中, 字节流类用两个类层次定义, 在顶层的是两个抽象类 :InputStream( 字节输入流 ) 和 OutputStream( 字节输出流 ) 这两个抽象类不能直接生成对象, 需要通过其子类重写继承过来的抽象方法来创建程序中所需的对象 1.InputStream 类和 OutputStream 类 InputStream 类中定义了与字节输入流操作有关的方法, 可以完成从输入流中读取数据的功能, 其主要方法如表 6-2 所示 表 6-2 InputStream 类的方法 方法 描述 int read() 读取一个 byte 无符号填充到 int 低 8 位, 如果到达流的末尾返回 -1 int read(byte[] buf) int read(byte[] buf,int start,int size) void close() 从输入流中读取字节填充到数组 buf 中 从输入流中读取 size 个字节并从数组 buf 的 start 位置开始填充 关闭输入流 OutputStream 类中定义了与字节输出流操作有关的方法, 可以完成向输入流中写入数据的 功能, 其主要方法如表 6-3 所示 表 6-3 OutputStream 类的方法 方法 描述 int write(int b) int write(byte[] buf) int write(byte[] buf,int start,int size) void close() void flush() 将一个整数中的最低一个字节中的内容写到输出流中, 高字节部分被弃 将字节数组中的所有内容写入到输出流对象中 将字节数组 buf 中从 start 位置开始的 size 个字节写入到输出流对象中 关闭输出流, 并释放相关的系统资源 强制把数据输出, 缓存区清空

169 项目 6 Java 的 I/O 流和文件 FileInputStream 类和 FileOutputStream 类 FileInputStream 和 FileOutputStream 类分别继承了 InputStream 和 OutputStream 类, 它们实 现了父类中的所有抽象方法, 通过这两个类可以打开本地文件, 对文件内容进行顺序的读写 (1)FileInputStream 类 首先, 介绍 FileInputStream 类的定义, 其构造方法定义如下 : public FileInputStream(String name): 按字符串参数所指定的文件名来创建一个文件输 入流对象 public FileInputStream(File file): 以一个 File 对象来构造文件输入流对象, 该 File 对 象表示一个文件 注意 : 以上两种构造方法创建输入流对象时, 可能会出现异常 例如, 文件名不存在或 文件名是一个目录, 则抛出 FileNotFoundException 异常, 程序必须使用一个 try-catch 块检测 并处理这个异常 若 FileInputStream 对象创建成功, 则打开了一个文件输入流, 下面要做的就是读操作 FileInputStream 类主要提供了三个 read() 方法, 这三个方法自 InputStream 类所继承, 并进行了 重新定义, 其方法描述如下 : public int read() public int read(byte[] b) public int read(byte[] b,int start,int size) 这三个方法参数含义见表 6-2 另外, 这三个方法都抛出 IOException, 且当读到文件结束 处无数据读出时返回 -1, 所以程序中可据此进行判断 最后, 文件输出流的关闭是通过 close() 方法来实现的, 该方法描述如下 : public void close() 例 6.6 使用字节流读取文件 import java.io.*; public class FileDemo4 public static void main(string[] args) int i; byte buf[]=new byte[1024];// 定义字节数组, 接收读取的字符 try File f=new File("FileDemo4.java"); FileInputStream readfile=new FileInputStream(f);// 创建输入流 // 循环读取字节存入字节数组 while((i=readfile.read(buf,0,25))!=-1) String str=new String(buf,0,i); System.out.println(str); readfile.close();// 关闭文件 catch(ioexception e) System.out.println(" 读取文件错误 ");

170 170 Java 程序设计 程序运行结果为 : import java.io.*; public class FileDemo4 public static void main(string[] args) int i; byte buf[]=new byte[1024];// 定义字节数组, 接收读取的字符 try File f=new File("FileDemo4.java"); FileInputStream readfile=new FileInputStream(f);// 创建输入流 // 循环读取字节存入字节数组 while((i=readfile.read(buf,0,25))!=-1) String str=new String(buf,0,i); System.out.println(str); readfile.close();// 关闭文件 catch(ioexception e) System.out.println(" 读取文件错误 "); (2)FileOutputStream 类 与 FileInputStream 类相对应的是 FileOutputStream 类, 表示文件输出流, 即可通过该类所 定义的方法向文件中写入数据, 构造方法如下 : public FileOutputStream(String name): 通过字符串变量来创建一个文件输出流 public FileOutputStream(String name,boolean append): 通过字符串变量来创建一个文件 输出流 如果第二个参数为 true, 则将字节写入文件末尾处, 否则写入文件开始处 public FileOutputStream(File file): 通过 File 对象来创建一个文件输出流 public FileOutputStream(File file,boolean append): 通过 File 对象来创建一个文件输出 流 如果第二个参数为 true, 则将字节写入文件末尾处, 否则写入文件开始处 创建文件输出流之后, 要做的就是写数据 FileOutputStream 类主要提供了三个 write() 方 法 这三个方法自 OutputStream 类所继承, 并进行了重新定义, 描述如下 : public void write(int b) public void write(byte[] b) public void write(byte[] b,int start,int size) 这三个方法的参数含义见表 6-3 所示 这三个方法都抛出 IOException 异常, 都是以字节 为单位来写入数据 最后, 文件输出流的关闭通过 close() 方法来实现, 该方法描述如下 : public void close()

171 项目 6 Java 的 I/O 流和文件 171 例 6.7 在程序中创建一个文件, 写入从键盘输入的一串字符, 然后再读该文件并将文件 内容显示在屏幕上 import java.io.*; class FileInOutDemo public static void main(string[] args) FileInputStream fin; // 声明文件输入流对象 fin FileOutputStream fout;// 声明文件输出流对象 fout char ch;int data; try fin=new FileInputStream(FileDescriptor.in);// 创建文件输入流对象 fin fout=new FileOutputStream("d:\\JAVA\\myfile.txt");// 创建文件输出流对象 fout System.out.println(" 请输入一串字符, 并以 # 结束 :"); while((ch=(char)fin.read())!='#') fout.write(ch); fin.close(); fout.close(); System.out.println(); fin=new FileInputStream("d:\\JAVA\\myfile.txt"); fout=new FileOutputStream(FileDescriptor.out); while(fin.available()>0) data=fin.read(); fout.write(data); fin.close(); fout.close(); catch(filenotfoundexception e) System.out.println(" 文件没有找到!"); catch (IOException e) 程序运行结果为 : D:\java>java FileInOutDemo 请输入一串字符, 并以 # 结束 : wreeo789890xcvbndfgner# wreeo789890xcvbndfgner 说明 :FileDescriptor.in 和 FileDescriptor.out 可以简单理解为标准输入设备 ( 键盘 ) 和标准 输出设备 ( 显示器 ) fin=new FileInputStream(FileDescriptor.in) 实际上就是输入流对象与标 准输入设备的关联, 从键盘接收输入 ; fout=new FileOutputStream(FileDescriptor.out) 实际上 就是输出流与标准输出设备的关联, 将结果输出到显示器 字符流 1.Reader 类和 Writer 类 Java 对字符文件的处理是以 Unicode 字符为基本单位的, 尽管在理论上所有文件都可以按

172 172 Java 程序设计 字节为单位来处理, 但在处理中文字符时非常不方便, 于是 Java 专门提供了 Reader 和 Writer 类来处理以字符为单位的数据流 Reader 和 Writer 类也是两个抽象类, 仅仅提供了一些用于字符流处理的方法, 本身不能用来生成实例 Java 类库中所有进行字符流处理的类都继承自 Reader 和 Writer 这两个类 Reader 类与 InputStream 类类似, 都是处理输入流的, 它们两者的差别在于 InputStream 类中是以字节为单位, 而 Reader 类中是以字符为单位, 其主要方法如表 6-4 所示 表 6-4 Reader 类的方法 方法 描述 int read() 读取一个字符, 返回范围为 0~65535 之间的 int 型值, 如果到达流的末尾返回 -1 int read(char[] buf) 读取多个字符到字符数组 buf 中, 如果到达流的末尾返回 -1 int read(char[] buf,int start,int size) void mark() boolean marksupport() boolean ready() void reset() void close() long skip(long n) 读取 size 个字符到字符数组 buf 的 start 开始的位置中在流中标记一个位置返回一个 boolean 值, 描述数据流是否支持标记和复位测试数据流是否可读取返回数据流中标记的位置关闭输入流跳过数据流中 n 个字符 Writer 类是输出字符数据时需要使用的类, 与字节输出流 OutputStream 类相似, 主要差别 是前者输出的是字符, 后者输出的是字节, 其主要方法如表 6-5 所示 表 6-5 Writer 类的方法 方法 描述 void write(int c) void write(char[] buf) void write(char[] buf,int start,int size) void write(string str) void write(string str,int start,int size) void flush() void close() 向输出流中写入一个字符, 实际上是将 int 型的 c 低 16 位写入输出流 将字符数组 buf 中字符写入输出流 将字符数组 buf 中从 start 开始的 size 个字符写入输出流 将字符串 str 写入输出流 将字符串 str 从 start 开始的 size 个字符写入输出流 刷新输出流 关闭输出流 2.FileReader 类和 FileWriter 类在学习了字节流方式的文件输入 / 输出之后, 通过类比可以很容易理解和掌握字符流方式的文件输入 / 输出 FileReader 类和 FileWriter 类作为输入 / 输出类, 它们以字符为单位从文件中读数据或向文件中写数据 (1)FileReader 类

173 项目 6 Java 的 I/O 流和文件 173 FileReader 类常用的构造方法定义如下 : public FileReader(String filename): 按字符串参数所指定的文件名来创建一个文件输 入流对象 public FileReader(File file): 按参数中的 File 对象来构造文件输入流对象 FileReader 类主要提供了三个 read() 方法, 其方法描述如下 : public int read() public int read(char[] buf) public int read(char[] buf,int start,int size) 这三个方法都是以字符为单位读入数据, 自 InputStreamReader 类所继承得到, 这三个方 法参数含义见表 6-4 最后, 文件输入流的关闭通过 close() 方法来实现, 该方法描述如下 : public void close() 例 6.8 利用 FileReader 类读取纯文本文件 import java.io.*; public class FileReadDemo public static void main(string[] args) throws IOException char[] c=new char[500]; // 创建可容纳 500 个字符的数组 FileReader fr=new FileReader("d:\\JAVA\\myfile.txt");// 创建对象 fr int num=fr.read(c); // 将数据读入字符数组 c 内, 并返回读取的字符数 String str=new String(c,0,num);// 将字符串数组转换成字符串 System.out.println(" 读取的字符个数为 :" + num + ", 其内容如下 :"); System.out.println(str); 程序运行结果 : 读取的字符个数为 :22, 其内容如下 : wreeo789890xcvbndfgner (2)FileWriter 类 FileWriter 类构造方法如下 : publicfilewriter(file file): 用参数中的 File 对象创建一个文件输出流 publicfilewriter(file file,boolean append): 用参数中的 File 对象创建一个文件输出流 如果第二个参数为 true, 则将写入的数据流添加到文件后面, 否则将文件覆盖 publicfilewriter(string filename): 用参数中的字符串变量来创建一个文件输出流 publicfilewriter(string filename,boolean append): 用参数中的字符串变量来创建一个 文件输出流 如果第二个参数为 true, 则将写入的数据流添加到文件后面, 否则将文 件覆盖 FileWriter 类主要提供了三个 write() 方法, 其方法描述如下 : public void write(int c) public void write(char[] buf) public void write(char[] buf,int start,int size) 这三个方法都是以字符为单位写入数据, 自 OutputStreamWriter 类所继承得到, 这三个方 法参数含义见表 6-5 最后, 文件输出流的关闭通过 close() 方法来实现, 该方法描述如下 :

174 174 Java 程序设计 public void close() 例 6.9 利用 FileWriter 类将字符数组与字符串写到文件里 import java.io.*; public class FileWriterDemo public static void main(string[] args) throws IOException FileWriter fw=new FileWriter("d:\\JAVA\\test.txt"); char[] c='h','e','l','l','o','\r','\n'; String str=" 欢迎使用 Java!"; fw.write(c); // 将字符数组写到文件里 fw.write(str); // 将字符串写到文件里 fw.close(); 程序运行结果是在 d:\java 文件夹下生成了 test.txt 文件, 其内容为 : Hello 欢迎使用 Java! 习题 1. 选择题 (1) 字符流与字节流的区别在于 A. 前者带有缓冲, 后者没有 B. 前者是块读写, 后者是字节读写 C. 二者没有区别, 可以互换使用 D. 每次读写的字节数不同 (2) 下列流中哪个不属于字节流 A.FileInputStream C.FilterInputStream 2. 填空题 B.BufferedInputStream D.InputStreamReader (1)java.io 包中的接口中, 处理字节流的有 接口和 接口 (2) 所有的字节输入流都从 类继承, 所有的字节输出流都从 类继承 (3) 与用于读写字节流的 InputStream 类和 OutputStream 类相对应,Java 还提供了用于 读写 Unicode 字符的字符流 类和 类 (4) 如果希望从磁盘文件读取数据, 或者将数据写入文件, 还需要使用文件输入输出流 类 和 (5)Java 系统提供的 FileInputStream 类是用于读取文件中的 数据的 文件 输入流类 ;FileOutputStream 类是用于向文件写入 数据的 文件输出流类 (6) 输入流类 (InputStream) 和输出流类 (OutputStream) 是 java.io 包中所有 流 的抽象基类 3. 什么时候用字节流? 什么时候用字符流? 4. 使用字符输入流类实现一个简单的学生成绩录入程序 要求从键盘输入人数, 然后依 次输入每个学生的英语 计算机语言和数据库的成绩, 分别统计出每个学生的总成绩, 并将各 门课程的名称 成绩和总成绩全部显示在屏幕上 5. 编程 : 输入 5 个学生的信息 ( 包含学号 姓名 英语成绩 计算机语言成绩和数据库成

175 项目 6 Java 的 I/O 流和文件 175 绩 ), 统计各学生的总分, 然后将学生信息和统计结果存入二进制数据文件 STUDENT.DAT 中 任务 4 随机访问流 前面我们谈到的字节流和字符流都是对磁盘文件的顺序读写, 而且为了读和写必须分别 创建不同的对象 为了能实现对文件的随机读写, Java 语言定义了随机访问流 RandomAccessFile 类 RandonAccessFile 类创建的流既可以是输入流也可以是输出流, 当需要对文件进行读写操 作时, 创建一个指向该文件的 RandomAccessFile 流即可, 这样既可以从这个流中读取文件的 数据, 也可以通过这个流写入数据到文件 另外,Java 的 RandomAccessFile 提供对文件的读 写功能, 与普通的输入输出流不一样的是 RandomAccessFile 可以任意地访问文件的任何地方 这就是 Random 的意义所在 RandomAccessFile 类 1.RandomAccessFile 对象的创建 RandomAccessFile 类的类构造方法的描述如下 : public RandomAccessFile(String name,string mode): 用参数中包含文件名的字符串创 建一个随机访问流对象同时, 用第二参数设置文件读写的模式 mode 最常用的两种 取值 : r 代表以只读方式打开文件, rw 代表以读写方式打开文件 public RandomAccessFile(File file,string mode): 用参数中包含文件对象创建一个随机 例如 : 访问流对象同时, 用第二参数设置文件读写的模式 mode 最常用的两种取值 : r 代表了以只读方式打开文件, rw 代表以读写方式打开文件 File f1=new File("d:\\java\\test.txt"); RandomAccessFile raf=new RandomAccessFile(f1,"rw"); 2. 对文件位置指针的操作 随机访问文件任意位置的数据读 / 写操作, 是通过移动文件指针指定文件读 / 写位置来实现 的 与文件指针有关的常用方法有 : long getfilepointer( ): 记录文件指针的当前位置 ( 以字节为单位 ) void seek(long pos): 将文件位置指针定位到 pos 位置 ( 以字节为单位 ), 其中 0 表示 文件的开头 int skipbytes(int n): 将文件位置指针向后移动 n 个字节 RandomAccessFile 对象的文件位置指针遵循下面的规律 : (1) 新建 RandomAccessFile 对象的文件位置指针位于文件的开头处 (2) 每次读写操作之后, 文件位置的指针都相应后移到读写的字节数 (3) 可以通过 getfilepointer() 方法来获得文件位置指针的位置, 通过 seek() 方法来设置文 件指针的位置

176 176 Java 程序设计 3. 随机访问的读写操作 RandomAccessFile 类中针对所有的基本类型都有相应的读 / 写方法, 读写方法描述见表 6-6 所示 表 6-6 RandomAccessFile 类中读写数据的方法 方法 含义 方法 含义 byte readbyte() 读取一个字节数 void writebyte(int v) 写入一个字节 short readshort() 读取一个短整型数 void writeshort(int v) 写入一个短整型数 int readint() 读取一个整型数 void writeint(int v) 写入一个整型数 long readlong() 读取一个长整型数 void writelong(long v) 写入一个长整型数 float readfloat() 读取一个单精度浮点数 void writefloat(float v) 写入一个单精度浮点数 double readdouble() 读取一个双精度浮点数 void writedouble(double v) 写入一个双精度浮点数 boolean readboolean() 读取一个布尔值 void writeboolean(boolean v) 写入一个布尔值 char readchar() 读取一个字符 void writechar(int v) 写入一个字符 String readutf() 读取一个 UTF 字符串 void writeutf(string v) 写入一个 UTF 字符串 RandomAccessFile 类所有方法都可能抛出 IOException, 所以在实际编程中需要完成相应 的异常处理 随机读写应用 例 6.10 用随机读写的方式打开文件 test.dat, 然后向文件写入 10 个 double 型数后关闭 该文件 ; 随后再打开该文件, 用新的值覆盖第 6 个浮点数后关闭文件 ; 最后打开该文件, 将 10 个数以逆序的方式输出 import java.io.randomaccessfile; import java.io.ioexception; import java.io.filenotfoundexception; public class RAFDemo public static void main(string args[]) try RandomAccessFile raf=new RandomAccessFile("test.dat","rw"); // 创建一个 RandomAccessFile 类的对象 rf, 该对象针对文件 test.dat 是可读和可写的, // 这是由 rw 模式所指定的 for(int i=0;i<10;i++)// 通过 for 循环往 test.dat 文件中写 10 个 double 类型的浮点数 raf.writedouble(i*1.414); raf.close();// 关闭文件 raf=new RandomAccessFile("test.dat","rw"); raf.seek(5*8); // 定义 seek() 方法, 将文件指针定位到 40 的位置, 因为 double 类型的浮点数占 8 个字节, // 所以该行的含义表示将文件指定移到第 6 个 double 类型的浮点数位置 raf.writedouble( ); // 在该文件位置写一个 double 类型的浮点数, 而将原来存储在该位置的浮点数覆盖 raf.close();// 关闭文件

177 项目 6 Java 的 I/O 流和文件 177 raf = new RandomAccessFile("test.dat","r"); // 再次定义了 RandomAccessFile 类的对象 rf, 访问 test.dat 文件, 这次仅以只读方式访问 for(int i = 0;i<10;i++)// 将这 10 个浮点数按逆序的方式打印输出 raf.seek((9-i)*8);// 指针定位是从后往前移动 System.out.println("Value"+(9-i)+":" + raf.readdouble()); raf.close(); catch(filenotfoundexception e) System.out.println(e); catch(ioexception e) System.out.println(e); 程序运行的结果为 : Value9: Value8: Value7:9.898 Value6:8.484 Value5: Value4:5.656 Value3:4.242 Value2:2.828 Value1:1.414 Value0:0.0 习题 1. 选择题 (1) 使用哪一个类可以实现在文件的任一个位置读写一个记录? A.BufferedInputStream C.FileWriter B.RandomAccessFile D.FileReader (2) 若文件是 RandomAccessFile 的实例 f, 并且其基本文件长度大于 0, 则下面的语句 实现的功能是 f.seek(f.length()-1); A. 将文件指针指向文件的第一个字符后面 B. 将文件指针指向文件的最后一个字符前面 C. 将文件指针指向文件的最后一个字符后面 D. 会导致 seek() 方法抛出一个 IOException 异常 (3) 下列关于 RandomAccessFile 类的叙述, 不正确的是 A.RandomAccessFile 类可以随机访问文件 B.RandomAccessFile 类实现 DataInput 和 DataOutput 接口

178 178 Java 程序设计 C.RandomAccessFile 类不能写文件 D.RandomAccessFile 类兼有输入输出功能 2. 利用 RandomAccessFile 类编写应用程序, 要求输入 10 组数据到文件中, 每组数据为 1 个整型数和 1 个双精度数, 然后随机修改文件的某组数, 并显示修改的结果 任务 5 其他流 缓冲流所谓 缓冲 操作是指在执行缓冲输入时, 每次从输入流中读取较大的数据量, 这在内存中构成了一个缓冲区, 而具体的读操作则针对该缓冲区进行 ; 同样在执行缓冲输出时, 每次向输出流所写出的数据是写到内存中一个较大的数据块中, 这也是一个缓冲区 ; 直到缓冲区写满, 才将数据真正写入到最终的介质中, 比如硬盘中 前面使用的字节流 字符流都是无缓冲的输入 输出流, 这意味着, 每一次的读 写操作都会交给操作系统来处理 这样的做法可能会对系统的性能造成很大的影响, 因为每一次操作都可能引发磁盘硬件的读 写或网络的访问, 这些磁盘硬件读 写或网络访问会占用大量系统资源, 影响效率 在 Java 的输入 / 输出类中, 针对字节流和字符流分别定义了两类不同的缓冲类 字节流的缓冲类为 BufferedInputStream 类和 BufferedOutputStream 类, 其构造方法定义如下 : public BufferedInputStream(InputStream in): 用参数中包含的 InputStrean 对象创建一个字节输入流的缓冲类对象 public BufferedInputStream(InputStream in,int size): 用参数中包含的 InputStrean 对象创建一个字节输入流的缓冲类对象同时, 可以用第二个参数指定输入缓冲区的大小, 默认情况下输入缓冲区是 2048 个字节 public BufferedOutputStream(OutStream out): 用参数中包含的 OutputStrean 对象创建一个字节输出流的缓冲类对象 public BufferedOutputStream(OutStream out,int size): 用参数中包含的 OutputStrean 对象创建一个字节输出流的缓冲类对象同时, 可以用第二个参数指定输出缓冲区的大小, 默认情况下输出缓冲区是 512 个字节 字符流的缓冲类为 BufferedReader 类和 BufferedWriter 类, 其构造方法定义如下 : public BufferedReader(Reader in): 用参数中包含的 Reader 对象创建一个字符输入流的缓冲类对象 public BufferedReader(Reader in,int sz): 用参数中包含的 Reader 对象创建一个字符输入流的缓冲类对象同时, 可以用第二个参数指定输入缓冲区的大小, 默认情况下输入缓冲区是 8192 个字节 public BufferedWriter(Writer out): 用参数中包含的 Writer 对象创建一个字符输出流的缓冲类对象 public BufferedWriter(Writer out,int sz): 用参数中包含的 Writer 对象创建一个字符输出流的缓冲类对象同时, 可以用第二个参数指定输入缓冲区的大小, 默认情况下输入

179 项目 6 Java 的 I/O 流和文件 179 缓冲区是 8192 个字节 例 6.11 不使用缓冲来读一个较大的文件, 并判断文件中出现字符 A 的次数, 这里所使 用的文件是一个有 3596 行的文本文件 import java.io.fileinputstream; import java.io.ioexception; public class FileTest1 public static void main(string args[]) throws IOException long t = System.currentTimeMillis();// 获得当前的系统时间, 单位为毫秒 String filename = args[0]; FileInputStream fis = new FileInputStream(filename); // 根据命令行参数构造一个 FileInputStream 对象 int count = 0; int c; while ((c = fis.read())!= -1) if(c=='a')// 判断每个读入的字节是否为 A, 并计数 count++; fis.close(); System.out.println(count); t=system.currenttimemillis()-t; // 再次获得当前的系统时间, 两次时间之差即为程序运行所花费的时间 System.out.println("Time elapsed:" + t); 在命令行输入如下命令 : java FileTest1 accmnsrc.txt 程序运行结果为 : 5427 Time elapsed:615 例 6.12 使用缓冲来读一个较大的文件, 并判断文件中出现字符 A 的次数, 这里所使用 的文本文件与例 6.10 相同 import java.io.fileinputstream; import java.io.bufferedinputstream; import java.io.ioexception; public class FileTest2 public static void main(string args[]) throws IOException long t = System.currentTimeMillis(); String filename = args[0]; FileInputStream fis = new FileInputStream(filename); BufferedInputStream bis = new BufferedInputStream(fis); int count = 0; int c; while ((c=bis.read())!=-1) if(c=='a')

180 180 Java 程序设计 count++; fis.close(); System.out.println(count); t=system.currenttimemillis()-t; System.out.println("Time elapsed:" + t); 在命令行输入如下命令 : java FileTest1 accmnsrc.txt 程序运行结果为 : 5427 Time elapsed:15 说明 : 本例与例 6.10 的区别仅在于 FileTest2 程序的第 9 行, 该行语句以 FileInputStream 对象为参数构造了一个 BufferedInputStream 对象 从 FileTest2 程序的执行结果可看出, 其执 行时间对比与 FileTest1 有显著提高, 从 615 毫秒缩减到了 15 毫秒 数据流 数据流, 简单说就是允许字节流直接操作的基本数据类型和字符串 DataInputStream 和 DataOutputStream 类创建的对象分别称为数据输入流和数据输出流 这两个流可实现输入输出 的 可移植性, 所谓 可移植性 允许程序以与机器无关的方式读取与写入 Java 原始数据 例如, 当读取一个数值时, 不必再关心这个数值应当是多少个字节 这两个类的构造方法如下 : public DataInputStream(InputStream in): 以一个 InputStream 对象作为参数, 表示它是 建立在一个已存的输入流对象之上, 并以字节流的方式读取数据 public DataOutputStream(OutputStream in): 以一个 OutputStream 对象作为参数, 表示 它建立在一个已存在的输出流对象之上, 以字节流的方式写数据 DataInputStream 和 DataOutputStream 类的读写方法见表 6-7 所示 表 6-7 DataInputStream 的读方法和 DataOutputStream 的写方法 方法含义方法含义 int readint() 读取一个整型值 void writeint(int v) 写入一个整型值 long readlong() 读取一个长整型值 void writelong(long v) 写入一个长整型值 float readfloat() 读取一个单精度浮点值 void writefloat(float v) 写入一个单精度浮点值 double readdouble() 读取一个双精度浮点值 void writedouble(double v) 写入一个双精度浮点值 boolean readboolean() 读取一个布尔值 void writeboolean(boolean v) 写入一个布尔值 char readchar() 读取一个字符 void writechar(int v) 写入一个字符 String readutf() 读取一个 UTF 字符串 void writeutf(string str) 写入一个 UTF 字符串 void close() 关闭流 void writechars(string s) 写入一个字符串

181 项目 6 Java 的 I/O 流和文件 181 例 6.13 用 DataOutputStream 类生成一个文件, 然后用 DataInputStream 类读取这个文件 import java.io.*; public class DataInOutDemo public static void main(string[] args) try FileOutputStream fod=new FileOutputStream("data.dat");// 定义输出字节流 DataOutputStream outdat=new DataOutputStream(fod);// 定义数据流 outdat.writeint(800); outdat.writelong( ); outdat.writefloat(123.45f); outdat.writedouble( ); outdat.writeboolean(true); outdat.writechars("hello Java!"); catch (IOException e) try FileInputStream fid=new FileInputStream("data.dat");// 定义输入字节流 DataInputStream indat=new DataInputStream(fid);// 定义输入数据流 System.out.println(inDat.readInt());// 读入整型数 System.out.println(inDat.readLong());// 读入长整型数 System.out.println(inDat.readFloat());// 读入单精度浮点数 System.out.println(inDat.readDouble());// 读入双精度浮点数 System.out.println(inDat.readBoolean()); char c; while((c=indat.readchar())!='\0')// 循环读入字符 System.out.print(c); catch (IOException e) 程序运行结果为 : E9 true Hello Java! 字节流转换为字符流 假设有这样的需求 ; 使用一个字符缓冲流读取用户在命令行输入的一行数据 我们知道, System.in 是 InputStream 类 ( 字节输入流 ) 的静态对象, 可以从命令行读取数据字节 现在问 题出现了, 需要把一个字节流转化成一个字符流 我们可以使用 InputStreamReader 和 OutputStreamWriter 这两个类来进行转换

182 182 Java 程序设计 例 6.14 将字节流转换成字符流 import java.io.*; public class ByteToCharDemo public static void main(string[] args) BufferedReader in=null; try // 将字节流 System.in 通过 InputStreamReader 转换成字符流 in=new BufferedReader(new InputStreamReader(System.in)); System.out.print(" 请输入一行话 :"); String s=in.readline(); System.out.println(" 刚才输入的一行话是 :"+s); catch (IOException e) System.out.println(e.getMessage()); 在命令行运行下列语句 : D:\java>java ByteToCharDemo 程序运行结果为 : 请输入一行话 : 中国人民万岁刚才输入的一行话是 : 中国人民万岁 习题 1. 选择题 (1) 下列流中哪一个使用了缓冲区技术? A.BufferedOutputStream C.DataOutputStream B.FileInputStream D.FileReader (2) 能读入字节数据进行 Java 基本数据类型判断过滤的类是 A.BufferedInputStream C.DataInputStream B.FileInputStream D.FileReader (3) 在通常情况下, 下列哪个类的对象可以作为 BufferedReader 类构造方法的参数? A.PrintStream C.InputStreamReader B.FileInputStream D.FileReader (4) 构造 BufferedInputStream 的合适参数是哪些? A.BufferedInputStream C.FileInputStream E.File B.BufferedOutputStream D.FileOuterStream 2.BufferedReader 流能直接指向一个文件对象吗? 为什么?

183 项目 6 Java 的 I/O 流和文件 将如下三组不同类型的数据利用 DataInputStream 和 DataOutputStream 写入文件, 然后 从文件中读出 三组数据如下 : 19.99,9.99,15.99,3.99,4.99; 12,8,13,29,50; "Java T-shirt","Java Mug","Duke Juggling Dolls","Java Pin","Java Key Chain" 4. 利用 BufferedReader 和 BufferedWriter 在文件中实现输入输出字符串 项目总结 1. 理解 Java 语言中流的基本概念, 了解与输入 / 输出流有关的类 Java 语言中的输入 / 输出处理是通过使用流技术, 用统一的接口表示而实现的 2. 在进行文件操作时, 可以通过 File 类维护文件的一些基本信息, 对文件的具体内容读写操作则需要通过字节流和字符流来完成 3. 输入 / 输出流根据处理的内容, 分为字符流和字节流两种, 其中字节流是以字节为基本处理单位的流 ; 而字符流是以 16 位的 Unicode 码为处理单位的流 4.Java 语言还提供了可以同时进行读 / 写的类 RandomAccessFile 类, 用于实现对文件的随机读写操作 5.Java 通过数据缓冲流类可以提高输入输出流的读写效率 在 Java 的输入 / 输出类中, 针对字节流和字符流分别定义了两类不同的缓冲类 6.Java 中的 DataInputStream 和 DataOutputStream 类创建的对象分别称为数据输入流和数据输出流, 这两个流允许字节流直接操作基本数据类型和字符串

184 项目 7 图形用户界面设计 Java 使用 AWT 和 Swing 类完成图形用户界面编程 其中 AWT 的全称是抽象窗口工具集 Abstract Window Toolkit 它是 Java 最早提供的 GUI 库 这个 GUI 库提供了一些基本功能 但这个 GUI 库的功能比较有限 所以后来又提供了 Swing 库 通过使用 AWT 和 Swing 提供 的图形界面组件库 Java 的图形用户界面编程非常简单 程序只是依次创建所需的图形组件 并以合适的方式将这些组件组织在一起 就可以开发出非常美观的用户界面 程序用一种 搭积木 的方式将这些图形用户组件组织在一起 就是实际可用的图形用 户界面 但这些图形用户界面还不能与用户交互 为了实现图形用户界面的用户交互操作 程 序还应为用户提供事件处理 事件处理负责让程序可以响应用户动作 理解 GUI 概念 掌握 AWT 基本组件的应用 掌握颜色 字体和绘图组件的应用 掌握布局管理器的应用 掌握 GUI 组件的事件处理方法 掌握 Swing 组件的应用 任务 1 GUI 简介 早先程序使用最简单的输入输出方式 用户在键盘输入数据 程序将信息输出在屏幕上 现代程序要求使用图形用户界面 Graphics User Interface 简称 GUI 界面中有菜单 按钮 等 用户通过鼠标选择菜单中的选项和点击按钮 命令程序执行功能模块 Java 的 GUI 主要 包括 AWT 组件和 Swing 组件两种 Swing 组件是在 AWT 组件基础之上发展而来的 并且是 当前应用最广泛的 GUI 组件 AWT 简介 当 JDK1.0 发布时 Sun 提供了一个基本的 GUI 类库 这个 GUI 类库希望可以在所有平台 下都能运行 这套基本类库被称为 抽象窗口工具集 Abstract Window Toolkit 它为 Java 应用程序提供了基本的图形组件 AWT 是窗口框架 它从不同平台的窗口系统中抽取出共同 组件 当程序运行时 将这些组件的创建和动作委托给程序所在的运行平台 简而言之 当使

185 项目 7 图形用户界面设计 185 用 AWT 编写图形界面应用时, 程序指定了界面组件的位置和行为, 并未提供真正的实现,JVM 调用操作系统本地的图形界面创建和平台一致的对等体 使用 AWT 创建的图形界面应和所在运行平台原有系统的界面风格一致, 例如在 Windows 操作系统上, 它就表现出 Windows 风格 ; 在 UNIX 操作系统上, 它就表现出 UNIX 风格 Sun 希望采用这种方式来实现 一次编写, 随处运行 (Write once, run anywhere) 的目标 但实际应用中,AWT 出现了如下几个问题 : 使用 AWT 作出的图形用户界面在所有平台上都显得很简陋, 功能非常有限 AWT 为了迎合所有主流操作系统的界面设计,AWT 组件只能使用这些操作系统上图形界面组件的交集, 所以不能使用特定操作系统上复杂的图形界面组件, 最多使用四种字体 AWT 用的是传统非面向对象的编程模式 Swing 简介在 JDK l.2 中,Sun 公司推出更加稳定 通用和灵活的用户界面组件库 Swing Swing 是建立在 AWT 体系之上, 完全用 Java 编写的一套轻量级的图形工具包 与 AWT 组件相比,Swing 组件占用的资源较少, 类比较小, 不借助本地系统来绘制自身 Swing 不仅重写了 AWT 的组件, 还为这些组件增添了新的功能, 并且提供了许多 AWT 没有的, 创建复杂图形用户界面的组件, 增强了 GUI 与 Java 程序的交互功能 大多数的 Swing 组件都直接使用 Java 代码绘制, 能够保持外观风格的一致性, 可以更少地依赖平台和更少地使用本地的 GUI 资源 Swing 是 AWT 的扩展, 它提供了许多新的图形界面组件 为了与 AWT 组件类相区别, Swing GUI 组件类的开头都有前缀字母 J 例如按钮组件在 AWT 中是 Button 类, 而在 Swing 中为 JButton 类 除了有与 AWT 类似的按钮 (JButton) 标签(JLabel) 复选框(JCheckBox) 菜单 (JMenu) 等基本组件外, 还增加了一个丰富的高层组件集合, 如表格 (JTable) 树 (JTree) 与 AWT 的部件不同, 许多 Swing 组件如按钮 标签, 除了使用文字外, 还可以使用图标修饰自己 Swing 组件被称为轻量组件 (Lightweight Component), 而依赖平台的 AWT 组件被称为重量组件 (Heavyweight Component) 但是,Swing 并没有代替 AWT 在 JDK 1.1 中,AWT 事件处理模型有了根本改变 Swing 使用的仍然是 Java 1 的事件处理模型 AWT 和 Swing 的区别 AWT 和 Swing 之间的区别 : (1)AWT 是基于本地方法的 C/C++ 程序, 其运行速度比较快 ;Swing 是基于 AWT 的 Java 程序, 其运行速度比较慢 (2)AWT 的控件在不同的平台可能表现不同, 而 Swing 在所有平台表现一致 在实际应用中, 应该使用 AWT 还是 Swing 取决于应用程序所部署的平台类型 基本原则如下 : (1) 对于一个嵌入式应用, 目标平台的硬件资源往往非常有限, 而应用程序的运行速度又是项目中至关重要的因素 在这种矛盾的情况下, 简单而高效的 AWT 当然成了嵌入式 Java 的第一选择

186 186 Java 程序设计 (2) 在普通的基于 PC 或者是工作站的标准 Java 应用中, 硬件资源对应用程序所造成的限制往往不是项目中的关键因素 所以在标准版的 Java 中则提倡使用 Swing, 也就是通过牺牲速度来实现应用程序的功能 说明 : (1) 本章在理解 AWT 基本组件和事件处理的基础上, 重点讲解 Swing 的功能 (2) 随着 IA( 智能家电 ) 产品热潮逐渐兴起,AWT 组件显得越来越重要, 许多 IA 产品上会有 JRE, 能够执行 Java 程序, 比起桌上型计算机, 掌上型的 IA 不但内存有限 CPU 不够快 屏幕小且色彩少 这类 IA 界面的 Java 程序一定要用 AWT 来设计 GUI, 因为这类装置的 JRE 根本不支持 Swing 这样耗费资源的庞然大物 (3)AWT 中的布局管理器和事件处理机制都具有平台的无关性, 所以在 Swing 中也可以使用这二者 习题 1. 选择题 (1) 关于 AWT 和 Swing 说法正确的是 A.Swing 是 AWT 的子类 B.AWT 在不同操作系统中显示相同的风格 C.AWT 和 Swing 都支持事件模型 D.Swing 在不同的操作系统中显示相同的风格 (2) 进行 Java 基本 GUI 设计需要用到的包是 A.java.io B.java.sql C.java.awt D.java.rmi (3) 下列不属于 java.awt 包中的基本概念的一项是 A. 容器 B. 构件 C. 线程 D. 布局管理器 2. 什么是 Swing? 它比 AWT 有什么优点? 使用上有何区别? 任务 2 AWT 组件 AWT 概述早期的 JDK 版本中提供了 Java 抽象工具集 AWT, 其目的是为程序创建图形用户界面提供了支持 AWT 组件定义在 java.awt 包中, 包括组件类 组件布局类等, 图 7-1 列出 AWT 常用图形组件类的继承关系 AWT 中包括了图形界面编程的基本类库 它是 Java 语言 GUI 程序设计的核心, 它为用户提供基本的界面组件 这些组件是为了使用户和机器之间能够更好地进行交互, 而用来建立图形用户界面的独立平台 其中主要由以下几部分组成, 包括 : 组件类 (Component) 容器类 (Container) 图形类(Graphics) 和布局管理器 (LayoutManager) 1. 组件组件 (component) 是构成图形界面的基本成分和核心元素 组件类 (Component) 是一个抽象类, 是 AWT 组件类层次结构的根类, 实际使用的组件都是 Component 类的子类

187 项目 7 图形用户界面设计 187 Component 类提供对组件的通用方法, 包括设置组件位置 设置组件大小 设置组件字体 响应鼠标或键盘事件 组件重绘等 Button Canvas Checkbox Dialog FileDialog Choice Window Object Component Container Panel Frame Label ScrollPane Scrollbar List TextArea TextComponent TextField 图 7-1 AWT 常用图形组件类的继承关系 2. 容器容器 (container) 是一种特殊组件, 它能容纳其他组件 它在可视区域内显示其他组件 由于容器是组件, 在容器之中还可以放置其他容器, 所以可以使用多层容器构成富于变化的界面 容器有两种 : 窗口 (Windows) 和面板 (Panel) 窗口可独立存在, 可被移动, 也可以被最大化和最小化, 有标题 边框, 可添加菜单栏 面板不能独立存在, 必须包含在另一个容器中, 面板没有标题, 没有边框, 不可以添加菜单栏 一个窗口可以包含多个面板, 一个面板也可以包含另一个面板, 但面板不能包含窗口 3. 布局管理器为了使我们生成的图形用户界面具有良好的平台无关性,Java 语言中, 提供了布局管理器 (LayoutManager) 这个工具来管理组件在容器中的布局, 而不使用直接设置组件位置和大小的方式 每个容器都有一个布局管理器, 当容器需要对某个组件进行定位或判断其大小尺寸时, 就会调用其对应的布局管理器 常用的布局管理器有顺序布局管理器 (FlowLayout), 网格布局管理器 (GridLayout), 边界布局管理器 (BorderLayout) 等 AWT 常用组件 1. 框架窗口类 Windows 主要有两个子类 : 框架类 (Frame) 和对话框类 (Dialog) 框架 (Frame) 是一种带标题栏并且可以改变大小的窗口 在应用程序中, 使用框架作为容器, 在框架中放置组件 框架类在实例化时默认是最小的 不可见的, 必须通过 setsize() 方法设置框架大小, 通过 setvisible(true) 方法使框架可见 Frame 类的构造方法和主要成员方法如表 7-1 和表 7-2 所示

188 188 Java 程序设计 表 7-1 Frame 的构造方法 Frame 类的构造方法 主要功能 Frame() Frame(String title) 创建没有标题的窗口 创建以 title 为标题的窗口 表 7-2 Frame 的成员方法 Frame 类的成员方法 int getstate() void setstate(int state) String gettitle() void settitle(string title) boolean isresizable() void setresizable(boolean r) Image geticonimage() void seticonimage(image img) 主要功能 获得 Frame 窗口的状态 ( Frame.Normal 表示一般状态, Frame.ICONIFIED 表示最小化状态 ) 设置 Frame 窗口的状态 ( Frame.Normal 表示一般状态, Frame.ICONIFIED 表示最小化状态 ) 获得 Frame 窗口的标题 设置 Frame 窗口的标题 测试 Frame 窗口是否可以改变大小 设置 Frame 窗口是否可以改变大小 返回窗口的最小化图标 设置窗口的最小化图标为 img 例 7.1 创建窗体 Import java.awt.*; class FrmApp static Frame fra=new Frame( FrmApp ); // 创建窗口对象 fra, 设置标题为 FrmApp public static void main(string args[]) fra.setsize(250,150); // 设置窗体大小, 水平 250 像素, 垂直 150 像素 fra.setlocation(100,200);// 窗口左上角定位距离显示器左上角水平方向 100, 垂直方向 200 位置 fra.setvisible(true);// 设置窗体可见 System.out.println( State: +fra.getstate()); System.out.println( Title: +fra.gettitle()); System.out.println( Visible: +fra.isvisible()); 程序运行出现如图 7-2 所示的窗口, 其程序运行结果为 : State:0 Title:FrmApp Visible:true 图 7-2 创建一个窗口

189 项目 7 图形用户界面设计 189 说明 : 当点击图 7-2 所示的窗口关闭按钮时窗口并没有关闭, 原因是没有为关闭窗口的事件编写 代码 关于事件处理将在后面章节中讲到, 这里只需在 public static void main() 方法最后添加 如下代码即可关闭窗口 Fra.addWindowListener(new WindowAdapter() public void windowclosing(windowevent e) System.exit(0); ); 2. 标签 标签类 (Label) 组件用于显示一行文本信息 标签只能显示信息, 不能用于输入 Label 类的构造方法和主要成员方法如表 7-3 和表 7-4 所示 表 7-3 Label 的构造方法 Label 类的构造方法 主要功能 Label() Label(String str) Label(String str,int align) 创建一个没有标题的标签 创建一个以 str 为标题的标签 创建一个以 str 为标题的标签, 并以 align 为对齐方式, 其中 Label.LEFT Label.CENTER Label.RIGHT 分别为居左 居中和居右 表 7-4 Label 的成员方法 Label 类的成员方法 int getalignment() void setalignment(int align) String gettext() void settext(string text) 主要功能返回标签标题的对齐方式设置标签标题的对齐方式获得标签标题设置标签标题为 text 例 7.2 在窗口中建立标签 Import java.awt.event.*; import java.awt.*; class LabApp public static void main(string args[]) Frame fra=new Frame("LabApp");// 创建窗口对象 fra, 设置标题为 LabApp Label lab=new Label();// 创建标签对象 lab fra.setsize(250,150);// 设置窗体大小, 水平 250 像素, 垂直 150 像素 lab.settext("this is a label");// 设置标签标题为 This is a label lab.setalignment(label.center);// 标签标题居中 lab.setbackground(color.white);// 标签标题背景颜色为白色 lab.setforeground(color.black);// 标签标题前景颜色为黑色 Font fnt=new Font("Serief",Font.ITALIC+Font.BOLD,22);// 创建字体对象 lab.setfont(fnt);// 设置标签标题字体 fra.add(lab);// 将标签加入到窗体

190 190 Java 程序设计 fra.setvisible(true);// 将窗体设置为可见 fra.addwindowlistener(new WindowAdapter()// 关闭按钮的事件处理 public void windowclosing(windowevent e) System.exit(0); ); 程序运行结果如图 7-3 所示 : 图 7-3 窗口中的标签 说明 : (1) 事件类包含在 java.awt.event 包中 关闭按钮事件处理在 main() 方法的最后四行 (2) 调用 Font 类的构造方法 Font("Serief",Font.ITALIC+Font.BOLD,22) 实例化 Font 类对象 fnt,fnt 为 Serief 字体 粗斜体 22 号 3. 颜色 Java 的 java.awt.color 类为 GUI 组件设置颜色 颜色中的 R( 红 ) G( 绿 ) B( 蓝 ) 为三原色的比例 一个 RGB 值由三部分组成 : 第一个 RGB 部分定义红色的量 ; 第二个 RGB 部分定义为绿色的量 ; 第三个 RGB 部分定义为蓝色的量 RGB 值中某一部分的值越大, 那种特定颜色的量就越大, 这就是通常所说的 RGB 模式 Color 类的构造方法和主要成员方法如表 7-5 和表 7-6 所示 表 7-5 Color 类的构造方法 Color 类的构造方法 Color(int r,int g,int b) Color(float r,float g,float b) Color(int rgb) 主要功能 使用在 0~255 范围内的整数指定红 绿和蓝三种颜色的比例来创建一种 Color 对象 使用在 0.0~1.0 范围内的浮点数指定红 绿和蓝三种颜色的比例来创建一种 Color 对象 使用指定组合 RGB 值创建一种 Colordx 对象 表 7-6 Color 类的成员方法 Color 类的成员方法 void setcolor(color c) void setcolor(new Color(int r,int g,int b)) setbackground(color c) Color getcolor() int getred() 主要功能设置前景颜色,c 代表颜色设置前景颜色的另一种方法设置背景颜色,c 代表颜色获取当前所使用的颜色获得对象的红色值

191 项目 7 图形用户界面设计 191 续表 Color 类的成员方法 主要功能 int getgreen() int getblue() int getrgb() Color brighter() Color darker() 获得对象的绿色值获得对象的蓝色值获得对象的 RGB 值获取此颜色的一种更亮版本获取此颜色的一种更暗版本 用户可以通过创建 Color 对象指定组件的颜色值, 也可以用 Java 提供的一些常用的颜色 Color 类数据成员常量取得颜色值或者是 Color 类的成员取得颜色值 如例 7.2 的 lab.setbackground(color.white) 将标签的背景色设置为白色 4. 字体 Font 类对象包含了对文档字的完整定义 Font 类用来设置字体样式 大小与字型 很多方法都需要使用 Font 类对象作为参数, 用以设置组件的字体 Font 类的构造方法和主要成员方法如表 7-7 和表 7-8 所示 表 7-7 Font 类的构造方法 Font 类的构造方法 Font(String name,int style, int size) 主要功能 name 为字型名, 如宋体 黑体 楷体 Arial Courier TimesRoman Helvetica 等 ;style 为字体样式, 如粗体 (Font.BOLD) 斜体 (Font.ITALIC) 正常体 (Font.PLAIN);size 为用像素点表示的字体大小 表 7-8 Font 类的成员方法 Font 类的成员方法 public static Font decode(string str) public string getfamily() public string getname() public int getstyle() public int getsize() public boolean isplain() public boolean isbold() public boolean isitalic() public string tostring() 主要功能使用传递进来的名称获得指定的字体获得指定平台的字体名获得字体的名称获得字体的样式获得字体的大小若字体是正常型, 则返回 true 若字体是粗体, 则返回 true 若字体是斜体, 则返回 true 将此对象转换为一个字符串表示 如例 7.2 中的 Font fnt=new Font("Serief",Font.ITALIC+Font.BOLD,22) 创建一个 fnt 对象并进行相关设置, 最好调用 lab.setfont(fnt) 来设置标签的字体 5. 按钮按钮是最常见的一种组件, 用来控制程序运行的方向 用户单击按钮时, 计算机将执行一系列命令, 完成一定功能

192 192 Java 程序设计 按钮通过 java.awt 包的 button 类创建, 表 7-9 和表 7-10 中分别给出了 Button 类的构造方 法和常用成员方法 表 7-9 Button 类的构造方法 Button 类的构造方法 主要功能 Button() Button(String str) 创建一个没有标题的按钮 创建一个以 str 为标题的按钮 表 7-10 Button 类的成员方法 Button 类的成员方法 主要功能 String getlabel() void setlabel(string str) 获得按钮的标题 设置按钮的标题为 str 例 7.3 在窗口中建立一个按钮 import java.awt.event.*; import java.awt.*; class ButtApp public static void main(string args[]) Frame fra=new Frame("ButtApp");// 创建窗体 fra.setsize(300,200); fra.setlayout(null);// 将窗口默认布局设置关闭, 组件按照设置的大小和位置显示 Button butt=new Button("Click");// 创建按钮对象, 设置其标题为 Click butt.setsize(100,50);// 设置大小, 其水平方向 100 像素, 垂直方向为 50 像素 butt.setlocation(75,60);// 设置位置, 距屏幕左上角水平方向 75 像素, 垂直方向 60 像素 fra.add(butt);// 将按钮加到窗体中 fra.setvisible(true); fra.addwindowlistener(new WindowAdapter()// 关闭按钮的事件处理 public void windowclosing(windowevent e) System.exit(0); ); 程序运行结果如图 7-4 所示 : 图 7-4 窗口中的按钮 6. 文本行 文本行由 java.awt 包中的 TextField 类来创建 TextField 类的构造方法和主要成员方法如

193 项目 7 图形用户界面设计 193 表 7-11 和表 7-12 所示 表 7-11 TextField 类的构造方法 TextField 类的构造方法 TextField() TextField(int columns) TextField(String text) TextField(String text,int columns) 主要功能创建一个空的文本行创建空的文本行, 具有指定列数创建文本为 text 的文本行创建具有指定列数 文本为 text 的文本行 表 7-12 TextField 类的成员方法 TextField 类的成员方法 String gettext() int getcolumns() void settext(string text) void setcolumns(int columns) char getechochar() void setechochar(char c) boolean echocharisset() 主要功能获得文本行的文本获得文本行的列数设置文本行的文本为 text 设置文本行的列数获得回显字符设置回显字符检测是否设置回显字符 例 7.4 创建文本行 import java.awt.event.*; import java.awt.*; class TextApp public static void main(string args[]) Frame fra=new Frame(" 文本框架程序 "); TextField txt1=new TextField(50);// 创建文本框对象 txt1t, 使其可以容纳 50 个字符 // 创建文本框对象 txt2, 使其显示 Text Field 文本, 且可以容纳 50 个字符 TextField txt2=new TextField("Text Field",50); fra.setbounds(0,0,300,200);// 使窗口左上角位于屏幕左上角, 使窗口大小为水平方向 300 像素 垂直方向 200 像素 fra.setlayout(null);// 将窗口默认布局关闭, 使窗口中的组件按照实际设置布局 txt1.setbounds(50,50,130,20); // 使 txt1 的左上角在水平方向距离屏幕左上角 50 像素, 垂直方向距离屏幕左上角 50 像素, 长度为 130 像素, 高度为 20 像素 txt2.setbounds(50,100,130,30); // 使 txt2 的左上角在水平方向距离屏幕左上角 50 像素, 垂直方向距离屏幕左上角 100 像素, 长度为 130 像素, 高度为 30 像素 fra.add(txt1);// 将文本框 txt1 加入到窗体 fra fra.add(txt2);// 将文本框 txt2 加入到窗体 fra fra.setvisible(true); fra.addwindowlistener(new WindowAdapter()// 关闭按钮的事件处理 public void windowclosing(windowevent e) System.exit(0); );

194 194 Java 程序设计 程序运行结果如图 7-5 所示 : 图 7-5 文本行 7. 文本区多行文本输入框的功能与单行文本输入框的功能相同, 只是它能显示更多的文字 因为单行文本输入框只能输入一行的文字, 所以在需要输入和显示较多的文字时, 就要用到多行文本输入框 多行文本输入框是由 TextArea 类实现的 TextArea 类的构造方法和主要成员方法如表 7-13 和表 7-14 所示 表 7-13 TextArea 类的构造方法 TextArea() TextArea 类的构造方法 TextArea(int rows,int columns) TextArea(String text) TextArea(String text,int rows,int columns) TextArea(String text,int rows, int columns,int scrollbars) 构造一个新的多行文本输入框 主要功能 构造一个指定 rows 行和 columns 列的多行文本输入框 构造一个显示 text 内容的多行文本输入框 构造一个指定 rows 行 columns 列, 并显示 text 内容的多行文本输入框 构造一个指定 rows 行 columns 列, 并显示 text 内容的多行文本输入框 scrollbars 参数用于确定文本区使用滚动条的情况, 其中 SCROLLBARS_BOTH( 具有水平与垂直两个滚动条 ) SCROLLBARS_VERTICAL_ONLY ( 只有垂直滚动条 ) SCROLLBARS_HORIZONTAL_ONLY( 只有水平滚动条 ) SCROLLBARS_NONE( 不使用滚动条 ) 表 7-14 TextArea 类的成员方法 TextArea 类的成员方法 void append(string str) void insert(string s,int pos) void settext(string text) String gettext() String getselectedtext() void seteditable(boolean b) int getrows() void setrows(int rows) int getcolumns() void setcolumns(int Columns) 主要功能在多行文本框尾部添加文本 str 在多行文本框指定位置 pos 插入文本 str 设置多行文本框的文本内容为 text 获取多行文本框的文本内容获取多行文本框中选中的文本内容设置多行文本框的可编辑状态返回此对象的行数设置此对象的行数获取此对象的列数设置此对象的列数

195 项目 7 图形用户界面设计 195 例 7.5 文本编辑的综合应用 import java.awt.*; import java.awt.event.*; class TxtareaApp public static void main(string args[]) Frame fra=new Frame(" 文本编辑程序 ");// 创建窗体, 标题为 文本编辑程序 Label lab1=new Label(" 口令 ");// 创建标签 lab1 Label lab2=new Label(" 密码 ");// 创建标签 lab2 TextField txt1=new TextField(" 输入口令 ",50);// 创建文本框, 缺省内容为 输入口令 TextField txt2=new TextField(50);// 创建文本框 TextArea txtarea1=new TextArea(" 文本区 ",40,50);// 创建文本区, 大小为 40 行,50 列 lab1.setbounds(10,50,30,20);// 使 lab1 的左上角在水平方向距离屏幕左上角 10 像素, 垂直方向距离屏幕左上角 50 像素, 长度为 30 像素, 高度为 20 像素 lab2.setbounds(170,50,30,20);// 设置 lab2 位置大小, 具体参数参考上面 fra.setbounds(0,0,350,200);// 设置 fra 位置大小, 具体参数参考上面 txt1.setbounds(40,50,130,20);// 设置 txt1 位置大小, 具体参数参考上面 txt2.setbounds(200,50,130,20);// 设置 txt2 位置大小, 具体参数参考上面 txtarea1.setbounds(100,80,100,100);// 设置 txtarea1 位置大小, 具体参数参考上面 fra.setlayout(null);// 将窗口默认布局设置关闭, 组件按照设置的大小和位置显示 fra.add(lab1); fra.add(lab2); fra.add(txt1); fra.add(txt2); fra.add(txtarea1); fra.setvisible(true); fra.addwindowlistener(new WindowAdapter()// 关闭按钮的事件处理 public void windowclosing(windowevent e) System.exit(0); ); 程序运行后结果如图 7-6 所示 : 图 7-6 文本编辑综合应用 习题 1. 选择题 (1) 容器类 java.awt.container 的父类是 A.java.awt.Frame B.java.awt.Panel C.java.awt.Componet D.java.awt.Windows

196 196 Java 程序设计 (2) 下列说法中错误的一项是 A. 组件是一个可视化的能与用户在屏幕上交互的对象 B. 组件能够独立显示出来 C. 组件必须放在某个容器中才能正确显示 D. 一个按钮可以是一个组件 (3)java.awt.Frame 的父类是 A.java.util.Window B.java.awt Window C.java.awt Panel D.java.awt.ScrollPane (4) 下列说法中错误的一项是 A. 在实际编程中, 一般使用的是 Component 类的子类 B. 在实际编程中, 一般使用的是 Container 类的子类 C.Container 类是 Component 类的子类 D. 容器中可以放置组件, 但是不能够放置容器 (5) 编写 AWT 图形用户界面的时候, 一定要 import 的语句是 A.import java.awt; B.import java.awt.*; C.import javax.awt D.import javax.swing.*; (6) 下列关于 AWT 构件的说法中错误的一项是 A.Frame 是顶级窗口, 它无法直接监听键盘输入事件 B. 对话框需要依赖于其他窗口而存在 C. 菜单只能被添加到菜单栏中 D. 可以将菜单添加到任意容器的某处 2. 什么是容器组件? 组件与容器有何区别? 3. 编程包含一个标签和一个按钮, 单击按钮时, 标签的内容在 你好 和 再见 之间切换 4. 编程确定当前鼠标的位置坐标 任务 3 布局管理 布局管理器可以对窗口中的组件做有效的布局 Java 中的容器和布局管理器相分离, 也就是容器只是把组件放置进来 至于如何放置就要使用到布局管理器了 布局管理器主要包括有 BorderLayout FlowLayout GridLayout 和 CardLayout 等 本节就主要介绍一下常用的布局管理器 边界布局 (BorderLayout) BorderLayout 将整个容器的布局划分成东 (EAST) 西 (WEST) 南 (SOUTH) 北 (NORTH) 中 (CENTER) 五个区域, 组件只能被添加到指定的区域 如不指定组件的加入部位, 则默认加到 CENTER 区 ; 每个区域只能加入一个组件, 如加入多个, 则先前加入的会被覆盖 BorderLayout 型布局其尺寸缩放原则 : 北 南两个区域在水平方向缩放 ; 东 西两个区域在垂直方向缩放 ; 中部区域可在两个方向上缩放 BorderLayout 类的构造方法如表 7-15 所示

197 项目 7 图形用户界面设计 197 表 7-15 BorderLayout 类的构造方法 BorderLayout() BorderLayout 类的构造方法 BorderLayout( int hgap, int vgap) 主要功能 创建一个 BorderLayout 布局管理器 创建一个 BorderLayout 布局管理器, 组件之间水平及垂直间距分别由两个参数指定 例 7.6 应用 BorderLayout 布局 import java.awt.*; import java.awt.event.*; public class TestBorderLayout public static void main(string args[]) Frame f = new Frame("Border Layout");// 创建窗体, 标题为 Border Layout Button bn = new Button("BN");// 创建按钮 bn, 按钮命名为 BN Button bs = new Button("BS");// 创建按钮 bs, 按钮命名为 BS Button bw = new Button("BW");// 创建按钮 bw, 按钮命名为 BW Button be = new Button("BE");// 创建按钮 be, 按钮命名为 BE Button bc = new Button("BC");// 创建按钮 bc, 按钮命名为 BC f.add(bn,borderlayout.north);// 将按钮添加到窗体, 并放置窗口上面 f.add(bs,borderlayout.south);// 将按钮添加到窗体, 并放置窗口下面 f.add(bw,borderlayout.west);// 将按钮添加到窗体, 并放置窗口左面 f.add(be,borderlayout.east);// 将按钮添加到窗体, 并放置窗口右面 f.add(bc,borderlayout.center);// 将按钮添加到窗体, 并放置窗口中心 f.setbounds(300,100,400,300); f.setvisible(true); f.addwindowlistener(new WindowAdapter()// 关闭按钮的事件处理 public void windowclosing(windowevent e) System.exit(0); ); 程序运行结果如图 7-7 所示 : 图 7-7 BorderLayout 布局 流式布局 (FlowLayout) 流式布局 (FlowLayout) 又称顺序布局 这种布局管理器很单纯地将组件由左至右依次排

198 198 Java 程序设计 列在容器上, 直到上端空间排满后才会移到下一列 ; 相同的水平排列方式不断持续进行下去 组件保持自己的尺寸, 一行能容纳的组件的数目随容器的宽度变化 FlowLayout 的构造方法如表 7-16 所示 表 7-16 FlowLayout 类的构造方法 FlowLayout 类的构造方法 FlowLayout() FlowLayout(int align) FlowLayout(int align, int hgap, int vgap) 主要功能 构造 FlowLayout 布局管理器, 默认居中对齐, 垂直 水平间距为 5 构造 FlowLayout 布局管理器并指定对齐方式,align 可取的值为 : FlowLayout.CENTER(LEFT,RIGHT) 构造 FlowLayout 布局管理器, 指定对齐方式 垂直及水平间距 例 7.7 应用 FlowLayout 布局 import java.awt.*; import java.awt.event.*; public class TestFlowLayout public static void main(string args[]) Frame f = new Frame("Flow Layout");// 创建窗体 FlowLayout layout=new FlowLayout();// 创建流式布局对象 layout f.setbounds(0,0,200,150);// 设置窗体大小, 宽 200, 高 150 //f.setbounds(0,0,400,100);// 设置窗体大小, 宽 400, 高 100 f.setlayout(layout);// 将窗体布局设置成流式布局 Button but1,but2; TextField txt1,txt2; but1=new Button("Button 1");// 创建按钮 1 but2=new Button("Button 2");// 创建按钮 2 txt1=new TextField("Text 1",10);// 创建文本框 1 txt2=new TextField("Text 2",10);// 创建文本框 2 f.add(but1); f.add(but2); f.add(txt1); f.add(txt2); f.setvisible(true) ; f.addwindowlistener(new WindowAdapter()// 关闭按钮的事件处理 public void windowclosing(windowevent e) System.exit(0); ); 程序运行结果如图 7-8(a) 所示 如果增大窗口界面程序运行结果如图 7-8(b) 所示 网格布局 (GridLayout) 网格布局 (GridLayout) 将容器划分成网格, 这样各个组件就可以按行列放置到每个网格

199 项目 7 图形用户界面设计 199 中, 每个组件的大小一样 在向 GridLayout 添加组件时, 其顺序是从网格的左上角开始, 从 左向右排列, 直到排满一行, 再从下一行开始从左向右依次排列 表 7-17 列出了 GridLayout 类的构造函数 (a) 初始界面 (b) 增大窗口宽度后的界面 图 7-8 FlowLayout 布局 表 7-17 GridLayout 类的构造方法 GridLayout 类的构造方法 GridLayout() GridLayout(int rows, int cols) GridLayout( int rows, int cols, int hgap, int vgap) 主要功能 创建具有一行一列的 GridLayout 布局 创建一个 GridLayout 布局管理器, 行及列数分别由两个参数指定 创建一个 GridLayout 布局管理器, 组件之间水平及垂直间距分别为 hgap 和 vgap 个像素 例 7.8 应用 GridLayout 布局 import java.awt.*; import java.awt.event.*; public class TestGridLayout public static void main(string args[]) Frame f=new Frame("Grid Layout");// 创建窗体 GridLayout layout=new GridLayout(2,2);// 创建网格布局管理器 f.setbounds(0,0,200,200); f.setlayout(layout);// 将窗体布局管理设置成网格布局 String names[]="but1","but2","but3","but4"; for(int i=0;i<names.length;i++) f.add(new Button(names[i]));// 依次创建四个按钮, 并添加到窗体 f.setvisible(true); f.addwindowlistener(new WindowAdapter()// 关闭按钮的事件处理 public void windowclosing(windowevent e) System.exit(0); ); 程序运行结果如图 7-9 所示

200 200 Java 程序设计 图 7-9 GridLayout 布局 卡片布局 (CardLayout) 卡片布局 (CardLayout) 布局像是将各个组件作为卡片排放到一个 卡片盒 中, 只有最上面的卡片可见 可以通过这种布局的方法将卡片中的任何一张移到最上面 通常每张卡片都是一个容器 ( 如面板 ) CardLayout 类的构造方法和主要成员方法如表 7-18 和表 7-19 所示 表 7-18 CardLayout 类的构造方法 CardLayout() CardLayout 类的构造方法 主要功能 创建一个 CardLayout 布局管理器 表 7-19 CardLayout 类的成员方法 CardLayout 类的成员方法 public void add(string name, Component c) public void first(container parent) public void last(container parent) public void next(container parent) public void previous(container parent) public void show(container parent, String name) 主要功能添加组件 c, 并指定组件的名称显示第一张卡片显示最后一张卡片显示下一张卡片显示上一张卡片显示指定名称的卡片 的组件 : 假设一个容器 c, 那么使用 CardLayout 的一般步骤如下 : (1) 创建 CardLayout 对象, 如 :CardLayout mycard=new CardLayout(); (2) 设置容器的布局为 CardLayout, 如 :c.setlayout(mycard); (3) 调用容器的 add 方法将组件加入容器 最先加入的是第一张, 依次类推 (4) 使用 show 方法, 根据容器名字及组件名称显示这一组件, 如下列语句显示名称为 s mycard.show(c,s); 也可以按组件加入容器的顺序显示组件, 如 : mycard.first(c); 即显示 c 中的第一个组件 下列语句 : mycard.next(c); 显示 c 中当前组件的下一个组件, 即第二个组件 例 7.9 应用 CardLayout 布局 import java.awt.*;

201 项目 7 图形用户界面设计 201 import java.awt.event.*; public class TestCardLayout public static void main(string args[]) Frame f = new Frame("Card Layout");// 创建窗体 CardLayout layout=new CardLayout();// 创建卡式布局管理器 Label card1,card2,card3,card4; card1=new Label("Card1");// 创建标签 Card1 card2=new Label("Card2");// 创建标签 Card2 card3=new Label("Card3");// 创建标签 Card3 card4=new Label("Card4");// 创建标签 Card4 f.add(card1);// 将标签加到窗体 f.add(card2);// 将标签加到窗体 f.add(card3);// 将标签加到窗体 f.add(card4);// 将标签加到窗体 f.setbounds(0,0,200,200);// 设置窗体位置大小 f.setlayout(layout);// 将窗体布局管理设置为卡式布局 for(int i=2;i<=4;i++) if(i==1) layout.first(f);// 显示第一个组件 else layout.next(f);// 显示下一个组件 f.setvisible(true) ; f.setvisible(true); f.addwindowlistener(new WindowAdapter()// 关闭按钮的事件处理 public void windowclosing(windowevent e) System.exit(0); ); 程序运行结果如图 7-10 所示 图 7-10 CardLayout 布局说明 : 为了显示每张卡片上的标签, 使用了一个循环语句, 并调用了上述翻动卡片的方法 因而可以依次轮流查看每一张卡片上的组件 当然, 因程序运行太快, 最后只能看到末尾的一张卡片内容

202 202 Java 程序设计 习题 1. 选择题 (1) 哪些布局管理器使用的是组件的最佳尺寸? A.FlowLayout B.BorderLayout C.GridLayout D.CardLayout E.GridBagLayout (2) 下列叙述中, 错误的一项是 A. 采用 GridLayout 布局, 容器中的每个组件平均分配容器空间 B. 采用 GridLayout 布局, 容器中的每个组件形成一个网络状的布局 C. 采用 GridLayout 布局, 容器中的组件按照从左到右 从上到下的顺序排列 D. 采用 GridLayout 布局, 容器大小改变时, 每个组件不再平均分配容器空间 (3) 下列哪一项不属于 AWT 布局管理器? A.GridLayout B.CardLayout C.BorderLayout D.BoxLayout (4) 布局管理器可以管理组件的哪个属性? A. 大小 B. 颜色 C. 名称 D. 字体 (5) 下列选项中不属于容器的一项是 A.Window B.Panel C.FlowLayout D.ScrollPane 2. 填空题 (1)FlowLayout 类是 直接子类 其布局策略是 : 将容器中的组件按照加入的先后顺序从 向 排列, 当一行排满之后就转到下一行继续从 向 排列, 每一行中的组件都 排列 它是 和 缺省使用的布局编辑策略 (2) 对于一个原本不使用 布局编辑器的容器, 若需要将其布局策略改为 FlowLayout, 可以使用 方法 (3)BorderLayout 类的布局策略是 : 把容器内的空间划分为 五个区域, 它们分别用字符串常量 表示 (4)BorderLayout 是 和 的缺省布局策略 3. 布局管理器的作用是什么? 在 JDK 中哪些常用布局管理器? 各有何特点? 任务 4 事件处理 事件处理机制概述事件处理是图形界面与用户进行交互的重要内容 Java 语言的事件处理机制包括事件源 事件 事件处理器三个主要的概念 首先需要为事件源注册相应的事件处理器, 事件源产生指定事件后, 事件处理器捕获后进行相应事件处理 其大致处理机制如图 7-11 所示 1. 事件源产生事件的组件叫事件源 例如, 在一个按钮上单击鼠标时, 该按钮就是事件源, 会产生一个 ActionEvent 类型的事件

203 项目 7 图形用户界面设计 事件 图 7-11 事件处理机制 事件是用户对事件源的各种操作动作 事件是用一个事件对象来表示, 都有相应的事件 类 事件类包含在 java.awt.event 和 javax.swing.event 包中 3. 事件处理器 事件处理器是一个接收事件对象并进行相应处理的方法 事件处理器包含在一个类中, 这个类的对象负责检查事件是否发生, 若发生就激活事件处理器进行处理 4. 事件监听器类 包含事件处理器, 并负责检查事件是否发生, 若发生就激活事件处理器进行处理的类叫 做事件监听器类 其实例就是事件监听器对象 事件监听器类必须实现事件监听器接口 5. 注册事件监听器 为了能够让事件监听器检查某个组件 ( 事件源 ) 是否发生了某些事件, 并且在发生时激 活事件处理器进行相应的处理, 必须在事件源上注册事件监听器 在 Java 中进行事件处理的一般方法归纳如下 : (1) 对于某种类型的事件 XXXEvent, 要想接收并处理这类事件, 必须定义相应的事件 监听器类, 该类需要实现与该事件相对应的监听接口 XXXListener (2) 事件源实例化以后, 必须注册该类事件的监听器 这是通过使用事件源组件的方法 addxxxlistener( 事件监听器对象 ) 来注册监听器 说明 : (1) 其中 XXX 为对应的事件类 (2) 监听器类需要实现与该事件相对应的监听器接口 XXXListener 意味着该监听器类需 声明为 implements XXXListener, 必须在类体中实现 XXXListener 接口中的所有方法 例 7.10 事件监听器类应用 import java.awt.*; import java.awt.event.*; class ButtonHandler implements ActionListener// 定义外部类实现监听接口 public void actionperformed(actionevent e)// 实现接口中的方法 System.out.println("Action occurred"); System.out.println("Button s label is:"+e.getactioncommand());// 返回事件源名称 public class TestButton public static void main(string args[]) Frame f = new Frame("Test");// 创建窗体

204 204 Java 程序设计 Button b = new Button("Press Me!");// 创建按钮 b.addactionlistener(new ButtonHandler( ));// 向事件源注册监听器 f.add(b, "Center");// 按钮添加到窗体 f.pack(); f.setvisible(true); f.addwindowlistener(new WindowAdapter()// 关闭按钮的事件处理 public void windowclosing(windowevent e) System.exit(0); ); 程序运行结果如图 7-12 所示 点击按钮 Press Me! 显示结果为 : Action occurred Button s label is:press Me! 图 7-12 例 7.10 运行结果 事件类和监听器接口 1. 事件类 Java 定义的多数事件类在 java.awt.event 包中 AWTEvent 类是所有事件类的祖先类, 它又继承了 java.util.eventobject 类, 而 EventObject 类又继承了 java.lang.object 类, 其继承关系如图 7-13 所示 图 7-13 java.awt.event 的继承关系图 AWT 事件分为两大类 : 低级事件和高级事件, 低级事件是指基于组件和容器的事件, 当一个组件上发生事件, 如 : 鼠标的进入, 单击, 拖放等, 或组件的窗口开关等, 触发了组件事件 高级事件是基于语义的事件, 它可以不和特定的动作相关联, 而依赖于触发此事件的类, 如在 TextField 中按 Enter 键会触发 ActionEvent 事件, 滑动滚动条会触发 AdjustmentEvent 事件, 或是选中项目列表的某一条就会触发 ItemEvent 事件

205 项目 7 图形用户界面设计 205 (1) 低级事件 ComponentEvent( 组件事件 : 组件尺寸的变化 移动 ); ContainerEvent( 容器事件 : 组件增加 移动 ); WindowEvent( 窗口事件 : 关闭窗口 窗口闭合 图标化 ); FocusEvent( 焦点事件 : 焦点的获得和丢失 ); KeyEvent( 键盘事件 : 键按下 释放 ); MouseEvent( 鼠标事件 : 鼠标单击 移动 ) (2) 高级事件 ( 语义事件 ) ActionEvent( 动作事件 : 按钮按下,TextField 中按 Enter 键 ); AdjustmentEvent( 调节事件 : 在滚动条上移动滑块以调节数值 ); ItemEvent( 项目事件 : 选择项目, 不选择 项目改变 ); TextEvent( 文本事件 : 文本对象改变 ) 2. 事件监听器接口 Java 中的每个事件类都有一个监听器接口, 接口中声明了一个或多个抽象的事件处理方法 如果某个类实现事件监听器接口, 其对象就可以作为对应事件的监听器, 具备监视和处理事件能力 AWT 中的主要事件类 所对应的事件监听器接口及接口中所提供的方法如表 7-20 所示 表 7-20 事件监听器接口和事件监听器接口中的方法事件类描述信息监听器接口监听器接口所提供的事件处理方法 ActionEvent 激活组件 ActionListener actionperformed(actionevent) ItemEvent 选择某些项目 ItemListener itemstatechanged(itemevent) MouseEvent 鼠标移动 鼠标点击等 MouseMotionListener MouseListener KeyEvent 键盘输入 KeyListener FocusEvent AdjustmentEvent ComponentEvent 组件收到或失去焦点 移动滚动条组件等组件 对象移动 缩放 显示 隐藏等 FocusListener AdjustmentListener ComponentListener mousedragged(mouseevent) mousemoved(mouseevent) mousepressed(mouseevent) mousereleased(mouseevent) mouseentered(mouseevent) mouseexited(mouseevent) mouseclicked(mouseevent) keypressed(keyevent) keyreleased(keyevent) keytyped(keyevent) focusgained(focusevent) focuslost(focusevent) adjustmentvaluechanged(adjustmentevent) ComponentMoved(ComponentEvent) ComponentHidden(ComponentEvent) ComponentResized(ComponentEvent) ComponentShown(ComponentEvent)

206 206 Java 程序设计 事件类描述信息监听器接口监听器接口所提供的事件处理方法 续表 windowclosing(windowevent) windowopened(windowevent) WindowEvent 窗口收到窗口级事件 WindowListener windowiconified(windowevent) windowdeiconified(windowevent) windowclosed(windowevent) windowactivated(windowevent) windowdeactivated(windowevent) ContainerEvent 容器中增加或删除了组件 ContainerListener componentadded(containerevent) componentrmoved(containerevent) TextEvent 文本字段或文本区发生改变 TextListener textvaluechanged(textevent) AWT 中的组件和可触发的事件类的对应关系如表 7-21 所示 表 7-21 AWT 中组件和可触发的事件类 组件类 生产的事件类 Button CheckBox Component MenuItem Scrollbar TextArea TextField Windows ActionEvent ActionEvent Itemevent ComponentEvent FocusEvent KeyEvent MouseEvent ActionEvent AdjustmentEvent ActionEvent ActionEvent WindowsEvent 每个事件类都提供下面常用的方法 : (1)public int getid(), 返回事件的类型 (2)public Object getsource(), 返回事件源的引用 当多个事件源触发的事件由一个共同的监听器处理时, 我们可以通过 getsource 方法判断当前的事件源是哪一个组件 3. 注册和注销监听器的方法 (1) 注册监听器 :public void addxxxlistener (XXXListener e) (2) 注销监听器 :public void removexxxlistener (XXXListener e) 处理 ActionEvent 事件当用户单击按钮 (Button) 选择列表框(List) 选项 选择菜单项 (MenuItem), 或者是在文本行 (TextField) 输入文字并按 Entet 键, 便触发动作事件 (ActionEvent), 触发事件的组件 ActionEvent 类的对象传递给事件监听器, 事件监听器负责执行 actionperformed() 方法进行

207 项目 7 图形用户界面设计 207 相应的事件处理 ActionEvent 类继承了 EventObject 类的一个常用方法 getsource(), 其功能是返回事件源 ( 对 象 ) 名 ActionEvent 类本身还定义了一些成员, 如 getactioncommand(), 其功能是返回事件 源的字符串信息 例 7.11 处理 ActionEvent 事件 import java.awt.*; import java.awt.event.*; public class actionevent extends Frame implements ActionListener// 用容器类实现监听器接口 static actionevent myframe=new actionevent();// 创建窗体 static Button mybutton1=new Button("yellow");// 创建按钮 static Button mybutton2=new Button("blue");// 创建按钮 static Button mybutton3=new Button("red");// 创建按钮 static Button mybutton4=new Button("exit");// 创建按钮 public static void main(string args[]) mybutton1.addactionlistener(myframe);// 注册事件监听器 mybutton2.addactionlistener(myframe);// 注册事件监听器 mybutton3.addactionlistener(myframe);// 注册事件监听器 mybutton4.addactionlistener(myframe);// 注册事件监听器 myframe.settitle("color change example"); myframe.setsize(200,100); myframe.setlayout(new FlowLayout(FlowLayout.CENTER)); myframe.add(mybutton1); myframe.add(mybutton2); myframe.add(mybutton3); myframe.add(mybutton4); myframe.setvisible(true); public void actionperformed(actionevent e) Button mybutton5=(button)e.getsource();// 得到事件源名称 if(mybutton5==mybutton1) myframe.setbackground(color.yellow);// 选择按钮 1, 设置背景为黄色 else if(mybutton5==mybutton2) myframe.setbackground(color.blue);// 选择按钮 2, 设置背景为蓝色 else if(mybutton5==mybutton3) myframe.setbackground(color.red);// 选择按钮 3, 设置背景为红色 else System.exit(0);// 选择按钮 4, 关闭窗口 程序运行结果如图 7-14 所示 图 7-14 处理 ActionEvent 事件

208 208 Java 程序设计 处理 KeyEvent 事件当按下键盘中的任意键时, 将触发键盘事件,Java 用 KeyEvent 类处理该事件 KeyEvent 类中常用的成员方法如表 7-22 所示 表 7-22 KeyEvent 类成员方法 成员方法 功能 char getchar() char getcharcode() public boolean isactionkey() 返回按下的字符 返回按下字符的代码 判断按下的是否是 Action Key Action Key 包括方向键 PgUp PgDn F1~ F12 键 KeyEvent 类事件的监听器必须实现 KeyListener 接口中声明的三个方法, 如表 7-23 所示 表 7-23 KeyListener 接口的成员方法 成员方法 void keypressed() void keyrelease() void keytyped() 功能 对应键被按下事件, 键被按下时调用该方法 对应键被释放事件, 键被释放时调用该方法 对应输入字符事件, 输入字符时调用该方法 按下并释放一个字符键时调用该方法, 但输入 Action Key 时, 不调用该方法 例 7.12 处理 KeyEvent 事件 import java.awt.*; import java.awt.event.*; public class keyevent extends Frame implements KeyListener// 用容器类实现监听接口 static keyevent myframe=new keyevent();// 创建窗体 static TextField mytextfield=new TextField(20);// 创建文本框 static TextArea mytextarea=new TextArea("",5,50);// 创建文本区 static TextArea mytextarea1=new TextArea("",5,16);// 创建文本区 public static void main(string args[]) myframe.settitle("key event class"); myframe.setsize(400,300); myframe.setbackground(color.red); myframe.setlayout(new FlowLayout(FlowLayout.CENTER)); mytextfield.addkeylistener(myframe);// 向事件源注册监听器 mytextarea.seteditable(false); myframe.add(mytextfield); myframe.add(mytextarea); myframe.add(mytextarea1); myframe.setvisible(true); myframe.addwindowlistener(new WindowAdapter()// 窗口关闭按钮事件 ( 用内部类实现监听接口 ) ); public void windowclosing(windowevent e) System.exit(0);

209 项目 7 图形用户界面设计 209 public void keypressed(keyevent e)// 键盘中键按下事件方法实现 mytextarea.settext(""); mytextarea.append("keypressed()called\n"); public void keyreleased(keyevent e)// 键盘中键被释放事件方法实现 mytextarea.append("keyreleased()called\n"); mytextarea1.settext(mytextfield.gettext());// 将文本框的内容写入文本区中 public void keytyped(keyevent e)// 字符键被按下并释放事件方法实现 mytextarea.append("keytyped()called\n"); 程序运行结果见图 7-15(a) 所示 在文本框输入按下 a 键, 显示结果见图 7-15(b) 所示 (a) 初始界面 (b) 键入字符 a 图 7-15 处理 KeyEvent 事件初始及键入字符 a 界面 处理 MouseEvent 事件当按下鼠标键 鼠标指针进入或离开某一区域, 或者移动 拖动鼠标时, 触发鼠标事件 Java 用鼠标 MouseEvent 类处理该事件 MouseEvent 类中的常用方法如表 7-24 所示 表 7-24 MouseEvent 类成员方法 成员方法 功能 int getx() int gety() point getpoint() point getclickcount() 返回鼠标事件发生点的 X 坐标返回鼠标事件发生点的 Y 坐标返回鼠标事件发生点的坐标返回鼠标的点击数 Java 提供了 MouseListener 和 MouseMotionListener 接口, 用来处理 MouseEvent 事件 MouseListener 接口和 MouseMotionListener 接口的成员方法如表 7-25 和表 7-26 所示

210 210 Java 程序设计 表 7-25 MouseListener 接口的成员方法 成员方法 功能 void mousepressed(mouseevent e) void mousereleased(mouseevent e) void mouseentered(mouseevent e) void mouseexited(mouseevent e) void mouseclicked(mouseevent e) 对应按下鼠标键事件对应释放鼠标键事件对应鼠标进入事件对应鼠标离开事件对应鼠标单击事件 表 7-26 MouseMotionListener 接口的成员方法 成员方法 功能 movedragged(mouseevent e) movemoved(mouseevent e) 对应鼠标拖动事件 对应鼠标移动事件 例 7.13 利用 MouseListener 接口处理 MouseEvent 事件 import java.awt.*; import java.awt.event.*; public class mouseevent extends Frame implements MouseListener// 用容器类实现监听接口 static mouseevent myframe=new mouseevent(); static Button mybutton=new Button("click me");// 创建按钮 static TextArea mytextarea=new TextArea("",2,5);// 创建文本区 public static void main(string args[]) myframe.settitle("mouse event class"); BorderLayout myb=new BorderLayout(2,5);// 创建边界布局对象 myframe.setlayout(myb);// 将窗体布局管理设置为边界布局 myframe.setsize(250,200); myframe.setbackground(color.green); mybutton.addmouselistener(myframe);// 向事件源 ( 按钮 ) 注册鼠标监听器 myframe.add(mybutton,myb.west);// 按钮添加到窗体左边 myframe.add(mytextarea,myb.center);// 文本区添加到窗体中央 myframe.setvisible(true); myframe.addwindowlistener(new WindowAdapter()// 窗体关闭按钮事件 ( 采用内部类实现监听接口 ) public void windowclosing(windowevent e) System.exit(0); ); public void mouseentered(mouseevent e)// 鼠标进入事件 mytextarea.append("mouse entered\n"); public void mouseclicked(mouseevent e)// 鼠标点击事件 mytextarea.append("mouse click at["+e.getx()+","+e.gety()+"]\n"); public void mouseexited(mouseevent e)// 鼠标离开事件

211 项目 7 图形用户界面设计 211 mytextarea.append("mouse exited\n"); public void mousereleased(mouseevent e)// 鼠标释放事件 mytextarea.append("mouse released at["+e.getx()+","+e.gety()+"]\n"); public void mousepressed(mouseevent e)// 鼠标按下事件 mytextarea.append("mouse pressed at["+e.getx()+","+e.gety()+"]\n"); 程序运行后点击 Click me 按钮, 显示结果如图 7-16 所示 图 7-16 利用 MouseListener 处理 MouseEvent 事件 例 7.14 利用 MouseMotionListener 接口处理 MouseEvent 事件 import java.awt.*; import java.awt.event.*; public class motionevent extends Frame implements MouseMotionListener// 用容器类实现监听接口 static motionevent myframe=new motionevent(); static TextArea mytextarea=new TextArea(5,30);// 创建文本区 static TextField myfield1=new TextField(30);// 创建文本框 static TextField myfield2=new TextField(30);// 创建文本框 public static void main(string args[]) myframe.settitle("mousemotion Event"); myframe.setsize(240,200); myframe.setlayout(new FlowLayout());// 将窗体布局管理设置为流式布局 mytextarea.seteditable(false); mytextarea.addmousemotionlistener(myframe);// 向事件源 ( 文本区 ) 注册监听器 myframe.add(mytextarea);// 文本区添加到窗体 myframe.add(myfield1);// 文本框添加到窗体 myframe.add(myfield2);// 文本框添加到窗体 myframe.setvisible(true); myframe.addwindowlistener(new WindowAdapter()// 窗体关闭按钮事件 ( 用内部类实现监听器 ) public void windowclosing(windowevent e) System.exit(0); ); public void mousemoved(mouseevent e)// 鼠标移动事件方法实现 myfield1.settext("mouse is moved int TextArea");

212 212 Java 程序设计 public void mousedragged(mouseevent e)// 鼠标按下拖动事件方法实现 myfield2.settext("mouse is dragged int TextArea"); 程序运行后将鼠标移动到文本区, 然后按下鼠标左键进行拖动, 其结果显示如图 7-17 所示 图 7-17 利用 MouseMotionListener 处理 MouseEvent 事件 处理 WindowEvent 事件 WindowEvent 事件指示窗口状态改变的低级别事件 当打开 关闭 激活 停用 图标化或取消图标化 Window 对象时, 或者焦点转移到 Window 内或移出 Window 时, 由 Window 对象生成此低级别事件 对于该事件 Java 提供了 WindowListener 接口来监听该类事件 WindowListener 接口中声明的七个抽象方法, 如表 7-27 所示 表 7-27 WindowListener 接口的成员方法 成员方法 windowactivated(windowevent e) windowclosed(windowevent e) windowclosing(windowevent e) windowdeactivated(windowevent e) windowiconified(windowevent e) windowdeiconified(windowevent e) windowopened(windowevent e) 功能对应窗口由 非活动 状态转变为 活动 状态事件对应窗口已关闭事件对应窗口关闭 ( 按下窗口关闭按钮 ) 事件对应窗口由 活动 状态转变为 非活动 状态事件对应窗口由一般状态转变为最小化状态事件对应窗口由最小化状态转变为一般状态事件对应窗口打开事件 例 7.15 处理 WindowEvent 事件 import java.awt.*; import java.awt.event.*; public class winevent extends Frame implements WindowListener// 用容器类实现监听接口 static winevent myframe=new winevent();// 创建窗体 static TextArea mytextarea=new TextArea();// 创建文本区 public static void main(string args[]) myframe.settitle("window Event"); myframe.setsize(200,150); myframe.setbackground(color.lightgray); myframe.add("center",mytextarea);// 文本区添加到窗体, 并置中

213 项目 7 图形用户界面设计 213 myframe.addwindowlistener(myframe);// 向事件源 ( 窗口 ) 注册监听器 myframe.setvisible(true); public void windowclosing(windowevent e)// 窗口关闭按钮事件方法实现 System.exit(0); public void windowactivated(windowevent e)// 当前窗口为活动窗口事件方法实现 mytextarea.append("\n 当前窗口是活动窗口!"); public void windowdeactivated(windowevent e)// 当前窗口为非活动窗口事件方法实现 mytextarea.append("\n 当前窗口是非活动窗口!"); public void windowiconified(windowevent e)// 当前窗口最小化事件方法实现 mytextarea.append("\n 当前窗口被最小化!"); public void windowdeiconified(windowevent e)// 当前窗口恢复正常事件方法实现 mytextarea.append("\n 当前窗口恢复正常!"); public void windowclosed(windowevent e)// 当前窗口关闭事件方法实现 public void windowopened(windowevent e)// 当前窗口打开事件方法实现 程序运行后, 先点击最小化按钮, 然后点击该窗口图标恢复正常, 最后显示的结果如图 7-18 所示 图 7-18 WindowEvent 事件演示结果图 事件适配器从例 7.15 中可以看出, 不少事件的监听器接口中定义了多个方法, 而程序员往往只关心其中的一两个方法, 为了符合接口的实现要求, 却必须将其他方法写出来并为其提供空的方法体 为此,Java 中为那些具有多个方法的监听器接口提供了事件适配器类, 这个类通常命名为 XXXAdapter, 在该类中以空方法体实现了相应接口的所有方法, 程序员可通过继承适配器类来编写监听器类, 在类中只需改写关心的方法, 从而减轻工作量 监听器接口与适配器类的对应关系如表 7-28 所示 表 7-28 监听器接口与对应的适配器类 监听器接口 适配器类 ComponentListener ContainerListener ComponentAdapter ContainerAdapter

214 214 Java 程序设计 续表 监听器接口 适配器类 FocusListener KeyListener MouseListener MouseMotionListener WindowListener FocusAdapter KeyAdapter MouseAdapter MouseMotionAdapter WindowAdapter 由于 WindowListener 接口中有七个方法, 为了方便用户编程,Java 也提供了适配器类 WindowAdapter, 用空方法体实现 WindowListener 接口中的七个方法 处理窗口事件时, 其监 听器也可以是继承 WindowAdapter 的类对象, 在其中只需覆盖需要处理的事件所对应的方法 在前面很多例子中就使用了监听器继承适配器类 WindowAdapter 来完成窗口关闭, 其代码如下 : myframe.addwindowlistener(new WindowAdapter() public void windowclosing(windowevent e) System.exit(0); ); 用内部类实现监听接口 内部类 (inner class) 是被定义于另一个类中的类, 使用内部类的主要原因是由于 : (1) 一个内部类的对象可访问外部类的成员方法和变量, 包括私有的成员 (2) 实现事件监听器时, 采用内部类编程非常容易实现其功能 (3) 编写事件驱动程序, 内部类很方便 因此内部类所能够应用的地方往往是在 Java 事件处理机制中 例 7.16 内部类实现监听器接口的应用 import java.awt.*; import java.awt.event.*; public class innerevent extends Frame static int count=1; public innerevent(string title)super(title); public static void main(string args[]) innerevent f=new innerevent("hello");// 创建窗体 f.setlayout(new FlowLayout());// 窗体设置为流式布局 final Button b=new Button("1");// 创建按钮 // 向事件源 ( 按钮 ) 注册监听器, 同时用内部类实现监听接口 b.addactionlistener(new ActionListener() public void actionperformed(actionevent evt) b.setlabel(new Integer(++count).toString()); ); f.add(b);// 按钮加到窗体 f.setsize(200,100); f.setbackground(color.blue);

215 项目 7 图形用户界面设计 215 f.setvisible(true); // 向事件源 ( 窗体 ) 注册窗口事件监听器, 同时用内部类采用适配器实现监听接口 f.addwindowlistener(new WindowAdapter() public void windowclosing(windowevent e) System.exit(0); ); 程序运行后, 每点击一次按钮, 其标签显示的值加 1 运行结果如图 7-19 所示 图 7-19 例 7.16 运行后结果图 习题 1. 选择题 (1) 当单击鼠标或拖动鼠标时, 触发的事件是 A.KeyEvent B.ActionEvent C.ItemEvent D.MouseEvent (2) 在类中若要处理 ActionEvent 事件, 则该类需要实现的接口是 A.Runnable B.ActionListener C.Serializable D.Event (3) 在 Java 编程中,Swing 包中的组件处理事件时, 下面 是正确的 A.Swing 包中的组件也是采用事件的委托处理模型来处理事件的 B.Swing 包中的组件产生的事件类型, 也都带有一个 J 字母, 如 JMouseEvent C.Swing 包中的组件也可以采用事件的传递处理机制 D.Swing 包中的组件所对应的事件适配器也是带有 J 字母的, 如 JMouseAdapter (4) 下列说法中错误的是 A. 同一个对象可以监听一个事件源上多个不同的事件 B. 一个类可以实现多个监听器接口 C. 一个类中可以同时出现事件源和事件处理者 D. 一个类只能实现一个监听器接口 (5) 在类中若要处理 ActionEvent 事件, 则该类需要实现的接口是 A.Runnable B.ActionListener C.Serializable D.Event (6) 下列说法中错误的一项是 A.MouseAdapter 是鼠标运动适配器 B.WindowAdapter 是窗口适配器 C.ContainerAdapter 是容器适配器

216 216 Java 程序设计 D.KeyAdapter 是键盘适配器 2. 填空题 (1)Java 事件处理包括建立 和将事件源注册到监听器 (2) 事件类主要有两个 : 类以及 类 (3) 根据监听器和注册监听器所在的类之间的关系, 我们可以把事件处理分为以下几种情况 : 利用 对象 本类 对象和 对象处理事件 (4) 按钮可以引发 事件,TextField 可产生 和 事件, 下拉列表可产生 项目事件 当用户单击复选框使其选中状态发生变化时就会引发 类代表的选择事件 滚动条可以引发 类代表的调整事件 (5)ActionEvent 事件类包含 事件, 该事件通过 接口进行监听, 通过调用 方法将事件源注册到监听器, 通过调用 方法实现监听后的动作, 通过调用 方法可以获得发生事件的事件源对象, 调用 方法可以获取引发事件动作的事件源 (6) 列表的双击事件 ( 能 / 不能 ) 覆盖单击事件 当用户双击一个列表选项时, 首先产生一个 事件, 然后再产生一个 事件 (7) 调用 MouseEvent 对象的 方法就可以知道用户引发的是哪个具体的鼠标事件 3. 试述 AWT 的事件处理机制 任务 5 Swing 组件 Swing 概述前面已经描述,Swing 为所有 AWT 提供了对应实现 ( 除了 Canvas 组件之外, 因为在 Swing 中无需继承 Canvas 组件 ), 通常在 AWT 组件的组件名前增加 J 就变成对应的 Swing 组件 大部分 Swing 组件都是 JComponent 抽象类的直接或间接子类 ( 并不是全部 Swing 组件都是 ), JComponent 类定义了所有子类组件的通用方法, JComponent 类是 AWT 里 java.awt.container 类的子类, 这也是 AWT 和 Swing 的联系之一 绝大部分 Swing 组件类继承了 Container 类, 所以 Swing 组件都可作为容器使用 (JFrame 继承了 Frame 类 ) 图 7-20 显示了 Swing 组件继承层次图 Swing 组件从功能上可以分成如下几类 : (1) 顶层容器 :JFrame JApplet JDialog 和 JWindow (2) 中间容器 :JPanel JScrollPane JSplitPane JToolBar 等 (3) 特殊容器 : 在用户界面上具有特殊作用的中间容器, 如 JInternalFrame JRootPane JLayeredPane 和 JDestopPane 等 (4) 基本控件 : 可以实现人机交互的组件, 如 JButton JToggleButton JRadioButton JCheckBox JComboBox JList JSlider JScrollBar 等, 通常是界面上的主要角色 (5) 不可编辑信息显示 : 向用户显示不可编辑信息的组件, 如 JLabel JProgressBar JToolTip 等, 大多是起一个信息提示的作用 (6) 可编辑的格式化信息的显示 : 向用户显示能被编辑的格式化信息的组件, 如 JTable

217 项目 7 图形用户界面设计 217 JTree JColorChooser 和 JFileChooser, 用来实现用户与机器之间的信息交互工作 图 7-20 Swing 组件继承层次图 (7) 特殊对话框组件 : 可以直接产生特殊对话框的组件, 如 JColorChoosor 和 JFileChooser 等 容器组件 1.JFrame Java 应用程序要创建一个用户界面, 最常用的 Swing 容器是 JFrame 类 JFrame 类是从 Frame 类派生的,JFrame 类提供了一个包含标题 边框等的顶层窗口, 其构造方法及成员方法如表 7-29 和表 7-30 所示 表 7-29 JFrame 的构造方法 构造方法 主要功能 JFrame() JFrame(String title) 创建没有标题的窗口 创建以 title 为标题的窗口

218 218 Java 程序设计 表 7-30 JFrame 的成员方法 成员方法 int getdefaultcloseoperation() int setdefaultcloseoperation(int operation) void update(graphics g) void remove(componet componet) JmenuBar getmenubar() void setlayout(layoutmanager manager) 主要功能返回关闭窗口的处理方法设置用户关闭窗口时默认处理方法调用 paint() 方法重绘窗口将窗口中的 component 组件删除返回窗口中的菜单栏组件设置窗口布局 注 : 其中 operation 的有效值如下 :JFrame.DO_NOTHING_CLOSE( 什么也不做 ) JFrame.HIDE_ON_CLOSE ( 隐藏当前框架 ) JFrame.DISPOSE_ON_CLOSE( 隐藏当前框架, 并释放框架所占的其他资源 ) JFrame.EXIT_ ON_CLOSE( 结束框架所在的应用程序 ) 2.JPanel JPanel 是用来创建面板的容器组件, 面板可以帮助开发人员对界面进行规划, 方便界面元 素的管理和布局 设计界面时, 通常先创建一个面板, 再向这个面板添加组件, 然后把这个面 板添加到顶层容器或其他中间容器中 JPanel 的构造方法和常用成员方法见表 7-31 所示 表 7-31 JPanel 构造方法及常用成员方法 方法 主要功能 JPanel() JPanel(LayoutManager layout) void add(component comp) void setbackground(color c) void setlayout(layoutmanager manager) 创建一个 JPanel 中间容器创建一个 JPanel 中间容器, 具有指定的布局管理将组件添加到 JPanel 面板上设置 JPanel 的背景颜色设置 JPanel 的布局管理 例 7.17 JFrame JPanel 类的应用 import javax.swing.*; import java.awt.*;// 引入 AWT 包, 因为要使用到颜色类 class PanelDemo public static void main(string[] args)throws Exception JFrame f=new JFrame(" 第一个 Java 窗口 "); f.setsize(300,200);// 设置窗体大小 f.setdefaultcloseoperation(jframe.exit_on_close);// 点击窗口关闭按钮后结束所有应用程序 f.setvisible(true);// 设置窗体可见 f.setresizable(false);// 生成的窗体大小是由程序员决定的, 用户不可以改变大小 f.setlocationrelativeto(null);// 设置窗体位于屏幕的中央 f.setlayout(null);// 设置窗体布局为空布局 JPanel p=new JPanel();// 实例化一个面板 // 设置面板背景色为蓝色, 如果不引入 AWT 包, 程序将出错 p.setbackground(color.blue); p.setsize(100,100);// 设置面板对象大小

219 项目 7 图形用户界面设计 219 f.add(p);// 将面板添加到窗体中 // 如果使用下面添加面板的方法, 面板将布满整个窗口 //f.setcontentpane(p); 程序运行结果如图 7-21 所示 图 7-21 JFrame JPanel 类应用说明 : (1) 在 JDK1.5 之前,Swing 组件不能直接添加到顶层容器中, 它必须添加到一个与 Swing 顶层容器相关联的内容面板 (content pane) 上 先调用 JFrame 的 getcontentpane() 方法获得 JFrame 的内容面板, 然后将所有组件添加到该内容面板 从 JDK1.5 以后,Java 改写了 JFrame 的 add 和 setlayout 等方法, 当程序调用 JFrame 的 add 和 setlayout 等方法, 实际上对 JFrame 的内容面板进行操作, 而不是操作 JFrame (2) 用户希望点击窗口的关闭按钮来结束程序, 也无需使用事件机制, 只要调用 setdefaultcloseoperation(jframe.exit_on_close) 方法即可 (3)Swing 仍然使用 AWT 包中的 Font Color Graphics 等类设置字体, 颜色和绘图 标签和按钮 Swing 中的标签组件 JLable 与 AWT 中的标签组件 Lable 相似, 可以显示文本 但 JLable 组件还可以显示图标, 当鼠标的指针移动到标签上时, 还会显示一段信息 JLable 类的构造方法和成员方法如表 7-32 和表 7-33 所示 表 7-32 JLabel 的构造方法 构造方法 主要功能 JLabel() JLable(Icon icon) JLable(Icon icon,int align) JLabel(String str) JLabel(String str,int align) JLabel(String str,icon icon,int align) 创建一个没有标题的标签创建一个图标为 icon 的标签创建一个图标为 icon 的标签并指定它的水平对齐方式为 align 创建一个以 str 为标题的标签创建一个以 str 为标题的标签并指定它的水平对齐方式为 align 创建一个图标为 icon 标题为 str, 并指定它的水平对齐方式

220 220 Java 程序设计 表 7-33 JLabel 的成员方法 成员方法 主要功能 Icon geticon() void seticon(icon icon) String gettext() void settext(string str) void sethorizontalalignment(int align) void setvertiaclalignment(int align) void setverticaltextposition(int pos) 返回标签的图标设置标签的图标为 icon 返回标签的标题设置标签标题为 str 设置标签的水平对齐方式为 align 设置标签的垂直对齐方式为 align 设置标签标题的垂直位置为 pos 在 Swing 中, 所有按钮都是由 AbstractButton 类派生的 Swing 中按钮的功能较 AWT 中的按钮功能强大, 包括给按钮添加图像 使用快捷键以及设置按钮的对齐方式, 还可以将多个图像分配给一个按钮以处理鼠标在按钮上的停留等 JButton 类的构造方法如表 7-34 所示 表 7-34 JButton 的构造方法 构造方法 主要功能 JButton() JButton(Icon icon) JButton(String str) JButton(String str,icon icon) 创建一个没有标题和图标的按钮创建一个图标为 icon 的按钮创建一个标题为 str 的按钮创建一个图标为 icon 标题为 str 的按钮 文本编辑组件 Swing 文本编辑组件有文本框 密码行和文本区 1. 文本框文本框 (JTextField) 只能显示和编辑一行文本, 其基本操作与 TextField 类似 JTextField 类的构造方法和成员方法见表 7-35 和表 7-36 所示 表 7-35 JTextField 的构造方法 构造方法 主要功能 JTextField( ) JTextField(int columns) JTextField(String text) JTextField(String text, int columns) 创建单行文本框对象创建指定列数的单行文本框对象创建以字符串 text 为指定内容的单行文本框对象创建具有指定列数的 以字符串 text 为指定内容的单行文本框对象 表 7-36 JTextField 的成员方法 成员方法 主要功能 int getcolumns() void setcolumns(int col) 获取此对象的列数 设置此对象的列数

221 项目 7 图形用户界面设计 221 续表 成员方法 主要功能 void setfont(font font) void sethorizontalalignment(int align) String gettext() void seteditable(boolean b) void settext(string s) 设置当前字体设置文本的水平对齐方式 (LEFT CENTER RIGHT) 获取文本行的文本信息指定文本行是否可以编辑, 默认是可编辑设置文本行的内容为 s, 原来的内容被清除 2. 密码行密码行 JPasswordField 是 JTextField 的子类, 用于编辑作为密码的一行文本 在其中输入字符时, 不显示字符, 而是显示 * JPasswordField 类的构造方法和其他成员方法分别如表 7-37 和表 7-38 所示 表 7-37 JPasswordField 的构造方法 构造方法 主要功能 JPasswordField( ) JPasswordField(int col) JPasswordField(String text) JPasswordField(String text, int col) 创建单行密码行对象创建指定列数的单行密码行对象创建以字符串 text 为指定内容的单行密码行对象创建具有指定列数的 以字符串 text 为指定内容的单行密码行对象 表 7-38 JPasswordField 的成员方法 成员方法 主要功能 getpassword() getechochar() setechochar(char c) 获取密码行的文本内容 获取密码行的回显字符 设置密码行的回显字符 3. 文本区文本区 JTextArea 是一个多行文本编辑区, 其基本操作与 TextArea 类似, 但增加了滚动条功能 JTextArea 类的构造方法和其他成员方法分别如表 7-39 和表 7-40 所示 表 7-39 JTextArea 的构造方法 构造方法 主要功能 JTextArea( ) JTextArea(int col) JTextArea(String text) JTextArea(String text, int col) 创建一个空的文本区创建指定列数的空的文本区创建以字符串 text 为指定内容的文本区创建具有指定列数的 以字符串 text 为指定内容的文本区

222 222 Java 程序设计 表 7-40 JTextArea 的成员方法 成员方法 void append(string str) void insert(string str, int pos) void replacerange(string str, int start, int end) void setfont(font font) int getrows() void setrows(int rows) int getcolumns() void setcolumns(int col) setlinewrap(boolean b) setwrapstyleword(boolean b) getcaretposition() setcaretpoisiton(int position) 主要功能 将所给 str 内容添加到显示的文本内容之后 将所给 str 内容插入到显示的文本的指定位置 将显示文本中从 start 开始至 end 结束的部分替换为指定内容 str 设置文本区中文本字体为 font 返回文本区中文本的行数 设置文本区文本的行数为 rows 返回文本区文本的列数 设置文本区文本的列数为 col 设置文本在文本区域的右边界是否可以换行 设置以单词为界或以字符为界自动换行 获取文本区域中输入光标的位置 设置文本区域中输入光标的位置 界面 例 7.18 使用 Swing 组件构建一个包含用户名, 密码和 登录 及 取消 按钮的登录 import javax.swing.*; import java.awt.*; import java.awt.event.*; public class LogonWindow extends JFrame implements ActionListener JLabel lblusername,lblpassword; JTextField tfusername; JPasswordField tfpassword; JButton btnlogon,btncancel; public LogonWindow() this.setlayout(new GridLayout(0,2));// 设置网格布局 lblusername = new JLabel(" 用户名 :",JLabel.LEFT);// 创建标签 lblpassword = new JLabel(" 密码 :",JLabel.LEFT);// 创建标签 tfusername = new JTextField(10);// 创建文本框, 长度为 10 tfpassword = new JPasswordField(10);// 创建密码行, 长度为 10 btnlogon = new JButton(" 登录 ");// 创建按钮, 标题为 登录 btnlogon.addactionlistener(this);// 向事件源 ( 按钮 ) 注册监听器 btncancel = new JButton(" 取消 ");// 创建按钮, 标题为 取消 btncancel.addactionlistener(this);// 向事件源 ( 按钮 ) 注册监听器 this.add(lblusername);// 标签添加到窗体 this.add(tfusername);// 标签添加到窗体 this.add(lblpassword);// 文本框添加到窗体 this.add(tfpassword);// 密码行加到窗体 this.add(btnlogon);// 按钮添加到窗体 this.add(btncancel);// 按钮添加到窗体

223 项目 7 图形用户界面设计 223 public static void main(string[] args) LogonWindow win = new LogonWindow();// 创建窗体 win.settitle("logon Window"); win.pack();// 依据放置的组件大小设定窗口的大小 win.setvisible(true); win.setdefaultcloseoperation(jframe.exit_on_close);// 点击窗口关闭按钮后结束所有应用程序 public void actionperformed(actionevent e)// 事件处理 if(e.getsource()==btnlogon)// 点击的是 登录 按钮, 显示输入的用户名和密码 String lsusername=this.tfusername.gettext();// 读取用户名 char[] apassword=this.tfpassword.getpassword();// 读取密码 String strpassword=""; for(int i=0; i<apassword.length; i++) strpassword+=apassword[i]; JOptionPane.showMessageDialog(this," 用户名 :"+lsusername+" "+" 密码 :"+strpassword); // 弹出登录成功的消息对话框 else if (e.getsource()==btncancel)// 点击的是 取消 按钮 tfusername.settext("");// 将文本框设置为空 tfpassword.settext("");// 将密码行设置为空 程序运行结果如图 7-22 和图 7-23 所示 图 7-22 登录界面 图 7-23 点击 登录 按钮出现的消息对话框 选择组件常用的选择组件有单选按钮组件 复选框组件 下拉列表框组件等 1. 单选按钮在 Swing 中, 单选按钮 JRadioButton 让用户从一组选项中选择唯一的选项 单选按钮是圆形的, 同一时刻只能有一个被选中 一旦选中一个单选按钮, 以前选中的按钮自动变为未选中状态 如果创建多个单选按钮, 应使用 ButtonGroup 先创建一个对象, 然后将这些单选按钮归于一组, 归到一组的单选按钮同一时刻只能有一个被选中 调用 ButtonGroup 类的 add() 方法可以将单选按钮添加到一个 ButtonGroup 对象中 单选按钮也产生 ItemEvent 事件 JRadioButton 类的构造方法如表 7-41 所示

224 224 Java 程序设计 表 7-41 JRadioButton 的构造方法 构造方法 主要功能 JRadioButton() JRadioButton(Icon icon) JRadioButton(Icon icon,boolean sele) JRadioButton(String str) JRadioButton(String str,boolean sele) JRadioButton(String str,icon icon) JRadioButton(String str,icon icon,boolean sele) 创建一个没有标题的单选按钮 创建一个图标为 icon 的单选按钮 创建一个图标为 icon 且初始状态为 sele 的单选按钮 创建一个标题为 str 的单选按钮 创建一个标题为 str 且初始状态为 sele 的单选按钮 创建一个标题为 str 且图标为 icon 的单选按钮 创建一个标题为 str 图标为 icon 且初始状态为 sele 的单选按钮 例 7.19 Swing 单选按钮的应用 import java.awt.* ; import javax.swing.*; import java.awt.event.*; public class JRadioDemo extends JFrame private JRadioButton red,blue,green; private JLabel label1; private ButtonGroup btngrp; public JRadioDemo() super("jradiobutton Demo");// 调用父类构造方法设置窗体标题 this.setlayout(new FlowLayout());// 设置窗体为流式布局 label1=new JLabel("Red is selected");// 创建标签, 并设置初始值 red = new JRadioButton("red",true);// 创建单选按钮 blue = new JRadioButton("blue",false);// 创建单选按钮 green = new JRadioButton("green",false);// 创建单选按钮 this.add(red);// 单选按钮加入窗体 this.add(blue);// 单选按钮加入窗体 this.add(green);// 单选按钮加入窗体 this.add(label1);// 标签加入窗体 red.additemlistener(new Handler1());// 向事件源注册监听器 blue.additemlistener(new Handler1());// 向事件源注册监听器 green.additemlistener(new Handler1());// 向事件源注册监听器 btngrp= new ButtonGroup();// 创建按钮组 btngrp.add(red);// 将按钮添加到按钮组 btngrp.add(blue);// 将按钮添加到按钮组 btngrp.add(green);// 将按钮添加到按钮组 setsize(200,100); setvisible(true); public static void main(string args[]) JRadioDemo frm=new JRadioDemo();// 创建窗体 frm.addwindowlistener(new Handler2());// 向事件源注册监听器

225 项目 7 图形用户界面设计 225 class Handler1 implements ItemListener// 用外部类实现 ItemEvent 事件监听器接口 public void itemstatechanged(itemevent e) if(e.getsource()==red)// 选择红色按钮时重新设置标签内容 label1.settext("red is selected"); if(e.getsource()==blue)// 选择蓝色按钮时重新设置标签内容 label1.settext("blue is selected"); if(e.getsource()==green)// 选择绿色按钮时重新设置标签内容 label1.settext("green is selected"); static class Handler2 extends WindowAdapter// 用外部类实现 WindowEvent 事件监听器接口 public void windowclosing(windowevent e) System.exit(0); 程序运行结果如图 7-24 所示, 选择 blue 按钮后的结果如图 7-25 所示 图 7-24 初始界面 图 7-25 选择 blue 按钮后界面 2. 复选框在 Swing 中, 复选框 JCheckBox 用来显示一组选项 在一组复选框中, 可以同时选中多个复选框, 也可以不选中任何复选框 JCheckBox 类的构造方法如表 7-42 所示 表 7-42 JCheckBox 的构造方法 构造方法 主要功能 JCheckBox() JCheckBox(Icon icon) JCheckBox(Icon icon,boolean sele) JCheckBox(String str) JCheckBox(String str,boolean sele) JCheckBox(String str,icon icon) JCheckBox(String str,icon icon,boolean sele) 创建一个没有标题的复选框创建一个图标为 icon 的复选框创建一个图标为 icons 且初始状态为 sele 的复选框创建一个标题为 str 的复选框创建一个标题为 str 且初始状态为 sele 的复选框创建一个标题为 str 且图标为 icon 的复选框创建一个标题为 str 图标为 icon 且初始状态为 sele 的复选框 例 7.20 Swing 复选框应用实例 import java.awt.*; import java.awt.event.*; import javax.swing.*; public class JCheckBoxDemo extends JPanel JCheckBox right1;

226 226 Java 程序设计 JCheckBox right2; JCheckBox right4; JCheckBox right8; StringBuffer choices; JLabel displaylabel; public JCheckBoxDemo() right1 = new JCheckBox("1");// 创建复选框, 设置标题为 1 right1.setmnemonic(keyevent.vk_4);// 设置复选框可以使用 Alt+4 来选中或取消 right1.setselected(true);// 该复选框被选中 right2 = new JCheckBox("2");// 创建复选框, 设置标题为 2 right2.setmnemonic(keyevent.vk_3);// 设置复选框可以使用 Alt+3 来选中或取消 right2.setselected(true);// 该复选框被选中 right4 = new JCheckBox("4");// 创建复选框, 设置标题为 4 right4.setmnemonic(keyevent.vk_2);// 设置复选框可以使用 Alt+2 来选中或取消 right4.setselected(true);// 该复选框被选中 right8 = new JCheckBox("8");// 创建复选框, 设置标题为 8 right8.setmnemonic(keyevent.vk_1);// 设置复选框可以使用 Alt+1 来选中或取消 right8.setselected(true);// 该复选框被选中 // 创建事件服务类 CheckBoxListener 的对象 mylistener CheckBoxListener mylistener = new CheckBoxListener( ); // 为 right1 right2 right4 right8 添加事件监听器 right1.additemlistener(mylistener);// 向事件源注册监听器 right2.additemlistener(mylistener);// 向事件源注册监听器 right4.additemlistener(mylistener);// 向事件源注册监听器 right8.additemlistener(mylistener);// 向事件源注册监听器 choices = new StringBuffer("8421");// 初始化 choices displaylabel = new JLabel(choices.toString( ));// 创建标签对象 JPanel checkpanel = new JPanel( );// 创建面板对象 checkpanel // 将复选框按钮添加到 checkpanel 的同一列 checkpanel.setlayout(new GridLayout(0, 1)); checkpanel.add(right1); checkpanel.add(right2); checkpanel.add(right4); checkpanel.add(right8); setlayout(new BorderLayout( ));// 窗体设置成边界布局 add(checkpanel, BorderLayout.WEST);// 复选框放在左边 add(displaylabel, BorderLayout.CENTER);// 标签放在中心 setborder(borderfactory.createemptyborder(10,60,10,60));// 设置边距 class CheckBoxListener implements ItemListener // 复选框的事件服务类 public void itemstatechanged(itemevent e) int index = 0; char c = '-'; Object source = e.getitemselectable();// 获取事件源名称 if (source == right8) index = 0;

227 项目 7 图形用户界面设计 227 c = '8'; else if (source == right4) index = 1; c = '4'; else if (source == right2) index = 2; c = '2'; else if (source == right1) index = 3; c = '1'; if (e.getstatechange()==itemevent.deselected)// 选项被取消 c = '-'; choices.setcharat(index, c);// 设置字符串的值 displaylabel.settext(choices.tostring( ));// 修改标签的值 public static void main(string s[ ]) JFrame frame = new JFrame("CheckBoxDemo"); frame.addwindowlistener(new WindowAdapter()// 主窗口关闭事件处理 public void windowclosing(windowevent e) System.exit(0); ); frame.add(new JCheckBoxDemo()); frame.pack( ); frame.setvisible(true); 程序运行结果如图 7-26 所示, 点击 2 4 复选框后的结果如图 7-27 所示 图 7-26 初始界面 图 7-27 选择 blue 按钮后界面 3. 下拉列表框下拉列表框 JComboBox 由一个文本行和一个列表框组成 下拉列表框通常的显示形式是右边带有下拉箭头的文本行, 列表框是隐藏的, 单击右边的下拉箭头才可以显示列表框 既可以在下拉列表框的文本行中直接输入数据, 也可以从其列表框中选择数据项, 被选择的数据项显示在文本行中 JComboBox 类的构造方法及其他成员方法分别如表 7-43 和表 7-44 所示

228 228 Java 程序设计 表 7-43 JComboBox 的构造方法 构造方法 主要功能 JComboBox( ) JComboBox(Vector vect) JComboBox(Object[] listdata) 创建一个没有选项的 JComboBox 对象 创建一个指定向量内容的 JComboBox 对象 创建一个指定对象数组内容的 JComBoBox 对象 表 7-44 JComboBox 的成员方法 成员方法 void addactionlistener(actionlisteber e) void additemlistener(itemlistener e) void additem(object object) Object getitem(int index) int getitemat(int index) Object getselecteditem() int getselectedindex() 主要功能向 JComboBox 对象注册 ActionEvent 事件监听器向 JComboBox 对象注册 ItemEvent 事件监听器为 JComboBox 对象添加选项 object 返回 JComboBox 对象中下标为 index 的选项返回 JComboBox 对象中的选项数返回 JComboBox 对象中当前选中的选项返回 JComboBox 对象中当前选中选项的下标 例 7.21 Swing 下拉列表框应用实例 import java.awt.*; import java.awt.event.*; import javax.swing.*; public class JComboBoxDemo extends JFrame private JComboBox cmb; private JLabel lbl; private Object cities[]=" 上海市 "," 南京市 "," 合肥市 "," 武汉市 "," 长沙市 "," 南昌市 "," 杭州市 "; public JComboBoxDemo() super("jcombobox");// 调用父类的构造方法, 设置窗体标题 this.setlayout(new FlowLayout());// 设置窗体布局管理为流式布局 cmb=new JComboBox(cities);// 创建下拉列表框, 数据项成员来自数组 cities cmb.setmaximumrowcount(4);// 下拉列表框中仅显示四项 lbl=new JLabel(" 请从组合框中选择 ");// 创建标签, 设置标题 this.add(cmb);// 将复选框添加到窗体 this.add(lbl);// 将标签添加到窗体 cmb.additemlistener(new Handler());// 向事件源注册监听器 setsize(300,150); setvisible(true); public static void main(string args[]) JComboBoxDemo app=new JComboBoxDemo(); app.addwindowlistener(new WindowAdapter()// 主窗口关闭事件处理 ); public void windowclosing(windowevent e) System.exit(0);

229 项目 7 图形用户界面设计 229 class Handler implements ItemListener public void itemstatechanged(itemevent e) lbl.settext(" 您选中了 :"+cmb.getselecteditem());// 用选中的事件源名称更新标签标题 程序运行结果如图 7-28 所示, 从下拉列表框中选中 杭州 后的结果如图 7-29 所示 图 7-28 初始界面 图 7-29 选中 杭州 后的界面 菜单 Swing 菜单可提供简单明了的指示说明, 广泛应用于各种视窗应用的程序, 让用户顺利地 完成操作, 程序员可以将其当做布局管理器或容器看待 Java 提供了五个实现菜单的类 : JMenuBar JMenu JMenuItem JCheckBoxMenuItem 和 JRadioButtonMenuItem 菜单一般由菜单栏 (JMenuBar) 菜单 (JMenu) 和菜单项 (JMenuItem) 三类对象组成, JMenuBar 是顶层菜单栏, 可以添加一个 JMenu 对象到 JMenuBar 内, 构造一个菜单, 菜单由 用户可以选择的菜单项组成 1. 菜单栏 菜单栏 (JMenuBar) 是窗口中用于容纳菜单 (JMenu) 的容器 JMenuBar 提供的 add() 方法用来添加菜单, 一个菜单栏通常可以添加多个菜单 菜单栏不支持事件监听器, 在菜单栏 区域所产生事件都会被菜单栏自动处理 该 JMenuBar 可以放在窗体的任何地方, 通常我们将它放在窗体的顶部 可以使用如下方 法将 JMenuBar 加入窗体 : JFrame.setJMenuBar(menubar); JMenuBar 类的构造方法和常用成员方法如表 7-45 所示 表 7-45 JMenuBar 类的方法 方法 主要功能 JMenuBar( ) JMenu add(jmenu c) 创建菜单栏对象 将菜单 c 添加到菜单栏中 2. 菜单类菜单 (JMenu) 是一组菜单项 (JMenuItem) 的容器或另一个菜单的容器, 每个菜单有一个标题 JMenu 类提供的 add() 方法用来添加菜单项或另一个菜单 如果一个菜单中加入了另一个菜单, 则构成二级子菜单 JMenu 类的构造方法和常用成员方法分别如表 7-46 和表 7-47 所示

230 230 Java 程序设计 表 7-46 JMenu 的构造方法 构造方法 主要功能 JMenu() JMenu(String str) 创建没有标题的菜单 创建标题为 str 的菜单 表 7-47 JMenu 的成员方法 成员方法 JMenuItem add(jmenuitem menuitem) void addseparator() 主要功能 将菜单项 menuitem 添加到菜单中 在菜单中添加一条分割线 3. 菜单项菜单项 (JMenuItem) 是组成菜单的最小单位, 在菜单项上可以注册 ActionEvent 事件监听器 当单击菜单项时, 执行 actionperformed() 方法 JMenuItem 类的构造方法如表 7-48 所示 表 7-48 JMenuItem 类的构造方法 构造方法 主要功能 JMenuItem( ) JMenuItem(String str) JMenuItem(Icon icon) JMenuItem(String str,icon icon) 创建一个菜单项创建标题为 str 的菜单项创建图标为 icon 的菜单项创建标题为 str 图标为 icon 的菜单项 例 7.22 Swing 菜单应用实例 import java.awt.*; import java.awt.event.*; import javax.swing.*; class JMenuDemo extends JFrame implements ActionListener private JLabel lbl; private JMenuBar mb; private JMenu col,ext; private JMenuItem gre,yel,blu,clo; public JMenuDemo() settitle(" 普通菜单示例程序 "); mb=new JMenuBar();// 创建菜单栏 col=new JMenu("color");// 创建菜单 color ext=new JMenu("exit");// 创建菜单 exit gre=new JMenuItem("green");// 创建菜单项 green yel=new JMenuItem("yellow");// 创建菜单项 yellow blu=new JMenuItem("blue");// 创建菜单项 blue clo=new JMenuItem("close window");// 创建菜单项 close window gre.addactionlistener(this);// 向事件源 ( 菜单项 ) 注册监听器 yel.addactionlistener(this);// 向事件源 ( 菜单项 ) 注册监听器

231 项目 7 图形用户界面设计 231 blu.addactionlistener(this);// 向事件源 ( 菜单项 ) 注册监听器 clo.addactionlistener(this);// 向事件源 ( 菜单项 ) 注册监听器 mb.add(col);// 将 color 菜单加到菜单栏中 mb.add(ext);// 将 exit 菜单加到菜单栏中 col.add(gre);// 将菜单项 green 添加到菜单 color col.add(yel);// 将菜单项 yellow 添加到菜单 color col.add(blu);// 将菜单项 blue 添加到菜单 color ext.add(clo);// 将菜单项 close window 添加到菜单 exit setjmenubar(mb);// 将菜单栏加入到窗体 lbl=new JLabel("Menu Demo");// 创建标签, 设置初始值 add(lbl);// 标签加入到窗体 setsize(200, 150); setvisible(true); setdefaultcloseoperation(jframe.exit_on_close);// 点击窗口关闭按钮后结束所有应用程序 public static void main(string args[]) JMenuDemo app=new JMenuDemo(); public void actionperformed(actionevent evt)// 菜单项被选中触发 ActionEvent 事件处理 JMenuItem mi=(jmenuitem)evt.getsource(); if(mi==gre)lbl.setforeground(color.green);// 选中 green 时设置标签前景为绿色 if(mi==yel)lbl.setforeground(color.yellow);// 选中 yellow 时设置标签前景为黄色 if(mi==blu)lbl.setforeground(color.blue);// 选中 blue 时设置标签前景为蓝色 if(mi==clo)system.exit(0);// 关闭所有程序 程序运行结果如图 7-30 所示, 点击 color 菜单后结果如图 7-31 所示 图 7-30 初始界面 图 7-31 点击 color 菜单后的界面 习题 1. 选择题 (1)Swing 组件必须添加到 Swing 顶层容器相关的 A. 分隔板上 B. 内容面板上 C. 选项板上 D. 复选框内 (2) 关于使用 Swing 的基本规则, 下列说法正确的是 A.Swing 组件可直接添加到顶级容器中

232 232 Java 程序设计 B. 要尽量使用非 Swing 的重要级组件 C.Swing 的 JButton 不能直接放到 Frame 上 D. 以上说法都对 (3) 下列哪一项不属于 Swing 的顶层组件? A.JApplet B.JDialog C.JTree D.JFrame (4)JPanel 的默认布局管理器是 A.BorderLayout B.GridLayout C.FlowLayout D.CardLayout 2. 填空题 (1) 在 Swing 中, 可以根据不同用户的习惯, 设置不同的界面显示风格,Swing 提供了三种显示风格, 分别是 风格 风格和 风格 (2)Swing 的顶层容器有 和 (3) 在 Swing 中完全可以使用 包中的各种类进行事件处理, 同时它也可以使用 包中的类处理事件, 而 AWT 则只能使用 包中的各种类进行事件处理 (4) 可将 JOptionPane 类的对话框分为 4 种类型, 分别是只给出提示信息的 要求用户进行确认的 可输入数据的 和由用户自己定义类型的 3. 编程包含一个复选按钮和一个普通按钮, 复选按钮选中时, 普通按钮的背景色为青色, 未选中时为灰色 4. 编程包含一个下拉列表和一个按钮, 下拉列表中有 三个选项 选择 10 时, 按钮中文字的字号为 10, 选择 14 时, 按钮中文字的字号为 14, 选择 18 时, 按钮中文字的字号为 编程包含一个列表和两个标签, 在第一个标签中显示列表中被双击的选项的内容, 在第二个标签中显示列表中被选中的所有选项的内容 项目总结 1.Java 语言提供了两类组件用于构建图形用户界面 (GUI), 即 AWT 组件和 Swing 组件, AWT 称为重量级组件,Swing 称为轻量级组件,Swing 组件是在 AWT 组件基础之上发展而来的 2. 用 AWT 来生成图形化用户界面时, 组件和容器的概念非常重要 组件是各种各样的类, 封装了图形系统的许多最小单位, 例如按钮 窗口等等 ; 而容器也是组件, 它的最主要的作用是装载其他组件, 但是像 Panel 这样的容器也经常被当作组件添加到其他容器中, 以便完成复杂的界面设计 3. 布局管理器是 Java 语言与其他编程语言在图形系统方面较为显著的区别, 容器中各个组件的位置是由布局管理器来决定的, 共有 5 种布局管理器, 每种布局管理器都有自己的放置规律 4. 事件处理机制能够让图形界面响应用户的操作, 主要涉及到事件源 事件 事件处理者等三方, 事件源就是图形界面上的组件, 事件就是对用户操作的描述, 而事件处理者是处理事件的类 5.Swing 是 Java 2 新增特性, 它对图形化用户界面提供了庞大而复杂的类库支持 Swing 和 AWT 无论是布局管理器还是事件处理机制, 以及对一些重量容器的保留和使用, 都是一样的

233 项目 8 JDBC 数据库编程 软件的本质就是处理数据 数据往往需要专门的数据库来存放 所以任何应用程序的开 发几乎都离不开数据库 在 Java 项目中是如何对数据库进行操作的呢 这就需要用到 Java 数 据库应用程序 JDBC Java DataBase Connectivity 是为 Java 提供的一个平台无关的数据库标准 API 它 提供了一个通用的 SQL Structured Query Language 数据库存储机制 该机制为多数关系型 DBMS 提供统一接口 本章主要介绍 JDBC 的基础知识及如何使用 JDBC 提供的 API 进行 Java 数据库程序设计 理解 JDBC 概念 掌握 JDBC 主要类和接口 掌握 JDBC 连接数据库的方法 掌握 JDBC 访问数据库的方法 掌握 JDBC 事务处理方法 任务 1 JDBC 介绍 JDBC 是 Java 程序与数据库连接以及存取数据库的应用程序接口 API JDBC 由一组用 Java 语言编写的类与接口组成 通过调用这些类和接口提供的方法 用户能够以一致的方式 连接多种不同的数据库系统 如 Orcale Sybase Informix SQL Server DB2 MySQL 等 从而使用标准的结构化查询语言 SQL 来查询或更新数据库中的数据 而不必再为每一种数据 库系统编写不同的 Java 程序代码 JDBC 结构 JDBC 采用与 ODBC 相同的开发思路 但 ODBC 只对 Windows 平台 而且 ODBC 需要在 客户机上安装和注册 因而维护成本相对较大 JDBC 是由 Java 语言编写的 这使得 JDBC 代 码可以在所有 Java 平台上运行 使得程序的可移植性和安全性得到显著提高 图 8-1 为 JDBC 驱动示意图 从图中可以看出 与 ODBC 相类似 JDBC 接口 API 包含两层 1 JDBC API 抽象接口 负责与 JDBC 驱动程序管理器进行通信 供应用程序开发人

234 234 Java 程序设计 员使用 ( 连接数据库, 发送 SQL 语句, 处理结果 ) 图 8-1 JDBC 驱动示意图 (2)JDBC 驱动程序 API:JDBC 驱动程序管理器与实际连接到数据库的第三方驱动程序进行通信 ( 执行 SQL 语句, 返回查询信息 ) 供各开发商开发数据库驱动程序(JDBC Driver) 使用 JDBC Driver 是一个类的集合, 实现了 JDBC 所定义的类和接口, 提供了一个能实现 java.sql.driver 接口的类 根据所使用的数据库驱动程序类型, 在编写数据库程序时, 通常采用两种方式与数据库进行交互 一种是通过 JDBC-ODBC 桥, 它将 JDBC 中的方法映射到 ODBC 上, 从而通过 ODBC 对数据库进行访问 ; 另一种方法是通过数据库提供商或第三方公司开发的 JDBC 驱动程序对数据库进行访问, 这种方式加强了应用程序的可移植性和安全性 在个人开发与测试中, 可以使用 JDBC-ODBC 桥连方式, 而在生产型开发中, 推荐使用纯 Java 驱动方式, 并且 JDK 8 不再提供 JDBC-ODBC 桥的连接 JDBC API JDBC 为 Java 语言提供一个调用级的接口, 主要完成三个方面的功能 : 建立与数据库的连接 ; 向数据库发送 SQL 语句 ; 处理数据库返回结果 JDBC API 调用流程关系图如图 8-2 所示 这些功能由一系列 API 实现, 其中主要的接口有驱动程序管理器 (DriverManager) 驱动 (Driver) 连接(Connection) SQL 语句 (Statement) 和结果集 (ResultSet) 1.DriverManager DriverManager(java.sql.DriverManager) 类处理驱动程序的加载, 建立和管理应用程序与驱动程序之间的连接 它跟踪可用的驱动程序, 并在数据库与相应的驱动程序之间建立连接, 处理驱动程序的登录时间等相关事务 2.Driver 每个数据库驱动程序必须实现 Driver 接口 驱动程序由开发商提供, 将应用程序的 JDBC

235 项目 8 JDBC 数据库编程 235 API 请求转换为特定的数据库请求 DriverManager Driver Driver Connection Connection Connection Statement ResultSet Statement Statement Statement ResultSet ResultSet 图 8-2 JDBC API 调用流程关系图 3.Connection Connection(java.sql.Connection) 用来表示数据连接的对象, 对数据库的一切操作都是在这个连接的基础上进行的 它将应用程序连接到特定的数据库 4.Statement Statement(java.sql.Statement) 是在已经建立的连接上, 向数据库发送 SQL 语句的对象 5.ResultSet ResultSet(java.sql.ResultSet) 类用来暂时存放查询操作返回的数据结果集 ( 包括行 列 ) JDBC 编程步骤了解了 JDBC 的相关接口和类之后, 下面就可以进行 JDBC 编程了,JDBC 程序访问数据库的步骤如图 8-3 所示 主要步骤说明如下 : 图 8-3 JDBC 编程的步骤

236 236 Java 程序设计 (1) 加载数据库驱动程序 : 各个数据库都会提供 JDBC 驱动程序开发包, 直接把 JDBC 操作所需要的开发包 ( 一般为 *.jar 或 *.zip) 配置到 classpath 路径即可 (2) 连接数据库 : 根据各个数据库不同, 连接的地址也不同, 此连接地址由数据库厂商提供 一般在使用 JDBC 连接数据库时都要求用户输入数据库连接的用户名和密码 用户在取得数据库连接之后, 才能对数据库进行更新和查询操作 (3) 使用语句进行数据库操作 : 数据库操作分为查询和更新两种, 除了可以使用标准的 SQL 外, 对于各个数据库也可以使用其各自提供的命令 (4) 关闭数据库连接 : 数据库操作完毕之后需要关闭连接以释放资源 习题 1. 选择题关于 JDBC 操作数据库, 以下说法不正确的 A.JDBC 只能操作 MySQL 数据库 B.JDBC 中定义的 Connection Statement ResultSet 都是接口 C.JDBC 操作数据库必须要有相应的实现了 JDBC 接口的驱动 D.JDBC 可以通过将客户端的 SQL 传递给数据库服务器来实现数据库的操作 2. 填空题 (1)JDBC 的基本层次结构由 和数据库五部分组成 (2) 根据访问数据库的技术不同,JDBC 驱动程序相应地分为 和 四种类型 (3)JDBC API 所包含的接口和类非常多, 都定义在 包和 包中 3. 画图表示 JDBC 中的各种接口与类之间的关系 任务 2 JDBC 连接数据库 加载数据库驱动程序本教材中以 MySQL 数据库为例来讲解 JDBC 编程方法, 其他数据库 JDBC 编程可以参考 MySQL 编程方法 MySQL 是一个多用户 多线程的数据库, 由瑞典 MySQL AB 公司开发, 2010 年被甲骨文公司收购 目前 MySQL 被广泛地应用在 Internet 上的中小型网站中 其特点是体积小 速度快 总体拥有成本低, 为开源软件,MySQL 已经成为目前最受欢迎的中小型企业数据库之一 MySQL 数据库可直接在 下载, 下载后请参阅相关资料完成安装 加载数据库驱动程序是 JDBC 连接数据库的第一步, 在此之前必须下载所要使用数据库相对应的驱动程序 在安装完 MySQL 数据库后, 必须下载 MySQL 数据库驱动程序, 其网址为 : 参照 节中 JDK 环境配置, 将数据库驱动程序的路径配置到系统环境变量 CLASSPATH 中 现假定 MySQL 的数据库驱动程序保存在 C:\Program Files\Java\jdk1.7.0_75\lib 路径中, 文件名为 :mysql-

237 项目 8 JDBC 数据库编程 237 connector-java bin.jar CLASSPATH 环境变量新添加的值与前面的值以 ; 隔开, 新添 加值的内容为 %JAVA_HOME%\lib\mysql-connector-java bin.jar, 则驱动程序路径的配 置如图 8-4 所示 图 8-4 驱动路径配置图 Java 加载数据库驱动的方法是调用 Class 类的静态方法 forname() 写法如下 : Class.forName(String drivemanager); forname() 方法的参数用于指定要加载的数据库驱动, 需要传入数据库驱动包中类的完整 名称, 采用 包. 类 名称的方式 加载成功, 将会加载驱动类注册给 DriveManager, 失败的 话抛出 ClassNotFoundExecption 异常 不同的数据库, 驱动包中类的完整名称不一样 表 8-1 列出了现在流行的 JDBC 驱动程序名和数据库的 URL 表 8-1 常用的 JDBC 驱动程序名和数据库的 URL 数据库名称 JDBC 驱动程序的名称 URL 格式 MySQL com.mysql.jdbc.driver jdbc:mysql://hostname:port /databasename ORACLE oracle.jdbc.driver.oracledriver jdbc:oracle:thin:@hostname:port Number:databaseName DB2 COM.ibm.db2.jdbc.net.DB2Driver jdbc:db2:hostname:port Number/databaseName Sybase com.sybase.jdbc.sybdriver jdbc:sybase:tds:hostname: port Number/databaseName 注 :URL 格式中所有加粗的部分是不变的, 剩余部分根据不同数据库产品的要求和实际名称进行替换 例 8.1 JDBC 数据库驱动程序的加载 import java.sql.*; public class ConnectionDemo01 static final String DBDRIVER="com.mysql.jdbc.Driver";// 定义 MySQL 数据库驱动程序类名 public static void main(string[] args) try Class.forName(DBDRIVER);// 加载数据库 JDBC 驱动程序 catch (ClassNotFoundException e)// 加载失败抛出异常 e.printstacktrace(); 如果以上程序可以正常运行, 则证明数据库驱动程序已经配置成功 连接数据库 如果数据库驱动程序可以正常加载, 下面就可以使用 DriverManager 类连接数据库

238 238 Java 程序设计 DriverManager 类是 JDBC 的管理层, 作用于用户和驱动程序之间 它跟踪可用的驱动程序, 并在数据库和相应驱动程序之间建立连接 另外,DriverManager 类也处理诸如驱动程序登录时间限制及登录和跟踪消息的显示等事务 DriverManager 类中的方法都是静态方法, 所以用的时候不需要实例化, 直接调用类名就可以, 其主要成员方法如表 8-2 所示 表 8-2 DriverManager 类的常用方法 方法 static Connection getconnection(sting URL) static Connection getconnection(string URL,Properties info) 功能 建立地址为 URL 的数据库的连接 建立地址为 URL 及属性信息创建数据库连接 static Connection getconnection(string URL,String user,string password) 连接到地址为 URL 的数据库, 使用的用户名是 user, 密码是 password static Driver getdriver(string URL) 通过指定 URL 获取数据库的驱动程序 对于简单的应用程序, 程序员只需要使用 DriverManager 的 getconnection() 方法来建立与数据库的连接即可 JDBC 虽然提供了与平台无关的数据库操作, 但是各个数据库的连接地址 URL 是不同的 JDBC 数据库的连接地址 URL 由 JDBC 协议 子协议 子名称共 3 个部分组成 JDBC URL 中的协议总是 jdbc 子协议是数据库驱动或数据库连接机制的名称, 如 mysql 子名称是一种标识数据库的方法 必须遵循 // 主机名 : 端口 / 数据库名称 的标准 URL 命名约定 不同数据库的连接地址 URL 写法可能存在较大的差异, 常用几种数据库的 URL 格式见表 8-1 所示 在本章例子中我们将对一个 MySQL 数据库 testdb 进行数据库的连接和操作, 其 URL 为 : jdbc:mysql://localhost:3306/testdb Connection 是用来表示数据库连接的接口, 对数据库的一切操作都是在这个连接上进行的 Connection 接口的主要成员方法如表 8-3 所示 表 8-3 Connection 接口的常用成员方法 方法 功能 Satement createstatement() Satement createstatement(int resultsettype,int resultsetconcurrency) boolean isreadonly() void setreadonly() void commit() void roolback() void close() 创建 Statement 对象 创建一个 Statement 对象, 它将生成具有特定类型和并发性的结果集 判断连接是否为只读模式 设置连接的只读模式 事务提交, 更改成为持久更改 取消在当前事物中进行更改, 并释放当前 Connection 对象当前持有的所有数据库锁 立即释放 Connection 对象数据库和 JDBC 资源 例 8.2 连接 MySQL 数据库 import java.sql.*; public class ConnectionDemo02

239 项目 8 JDBC 数据库编程 239 // 定义 MySQL 数据库驱动程序类名 public static final String DBDRIVER="com.mysql.jdbc.Driver"; // 定义 MySQL 数据库的连接地址 public static final String DBURL="jdbc:mysql://localhost:3306/testDB"; // 定义 MySQL 数据库连接的用户名 public static final String DBUSER="root"; // 定义 MySQL 数据库的连接密码 public static final String DBPASS=""; public static void main(string[] args) Connection conn=null; try Class.forName(DBDRIVER);// 加载数据库驱动程序 catch(classnotfoundexception e) e.printstacktrace(); try conn=drivermanager.getconnection(dburl,dbuser,dbpass);// 连接数据库 catch(sqlexception e) //TODO Auto-generated catch block e.printstacktrace(); System.out.println(conn);// 打印输出连接信息 try conn.close();// 关闭连接 catch(sqlexception e) e.printstacktrace(); 程序运行结果为 : com.mysql.jdbc.connection@ 习题 1. 填空题 (1) 使用 方法加载和注册驱动程序后, 由 类负责管理并跟踪 JDBC 驱 动程序, 在数据库和相应驱动程序之间建立连接 (2) 接口负责建立与指定数据库的连接 2. 简述使用 JDBC 连接 ODBC 数据源 Microsoft SQL Server Oracle MySQL 和 IBM DB2 等数据库所对应的 JDBC 驱动程序名和数据库连接的 URL 值 任务 3 JDBC 操作数据库 Statement 接口 Statement 用于在已经建立连接的基础上向数据库发送 SQL 语句的对象 它只是一个接口

240 240 Java 程序设计 的定义, 其中包括了执行 SQL 语句和获取返回结果的方法 Statement 接口常用的方法如表 8-4 所示 表 8-4 Statement 接口的常用成员方法 方法 功能 boolean execute(string sql) ResultSet executequery(string sql) int executeupdate(string sql) Connection getconnection() ResultSet getresultset() void clearbatch() void addbatch(string sql) void close() 执行 SQL 语句进行数据库查询, 返回结果集进行数据库更新获取对数据库的连接获取结果集清除 Statement 中的 SQL 批处理语句在 Statement 语句中增加用于数据库操作的 SQL 批处理语句关闭 Statement 语句指定的数据库连接 Statement 接口提供了 3 种执行 SQL 语句的方法 : execute() executequery() executeupdate(), 具体使用哪一种方法由 SQL 语句类型决定 execute() 方法用于执行返回多个 结果集或多个更新操作的语句, 如果查询或更新失败返回 false;executequery() 方法用于产生 单个结果集的 SQL 语句, 如 SELECT 语句 ;executeupdate() 方法用于执行 INSERT UPDATE DELETE 及 DDL( 数据定义语言 ) 类型的 SQL 语句, 例如 CREATE TABLE 和 DROP TABLE 等,executeUpdate() 的返回值是一个整数, 表示它执行 SQL 语句所影响的数据库中的表的行数, 如果更新失败返回 -1 例 8.3 在数据库 testdb 中创建了表 user, 并向其添加了一条记录 import java.sql.*; public class ConnectionDemo03 // 定义 MySQL 数据库驱动程序类名 public static final String DBDRIVER="com.mysql.jdbc.Driver"; // 定义 MySQL 数据库的连接地址 public static final String DBURL="jdbc:mysql://localhost:3306/testDB?useUnicode=true&character- Encoding=gbk"; // 定义 MySQL 数据库连接的用户名 public static final String DBUSER="root"; // 定义 MySQL 数据库的连接密码 public static final String DBPASS=""; public static void main(string[] args) Connection conn=null; Statement stat=null; // 在数据库 testdb 中创建表 user 的 SQL 语句 String createtable=" create table user(id int auto_increment primary key,"+ "name varchar(30) not null,password varchar(32) not null,age int(3))"; // 插入一条数据到表 user 中的 SQL 语句 String insertvalue=" insert into user(name,password,age) values(' 元芳 ',123456',38);"; try

241 项目 8 JDBC 数据库编程 241 // 加载数据库驱动程序 Class.forName(DBDRIVER); // 连接数据库 testdb conn=drivermanager.getconnection(dburl,dbuser,dbpass); // 获取 Statement 对象 stat=conn.createstatement(); // 在数据库 test 中创建表 user stat.executeupdate(createtable); // 插入一条记录到表 user 中 stat.executeupdate(insertvalue); // 关闭 Statement 对象 stat.close(); // 关闭数据库的连接 conn.close(); catch(exception e) // TODO Auto-generated catch block e.printstacktrace(); 如果以上程序可以正常运行, 则证明在数据库 testdb 中添加表 user, 并向该表添加一条 记录成功 注意 : 如果查询表 user 中刚添加的记录, 汉字显示都为问号 (?), 说明 MySQL 中的字 符编码的设置有问题, 采用以下步骤可以解决 : (1) 重装 MySQL, 在配置时, 选择 gbk 作为默认编码 (2)URL 后面添加?useUnicode=true&characterEncoding=gbk ResultSet 接口 ResultSet 接口用来暂时存放数据库查询操作获得的结果 它包含了符合 SQL 语句中条件 的所有行, 并且它提供了一套 getxxx() 方法对这些行中的数据进行访问 ResultSet 类的主要 成员方法及其功能如表 8-5 所示 表 8-5 ResultSet 接口的常用成员方法 方法 boolean absolute(int row) void afterlast() void beforefirst() boolean first() Array getarray(int row) boolean getboolean(int colindex) byte getbyte(int colindex) int getint(int colindex) 功能将指针移动到结果集对象的某一行将指针移动到结果集对象的末尾将指针移动到结果集对象的头部将指针移动到结果集对象的第一行获取结果集中的某一行并将其存入一个数组获取当前行中某一列的值, 返回一个布尔型值获取当前行中某一列的值, 返回一个字节型值获取当前行中某一列的值, 返回一个整型值

242 242 Java 程序设计 续表 方法 Long getlong(int colindex) double getdouble(int colindex) String getstring(int colindex) Date getdate(int colindex) Object getobject(int colindex) boolean isbeforefirst() boolean isafterlast() boolean isfirst() boolean islast() 功能获取当前行中某一列的值, 返回一个长整型值获取当前行中某一列的值, 返回一个双精度型值获取当前行中某一列的值, 返回一个字符串获取当前行中某一列的值, 返回一个日期型值获取当前行中某一列的值, 返回一个对象判断指针是否在结果集的头部判断指针是否在结果集的末尾判断指针是否在结果集的第一行判断指针是否在结果集的最后一行 ResultSet 接口不仅提供了一套用于访问数据的 getxxx() 方法, 还提供了很多移动指针 (cursor, 有时也译为光标 ) 的方法 cursor 是 ResultSet 维护指向当前数据行的指针 最初它 位于第一行之前, 因此第一次访问结果集时通常调用 next 方法将指针置于第一行上, 使它成 为当前行 随后每次调用 next 指针向下移动一行 例 8.4 从 user 表中查询数据 import java.sql.*; public class ConnectionDemo04 // 定义 MySQL 数据库驱动程序类名 public static final String DBDRIVER="com.mysql.jdbc.Driver"; // 定义 MySQL 数据库的连接地址 public static final String DBURL="jdbc:mysql://localhost:3306/testDB"; // 定义 MySQL 数据库连接的用户名 public static final String DBUSER="root"; // 定义 MySQL 数据库的连接密码 public static final String DBPASS=""; public static void main(string[] args) Connection conn=null; Statement stat=null; ResultSet rs=null; // 查询表 user 当中的数据 String query="select id,name,password,age from user"; try // 加载数据库驱动程序 Class.forName(DBDRIVER); // 连接数据库 testdb conn=drivermanager.getconnection(dburl,dbuser,dbpass); // 获取 Statement 对象 stat=conn.createstatement(); // 执行查询操作, 获取包含查询结果的 ResultSet 对象 rs=stat.executequery(query); while(rs.next())

243 项目 8 JDBC 数据库编程 243 int id=rs.getint("id"); // 获取查询到的 id 字段的值 String name=rs.getstring("name");// 获取查询到的 name 字段的值 String password=rs.getstring("password");// 获取查询到的 password 字段的值 int age=rs.getint("age");// 获取查询到的 age 字段的值 System.out.println(id+"--->"+name+"--->"+password+"--->"+age); rs.close(); // 关闭结果集 stat.close();// 关闭操作 conn.close(); // 关闭数据库的连接 catch(exception e) e.printstacktrace(); 程序运行结果 : 1---> 元芳 ---> > PreparedStatement 接口 PreparedStatement 接口继承了 Statement 接口, 但 PreparedStatement 语句中包含了经过预 编译的 SQL 语句, 因此可以获得更高的执行效率 在 PreparedStatement 语句中可以包含多个 用? 代表的字段, 在程序中可以利用 setxxx 方法设置该字段的内容, 从而增强了程序设 计的动态性 PreparedStatement 接口的常用成员方法及其功能如表 8-6 所示 表 8-6 PreparedStatement 接口的常用成员方法 方法 功能 ResultSet executequery(string sql) int executeupdate(string sql) void setboolean(int index, boolean x) void setbyte(int index,byte b) void setbytes(int byte[] b) void setdate(int index,date x) void setfloat(int index,float x) void setint(int index,int x) void setlong(int index,long x) void setref(int index,int ref) void setshort(int index,short x) void setstring(int index,string x) void settime(int index,time x) 执行 SQL 查询语句进行数据更新, 返回值为修改记录的行数设置为逻辑类型设置为字节类型设置为字节数组类型设置为日期类型设置为浮点类型设置为整数类型设置为长整数类型设置为引用类型设置为短整数类型设置为字符串类型设置为时间类型 PreparedStatement 与 Statement 的区别在于它构造的 SQL 语句不是完整的语句, 而需要在 程序中进行动态设置 这一方面增强了程序设计的灵活性 ; 另一方面, 由于 PreparedStatement

244 244 Java 程序设计 语句是经过预编译的, 因此它构造的 SQL 语句的执行效率比较高 所以对于某些使用频繁的 SQL 语句, 用 PreparedStatement 语句比用 Statement 具有明显的优势 例 8.5 使用 PreparedStatement 对象操作数据库 import java.sql.*; public class ConnectionDemo05 // 定义 MySQL 数据库驱动程序类名 public static final String DBDRIVER = "com.mysql.jdbc.driver"; // 定义 MySQL 数据库的连接地址 public static final String DBURL="jdbc:mysql://localhost:3306/testDB"; // 定义 MySQL 数据库连接的用户名 public static final String DBUSER="root"; // 定义 MySQL 数据库的连接密码 public static final String DBPASS=""; public static void main(string[] args) Connection conn=null; PreparedStatement ps=null; try // 加载数据库驱动程序 Class.forName(DBDRIVER);// 加载数据库驱动 // 连接数据库 testdb conn=drivermanager.getconnection(dburl,dbuser,dbpass); ps=conn.preparestatement("insert into user(name,password,age)values(?,?,?)"); for(int i=0;i<10;i++) ps.setstring(1,"name"+i); // 设置第一个占位符的值 ps.setstring(2,"6666"+i); // 设置第二个占位符的值 ps.setint(3, 20+i);// 设置第三个占位符的值 ps.executeupdate(); ps.close();// 关闭操作 conn.close(); // 关闭数据库的连接 catch (Exception e) e.printstacktrace(); 若程序正常运行, 则表明数据插入成功 再次运行例 8.3 结果为 : 1---> 元芳 ---> > >name0---> > >name1---> > >name2---> > >name3---> > >name4---> > >name5---> > >name6---> > >name7---> > >name8---> >28

245 项目 8 JDBC 数据库编程 >name9---> >29 习题 1. 选择题 (1) 用于执行 SQL 语句并将数据检索到 ResultSet 中 A.Statement C.CalledStatement (2) 用于保存数据查询的结果集 A.Connection C.PreparedStatement B.Connection D.ResultSet B.Statement D.ResultSet (3) 现在有默认 ResultSet 类型的对象 rs, 那么获取下一行的数据正确的语句是 A.rs.next( ) B.rs.nextRow( ) C.rs.getNext( ) D.rs.getNextRow( ) (4) 类试图找到一个能够连接到 URL 中指定的驱动程序 A.DriverManager C.PreparedStatement B.Connection D.CalledStatement (5)JDBC 使用 SQL 语句操作数据库数据时, 是必须捕获的异常 A.EOFException C.InterruptedException 2. 填空题 B.SQLException D.ArithmeticException (1) 接口的对象可以代表一个预编译的 SQL 语句, 它是 接口的子接口 (2) 接口表示从数据库中返回的结果集 3. 简述 Statement 接口和 PreparedStatement 接口的主要区别 4. 设有客户数据表 Customer(CNO,name,sex,principalship,company,telephone,address, back- ground), 其中每个字段的类型和含义如下 : 字段名类型字段说明 CNO Varchar(20) 编号 (primary key) name Varchar(20) 姓名 sex Varchar(4) 性别 principalship Varchar(10) 职务 company Varchar(40) 公司 / 单位 telephone Varchar(20) 公司电话 address Varchar(40) 公司地址 background Varchar(80) 公司背景 (1) 使用 JDBC 在 Access 或 SQL Server 或 Oracle 或 MySQL 中创建数据库 CRMDB (2) 使用 JDBC 在数据库 CRMDB 中建立上述数据表 Customer (3) 使用 JDBC 将下面的数据添加到 Customer 表中 CNO name sex principalship company telephone address background 胡振男总经理华夏大邦 上海上市公司

246 246 Java 程序设计 李兴男经理九洲方圆 北京上市公司 江中女董事长时代在线 长沙上市公司 郭华女董事长光华集团 深圳上市公司 (4) 从 Customer 表中查找 时代在线 公司的基本信息 (5) 将 胡振 的电话改为 (6) 从 Customer 表中查找全部 男 客户的信息 (7) 删除 号记录 5. 在 MySQL 中依次创建数据库 DBPhoto 和数据表 tblphoto(id varchar(50) primary key not null,name varchar(100),description varchar(200),photo image not null), 然后将你的一组照片存储到 tblphoto 表中, 并能方便地存取与浏览照片 项目总结 1. 介绍了 JDBC 的基本结构与原理 2. 详细讲解了 DriverManager Statement PreparedStatement ResultSet 等类的用法 3. 重点讲解了 JDBC 数据库访问的详细步骤, 包括加载数据库的驱动 获取数据库连接, 执行 SQL 语句 处理执行结果等

247 项目 9 Java 多线程 Java 语言提供了非常优秀的多线程支持 程序可以通过非常简单的方式来启动多线程 本章将会介绍 Java 多线程编程相关概念和方法 包括创建 启动线程 控制线程 以及线程 同步操作 理解线程的基本概念及 Java 多线程处理机制 掌握多线程的生命周期 掌握线程的两种创建方法 掌握线程的基本控制方法 掌握线程的同步操作 任务 1 线程的基本概念 前面章节我们编写的程序运行后都只有一条顺序执行流 程序从 main()方法开始执行 依 次向下执行每行代码 如果程序执行某行代码遇到阻塞 则程序将会停滞在该处 我们把这种 程序运行方式称为单进程或单任务 现在几乎所有的操作系统都支持同时运行多个任务 一个任务通常就是一个程序 每个 运行中的程序就是一个进程 当一个程序运行时 内部可能包含了多个顺序执行流 每个顺序 执行流就是一个线程 什么是进程和线程 当一个程序进入内存运行 即变成一个进程 进程是处于运行过程中的程序 并且具有 一定独立功能 进程是系统进行资源分配和调度的一个独立单位 它的三个主要特征为 独立性 进程是系统中独立存在的实体 它可以拥有自己独立的资源 每一个进程都 拥有自己私有的地址空间 在没有经过进程本身允许的情况下 一个用户进程不可以 直接访问其他进程的地址空间 多态性 进程与程序的区别在于 程序只是一个静态的指令集合 而进程是一个正在 系统中活动的指令集合 在进程中加入了时间的概念 进程具有自己的生命周期和各 种不同的状态 这些概念在程序中都是不具备的 并发性 多个进程可以在单个处理器上进行并发执行 多个进程之间不会相互影响

248 248 Java 程序设计 现代的操作系统都支持多进程的并发, 但在具体的实现细节上可能因为硬件和操作系统的不同而采用不同的策略 比较常用的方式有 : 共用式的多任务操作策略, 例如 Windows 3.1 和 Mac OS9, 目前操作系统绝大多数采用效率更高的抢占式多任务策略, 例如 Windows NT, Win8 以及 UNIX/Linux 等操作系统 多线程则扩展了多进程的概念, 使得同一个进程可以同时并发处理多个任务 线程 (Thread) 也被称作轻量级进程 (Lightweight Process), 线程是进程执行单元 就像进程在操作系统中的地位一样, 线程在程序中是独立的 并发的执行流 当进程被初始化后, 主线程就被创建了 对于绝大多数的应用程序来说, 通常仅要求有一个主线程, 但我们也可以在该进程内创建多条顺序执行流, 这些顺序执行流程就是线程, 每条线程也是相互独立的 线程是进程的组成部分, 一个进程可以拥有多个线程, 一个线程必须有一个父进程 线程可以拥有自己的堆栈 自己的程序计数器和自己的局部变量, 但不再拥有系统资源, 它与父进程的其他线程共享该进程所拥有的全部资源 因为多个线程共享父进程里的全部资源, 因此编程更加方便 ; 但必须更加小心, 开发人员必须确保线程不会妨碍同一进程里的其他线程 线程可以完成一定任务, 可与其他线程共享父进程中的共享变量及部分环境, 相互之间协同来完成进程所要完成的任务 线程是独立运行的, 它并不知道进程中是否有其他线程存在 线程的执行是抢占式的, 也就是说, 当前运行的线程在任何时候都可能被挂起, 以便另外一个线程可以运行 一个线程可以创建和撤销另一个线程, 同一个进程中的多个线程之间可以并发执行 从逻辑角度来看, 多线程存在于一个应用程序中, 让一个应用程序中可以有多个执行部分同时执行, 但操作系统无须将多个线程看做多个独立的应用, 对多线程实现调度和管理以及资源分配 线程的调度和管理由进程本身负责完成 进程和线程的区别主要体现在 : (1) 一个程序至少有一个进程, 一个进程至少有一个线程 (2) 进程在执行过程中拥有独立的内存单元, 而多个线程共享内存, 极大地提高了程序的运行效率 (3) 线程不能独立执行, 必须依附在应用程序中, 由应用程序提供多个线程执行控制 Java 是第一个自身支持线程的主流编程语言, 在 Java 语言的运行环境中 (JVM), 内置了一个线程调度器, 用于确定某一时刻由哪一个线程占用计算机资源进行执行, 从而实现了多线程操作 多线程的优缺点由于多线程应用程序将程序划分成多个独立的任务, 因此可以在以下方面显著提高性能 : (1) 多线程技术使程序的响应速度更快, 因为用户界面可以在进行其他工作的同时一直处于活动状态 (2) 当前没有进行处理的任务时可以将处理器时间让给其他任务 (3) 占用大量处理时间的任务可以定期将处理器时间让给其他任务 (4) 可以随时停止任务 (5) 可以分别设置各个任务的优先级以优化性能 是否需要创建多个线程取决于各种因素 在以下情况下, 最适合采用多线程处理 :

249 项目 9 Java 多线程 249 (1) 耗时或大量占用处理器的任务阻塞用户界面操作 (2) 各个任务必须等待外部资源 ( 如远程文件或 Internet 连接 ) 同样的, 多线程也存在许多缺点, 在考虑多线程时需要进行充分的考虑 多线程的主要缺点包括 : (1) 等候使用共享资源时造成程序的运行速度变慢 这些共享资源主要是独占性的资源, 如打印机等 (2) 对线程进行管理要求额外的 CPU 开销 线程的使用会给系统带来上下文切换的额外负担 当这种负担超过一定程度时, 多线程的特点主要表现在其缺点上, 比如用独立的线程来更新数组内每个元素 (3) 线程的死锁 即较长时间的等待或资源竞争以及死锁等多线程症状 (4) 对公有变量的同时读或写 当多个线程需要对公有变量进行写操作时, 后一个线程往往会修改掉前一个线程存放的数据, 从而使前一个线程的参数被修改 ; 另外, 当公用变量的读写操作是非原子性时, 在不同的机器上, 中断时间的不确定性, 会导致数据在一个线程内的操作产生错误, 从而产生莫名其妙的错误, 而这种错误是程序员无法预知的 习题 1. 什么是进程? 什么是线程? 线程与进程有什么关系? 2. 多线程的优缺点 任务 2 线程的创建和启动 Java 多线程是从创建线程对象开始的 线程对象的创建主要通过 Thread 类和 Runnable 接口来实现 Thread 类较为简单,Runnable 接口更加灵活 Thread 类通过继承 Thread 类来创建并启动多线程的步骤如下 : (1) 定义 Thread 类的子类, 并重写该类的 run 方法, 该 run 方法的方法体就代表了线程需要完成的任务 因此, 经常把 run 方法称为线程执行体 (2) 创建 Thread 子类的对象 (3) 用线程对象的 start 方法来启动该线程 Thread 类的构造方法和常用的方法如下 : public Thread(): 创建新的 Thread 对象 public Thread(String name): 分配名字为 name 的新 Thread 对象 public Thread(Runnable target): 使用 Runnable 接口对象创建新的 Thread 对象 public static void sleep(long millis) throws interruptedexception: 在指定的毫秒数内让当前正在执行的线程休眠 ( 暂停执行 ) public void start(): 使该线程开始执行 ;Java 虚拟机调用该线程的 run() 方法 public void run(): 定义该线程需执行的任务 public final String getname(): 返回该线程的名称

250 250 Java 程序设计 public final Boolean isalive(): 测试线程是否处于活动状态 如果线程已经启动且尚未 终止, 则为活动状态 public static Thread currentthread(): 返回当前正在执行的线程对象的引用 例 9.1 通过继承 thread 类的方法创建线程 import java.lang.math; public class TestThreadDemo01 public static void main(string[] args) System.out.println(Thread.currentThread().getName()+" 线程运行开始!");//main 线程开始 new MitiSay("A").start();// 启动线程 A new MitiSay("B").start();// 启动线程 B System.out.println(Thread.currentThread().getName()+" 线程运行结束!");//main 线程结束 class MitiSay extends Thread public MitiSay(String threadname) super(threadname); public void run()// 重写 run() 方法 System.out.println(getName() + " 线程运行开始!");// 子线程开始 for(int i = 0; i < 10; i++) System.out.println(i + " " + getname()); try sleep((int)math.random()*100000);// 当前线程根据随机时间休眠 catch(interruptedexception e) e.printstacktrace(); System.out.println(getName() + " 线程运行结束!");// 子线程结束 程序运行结果为 : main 线程运行开始! main 线程运行结束! A 线程运行开始! 0 A B 线程运行开始! 0 B 1 A 1 B 2 A 2 B 3 A 3 B 4 A 4 B

251 项目 9 Java 多线程 A 5 B 6 A 6 B 7 A 7 B 8 A 8 B 9 A 9 B A 线程运行结束! B 线程运行结束! 说明 : (1) 程序启动运行 main 时,Java 虚拟机启动一个进程, 主线程 main 在 main() 调用时被 创建 随着调用 MitiSay 的两个对象的 start 方法, 另外两个线程也启动了, 这样, 整个应用就 在多线程下运行 (2) 在一个方法中调用 Thread.currentThread().getName() 方法, 可以获取当前线程的名字 在 mian 方法中调用该方法, 获取的是主线程的名字 (3)start() 方法的调用后并不是立即执行多线程代码, 而是使得该线程变为可运行态 (Runnable), 什么时候运行是由操作系统决定的 从程序运行的结果可以发现, 多线程程序是乱序执行 因此, 只有乱序执行的代码才有 必要设计为多线程 (4)Thread.sleep() 方法调用目的是不让当前线程独自霸占该进程所获取的 CPU 资源, 以 留出一定时间给其他线程执行的机会 实际上所有的多线程代码执行顺序都是不确定的, 每次 执行的结果都是随机的 Runnable 接口 由于 Java 仅支持单继承, 因此, 当一个类已经是某个类的子类时, 就无法再继承 Thread 类 此时继承 Thread 类创建线程的方法就行不通了, 必须采实现 Runnable 接口创建线程 通过实现 Runnable 接口来编写多线程程序, 只需要重写 run() 方法, 而且实现 Runnable 接口的类还可以继承其他类, 在这一点上它与定义 Thread 类的子类实现多线程不同, 也是它 的优势所在 具体实现步骤如下 : (1) 定义一个实现 Runnable 接口的类 (2) 定义方法 run() Runnable 接口中有一个空的方法 run(), 必须重写该方法 (3) 创建该类的一个线程对象, 并以该对象为参数, 传递给 Thread 类的构造方法, 从而 生成 Thread 类的一个对象 (4) 通过 start() 方法启动线程 例 9.2 通过实现 runnable 接口的方法创建线程 import java.lang.math; public class TestThreadDemo02 implements Runnable public static void main(string[] args)

252 252 Java 程序设计 System.out.println(Thread.currentThread().getName()+" 线程运行开始!"); TestThreadDemo02 test=new TestThreadDemo02();// 创建实现 Runnable 接口的对象 test Thread thread1 = new Thread(test);// 用 test 对象作参数创建线程 1 Thread thread2 = new Thread(test);// 用 test 对象作参数创建线程 2 thread1.start();// 启动线程 1 thread2.start();// 启动线程 2 System.out.println(Thread.currentThread().getName()+" 线程运行结束!"); public void run() System.out.println(Thread.currentThread().getName()+" 线程运行开始!");// 子线程开始 for(int i=0; i<10; i++) System.out.println(i+" "+Thread.currentThread().getName()); try Thread.sleep((int) Math.random()*100000); catch(interruptedexception e) e.printstacktrace(); System.out.println(Thread.currentThread().getName()+" 线程运行结束!");// 子线程结束 程序运行结果为 : main 线程运行开始! Thread-0 线程运行开始! 0 Thread-0 Thread-1 线程运行开始! 0 Thread-1 1 Thread-0 1 Thread-1 2 Thread-0 2 Thread-1 3 Thread-0 3 Thread-1 4 Thread-0 4 Thread-1 5 Thread-0 main 线程运行结束! 6 Thread-0 5 Thread-1 7 Thread-0 6 Thread-1 8 Thread-0 7 Thread-1 9 Thread-0 8 Thread-1 Thread-0 线程运行结束!

253 项目 9 Java 多线程 Thread-1 Thread-1 线程运行结束! 说明 : (1)TestThreadDemo02 类通过实现 Runnable 接口, 使得该类有了多线程类的特征 run() 方法是多线程程序的一个约定 所有的多线程代码都在 run() 方法里面 Thread 类实际上也是 实现了 Runnable 接口的类 (2) 在启动多线程的时候, 需要先通过 Thread 类的构造方法 Thread(Runnable target) 构 造出对象, 然后调用 Thread 对象的 start() 方法来运行多线程代码 (3) 实际上所有的多线程代码都是通过运行 Thread 的 start() 方法来运行的 因此, 不管 是扩展 Thread 类还是实现 Runnable 接口来实现多线程, 最终还是通过 Thread 的对象的 API 来控制线程的, 熟悉 Thread 类的 API 是进行多线程编程的基础 两种创建线程方法比较 通过继承 Thread 类或实现 Runnable 接口都可以实现多线程, 但两种方式存在一定的差别, 相比之下两种方式的主要差别如下 采用继承 Thread 类方式 : (1) 优点 : 编写简单, 如果需要访问当前线程, 无需使用 Thread.currentThread() 方法, 直接使用 this, 即可获得当前线程 (2) 缺点 : 因为线程类已经继承了 Thread 类, 所以不能再继承其他的父类 采用实现 Runnable 接口方式 : (1) 优点 : 线程类只是实现了 Runnable 接口, 还可以继承其他的类 在这种方式下, 可 以多个线程共享同一个目标对象, 所以非常适合多个相同线程来处理同一份资源的情况, 从而 可以将 CPU 代码和数据分开, 形成清晰的模型, 较好地体现了面向对象的思想 方法 (2) 缺点 : 编程稍微复杂, 如果需要访问当前线程, 必须使用 Thread.currentThread() 例 9.3 假定某车站有四个售票点, 发售某日某次列车的 10 张车票 要求 : 一个售票点 用一个线程表示, 用 Thread 类创建线程 public class TestThreadDemo03 public static void main(string[] agrs) new ThreadTest().start(); new ThreadTest().start(); new ThreadTest().start(); new ThreadTest().start(); class ThreadTest extends Thread private int ticket=10; public void run() while(true) if(ticket>0) System.out.println(Thread.currentThread().getName()+

254 254 Java 程序设计 "is selling 10ticket:"+ticket--); else break; 程序运行结果为 : Thread-0is selling 10ticket:10 Thread-0is selling 10ticket:9 Thread-0is selling 10ticket:8 Thread-0is selling 10ticket:7 Thread-0is selling 10ticket:6 Thread-0is selling 10ticket:5 Thread-0is selling 10ticket:4 Thread-0is selling 10ticket:3 Thread-0is selling 10ticket:2 Thread-0is selling 10ticket:1 Thread-1is selling 10ticket:10 Thread-1is selling 10ticket:9 Thread-1is selling 10ticket:8 Thread-1is selling 10ticket:7 Thread-1is selling 10ticket:6 Thread-1is selling 10ticket:5 Thread-1is selling 10ticket:4 Thread-1is selling 10ticket:3 Thread-1is selling 10ticket:2 Thread-1is selling 10ticket:1 Thread-2is selling 10ticket:10 Thread-2is selling 10ticket:9 Thread-2is selling 10ticket:8 Thread-2is selling 10ticket:7 Thread-2is selling 10ticket:6 Thread-2is selling 10ticket:5 Thread-2is selling 10ticket:4 Thread-2is selling 10ticket:3 Thread-2is selling 10ticket:2 Thread-2is selling 10ticket:1 Thread-3is selling 10ticket:10 Thread-3is selling 10ticket:9 Thread-3is selling 10ticket:8 Thread-3is selling 10ticket:7 Thread-3is selling 10ticket:6 Thread-3is selling 10ticket:5 Thread-3is selling 10ticket:4

255 项目 9 Java 多线程 255 Thread-3is selling 10ticket:3 Thread-3is selling 10ticket:2 Thread-3is selling 10ticket:1 从结果上看每个票号都被打印了四次, 即四个线程各自卖各自的 10 张票, 而不去卖共同 的 10 张票 这种情况是怎么造成的呢? 我们需要的是, 多个线程去处理同一个资源, 一个资 源只能对应一个对象, 在上面的程序中, 我们创建了四个 ThreadTest 对象, 就等于创建了四个 资源, 每个资源都有 10 张票, 每个线程都在独自处理各自的资源 例 9.4 用 Runnable 接口方式完成例 9.3 public class TestThreadDemo04 public static void main(string[] args) ThreadTest t = new ThreadTest(); new Thread(t).start(); new Thread(t).start(); new Thread(t).start(); new Thread(t).start(); class ThreadTest implements Runnable private int tickets = 10; public void run() while(true) if(tickets > 0) System.out.println(Thread.currentThread().getName()+ " is saling ticket " + tickets--); else break; 程序运行结果为 : Thread-1is selling ticket:10 Thread-2is selling ticket:8 Thread-2is selling ticket:6 Thread-2is selling ticket:5 Thread-4is selling ticket:4 Thread-4is selling ticket:2 Thread-4is selling ticket:1 Thread-3is selling ticket:9 Thread-2is selling ticket:3 Thread-1is selling ticket:7 上面的程序中, 创建了四个线程, 每个线程调用的是同一个 ThreadTest 对象中的 run() 方 法, 访问的是同一个对象中的变量 (tickets) 的实例, 这个程序满足了我们的需求

256 256 Java 程序设计 习题 1. 选择题 (1) 下面哪些方法可用于创建一个可运行的多线程类? A.public class T implements Runable public void run() B.public class T extends Thread public void run() C.public class T implements Thread public void run() D.public class T implements Thread public int run(). E.public class T implements Runable protected void run() (2) 下列方法中哪个是执行线程的方法? A.run() B.start() C.sleep() D.suspend() (3)Java 语言具有许多优点和特点, 下列选项中, 哪个反映了 Java 程序并行机制的特点? A. 安全性 B. 多线性 C. 跨平台 D. 可移植性 (4)Runnable 接口中的抽象方法是 A.start B.stop C.yield D.run (5) 声明了类 public class A implements Runnable, 下面启动该类型线程的是 A.Thread a=new Thread(new A()).start() B.A a=new A(); a.start(); C.A a=new A();a.run(); D.new A().start() 2. 一个线程执行完 run() 方法后, 进入了什么状态? 该线程还能再调用 start() 方法吗? 3. 建立线程的方法有哪几种?Runnable 接口在线程创建中的作用? 4.Runnable 接口中包括哪些抽象方法?Thread 类有哪些主要的成员变量和方法? 5. 编写一个有两个线程的程序, 第一个线程用来计算 2~ 之间的质数及个数, 第二个线程用来计算 ~ 之间的质数及个数 任务 3 线程的生命周期 在 Java 中每个线程都要经历五种状态 : 新建 就绪 运行 阻塞和死亡 线程从新生到 死亡的状态变化过程称为生命周期, 如图 9-1 所示 图 9-1 线程的生命周期

257 项目 9 Java 多线程 新建就绪状态 当程序使用 new 关键字创建了一个线程之后, 该线程就处于新建状态, 此时它和其他 Java 对象一样, 仅仅由 Java 虚拟机为其分配了内存, 并初始化了其成员变量的值 此时的线程对 象并没有表现出任何线程的多态特征, 程序也不会执行线程执行体中的代码 当线程对象调用了 start() 方法之后, 该线程处于就绪状态,Java 虚拟机会为其创建方法调 用栈和程序计数器, 处于这个状态中的线程并没有开始运行, 它只是表示该线程可以运行了 至于该线程何时开始运行, 取决于 JVM 里线程调度器的调度 例 9.5 线程的调度 public class TestThreadDemo05 extends Thread private int i; public void run() for(;i<100;i++) System.out.println(Thread.currentThread().getName()+" "+i); public static void main(string[] args) for(int i=0;i<100;i++) System.out.println(Thread.currentThread().getName()+" "+i); if(i==20) new TestThreadDemo05().start(); new TestThreadDemo05().start(); 程序运行后部分结果为 : main 0 main 81 main 82 Thread-0 0 Thread-0 10 main 83 main 84 main 85 从结果中可以看出, 当主线程在 i 等于 20 时调用该子线程的 start() 方法来启动当前线程, 但当前线程并没有立即执行, 而是等到主线程为 82 时才看到子线程开始执行

258 258 Java 程序设计 运行和阻塞状态如果处于就绪状态的线程就获得了 CPU, 开始执行 run() 方法的线程执行体, 则该线程处于运行状态 单 CPU 的机器, 任何时刻只有一条线程处于运行状态 当然, 在多 CPU 机器上将会有多线程并行执行, 当线程大于 CPU 数量时, 依然会在同一个 CPU 上切换执行 一个线程运行后, 它不可能一直处于运行状态 ( 除非它执行的时间很短, 瞬间执行完成 ), 线程在运行过程中需要中断, 目的是让其他的线程有运行机会, 线程的调度取决于底层的策略 对应抢占式的系统而言, 系统会给每个可执行的线程一个小时间段来处理任务, 当时间段到达系统就会剥夺该线程的资源, 让其他的线程有运行的机会 在选择下一个线程时, 系统会考虑线程优先级 以下情况会出现线程阻塞状态 : 线程调用 sleep 方法, 主动放弃占用的处理器资源 线程调用了阻塞式 I/O 方法, 在该方法返回前, 该线程被阻塞 线程试图获得一个同步监视器, 但该同步监视器正被其他线程所持有 线程等待某个通知 (notify) 程序调用了 suspend 方法将该线程挂起 当线程被阻塞后, 其他线程将有机会执行 被阻塞的线程会在合适的时候重新进入就绪状态, 注意是就绪状态不是运行状态 也就是被阻塞线程在阻塞解除后, 必须重新等待线程调度器再次调用它 针对上面线程阻塞的情况, 发生以下特定的情况可以解除阻塞, 让进程进入就绪状态 : 线程被置于睡眠状态, 且已经经过指定的毫秒数 线程正在等待 I/O 操作的完成, 且该操作已经完成 线程正在等待另一个线程所持有的锁, 且另一个线程已经释放该锁的所有权 线程正在等待某个触发条件, 且另一个线程发出了信号表明条件已经发生了变化 线程已经被挂起, 且有人调用了它的 resume 方法 线程从阻塞状态只能进入就绪状态, 无法进入运行状态 而就绪和运行状态之间的转换通常不受程序控制, 而是由系统调度所致的 当就绪状态的线程获得资源时, 该线程进入运行状态 ; 当运行状态的线程释放处理器资源时就进入了就绪状态 但对调用了 yield 的方法就例外, 此方法可以让运行状态转入就绪状态 线程死亡线程会在以下三种方式进入死亡状态 : run() 方法执行完成, 线程正常结束 线程抛出未捕获的异常或 Error 直接调用该线程的 stop() 方法来结束线程 ( 该方法易导致死锁, 不推荐使用 ) 线程对象的 isalive() 方法可以测试当前线程是否死亡, 当线程处于就绪 运行 阻塞状态, 该方法返回 true, 如果线程处于新建或死亡状态就会返回 false 不要试图对死亡的线程调用 start() 方法来启动它 死亡线程不可能再次运行 注意 : 当主线程结束的时候, 其他线程不受任何影响 一旦子线程启动后, 会拥有和主

259 项目 9 Java 多线程 259 线程相同的地位, 不受主线程影响 例 9.6 线程的死亡 public class TestThreadDemo06 extends Thread private int i; // 重写 run 方法,run 方法的方法体就是线程执行体 public void run() for(;i<100;i++) System.out.println(getName()+" "+i); public static void main(string[] args) // 创建线程对象 TestThreadDemo06 sd=new TestThreadDemo06(); for(int i=0; i<300;i++) // 调用 Thread 的 currentthread 方法获取当前线程 System.out.println(Thread.currentThread().getName()+" "+i); if(i==20) // 启动线程 sd.start(); // 判断启动后线程的 isalive() 值, 输出 true System.out.println(sd.isAlive()); // 只有当线程处于新建 死亡两种状态时 isalive() 方法返回 false // 当 i>20, 则该线程肯定已经启动过了, 如果 sd.isalive() 为假时, 那只能是死亡状态了 if(i>20 &&!sd.isalive()) // 试图再次启动该线程 sd.start(); 上面查询运行后将引发 IllegalThreadStateException 异常, 这表明死亡状态的线程无法再次 运行了 习题 1. 选择题 (1) 下列关于 Thread 类的线程控制方法的说法中错误的一项是 A. 线程可以通过调用 sleep() 方法使比当前线程优先级低的线程运行 B. 线程可以通过调用 yield() 方法使和当前线程优先级一样的线程运行 C. 线程的 sleep() 方法调用结束后, 该线程进入运行状态

260 260 Java 程序设计 D. 若没有相同优先级的线程处于可运行状态, 线程调用 yield() 方法时, 当前线程将继续执行 (2) 方法 resume() 负责恢复下列哪一个线程的执行? A. 通过调用 stop() 方法而停止的线程 B. 通过调用 sleep() 方法而停止的线程 C. 通过调用 wait() 方法而停止的线程 D. 通过调用 suspend() 方法而停止的线程 (3) 线程在生命周期中要经历五种状态, 若线程当前是新建状态, 则它可以到达的下一个状态是 A. 运行状态 B. 可运行状态 C. 阻塞状态 D. 终止状态 (4) 下列哪些情况可以终止当前线程的运行? A. 当创建一个新线程时 B. 当该线程调用 sleep() 方法时 C. 抛出一个异常时 D. 当一个优先级高的线程进入就绪状态时 (5) 下面哪些方法可以在任何时候被任何线程调用? A.sleep() B.yield() C.synchronized(this) D.notify() E.wait() F.notifyAll() 2. 线程在什么样的状态时, 调用 isalive() 方法返回的值是 false? 3. 在什么方法中可以使用 wait() notify() 及 notifyall() 方法? 任务 4 控制线程 联合线程 Java 中可以将多个线程联合成一个线程, 但这又比单线程灵活得多 如果线程 A 在占有 CPU 期间, 一旦联合了线程 B, 那么线程 A 将立刻中断执行 ; 直到它联合的线程 B 执行完毕, 然后线程 A 再重新排队等待切换 CPU 资源, 以便恢复执行 当线程 A 联合的线程 B 已经结束, 线程 A 重新获取到 CPU 资源后, 继续执行没有执行完的内容 联合使用的语法是 Thread 类中的 join() 方法, 将 join() 线程 ( 线程 B) 加入到调用 join() 的线程 ( 线程 A) 中, 合成的新线程中, 先执行线程 B, 然后再执行线程 A 线程联合可以将大问题划分成许多小问题 每个小问题分配一个线程 当所有的小问题得到处理后, 再调用主线程进一步操作 join() 方法有三种重载的形式 : join(): 等待被 join 的线程执行完成 join(long millis): 等待被 join 的线程时间最长为 millis 毫秒, 如果超过 millis 毫秒, 被 join 的线程还没有执行完则不再等待 join(long millis, int nanos): 被 join 的线程等待时间最长为 millis 毫秒加上 nanos 微秒 ( 千分之一毫秒 ) 例 9.7 线程联合 public class TestThreadDemo07

261 项目 9 Java 多线程 261 public static void main(string args []) ThreadJoin a = new ThreadJoin(); Thread customer = new Thread(a); Thread cakemaker = new Thread(a); customer.setname(" 顾客线程 ");// 创建顾客线程 cakemaker.setname(" 蛋糕线程 ");// 创建蛋糕制作线程 a.setjointhread(cakemaker);// 将蛋糕线程对象做参数传入 customer.start();// 顾客线程启动 class ThreadJoin implements Runnable Cake cake; Thread jointhread; public void setjointhread(thread it) this.jointhread=it; public void run() //TODO Auto-generated method stub if(thread.currentthread().getname().equals(" 顾客线程 ")) System.out.println(Thread.currentThread().getName() +" 等待 "+jointhread.getname()+" 制作生日蛋糕 "); try jointhread.start();// 蛋糕线程启动 jointhread.join();// 线程联合, 顾客线程让出 CPU 给蛋糕线程工作 catch(interruptedexception e) //TODO Auto-generated catch block e.printstacktrace(); System.out.println(Thread.currentThread().getName() +" 买了 "+cake.name+" 价钱 : "+cake.price);// 顾客线程继续执行 else if(thread.currentthread()==jointhread) System.out.println(joinThread.getName()+" 开始制作生日蛋糕, 请等待..."); try Thread.sleep(2000); catch(interruptedexception e) // TODO Auto-generated catch block e.printstacktrace(); cake=new Cake(" 生日蛋糕 ",158); System.out.println(joinThread.getName()+" 制作完成!"); class Cake// 内部类 ( 相当于一个结构体 )

262 262 Java 程序设计 int price; String name; Cake(String name,int price) this.name=name; this.price=price; 程序运行结果为 : 顾客线程等待蛋糕线程制作生日蛋糕蛋糕线程开始制作生日蛋糕, 请等待... 蛋糕线程制作完成! 顾客线程买了生日蛋糕价钱 : 守护线程 有一种线程, 在后台运行, 它的任务是为其他线程提供服务, 这种线程被称为 守护线 程 (Daemon Thread), 又被称为 后台线程 JVM 的垃圾回收器线程就是守护线程 调用 Thread 的 setdaemon (true) 方法可以指定当前线程为后台线程, 但 setdaemon() 必须在 start() 方法前面调用, 否则会出现 java.lang.illegalthreadstateexception 异常 说明 : (1)setDaemon (false) 表示该线程为前台线程 ( 用户线程 ) (2) 前台线程执行完成死亡后,JVM 会通知后台线程, 后台线程就会死亡 但它从得到 通知到后台线程做出响应, 需要一段时间 例 9.8 守护线程 import java.util.scanner; public class TestThreadDemo08 extends Thread public void run() for(int i=1;i<=20;i++) try Thread.sleep(1000);// 休眠 1000 毫秒 catch(interruptedexception ex) System.out.println(ex); System.out.println(i); public static void main(string args []) TestThreadDemo08 test=new TestThreadDemo08(); test.setdaemon(true);// 设置守护线程 test.start();// 启动线程 System.out.println("isDaemon="+test.isDaemon()); Scanner scan=new Scanner(System.in); String str=scan.next();// 接受从键盘输入数据

263 项目 9 Java 多线程 263 System.out.println(str);// 在屏幕输入键盘输入内容 System.out.println(" 输入结束, 主线程结束, 守护线程结束!");// 主线程结束, 守护线程也结束 程序运行结果为 : isdaemon=true 我是中国人我是中国人输入结束, 主线程结束, 守护线程结束! 线程让步 yield() 方法和 sleep() 有点类似, 是 Thread 类的静态方法, 它也可以让当前执行的线程暂停, 但它不会阻塞线程, 只是将该线程转入到就绪状态 yield() 只是让当前线程暂停下, 让系统线 程调度器重新调度下, 让优先级与当前线程相同或是更高的线程运行 如果这种情况不存在, 系统线程调度器又会将该线程调度出来重新执行 例 9.9 线程让步 public class TestThreadDemo09 extends Thread public TestThreadDemo09(String name) super(name); public void run()// 定义 run 方法作为线程执行体 for(int i=0; i<50; i++) System.out.println(getName()+" "+i); // 当 i 等于 20 时, 使用 yield 方法让当前线程让步 if(i==20) Thread.yield(); public static void main(string[] args)throws Exception // 启动两条并发线程 TestThreadDemo09 yt1=new TestThreadDemo09(" 高级 "); TestThreadDemo09 yt2 = new TestThreadDemo09(" 低级 "); // 将 ty1 线程设置成最高优先级 //yt1.setpriority(thread.max_priority); // 将 yt2 线程设置成最低优先级 //yt2.setpriority(thread.min_priority); yt1.start();

264 264 Java 程序设计 yt2.start(); 程序运行结果为 : 高级 0 高级 1 高级 19 高级 20 低级 0 低级 1 低级 3 低级 4 低级 19 低级 20 高级 21 高级 22 习题 1. 线程联合有什么功能? 线程分为哪两类? 2. 什么是守护线程? 它的主要作用是什么? 3. 什么是线程让步? 它与 sleep() 区别是什么? 任务 5 线程的同步 线程同步问题 线程同步是为了防止多个线程同时访问一个数据对象时, 对数据造成的破坏 例如, 一 个线程可能尝试从一个文件中读取数据, 而另一个线程则尝试在同一文件中修改数据 在此情 况下, 数据可能会变得不一致 为了确保在任何时间点一个共享的资源只被一个线程使用, 使 用了 同步 例 9.10 线程同步问题 public class TestThreadDemo10 implements Runnable Timer timer=new Timer(); public static void main(string args[]) TestThreadDemo10 test=new TestThreadDemo10(); Thread t1=new Thread(test);// 创建线程 A Thread t2=new Thread(test);// 创建线程 B t1.setname("a"); t2.setname("b");

265 项目 9 Java 多线程 265 t1.start();// 启动线程 A t2.start();// 启动线程 B public void run() timer.add(thread.currentthread().getname()); class Timer private static int num=0;// 定义静态变量 public void add(string name) num++; try Thread.sleep(1); catch(interruptedexception e) System.out.println(name+" 你是第 "+num+" 个使用 timer 的线程 "); 程序运行结果为 : B 你是第 2 个使用 timer 的线程 A 你是第 2 个使用 timer 的线程 说明 : (1) 程序创建了 A,B 两个线程, 分别启动后同时使用静态变量 num,b 线程执行 num++ 后 num 值变为 1 后休眠 1 毫秒, 紧接着 A 线程也执行 num++, 此时 num 值变为 2, 并休眠 1 毫秒, 最后结果是 A B 线程都打印输出 num 的值为 2 (2) 程序运行结果出错的原因, 在于线程对共享数据操作的完整性没有得到保证 B 在 休眠时 ( 还没有打印输出 ), 由于 A 线程的介入, 导致 num 的值变为 对象锁的实现 例 9.10 程序的运行结果说明了多个线程访问同一个对象出现了冲突, 为了保证运行结果正确, 可以使用 Java 语言的 synchronized 关键字, 用该关键字修饰方法 用 synchronized 关键字修饰的 方法称为同步方法,Java 平台为每个具有 synchronized 代码段的对象关联一个对象锁 (object lock) 这样任何线程在访问对象的同步方法时, 首先必须获得对象锁, 然后才能进入 synchronized 方法, 这时其他线程就不能再同时访问该对象的同步方法了 ( 包括其他的同步方法 ) 通常有两种方法实现对象锁 : (1) 使用同步方法, 其定义格式为 : synchronized void methoda() // 方法体 一个方法使用 synchronized 关键字修饰后, 当一个线程调用该方法时, 必须先获得对象锁, 只有在获得对象锁以后才能进入 synchronized 方法 一个时刻对象锁只能被一个线程持有 如 果对象锁正在被一个线程持有, 其他线程就不能获得该对象锁, 其他线程就必须等待持有该对 象锁的线程释放锁

266 266 Java 程序设计 (2) 使用同步块, 其定义格式为 : synchronized(object) // 要同步的语句 如果要使用类库中的类或别人定义的类, 而该类又没有使用 synchronized 关键字修饰的方 法时, 又要获得对象锁, 就必须使用同步块 例 9.11 通过线程同步解决例 9.10 的问题 public class TestThreadDemo11 implements Runnable Timer timer=new Timer(); public static void main(string args[]) TestThreadDemo11 test=new TestThreadDemo11(); Thread t1=new Thread(test); Thread t2=new Thread(test); t1.setname("a"); t2.setname("b"); t1.start(); t2.start(); public void run() timer.add(thread.currentthread().getname()); class Timer private static int num=0; public synchronized void add(string name)// 在方法前加个 synchronized, 使得方法同步, 保证数据修改时的一致性 num++; try Thread.sleep(1); catch(interruptedexception e) System.out.println(name+" 你是第 "+num+" 个使用 timer 的线程 "); 程序运行结果为 : A 你是第 1 个使用 timer 的线程 B 你是第 2 个使用 timer 的线程 wait() 和 notify() 当某个类的某个方法或代码块被标记为 synchronized 时, 这个方法或代码块在同一时间只 能被一个线程访问 此时这个方法或代码块中的所有代码都是被同步的, 只有当一个线程执行 完所有的代码之后, 下一个线程才能开始执行 当被同步的代码量比较小, 而且每一步执行都 非常快的时候仅仅使用 synchronized 关键字就够了 但是, 如果被同步的方法里面有一些代码 是可以被共享的, 而且这些能够被共享的代码里面存在比较耗时的操作时, 仅仅使用 synchronized 关键字就无法达到最高的效率, 这个时候可以使用 wait() 与 notify() 方法来对并发

267 项目 9 Java 多线程 267 访问做更进一步的控制 wait(): 令当前线程挂起并放弃管程, 同步资源解锁, 使别的线程可访问并修改共享资源, 而当前线程排队等候再次对资源的访问 notify(): 唤醒正在排队等待资源管程的线程, 使之执行并拥有资源的管程 例 9.12 同步互斥实例 class TestThreadDemo12 public static void main(string[] args) ThreadB b=new ThreadB(); b.start(); System.out.println("b is starting..."); synchronized(b)// 同步代码块 try System.out.println("waiting for b to complete..."); b.wait();// 当前线程挂起 System.out.println("completed.now back to main thread"); catch(interruptedexception e) System.out.println("total is :"+b.total); class ThreadB extends Thread int total; public void run() synchronized(this)// 同步代码块 System.out.println("ThreadB is running..."); for(int i=0;i<10;i++) total+=i; System.out.println("total is"+total+" "+i); notify();// 唤醒挂起的线程 程序运行结果为 : b is starting... waiting for b to complete... ThreadB is running... total is0 0 total is1 1 total is3 2 total is6 3 total is10 4 total is15 5 total is21 6 total is28 7 total is36 8

268 268 Java 程序设计 total is45 9 completed.now back to main thread total is :45 说明 : b.wait() 的意思是临时释放锁 ( 针对同步代码块 b), 并阻塞当前线程, 好让其他使用 同一把锁的线程有机会执行, 在这里要用同一把锁的就是 b 线程本身 这个线程在执行到一定 地方后用 notify() 通知被 wait 的线程, 锁已经用完, 待 notify() 所在的同步块运行完之后,wait 所在的线程就可以继续执行 习题 机整数 1. 选择题 (1) 能最准确地描述 synchronized 关键字的是 A. 允许两线程并行运行, 而且互相通信 B. 保证在某时刻只有一个线程可访问方法或对象 C. 保证允许两个或更多处理同时开始和结束 D. 保证两个或更多线程同时开始和结束 (2) 哪个关键字可以保证对象同步? A.transient B.serialize C.synchronized D.static (3) 下列关于 Java 多线程并发控制机制的叙述中, 错误的是 A.Java 中没有提供检测与避免死锁的专门机制, 但应用程序可以采用某些策略防止 死锁的发生 B. 共享数据的访问权限都必须定义为 private C.Java 中对共享数据操作的并发控制是采用加锁技术 D. 线程之间的交互, 提倡采用 suspend()/resume() 方法 2. 请采用实现 Runnable 接口的多线程技术, 用 50 个线程, 生成 个 [1-1000] 间的随 3. 运用多线程技术在上下分割的两个窗口中, 分别从左到右与从右到左移动字符串 4. 编写一个应用程序, 创建三个线程分别显示各自的运行时间 项目总结 1. 介绍了多线程的基本概念, 以及它的特点 2.Java 语言用以实现多线程的两种机制 : 继承 thread 类和实现 Runnable 接口, 以及这两种方法的比较 3. 介绍了线程的生命周期, 重点讲解线程的五种状态 : 新建 就绪 运行 阻塞和线程死亡状态 4. 介绍了 Java 线程的三种控制方式, 即线程联合 守护线程和线程让步 5. 介绍了 Java 线程的同步方法, 并列举了线程控制的实例

269 项目 10 Java 网络编程 作为一门成功的网络编程语言 Java 为用户提供了十分完善的网络功能 比如获取网络各 种资源和数据 与服务器建立连接和通信 将数据传输到网络各处等 正是由于 Java 语言的平 台无关性和强大的网络支持功能 才使得 Java 语言迅速普及 并得到众多编程人员的认同 本 章将重点介绍如何在 Java 中实现访问 URL 资源 TCP Socket 通信和数据报 UDP 通信 了解 TCP/IP 协议 通信端口和 URL 等网络基础知识 掌握使用 URL 类访问网络资源的过程 掌握使用 InetAddress 类获取主机信息的方法 掌握使用 Socket 实现网络通信的方法 掌握 UDP 数据报通信 任务 1 网络基础知识 网络编程的目的就是直接或间接地通过网络协议与其他计算机进行通信 确切地说就是 在两台计算机上的应用程序之间进行通信 因此网络编程中重点要解决两个问题 一是如何准 确地定位网络上一台或多台主机及在主机上运行的应用程序 二是找到主机及应用程序后如何 可靠 高效地进行数据传输 TCP/IP 基本概念 TCP/IP 协议是由斯坦福大学的研究人员提出的 20 世纪 80 年代随着 UNIX 操作系统的 成功 TCP/IP 成为 UNIX 机器的标准网络协议 由于 TCP/IP 协议的跨平台特性 在美国军 方的 ARPANET 计划中 对 TCP/IP 进行了改进 并规定连入该网络的计算机必须采用 TCP/IP 协议 随着 ARPANET 逐渐发展为 Internet TCP/IP 协议便成了 Internet 的标准连接协议 在 TCP/IP 中有三个最常用的协议 理解 IP TCP UDP 这三个协议对于开发网络应用程 序是至关重要的 下面先了解几个基本概念 1 IP 地址 IP 是英文 Internet Protocol 的缩写 意思是 网络之间互连的协议 也就是计算机网络相 互连接并进行通信而设计的协议 在 Internet 中 IP 协议规定了计算机在 Internet 上通信时应 当遵守的规则 从而能使连接到网上的所有计算机实现相互通信

270 270 Java 程序设计 IP 协议中一个非常重要的内容就是给每个连接在 Internet 上的可访问对象都规定了一个唯一的地址, 这个地址称为 IP 地址 有了这种唯一的地址, 就可以保证用户在连网的计算机上操作时, 能够高效而且方便地从千千万万台计算机中选出自己所需的对象来 IP 地址在计算机内部的表现形式是一个 32 位的二进制数, 在实际使用时通常采用以圆点分隔的 4 个十进制数字表示, 如 , 每个数字代表一个 8 位二进制数 采用这种形式来表示一个 IP 地址, 记忆起来很不方便而且容易出错, 为了便于记忆,Internet 提供了域名解析服务, 将 IP 地址与某个域名对应起来, 而域名就是通常所说的网址 2. 端口号端口号是指网络通信时同一机器上的不同进程的标识, 其中 0~1023 为系统保留的端口号 每一项标准的 Internet 服务都有自己独特的端口号, 该端口号在所有的计算机上都相同 例如 :FTP 具有默认端口号 21,HTTP 具有默认端口号 服务类型服务类型是指网络中的各种服务 例如 : 超文本传输协议 (HTTP), 文件传输协议 (FTP), 远程登录 (Telnet), 简单邮件传输协议 (SMTP) 4. 套接字当一台主机上有多个进程同时进行通信, 如何区别从网络中传递来的数据是一个问题, 解决的方法就是在传输层提供向上服务的端口号, 端口号其实标识了运行在某台计算机上的某个进程, 所以一个在 Internet 上运行的应用程序可以用 IP 地址和端口号进行标识, 这就是套接字 (Socket) 也就是说, 一个套接字由一个 IP 地址和一个端口号唯一确定,IP 地址和端口号组成了所谓的 Socket 5. 客户机与服务器处于网络中的计算机进行通信和交流时, 通常有一个信息的提供者和一个信息的接收者, 就像打电话一样, 一方在说, 另一方在听 在网络中, 将信息的提供者称为服务器, 将信息的接收者称为客户机 客户机连接到服务器, 向服务器发送信息请求, 服务器则侦听客户机的请求, 对请求进行处理, 并将请求结果返回客户机, 这样便完成了客户机与服务器之间的通信 客户机 / 服务器 (Client/Server, 简称 C/S) 模式是一种先进的分布式计算模式, 这种模式最大的特点就是使用客户机和服务器两方的资源和计算能力执行同一个特定的任务, 实现负载由客户机和服务器两方共同承担 TCP 与 UDP 传输协议尽管 TCP/IP 协议的名称中只有 TCP 这个协议名, 但是在 TCP/IP 的传输层同时存在 TCP 和 UDP 两个协议 TCP 是 Tranfer Control Protocol 的简称, 是一种面向连接的保证可靠传输的协议 通过 TCP 协议传输, 得到的是一个顺序的无差错的数据流 发送方和接收方的成对的两个 Socket 之间必须建立连接, 以便在 TCP 协议的基础上进行通信, 当一个 Socket( 通常都是 Server Socket) 等待建立连接时, 另一个 Socket 可以要求进行连接, 一旦这两个 Socket 连接起来, 它们就可以进行双向数据传输, 双方都可以进行发送或接收操作 UDP 是 User Datagram Protocol 的简称, 是一种无连接的协议, 每个数据报都是一个独立的信息, 包括完整的源地址或目的地址, 它在网络上以任何可能的路径传往目的地, 因此能否

271 项目 10 Java 网络编程 271 到达目的地, 到达目的地的时间以及内容的正确性都是不能被保证的 下面我们对这两种协议做简单比较 : 使用 UDP 时, 每个数据报中都给出了完整的地址信息, 因此无需建立发送方和接收方的连接 对于 TCP 协议, 由于它是一个面向连接的协议, 在 Socket 之间进行数据传输之前必然要建立连接, 所以在 TCP 中多了一个连接建立的时间 使用 UDP 传输数据时是有大小限制的, 每个被传输的数据报必须限定在 64KB 之内 而 TCP 没有这方面的限制, 一旦连接建立起来, 双方的 Socket 就可以按统一的格式传输大量的数据 UDP 是一个不可靠的协议, 发送方所发送的数据报并不一定以相同的次序到达接收方 而 TCP 是一个可靠的协议, 它确保接收方完全正确地获取发送方所发送的全部数据 总之,TCP 在网络通信上有极强的生命力, 例如远程连接 (Telnet) 和文件传输 (FTP) 都需要不定长度的数据被可靠地传输 相比之下 UDP 操作简单, 而且仅需要较少的监护, 因此通常用于局域网高可靠性的分散系统中 Client/Server 应用程序 读者可能要问, 既然有了保证可靠传输的 TCP 协议, 为什么还要非可靠传输的 UDP 协议呢? 主要的原因有两个 一是可靠的传输是要付出代价的, 对数据内容正确性的检验必然占用计算机的处理时间和网络的带宽, 因此 TCP 传输的效率不如 UDP 高 二是在许多应用中并不需要保证严格的传输可靠性, 比如视频会议系统, 并不要求音频视频数据绝对的正确, 只要保证连贯性就可以了, 这种情况下显然使用 UDP 会更合理一些 习题 1. 名词解释 :TCP UDP IP 地址 端口号 URL 套接字 2. 客户 / 服务器模式有什么特点? 任务 2 URL 类和 InetAddress 类 URL(Uniform Resource Locator) 是统一资源定位符的简称, 它表示 Internet 上某一资源 的地址 具体来说,URL 就像一个指针, 指向 Web 上的 Web 页 二进制文件以及其他的信息 对象 当向浏览器地址栏输入一个网址时, 比如 实际上就是提供了该 站点主页的 URL 浏览器通过解析给定的 URL 就可以在网络上查找相应的文件或其他资源 Internet 上的资源主要包括 HTML 文件 图像文件 声音文件 动画文件以及其他内容 构如下 : URL 基础知识 一个 URL 包括两部分内容 : 协议名称和资源名称, 中间用冒号隔开 URL 的规范格式结 protocol://host_name:port_number/file_name/reference 说明 : (1)protocol( 传输协议 ): 指示所要获取资源的应用层协议, 如 :Http Ftp File 等 (2)host_name( 主机名称 ): 指示资源所在主机的名称或主机的 IP 地址, 如 com.cn 就是新浪网的主机名 (3)port_number( 端口号 ): 这是个可选项, 指示连接时所使用的服务器端口号 如 HTTP

272 272 Java 程序设计 的端口号是 80, 浏览器可以根据使用的协议自动识别出对应的端口号, 通常省略 比如新浪网的 URL 也可以写成 (4)file_name( 文件名 ): 指示资源在主机上的完整名字 ( 包括文件的完整路径名 ) (5)reference( 参考点 ): 指示资源中的特定位置, 用来标识一个文件中特定的偏移位置 实际上, 绝大部分 URL 地址的资源名称只包括主机名称和文件名 下面是几个合法的 URL 表示 : 协议名 :// 主机名 协议名 :// 机器名 + 文件名 协议名 :// 机器名 + 端口号 + 文件名 + 内部引用总之,URL 是最为直观的一种网络定位方法 使用 URL 符合人们的语言习惯, 容易记忆, 所以应用十分广泛, 而且采用 URL 还可以获得域名解析服务 使用 URL 进行网络编程, 不需要对协议本身有太多的了解 URL 类 1.URL 类构造方法 为了表示 URL,java.net 中实现了类 URL 我们可以通过下面的构造方法来初始化一个 URL 对象 : public URL (String spec): 通过一个表示 URL 地址的字符串可以构造一个 URL 对象 例如 : URL urlbase=new URL(" 263.net/") ; public URL(URL context, String spec): 通过基 URL 和相对 URL 构造一个 URL 对象 例如 : URL net263=new URL (" URL index263=new URL(net263, "index.html") public URL(String protocol, String host, String file): 根据指定 protocol host 和 file 创 建 URL 对象 例如 : new URL("http", " "/pages/gamelan.net. html"); public URL(String protocol, String host, int port, String file): 根据指定 protocol host port 号和 file 创建 URL 对象 例如 : URL gamelan=new URL("http", " 80, "Pages/Gamelan.network.html"); 注意 : 类 URL 的构造方法都声明抛出非运行时例外 (MalformedURLException), 因此生 成 URL 对象时, 我们必须要对这一例外进行处理, 通常是用 try-catch 语句进行捕获 2.URL 类常用方法 public String getprotocol(): 获取该 URL 的协议名 public String gethost(): 获取该 URL 的主机名 public int getport(): 获取该 URL 的端口号, 如果没有设置端口, 返回 -1 public String getfile(): 获取该 URL 的文件名 public String getref(): 获取该 URL 在文件中的相对位置

273 项目 10 Java 网络编程 273 public String getquery(): 获取该 URL 的查询信息 public String getpath(): 获取该 URL 的路径 public String getauthority(): 获取该 URL 的权限信息 public String getuserinfo(): 获得使用者的信息 public String getref(): 获得该 URL 的锚 例 10.1 通过 URL 获取网络站点的属性信息 import java.net.*; import java.io.*; public class GetURL public static void main(string args[]) throws Exception URL myurl=new URL(" try URL url=new URL(myURL,"/outpage.aspx?id=727"); // 输出该 URL 对象的协议名 System.out.println("protocol="+url.getProtocol()); // 输出该 URL 对象所连接的主机名称 System.out.println("host="+url.getHost()); // 输出该 URL 对象使用的端口号 System.out.println("port="+url.getPort()); // 输出该 URL 的获取文件的路径 System.out.println("path="+url.getPath()); // 输出该 URL 对象所连接主机中对应的文件 System.out.println("filename="+url.getFile()); // 输出该 URL 在文件中的相对位置 System.out.println("ref="+url.getRef()); // 输出该 URL 的查询信息 System.out.println("query="+url.getQuery()); // 输出使用者的信息 System.out.println("Userinfo="+url.getUserInfo()); System.out.println ("Authority="+url.getAuthority()); catch(malformedurlexception e) System.out.println("MalformedURLException:"+e); 程序运行结果为 : protocol=http host= port=8080 path=/outpage.aspx filename=/outpage.aspx?id=727 ref=null query=id=727 Userinfo=null Authority=

274 274 Java 程序设计 3.URL 读取 WWW 网络资源 在 URL 对象创建之后, 表示该 URL 地址是合理的 可访问的, 那么下一步要做的就是 把该 URL 所指的 Web 页面取回来, 这就用到 URL 类的一些实例方法, 如下所示 : public final InputStream openstream() 该方法是获取来自指定 URL 对象的输入流 有了该输入流, 可采用流的读操作获得该 URL 对象的具体内容 例 10.2 打印网易 Web 主页的过程 import java.io.*; import java.net.*; public class ReadURL1 public ReadURL1() public static void main(string args[]) URL url=null; InputStream in=null; try System.out.println("Creating URL "); url=new URL(" 根据所指定的地址实例化一个 URL 对象 System.out.println("Opening Stream "); in=url.openstream();// 打开该 URL 对象的输入流 InputStreamReader isr=new InputStreamReader(in); BufferedReader buffer=new BufferedReader(isr);// 将输入流包装进 BufferedReader 流中, // 以便能执行读取 System.out.println("Reading data "); String lineofdata; while((lineofdata=buffer.readline())!=null)// 通过一循环语句将输入流中内容读出并打印 System.out.println("LINE:"+lineOfData); catch(malformedurlexception e) System.out.println("Bad URL"); catch(ioexception e)system.out.println("io Error"+e.getMessage()); finally if(in!=null) try in.close(); System.out.println("Stream Closed."); catch (IOException e) System.exit (0); 程序的执行结果是将网易的首页打印出来, 限于篇幅, 在此不一一列出 URLConnection 类 Java 语言的 java.net.urlconnection 类与 java.net.url 类一样使得编程者能方便地利用 URL 在 Internet 上进行网络通信 但通过 URLConnection 类, 则可以在应用程序和 URL 资源

275 项目 10 Java 网络编程 275 之间进行交互, 既可以从 URL 中读取数据, 也可以向 URL 中发送数据 也就是说, URLConnection 类封装了应用程序和 URL 资源之间的通信连接 URL 和 URLConnection 的区别在于前者代表一个资源的位置, 后者代表一种连接 URLConnection 类是一个抽象公有类, 它提供了与远程对象进行通信和获取远程 URL 资源的方法 表 10-1 列出了 URLConnection 类的部分方法 表 10-1 URLConnection 类的部分方法 方法名 URLConnection openconnection() String getcontenttype() long getlastmodified() int getcontentlength() InputStream getinputstream() OutputStream getoutputstream() 说明返回与 URL 对象管理的 URLConnection 对象返回内容类型返回对象的最后修改时间返回内容长度返回该 URL 资源的字节输入流返回该 URL 资源的字节输出流 由于 URLConnection 类不能被直接实例化, 故可以通过 URL 类提供的 openconnection() 方法生成相应的实例对象, 从而获得一个 URLConnection 对象 格式如下 : public URLConnection openconnection(); 例 10.3 使用 URLConnction 类获取网站页面信息与 HTML 文本 import java.net.*; import java.io.*; import java.util.date; class URLConnect public static void main(string args[]) throws Exception int c; URL urlobj=new URL(" 创建一 URL 对象 URLConnection urlcon=urlobj.openconnection();// 获取 URLConnection 对象 System.out.println("Date:" + new Date(urlCon.getDate()));// 输出文件的属性 System.out.println("Content-Type:"+urlCon.getContentType());// 输出内容类型 System.out.println("Expires:" + urlcon.getexpiration());// 输出 expires 头字段的值 System.out.println("Last-Modified:"+new Date(urlCon.getLastModified()));// 输出最后修改时间 int len=urlcon.getcontentlength(); System.out.println("Content-Length:" + len);// 输出内容长度 if(len>0) System.out.println("***** 网页内容 *****"); InputStream input=urlcon.getinputstream(); int i=len; while(((c=input.read())!=-1) && (--i>0)) System.out.print((char)c); input.close();// 输出完毕, 关闭输入流 else// 如果连接失败, 输出对象不存在 System.out.println ("No Content Avaiable");

276 276 Java 程序设计 程序运行后部分内容如下 : Date:Mon Sep 05 16:34:52 CST 2016 Content-Type:text/html Expires: Last-Modified:Mon Sep 05 01:39:16 CST 2016 Content-Length:19923 ***** 网页内容 *****???<HTML><HEAD><TITLE>è?????????????é??</TITLE> InetAddress 类 InetAddress 类面向网络层, 用于标识网络上的硬件资源, 获得目标主机的 IP 地址 它提 供了一系列描述 获取及使用网络资源的方法, 这些方法通常会产生 UnknownHostException 异常, 应在程序中进行捕获处理 1.InetAddress 对象创建 由于 InetAddress 类没有构造函数, 因此不能用 new 来构造一个 InetAddress 实例, 一般通 过引用其静态方法而获得实际的 InetAddress 对象, 所采用的方法有 : public static InetAddress[] getallbyname(string host) : 返回指定主机名的所有 InetAddress 对象 参数 host 表示指定的主机 返回值存放于 InetAddress[] 数组中 public static InetAddress getbyaddress(byte[] addr): 根据给定的 IP 地址创建一个 InetAddress 对象, 可用来查找该 IP 对应的主机名 public static InetAddress getbyname(string host) : 根据给定的主机名创建一个 InetAddress 对象, 可用来查找该主机的 IP 地址 public static InetAddress GetLocalHost (): 返回当地主机的 InetAddress 对象 当引用上述方法时,Java 虚拟机根据所给定的参数进行 IP 地址或域名的解析工作, 若所 指定的 IP 地址或域名不存在, 则抛出 UnknownHostException 异常 2.InetAddress 类常用方法 在得到 InetAddress 对象之后, 就可以引用其实例方法实现地址类型的判断 地址解析等 功能, 所用到的方法有 : public String gethostaddress(): 得到 IP 地址 public String gethostname(): 得到主机名 ( 即域名 ) public boolean ismulticastaddress(): 判断是否组播地址 public boolean isloopbackaddress(): 判断是否回送地址 例 10.4 用 InetAddress 类方法获取本地地址 import java.net.*; public class GetLocalHostIP public static void main(string args[]) InetAddress localhost=null;

277 项目 10 Java 网络编程 277 程序运行结果为 : try// 通过类方法 getlocalhost 产生实例对象 localhost=inetaddress.getlocalhost(); catch(unknownhostexception ex) System.out.println(localHost);// 输出结果 lisuruo/ 例 10.5 用 InetAddress 类方法实现域名解析的功能 import java.net.*; public class DomainName public static void main(string args[]) try InetAddress address=inetaddress.getbyname(" String domain_name=address.gethostname();// 获取 address 所含的域名 String IP_name=address.getHostAddress(); // 获取 address 所含的 IP 地址 System.out.println(domain_name); System.out.println(IP_name); catch(unknownhostexception e) System.out.println(" 无法找到 程序运行结果为 : 习题 1. 选择题 (1) 若对 Web 页面进行操作, 一般会用到的类是 A.URL C.Socket (2)IP 地址或域名是由 类来表示的 A.URL C.NetworkInterface B.URLConnection D.DatagramSocket B.InetAddress D.Socket (3)URL 类的 gethost 方法的作用是 A. 返回主机的名字 B. 返回网络地址的端口 C. 返回文件名 D. 返回路径名 (4)URL 类的 getref 方法的作用是 A. 返回网页的特定地址 B. 返回主机的名字 C. 返回路径名 D. 返回协议的名字 2. 填空题 (1)URL 由协议标识符和资源名称组成, 资源名称又包含主机名 和文件名等

278 278 Java 程序设计 信息 (2) 与其他 Java 类不同的是, 类可表示 IP 地址及其所对应的域名, 其中没有定义构造方法, 一般通过引用其静态方法来实现对象的实例化 (3)URL 是 的简称, 它表示 Internet/Intranet 上的资源位置 这些资源可以是一个文件 一个 或一个 (4) 使用 URL 类可以简单方便地获取信息, 但是如果希望在获取信息的同时, 还能够向远方的计算机节点传送信息, 就需要使用另一个系统类库中的类 3. 简述并比较 URL 类的四种构造方法 任务 3 Socket 通信 Socket 编程是网络编程的核心内容, 最早关于网络编程的应用程序接口是 20 世纪 80 年代美国加州伯克利大学为支持 UNIX 操作系统上的 TCP/IP 应用而开发的 后来的网络编程接口都是在此基础上改进得到的 Socket 通信的结构网络上的两个程序通过一个双向的通讯连接实现数据的交换, 这个双向链路的一端称为一个 Socket Socket 通常用来实现客户方和服务方的连接 Socket 是 TCP/IP 协议的一个十分流行的编程界面, 一个 Socket 由一个 IP 地址和一个端口号唯一确定 一般的网络通信模式是客户机 / 服务器模式, 在 Java 应用程序中将 Socket 类和 ServerSocket 类分别用于客户端和服务器端, 在任意两台机器之间建立连接 在 java.net 包中提供的 Socket 类实现客户端的通信功能,ServerSocket 类实现服务器端的通信功能 当客户机和服务器连通后, 它们之间就建立了一种双向通信模式, 其工作过程如图 10-1 所示 Server Client 创建 ServerSocket 对象, 在某端口提供监听服务 等待来自 Client 端的服务请求 接收 Client 端的请求, 用返回的 Socket 建立连接 建立连接 创建 Socket 对象, 向 Server 端的监听端口请求连接 通过向 Socket 中读写数据与 Client 通信 数据通信 通过向新的 Socket 中读写数据与 Server 端通信 关闭 Socket, 结束与当前 Client 的通信, 等待其他请求 关闭 ServerSocket 对象, 结束监听任务 结束通信 关闭 Socket, 结束与 Server 端的通信 图 10-1 Socket 通信过程示意图

279 项目 10 Java 网络编程 279 对于一个功能齐全的 Socket, 都要包含以下基本结构, 其工作过程包含以下四个基本的步骤 : (1) 服务器端建立 ServerSocket 对象 服务器端在某个端口创建一个守护进程, 负责监听客户端请求并处于监听状态, 监听每个端口是否要求进行通信 (2) 客户端创建 Socket 对象, 包括连接的主机和端口号, 指定使用的通信协议, 向服务器端发出连接请求, 并试图与服务器建立连接 (3) 建立连接, 进行数据通信 服务器端监听到客户端的请求, 创建一个 Socket 接受连接对象, 与客户机进行通信 这样, 服务器端和客户端就建立了一个连接和一条传输数据的通道, 就可以利用 Socket 的输入 / 输出流, 按照一定的协议对 Socket 进行读 / 写操作 这一步是程序设计人员编程的主要工作所在 (4) 关闭输入 / 输出流和 Socket 套接字 通信结束, 拆除连接通道 服务器端套接字 ServerSocket 类用在服务器端, 它监听和响应客户端的 TCP 连接请求, 并接收客户端发送的数据 ServerSocket 类的主要任务是在服务器端耐心等候客户端与它进行连接, 一旦客户端程序申请建立一个套接字连接, ServerSocket 类就会通过 accept() 方法返回一个对应的服务器端套接字对象, 以进行直接通信 ServerSocket 类的构造方法如下 : public ServerSocket(): 创建非绑定服务器套接字 public ServerSocket(int port): 创建绑定到特定端口的服务器套接字 public ServerSocket(int port, int backlog): 利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号 参数 backlog 指定客户连接请求队列的长度 public ServerSocket(int port, int backlog, InetAddress bindaddr): 利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号和 IP 地址 该构造方法适用于具有多个 IP 地址的主机 参数 bindaddr 表示要将服务器绑定到的 IP 地址 ServerSocket 类常用方法如下 : public Socket accept(): 侦听并接收到此 ServerSocket 的连接, 此方法在连接传入之前一直阻塞 public void close(): 使服务器释放占用的资源, 并断开所有与客户端的连接 public InetAddress getinetaddress(): 返回当前服务器绑定的 IP 地址信息 注意 : 这些方法都将抛出 IOException 异常, 在程序中需要捕获处理 客户端套接字 Socket 类用在客户端 用户通过在客户端构造一个 Socket 类来建立与服务器的连接 Socket 连接可以是数据流连接, 也可以是数据报连接, 它取决于构造 Socket 类时使用的构造方法 一般使用数据流连接, 数据流连接的优点是所有数据都能准确 有序地送到接收方, 缺点是速度较慢 Socket 类的构造方法有如下几种 : public Socket(String host, int port): 创建一个流套接字并将其连接到指定主机上的指定

280 280 Java 程序设计 端口号 public Socket(InetAddress address, int port): 创建一个流套接字并将其连接到指定 IP 地址的指定端口号 address 参数指定 IP 地址 public Socket(String host, int port, InetAddress localaddr, int localport): 创建一个套接字并将其连接到指定远程主机的指定远程端口 参数 localaddr 和 localport 用来设置客户端的 IP 地址和端口 public Socket(InetAddress address, int port, InetAddress localaddr, int localport): 创建一个套接字并将其连接到指定远程地址的指定远程端口 参数 localaddr 和 localport 用来设置客户端的 IP 地址和端口 public Socket(String host,int port,boolean stream): 创建一个套接字并将其连接到指定主机上的指定端口号 参数 stream 为 true, 则创建流套接字, 否则创建数据报套接字 public Socket(InetAddress address,int port,boolean stream): 创建一个套接字并将其连接到指定 IP 地址的指定端口号 参数 stream 为 true, 则创建流套接字, 否则创建数据报套接字 Socket 类常用的方法如下 : public InetAddress getinetaddress(): 返回远程服务端的 IP 地址 public int getport(): 返回远程服务端的端口 public InetAddress getlocaladdress(): 返回本地客户端的 IP 地址 public int getlocalport(): 返回本地客户端的端口 public InputStream getinputstream(): 从 Socket 中获取输入流 public OutputStream getoutputstream(): 从 Socket 中获得输出流 public void close(): 关闭 Scoket 注意 : 这些方法都将抛出 IOException 异常, 在程序中需要捕获处理 Socket TCP 编程图 10-2 对采用 ServerSocket 类和 Socket 类所建立的客户 / 服务器模型进行了简单的描述 一般 Socket 编程步骤如下 : Socket 客户端 : (1)Socket s = new Socket(ip,port);// 打开一个套接字, 发送请求 (2)InputStream istream = s.getinputstream();// 接收数据 (3)OutputStream ostream = s.getoutputstream();// 发送数据 (4)istream.close();ostrem.close();s.close();// 关闭输入输出流和 Socket Socket 服务器 : (1)ServerSocket server = new ServerSocket(port);// 创建一个端口 (2)Socket s = server.accept();// 只有当客户端有请求并连接, 函数才会返回 (3)InputStream istream = s.getinputstream();// 接收数据 (4)OutputStream ostream = s.getoutputstream();// 发送数据 (5)istream.close();ostrem.close();s.close();// 关闭输入输出流和 Socket

281 项目 10 Java 网络编程 281 向操作系统注册服务 服务器进程 返回 客户端进程 从队列取出 Socket 或者等待客户端请求 发起与服务端的请求 交换数据 断开连接 断开连接 图 10-2 ServerSocket 类和 Socket 类对客户 / 服务器模型的描述 例 10.6 采用 Socket 编程实现客户端与服务器的通信 (1) 客户端程序 import java.io.*; import java.net.*; public class ChatClient public static void main(string args[]) try Socket socket=new Socket(" ",4000);// 创建 Socket, 服务器是本地, 端口 :4000 System.out.println(" 输入你要说的话, 如果要退出请输入 bye"); // 由系统标准输入设备构造 BufferedReader 对象 BufferedReader sin=new BufferedReader(new InputStreamReader(System.in)); // 由 Socket 对象得到输出流, 并构造 PrintWrter 对象 PrintWriter os=new PrintWriter(socket.getOutputStream()); BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream())); String readline; readline=sin.readline();// 从系统标准输入读入一字符串 while (!readline.equals("bye")) // 将从系统标准输入读入的字符串输出到 Server os.println(readline); os.flush();// 刷新输出流, 使服务器端马上收到该字符串 System.out.println("Client:"+readline); // 从服务器端读入一字符串, 并输出到显示器上 System.out.println("Server:"+is.readLine()); readline=sin.readline();// 从系统标准输入读入一字符串 // 循环结束 os.close();// 关闭 Socket 输出流 is.close();// 关闭 Socket 输入流 socket.close();// 关闭 Socket catch (Exception e) System.out.println ("Error" +e) ;// 出错, 则输出出错信息

282 282 Java 程序设计 (2) 服务器端程序 import java.net.*; import java.applet.applet; public class ChatServer public static void main (String args[]) try ServerSocket server=null; try server=new ServerSocket(4000);// 创建 ServerSocket, 端口号 :40000 System.out.println(" 准备好了 如果要退出请输入 bye"); catch (Exception e) System.out.println("can not listen to:"+e);// 服务器启动异常 Socket socket=null; try socket=server.accept();// 接受客户端连接请求 catch (Exception e) System.out.println("Error."+e);// 接受客户端连接请求失败 String line;// 由 Socket 对象得到输入流, 并构造相应的 BufferedReader 对象 BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream())); // 由 Socket 对象得到输出流, 并构造 PrintWriter 对象 PrintWriter os=new PrintWriter(socket.getOutputStream()); // 由系统标准输入设备构造 BufferedReader 对象 BufferedReader sin=new BufferedReader(new InputStreamReader(System.in)); // 在标准输出上输出从客户端读入的字符串 System.out.println("Client:"+is.readLine()); line=sin.readline();// 从标准输入读入一字符串 while (!line.equals("bye"))// 如果该字符串为 bye, 则停止循环 os.println(line);// 向客户端输出该字符串 os.flush();// 刷新输出流, 使 Client 马上收到该字符串 System.out.println("Server:"+line); // 从 Client 读入一字符串, 并输出到标准输出上 System.out.println("Client:"+is.readLine()); line=sin.readline();// 从系统标准输入读入一字符串 // 继续循环 os.close();// 关闭 Socket 输出流 is.close();// 关闭 Socket 输入流 socket.close();// 关闭 Socket server.close();// 关闭 SererSocket catch (Exception e) System.out.println ("Error:" + e);

283 项目 10 Java 网络编程 283 本程序由服务器端程序和客户端程序两部分组成 编译服务器端和客户端程序时可以不 分先后顺序, 但在运行服务器端和客户端程序时, 应首先启动 Server 端, 然后启动 Client 端 在 Client 端输入字符后, 在 Server 端可以显示 ; 在 Server 端输入信息后, 在 Client 端也可以 显示 结束通信时, 先在 Client 端输入 bye, 再在 Server 端输入 bye 如果要在同一台计算机上演示本程序时, 可以先打开一个 MS-DOS 窗口, 运行服务器端 程序, 不要关闭 再打开另一个 MS-DOS 窗口, 运行客户端程序 运行时, 按照提示输入内 容即可 图 10-3 所示为客户端运行情况, 图 10-4 所示为服务器端运行情况 图 10-3 客户端运行情况 图 10-4 服务器运行情况 Socket 多线程编程 在例 10.6 服务器程序的设计中, 服务器对客户请求采取一种顺序处理的方式 即当有多 个客户同时向该服务器发出请求时, 服务器程序是一个一个轮流处理的, 若服务器程序对每个 请求有较为复杂的处理, 就会导致某些客户有较长的等待时间 那么如何才能提高服务器程序 的处理效率? 比较切实的解决办法是在服务器程序中采用多线程的设计, 即当有客户的 Socket 连接到达则为其创建一个线程, 由该线程完成客户的请求工作 例 10.7 用 Socket 实现的基于多线程的客户端与服务器的通信 (1) 客户端程序 import java.io.*; import java.net.*; public class MultiTalkClient public static void main(string args[]) try Socket socket = new Socket(" ",2000);// 向本机的 2000 端口发出客户请求 // 由系统标准输入设备构造 BufferedReader 对象 BufferedReader sin =new BufferedReader(new InputStreamReader(System.in)); // 由 Socket 对象得到输出流, 并构造 PrintWriter 对象 PrintWriter os = new PrintWriter(socket.getOutputStream()); // 由 Socket 对象得到输入流, 并构造相应的 BufferedReader 对象 BufferedReader is = new BufferedReader(new InputStreamReader(socket.getInputStream())); String readline; readline=sin.readline(); // 从系统标准输入读入一字符串 while(!readline.equals("bye")) // 判断若从标准输入读入的字符串为 bye 则停止循环

284 284 Java 程序设计 os.println(readline); // 将从系统标准输入读入的字符串输出到 Server os.flush(); // 刷新输出流, 使 Server 马上收到该字符串 // 在系统标准输出上打印读入的字符串 System.out.println("Client:" + readline); // 在系统标准输出上打印读入的字符串 System.out.println("Server:" + is.readline()); // 从 Server 读入一字符串, 并打印到标准输出上 readline = sin.readline(); // 从系统标准输入读入一字符串, 然后继续循环 os.close(); // 关闭 Socket 输出流 is.close(); // 关闭 Socket 输入流 socket.close(); catch (Exception e) System.out.println("Error" + e);// 如果出错, 第 34 行为打印出错信息 (2) 服务器端程序 import java.io.*; import java.net.*; class ServerThread1 extends Thread Socket socket = null; // 定义变量 socket 保存与本线程相关的 Socket 对象 int clientnum; // 定义变量 clientnum 保存本进程的客户计数 public ServerThread1(Socket socket,int num) // 构造函数 ServerThread1 this.socket=socket; // 初始化 socket 变量 clientnum=num+1; // 对变量 clientnum 求和, 从而对客户端人数进行计数 public void run() try String line; BufferedReader is = new BufferedReader(new InputStreamReader(socket.getInputStream())); // 由 Socket 对象得到输入流, 并构造相应的 BufferedReader 对象 PrintWriter os = new PrintWriter(socket.getOutputStream()); // 由 Socket 对象得到输出流, 并构造 PrintWriter 对象 BufferedReader sin = new BufferedReader(new InputStreamReader(System.in)); // 由系统标准输出上打印从客户端读入的字符串 System.out.println("Client:" + clientnum + is.readline()); line = sin.readline();

285 项目 10 Java 网络编程 285 // 从标准输入读入一字符串 while(!line.equals("bye")) // 判断如果输入为字符串 bye, 就停止循环 os.println(line); // 向客户端输出该字符串 os.flush(); // 刷新输出流, 使 Client 马上收到该字符串 System.out.println("Server:" + line); // 在系统标准输出上打印该字符串 System.out.println("Client:" + clientnum + is.readline()); // 从 Client 读入一字符串, 并打印到标准输出上 line=sin.readline(); // 从系统标准输入读入一字符串, 然后继续循环 os.close(); // 关闭 Socket 输出流 is.close(); // 关闭 Socket 输入流 socket.close(); catch(exception e) System.out.println("Error:" + e); // 如果出错, 第 30 行打印出错信息 public class MultiTalkServer static int clientnum = 0; // 定义静态成员变量, 记录当前客户的个数 public static void main(string args[]) throws IOException ServerSocket serversocket=null; boolean listening=true; try serversocket=new ServerSocket(2000); // 创建一个 ServerSocket 在端口 2000 监听客户请求, 如果出错, 通过第 11 行打印出错信息 catch(ioexception e) System.out.println("Could not listen on port:2000"); System.exit(-1); // 退出监听 while(listening)// 如果没错误, 第 58 行为永远循环监听 new ServerThread1(serverSocket.accept(),clientnum).start(); // 监听到客户请求, 根据得到的 Socket 对象和客户计数创建服务线程, 并启动线程 clientnum++; // 增加客户计数 serversocket.close(); // 关闭 ServerSocket 程序执行需要打开多个 DOS 窗口, 一个窗口中启动服务器程序, 另外每个客户端在不同 的窗口中启动 这里演示的是两个客户端的例子, 客户端可以有多个 客户端 1 和客户端 2 运行结果如图 10-5 和图 10-6 所示, 服务器端运行结果如图 10-7 所示

286 286 Java 程序设计 图 10-5 客户端 1 运行情况图 10-6 客户端 2 运行情况图 10-7 服务器端运行情况 Socket UDP 编程数据报 ( 或称数据包 ) 是以 UDP 为通信协议的一种通信方式, 它为两台计算机之间提供一种非可靠的无连接投递报文的通信服务, 由于这种通信方式不建立连接, 所以它不能保证所有的数据都能准确 有序地送到目的地, 因此数据报服务一般用于传送非关键性的数据 UDP 协议是无连接的协议, 它以数据报作为数据传输的载体 数据报是一个在网络上发送的独立信息, 它的到达 到达时间以及内容本身等都不能得到保证 数据报的优点是通信速度比较快 在要求较快的数据传输速度而又对信息质量要求不高的情况下, 使用 UDP 协议进行通信更为合适 比如在需要很强的实时交互性的场合, 像网络游戏 视频会议等, 在计算机网络日益稳定的今天,UDP 通信越来越多地为人们所重视 1.DatagramPacket 类和 DatagramSocket 类在 Java 的网络 API 中与 UDP 数据报相关的类是 DatagramPacket 类和 DatagramSocket 类, 前者用于创建一个发送或接收的数据报对象 ( 类似信件 ), 后者是创建一个可用来发送或接收数据报的 Socket 对象 ( 类似邮局 ) DatagramPacket 类的常用构造方法 : public DatagramPacket(byte[] buf,int length): 表示创建一个指定长度的接收数据报对象 public DatagramPacket(byte[] buf,int length,inetaddress address,int port): 创建一个指向指定主机和指定端口发送的数据报对象 DatagramPacket 类常用方法 : public byte[] getdata(): 获取数据报中所包含的数据 public InetAddress getaddress(): 得到数据报中所包含的 IP 地址, 即是该数据报从该 IP 地址发送 public int getport(): 得到数据报中所包含的端口, 即是该数据报从该端口发送 DatagramSocket 类的作用主要是对 DatagramPacket 对象进行接收和发送, 其构造方法常有 : public DatagramSocket(): 创建 DatagramSocket 对象, 该对象与所在主机的任一个可用端口绑定, 该方法常用于发送数据报 public DatagramSocket(int port): 创建一个绑定于指定端口的 DatagramSocket 对象, 该方法常用于接收数据报 DatagramSocket 类的方法主要有 : public void receive(datagrampacket p): 调用 Socket 对象的接收方法接收数据报 public void send(datagrampacket p): 调用 Socket 对象的发送方法发送数据报 注意 : 这些方法都将抛出 IOException 异常, 在程序中需要捕获处理

287 项目 10 Java 网络编程 UDP 编程步骤 UDP 服务器要执行以下三步 : (1) 创建一个 DatagramSocket 实例, 指定本地端口号, 并可以选择指定本地地址 此时, 服务器已经准备好从任何客户端接收数据报文 (2) 使用 DatagramSocket 类的 receive() 方法来接收一个 DatagramPacket 实例 当 receive() 方法返回时, 数据报文就包含了客户端的地址与端口, 这样我们就知道回复信息该发送到什么地方 (3) 使用 DatagramSocket 类的 send() 和 receive() 方法发送和接收 DatagramPacket 实例, 进行通信 UDP 客户端首先向被动等待联系的服务器端发送一个数据报文 一个典型的 UDP 客户端主要执行以下三步 : (1) 创建一个 DatagramSocket 实例, 可以选择对本地地址和端口号进行设置 (2) 使用 DatagramSocket 类的 send() 和 receive() 方法来发送和接收 DatagramPacket 实例, 进行通信 (3) 通信完成后, 使用 DatagramSocket 类的 close() 方法来销毁该套接字 根据上述步骤, 将数据报的接收和发送流程做一个简单的总结, 如图 10-8 所示 3.UDP 编程实例 图 10-8 数据报的发送和接收流程 例 10.8 利用 UDP 进行客户机与服务器相互通信 (1) 主机 1: 上海 import java.net.*; import java.awt.*; import java.awt.event.*; class Shanghai_Frame extends Frame implements Runnable,ActionListener TextField out_message=new TextField(" 发送数据到北京 :"); TextArea in_message=new TextArea(); Button b=new Button(" 发送数据包到北京 ");

288 288 Java 程序设计 byte data[]=new byte[8192]; DatagramPacket pack=null; Shanghai_Frame() super(" 我是上海 "); setsize(200,200); setvisible(true); b.addactionlistener(this); add(out_message,"south"); add(in_message,"center"); add(b,"north"); pack=new DatagramPacket(data,data.length); Thread thread=new Thread(this); thread.start();// 线程负责接收数据包 // 点击按钮发送数据包 public void actionperformed(actionevent event) byte buffer[]=out_message.gettext().trim().getbytes(); try InetAddress address=inetaddress.getbyname("localhost"); // 数据包的目标端口是 888( 那么收方 ( 北京 ) 需在这个端口接收 ) DatagramPacket data_pack=new DatagramPacket(buffer,buffer.length, address,888); DatagramSocket mail_data=new DatagramSocket(); in_message.append(" 数据报目标主机地址 :"+data_pack.getaddress()+"\n"); in_message.append(" 数据报目标端口是 :"+data_pack.getport()+"\n"); in_message.append(" 数据报长度 :"+data_pack.getlength()+"\n"); mail_data.send(data_pack); catch(exception e) // 接收数据包 public void run() DatagramSocket mail_data=null; try// 使用端口 666 来接收数据包 ( 因为北京发来的数据报的目标端口是 666) mail_data=new DatagramSocket(666); catch(exception e) while(true) if(mail_data==null)break; else try mail_data.receive(pack); int length=pack.getlength(); // 获取收到的数据的实际长度 InetAddress adress=pack.getaddress();// 获取收到的数据包的始发地址 int port=pack.getport();// 获取收到的数据包的始发端口 String message=new String(pack.getData(),0,length);// 获取收到的数据包中的数据 in_message.append(" 收到数据长度 :"+length+"\n"); in_message.append(" 收到数据来自 :"+adress+" 端口 :"+port+"\n"); in_message.append(" 收到数据是 :"+message+"\n");

289 项目 10 Java 网络编程 289 catch(exception e) public class Shanghai public static void main(string args[]) Shanghai_Frame shanghai_win=new Shanghai_Frame(); shanghai_win.addwindowlistener(new WindowAdapter() public void windowclosing(windowevent e)system.exit(0);); shanghai_win.pack(); (2) 主机 2: 北京 import java.net.*; import java.awt.*; import java.awt.event.*; class Beijing_Frame extends Frame implements Runnable,ActionListener TextField out_message=new TextField(" 发送数据到上海 :"); TextArea in_message=new TextArea(); Button b=new Button(" 发送数据包到上海 "); byte data[]=new byte[8192]; DatagramPacket pack=null; Beijing_Frame() super(" 我是北京 "); setsize(200,200); setvisible(true); b.addactionlistener(this); add(out_message,"south"); add(in_message,"center"); add(b,"north"); pack=new DatagramPacket(data,data.length);// 用来收取数据的数据包 Thread thread=new Thread(this); thread.start();// 线程负责接收数据包 // 点击按钮发送数据包 public void actionperformed(actionevent event) byte buffer[]=out_message.gettext().trim().getbytes(); try InetAddress address=inetaddress.getbyname("localhost"); // 数据包的目标端口是 666( 那么收方 ( 上海 ) 需在这个端口接收 ) DatagramPacket data_pack=new DatagramPacket(buffer,buffer.length, address,666); DatagramSocket mail_data=new DatagramSocket(); in_message.append(" 数据报目标主机地址 :"+data_pack.getaddress()+"\n"); in_message.append(" 数据报目标端口是 :"+data_pack.getport()+"\n"); in_message.append(" 数据报长度 :"+data_pack.getlength()+"\n"); mail_data.send(data_pack);

290 290 Java 程序设计 catch(exception e) public void run() DatagramSocket mail_data=null; try// 使用端口 888 来接收数据包 ( 因为上海发来的数据报的目标端口是 888) mail_data=new DatagramSocket(888); catch(exception e) while(true) if(mail_data==null) break; else try mail_data.receive(pack); int length=pack.getlength(); // 获取收到的数据的实际长度 InetAddress adress=pack.getaddress();// 获取收到的数据包的始发地址 int port=pack.getport();// 获取收到的数据包的始发端口 String message=new String(pack.getData(),0,length);// 获取收到的数据包中的数据 in_message.append(" 收到数据长度 :"+length+"\n"); in_message.append(" 收到数据来自 :"+adress+" 端口 :"+port+"\n"); in_message.append(" 收到数据是 :"+message+"\n"); catch(exception e) public class Beijing public static void main(string args[]) Beijing_Frame beijing_win=new Beijing_Frame(); beijing_win.addwindowlistener(new WindowAdapter() public void windowclosing(windowevent e)system.exit(0);); beijing_win.pack(); 如果要在同一台计算机上演示本例中的程序, 可以先打开一个 MS-DOS 窗口, 运行主机 为北京的程序, 不要关闭, 然后再打开另一个 MS-DOS 窗口, 运行主机为上海的程序 当运行主机为北京的程序出现的标题栏是 我是北京 时, 删除最下方所显示的文本 发 送数据到上海 :, 然后再输入文本 北京欢迎您!, 最后单击最上方的按钮 发送数据包到上 海, 即可完成数据报的发送操作, 这时, 接收数据端 ( 上海 ) 将显示收到的相关数据信息, 包括数据长度 IP 地址 端口号和数据内容, 程序运行情况如图 10-9 所示 同样, 当运行主机为上海的程序出现的标题栏是 我是上海 时, 删除最下方所显示 的文本 发送数据到北京 :, 然后再输入文本 Welcome To Shanghai!, 最后单击最上方 的按钮 发送数据包到北京, 即可完成数据报的发送操作, 这时, 接收数据端 ( 北京 ) 将 显示收到的相关数据信息, 包括数据长度 IP 地址 端口号和数据内容, 程序运行情况如 图 所示

291 项目 10 Java 网络编程 291 图 10-9 北京端运行情况图 上海端运行情况 习题 1. 选择题 (1) 在套接字编程中, 客户方需用到 Java 类 来创建 TCP 连接 A.Socket B.URL C.ServerSocket D.DatagramSocket (2) 在套接字编程中, 服务器方需用到 Java 类 来监听端口 A.Socket B.URL C.ServerSocket D.DatagramSocket (3) 在 UDP 通信中, 数据报是由 类来创建的 A.Packet B.DatagramPacket C.DatagramSocket D.MulticastSocket (4) 建立客户端套接字的构造方法名是 A.Socket() B.ServerSocket() C.UrlSocket() D.UdpSocket() (5) 建立服务器端套接字的构造方法名是 A.Socket() B.ServerSocket() C.UrlSocket() D.UdpSocket() (6)UDP 数据报通信方式与 Socket 通信方式比较, 不需要传输层协议提供可靠的保证 A.UDP 方式 B.Socket 方式 C.UDP 和 Socket 方式 D. 以上都不对 (7)Socket 类的 getoutputstream 方法的作用是 A. 返回文件路径 B. 返回文件写出器 C. 返回文件大小 D. 返回文件读入器 (8)Socket 类的 getinputstream 方法的作用是 A. 返回文件路径 B. 返回文件写出器 C. 返回文件大小 D. 返回文件读入器 (9)DatagramSocket 类的 receive 方法的作用是 A. 根据网络地址接收数据包 B. 根据网络地址与端口接收数据包 C. 根据端口接收数据包 D. 根据网络地址与端口发送数据包

OOP with Java 通知 Project 4: 4 月 18 日晚 9 点 关于抄袭 没有分数

OOP with Java 通知 Project 4: 4 月 18 日晚 9 点 关于抄袭 没有分数 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 4: 4 月 18 日晚 9 点 关于抄袭 没有分数 复习 类的复用 组合 (composition): has-a 关系 class MyType { public int i; public double d; public char c; public void set(double

More information

エスポラージュ株式会社 住所 : 東京都江東区大島 東急ドエルアルス大島 HP: ******************* * 关于 Java 测试试题 ******

エスポラージュ株式会社 住所 : 東京都江東区大島 東急ドエルアルス大島 HP:  ******************* * 关于 Java 测试试题 ****** ******************* * 关于 Java 测试试题 ******************* 問 1 运行下面的程序, 选出一个正确的运行结果 public class Sample { public static void main(string[] args) { int[] test = { 1, 2, 3, 4, 5 ; for(int i = 1 ; i System.out.print(test[i]);

More information

OOP with Java 通知 Project 4: 4 月 19 日晚 9 点

OOP with Java 通知 Project 4: 4 月 19 日晚 9 点 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 4: 4 月 19 日晚 9 点 复习 类的复用 组合 (composition): has-a 关系 class MyType { public int i; public double d; public char c; public void set(double x) { d

More information

新・解きながら学ぶJava

新・解きながら学ぶJava 481! 41, 74!= 40, 270 " 4 % 23, 25 %% 121 %c 425 %d 121 %o 121 %x 121 & 199 && 48 ' 81, 425 ( ) 14, 17 ( ) 128 ( ) 183 * 23 */ 3, 390 ++ 79 ++ 80 += 93 + 22 + 23 + 279 + 14 + 124 + 7, 148, 16 -- 79 --

More information

Microsoft Word - 正文.doc

Microsoft Word - 正文.doc 第 2 章 Java 语言基础 通过本章的实践, 要掌握 Java 中的标识符 关键字 常量, 熟练掌握算术 关 系 逻辑 条件 赋值 位运算符的使用, 掌握简单顺序结构的程序设计 2.1 典型习题解答 2.1 Java 中怎样进行注释? 解答 Java 语言中的注释有 3 种形式 : (1) 单行 : // (2) 多行 : /* */ (3) 文档注释 : /** */ 第三种形式是第二种形式的变形,

More information

OOP with Java 通知 Project 2 提交时间 : 3 月 14 日晚 9 点 另一名助教 : 王桢 学习使用文本编辑器 学习使用 cmd: Power shell 阅读参考资料

OOP with Java 通知 Project 2 提交时间 : 3 月 14 日晚 9 点 另一名助教 : 王桢   学习使用文本编辑器 学习使用 cmd: Power shell 阅读参考资料 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 2 提交时间 : 3 月 14 日晚 9 点 另一名助教 : 王桢 Email: 51141201063@ecnu.cn 学习使用文本编辑器 学习使用 cmd: Power shell 阅读参考资料 OOP with Java Java 类型 引用 不可变类型 对象存储位置 作用域 OOP

More information

C++ 程序设计 告别 OJ1 - 参考答案 MASTER 2019 年 5 月 3 日 1

C++ 程序设计 告别 OJ1 - 参考答案 MASTER 2019 年 5 月 3 日 1 C++ 程序设计 告别 OJ1 - 参考答案 MASTER 2019 年 月 3 日 1 1 INPUTOUTPUT 1 InputOutput 题目描述 用 cin 输入你的姓名 ( 没有空格 ) 和年龄 ( 整数 ), 并用 cout 输出 输入输出符合以下范例 输入 master 999 输出 I am master, 999 years old. 注意 "," 后面有一个空格,"." 结束,

More information

软件工程文档编制

软件工程文档编制 实训抽象类 一 实训目标 掌握抽象类的定义 使用 掌握运行时多态 二 知识点 抽象类的语法格式如下 : public abstract class ClassName abstract void 方法名称 ( 参数 ); // 非抽象方法的实现代码 在使用抽象类时需要注意如下几点 : 1 抽象类不能被实例化, 实例化的工作应该交由它的子类来完成 2 抽象方法必须由子类来进行重写 3 只要包含一个抽象方法的抽象类,

More information

OOP with Java 通知 Project 4: 5 月 2 日晚 9 点

OOP with Java 通知 Project 4: 5 月 2 日晚 9 点 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 4: 5 月 2 日晚 9 点 复习 类的复用 组合 (composition): has-a 关系 class MyType { public int i; public double d; public char c; public void set(double x) { d =

More information

Java 1 Java String Date

Java 1 Java String Date JAVA SCJP Java 1 Java String Date 1Java 01 Java Java 1995 Java Java 21 Java Java 5 1-1 Java Java 1990 12 Patrick Naughton C++ C (Application Programming Interface API Library) Patrick Naughton NeXT Stealth

More information

2 Java 语 言 程 序 设 计 教 程 1.2.1 简 单 性 Java 语 言 的 语 法 与 C 语 言 和 C++ 语 言 很 接 近, 使 得 大 多 数 程 序 员 很 容 易 学 习 和 使 用 Java 另 一 方 面,Java 丢 弃 了 C++ 中 很 少 使 用 的 很 难

2 Java 语 言 程 序 设 计 教 程 1.2.1 简 单 性 Java 语 言 的 语 法 与 C 语 言 和 C++ 语 言 很 接 近, 使 得 大 多 数 程 序 员 很 容 易 学 习 和 使 用 Java 另 一 方 面,Java 丢 弃 了 C++ 中 很 少 使 用 的 很 难 第 1 章 Java 概 述 Java 的 诞 生 Java 的 特 点 Java 开 发 环 境 安 装 与 配 置 创 建 并 运 行 一 个 简 单 的 Java 程 序 Java 语 言 是 当 今 计 算 机 软 件 行 业 中 最 热 门 的 网 络 编 程 语 言, 以 Java 为 核 心 的 芯 片 技 术 编 译 技 术 数 据 库 连 接 技 术, 以 及 基 于 企 业 级

More information

1 下列类头定义中, 正确的是 面向对象程序设计网络课程 A class x { } B public x extends y { } C public class x extends y {.} D class x extends y implements y1 { } 2 现有两个类 A B,

1 下列类头定义中, 正确的是 面向对象程序设计网络课程 A class x { } B public x extends y { } C public class x extends y {.} D class x extends y implements y1 { } 2 现有两个类 A B, 1 下列类头定义中, 正确的是 A class x B public x extends y C public class x extends y. D class x extends y implements y1 2 现有两个类 A B, 以下描述中表示 B 继承自 A 的是 (D ) A) class A extends B B) class B implements A C) class A

More information

OOP with Java 通知 Project 3: 3 月 29 日晚 9 点 4 月 1 日上课

OOP with Java 通知 Project 3: 3 月 29 日晚 9 点 4 月 1 日上课 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 3: 3 月 29 日晚 9 点 4 月 1 日上课 复习 Java 包 创建包 : package 语句, 包结构与目录结构一致 使用包 : import restaurant/ - people/ - Cook.class - Waiter.class - tools/ - Fork.class

More information

C++ 程序设计 告别 OJ2 - 参考答案 MASTER 2019 年 5 月 3 日 1

C++ 程序设计 告别 OJ2 - 参考答案 MASTER 2019 年 5 月 3 日 1 C++ 程序设计 告别 OJ2 - 参考答案 MASTER 2019 年 5 月 3 日 1 1 TEMPLATE 1 Template 描述 使用模板函数求最大值 使用如下 main 函数对程序进行测试 int main() { double a, b; cin >> a >> b; cout c >> d; cout

More information

SDK 概要 使用 Maven 的用户可以从 Maven 库中搜索 "odps-sdk" 获取不同版本的 Java SDK: 包名 odps-sdk-core odps-sdk-commons odps-sdk-udf odps-sdk-mapred odps-sdk-graph 描述 ODPS 基

SDK 概要 使用 Maven 的用户可以从 Maven 库中搜索 odps-sdk 获取不同版本的 Java SDK: 包名 odps-sdk-core odps-sdk-commons odps-sdk-udf odps-sdk-mapred odps-sdk-graph 描述 ODPS 基 开放数据处理服务 ODPS SDK SDK 概要 使用 Maven 的用户可以从 Maven 库中搜索 "odps-sdk" 获取不同版本的 Java SDK: 包名 odps-sdk-core odps-sdk-commons odps-sdk-udf odps-sdk-mapred odps-sdk-graph 描述 ODPS 基础功能的主体接口, 搜索关键词 "odpssdk-core" 一些

More information

Guava学习之Resources

Guava学习之Resources Resources 提供提供操作 classpath 路径下所有资源的方法 除非另有说明, 否则类中所有方法的参数都不能为 null 虽然有些方法的参数是 URL 类型的, 但是这些方法实现通常不是以 HTTP 完成的 ; 同时这些资源也非 classpath 路径下的 下面两个函数都是根据资源的名称得到其绝对路径, 从函数里面可以看出,Resources 类中的 getresource 函数都是基于

More information

JAVA 单元 2.1 四则运算机 ( 一 ) 单元教学进度设计 教学环节 教学内容 教师学生活动活动 反馈 反馈课前作业完成情况 反馈加分 1. 下面哪些是合法的变量名? ( ) A.2variable 答案 :DEG B..variable2 解答 : C.._whatavariable A:/

JAVA 单元 2.1 四则运算机 ( 一 ) 单元教学进度设计 教学环节 教学内容 教师学生活动活动 反馈 反馈课前作业完成情况 反馈加分 1. 下面哪些是合法的变量名? ( ) A.2variable 答案 :DEG B..variable2 解答 : C.._whatavariable A:/ 单元 2.1 四则运算机 ( 一 ) 单元教学进度设计 教学环节 教学内容 教师学生活动活动 反馈 反馈课前作业完成情况 反馈加分 1. 下面哪些是合法的变量名? ( ) A.2variable 答案 :DEG B..variable2 解答 : C.._whatavariable A:// 不能以数字开头 D._3_ B:// 不能用点和空格 提问 抢答 E.$anothervar C: // 不能用点和空格

More information

OOP with Java 通知 Project 3 提交时间 3 月 29 日晚 9 点 Piazza Project 2 投票

OOP with Java 通知 Project 3 提交时间 3 月 29 日晚 9 点 Piazza Project 2 投票 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 3 提交时间 3 月 29 日晚 9 点 Piazza Project 2 投票 复习 创建对象 构造函数 函数重载 : 函数 = 函数名 + 参数列表 public class MyType { int i; double d; char c; void set(double x)

More information

Microsoft Word - 01.DOC

Microsoft Word - 01.DOC 第 1 章 JavaScript 简 介 JavaScript 是 NetScape 公 司 为 Navigator 浏 览 器 开 发 的, 是 写 在 HTML 文 件 中 的 一 种 脚 本 语 言, 能 实 现 网 页 内 容 的 交 互 显 示 当 用 户 在 客 户 端 显 示 该 网 页 时, 浏 览 器 就 会 执 行 JavaScript 程 序, 用 户 通 过 交 互 式 的

More information

Microsoft Word - 第3章.doc

Microsoft Word - 第3章.doc 第 3 章 lambda 表达式及其应用 lambda 表达式是 Java 8 提供的一种新特性, 它使得 Java 也能像 C# 和 C++ 语言一样进行简单的 函数式编程, 这不仅简化了某些通用结构的实现方式, 也大大增强了 Java 语言的表达功能 3.1 lambda 表达式简介 lambda 表达式是基于数学中的 λ 演算得名, 本质上就是一个没有方法名的匿名方法 例如, 有一个方法定义如下

More information

实验目的 (1) 熟练掌握顺序 分支 循环三种结构 (2) 会使用流程控制结构编写程序 第三章程序的流程控制 实验要求 (1) 掌握 if-else swith-case 的使用 (2) 掌握 while do-while for 的使用 (3) 掌握分支嵌套和循环嵌套 (4) 分析理解如何避免死循

实验目的 (1) 熟练掌握顺序 分支 循环三种结构 (2) 会使用流程控制结构编写程序 第三章程序的流程控制 实验要求 (1) 掌握 if-else swith-case 的使用 (2) 掌握 while do-while for 的使用 (3) 掌握分支嵌套和循环嵌套 (4) 分析理解如何避免死循 实验目的 (1) 熟练掌握顺序 分支 循环三种结构 (2) 会使用流程控制结构编写程序 第三章程序的流程控制 实验要求 (1) 掌握 if-else swith-case 的使用 (2) 掌握 while do-while for 的使用 (3) 掌握分支嵌套和循环嵌套 (4) 分析理解如何避免死循环 实验范例 系统常用类 : 字符串类 (String) a) 从字符串 s 中截取一个字符串方法 s.substring()

More information

untitled

untitled 1 Outline 數 料 數 數 列 亂數 練 數 數 數 來 數 數 來 數 料 利 料 來 數 A-Z a-z _ () 不 數 0-9 數 不 數 SCHOOL School school 數 讀 school_name schoolname 易 不 C# my name 7_eleven B&Q new C# (1) public protected private params override

More information

<4D F736F F D204A617661B3CCD0F2C9E8BCC6BBF9B4A1A3A8B5DA35B0E6A3A9CAB5D1E9D6B8B5BCD3EBCFB0CCE2BDE2B4F020B5DA33D5C22E646F63>

<4D F736F F D204A617661B3CCD0F2C9E8BCC6BBF9B4A1A3A8B5DA35B0E6A3A9CAB5D1E9D6B8B5BCD3EBCFB0CCE2BDE2B4F020B5DA33D5C22E646F63> 第 3 章 结构语句 本章知识点 : 流程控制语句是用来控制程序中各语句执行顺序的语句, 是程序中基本却又非常关键的部分 流程控制语句可以把单个的语句组合成有意义的 能完成一定功能的小逻辑模块 最主要的流程控制方式是结构化程序设计中规定的顺序结构 分支结构 ( 选择结构 ) 和循环结构三种基本流程结构 本章将指导读者掌握 Java 程序中的流程控制语句, 包括这些语句的语法结构和使用中需注意的要点

More information

Generated by Unregistered Batch DOC TO PDF Converter , please register! 浙江大学 C 程序设计及实验 试题卷 学年春季学期考试时间 : 2003 年 6 月 20 日上午 8:3

Generated by Unregistered Batch DOC TO PDF Converter , please register! 浙江大学 C 程序设计及实验 试题卷 学年春季学期考试时间 : 2003 年 6 月 20 日上午 8:3 浙江大学 C 程序设计及实验 试题卷 2002-2003 学年春季学期考试时间 : 2003 年 6 月 20 日上午 8:30-10:30 注意 : 答题内容必须写在答题卷上, 写在本试题卷上无效 一. 单项选择题 ( 每题 1 分, 共 10 分 ) 1. 下列运算符中, 优先级最低的是 A.

More information

Microsoft PowerPoint - 4. 数组和字符串Arrays and Strings.ppt [兼容模式]

Microsoft PowerPoint - 4. 数组和字符串Arrays and Strings.ppt [兼容模式] Arrays and Strings 存储同类型的多个元素 Store multi elements of the same type 数组 (array) 存储固定数目的同类型元素 如整型数组存储的是一组整数, 字符数组存储的是一组字符 数组的大小称为数组的尺度 (dimension). 定义格式 : type arrayname[dimension]; 如声明 4 个元素的整型数组 :intarr[4];

More information

<4D F736F F D205A572D2D A1AAA1AAD4ACE7F42D43D3EFD1D4CAB5D1B5BDCCB3CC2E646F6378>

<4D F736F F D205A572D2D A1AAA1AAD4ACE7F42D43D3EFD1D4CAB5D1B5BDCCB3CC2E646F6378> 第 1 部分 Visual Studio 6.0 开发环境介绍 本书以 Visual C++ 6.0 作为 C 源程序的实践开发环境, 本章将首先介绍 Visual C++ 6.0 环境的基本操作, 包括 Visual C++ 6.0 的安装和启动,C 源程序的编辑 运行与调试 1.1 安装与启动 Visual C++ 6.0 MSDN Visual C++ 6.0 1.1 Microsoft Visual

More information

《C语言程序设计》教材习题参考答案

《C语言程序设计》教材习题参考答案 教材名称 : C 语言程序设计 ( 第 1 版 ) 黄保和 江弋编著清华大学出版社 ISBN:978-7-302-13599-9, 红色封面 答案制作时间 :2011 年 2 月 -5 月 一 选择题 1. 设已定义 int a, * p, 下列赋值表达式中正确的是 :C)p=&a 2. 设已定义 int x,*p=&x;, 则下列表达式中错误的是 :B)&*x 3. 若已定义 int a=1,*b=&a;,

More information

无类继承.key

无类继承.key 无类继承 JavaScript 面向对象的根基 周爱 民 / aimingoo aiming@gmail.com https://aimingoo.github.io https://github.com/aimingoo rand = new Person("Rand McKinnon",... https://docs.oracle.com/cd/e19957-01/816-6408-10/object.htm#1193255

More information

OOP with Java 通知 : Project 2 提交时间 : 3 月 14 日晚 9 点 另一名助教 : 王桢

OOP with Java 通知 : Project 2 提交时间 : 3 月 14 日晚 9 点 另一名助教 : 王桢 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 : Project 2 提交时间 : 3 月 14 日晚 9 点 另一名助教 : 王桢 Email: 51141201063@ecnu.cn 复习 : Java 类型 基本类型 boolean, char, 封装 (wrappers) 类 (class) 定义 class MyType { int i;

More information

OOP with Java 通知 : Project 2 提交时间 : 3 月 15 日晚 9 点

OOP with Java 通知 : Project 2 提交时间 : 3 月 15 日晚 9 点 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 : Project 2 提交时间 : 3 月 15 日晚 9 点 复习 : Java 类型 基本类型 boolean, char, 封装 (wrappers) 类 (class) 定义 class MyType { int i; double d; 数据 (Fields) char c; void set(double

More information

chp3

chp3 Java 软件设计基础 3. 流程控制 3.1 语句控制结构 语句类型 变量声明语句 用来声明变量, 格式为 : 表达式语句 在一个表达式的最后加上一个分号构成的语句, 分号是语句不可缺少的部分, 格式为 : 变量 = 表达式 ; 复合语句 [ 修饰符 ] 类型名变量名 1[, 变量名 2][, ]; [ 修饰符 ] 类型名变量名 1[= 初值 1][, 变量名 2][= 初值 2][, ]; 将相关语句组合在一起就构成复合语句,

More information

《计算概论》课程 第十九讲 C 程序设计语言应用

《计算概论》课程 第十九讲  C 程序设计语言应用 计算概论 A 程序设计部分 字符数组与字符串 李戈 北京大学信息科学技术学院软件研究所 lige@sei.pku.edu.cn 字符数组的定义 #include int main() char a[10] = 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' ; for (int i = 0; i < 10; i++) cout

More information

Microsoft Word - 新1-12.doc

Microsoft Word - 新1-12.doc 实训 5 面向对象编程练习 实训 5 面向对象编程练习 5.1 实训目的 通过编程和上机实验理解 Java 语言是如何体现面向对象编程基本思想 以及如何创建类 和对象 了解成员变量和成员方法的特性 5.2 实训要求 编写一个体现面向对象思想的程序 编写一个创建对象和使用对象的方法的程序 5.3 实训内容 5.3.1 创建对象并使用对象 1 定义一个 Person 类 可以在应用程序中使用该类 成员属性

More information

没有幻灯片标题

没有幻灯片标题 指针作为函数参数 : 原因 : 1 需要修改一个或多个值,( 用 return 语句不能解决问题 ) 2 执行效率的角度 使用方法 : 在函数原型以及函数首部中需要声明能够接受指针值的形参, 具体的写法为 : 数据类型 * 形参名 如果有多个指针型形参, 则用逗号分隔, 例如 : void swap(int *p1, int *p2) 它说明了形参 p1 p2 是指向整型变量的指针 在函数调用时,

More information

Microsoft Word - 第3章.doc

Microsoft Word - 第3章.doc 第 3 章流程控制和数组 3.1 实验目的 (1) 熟练掌握控制台应用程序的代码编写和调试, 以及运行方法 (2) 掌握选择结构的一般语法格式和应用 (3) 掌握 switch 语句的用法 (4) 掌握选择结构的嵌套的用法, 能灵活使用选择结构解决实际问题 (5) 掌握 while 循环语句的一般语法格式 (6) 掌握 for 循环语句的一般语法格式 (7) 掌握循环嵌套的语法格式 (8) 掌握一维数组的定义

More information

chap07.key

chap07.key #include void two(); void three(); int main() printf("i'm in main.\n"); two(); return 0; void two() printf("i'm in two.\n"); three(); void three() printf("i'm in three.\n"); void, int 标识符逗号分隔,

More information

CHAPTER VC#

CHAPTER VC# 1. 2. 3. 4. CHAPTER 2-1 2-2 2-3 2-4 VC# 2-5 2-6 2-7 2-8 Visual C# 2008 2-1 Visual C# 0~100 (-32768~+32767) 2 4 VC# (Overflow) 2-1 2-2 2-1 2-1.1 2-1 1 10 10!(1 10) 2-3 Visual C# 2008 10! 32767 short( )

More information

第3章 Java语言基础

第3章 Java语言基础 第 3 章 Java 语言基础 第 1/55 页 习题 3 3. 8. 9. 13. 18. 实验 : 1. 实验 4( 实 P11~12) 2. 实验任务 : 1 例 3.1( 教 P30) 2 变量赋值 ( 教 P29) 3 编写两个缓冲区对应同一个输入流对象, 并键盘输入整型 a 和双精度 b, 求和 第 2/55 页 习题 3 3. 实验任务 : 1 实验 5( 实 P12~13) 2 实验

More information

FY.DOC

FY.DOC 高 职 高 专 21 世 纪 规 划 教 材 C++ 程 序 设 计 邓 振 杰 主 编 贾 振 华 孟 庆 敏 副 主 编 人 民 邮 电 出 版 社 内 容 提 要 本 书 系 统 地 介 绍 C++ 语 言 的 基 本 概 念 基 本 语 法 和 编 程 方 法, 深 入 浅 出 地 讲 述 C++ 语 言 面 向 对 象 的 重 要 特 征 : 类 和 对 象 抽 象 封 装 继 承 等 主

More information

《C语言程序设计》第2版教材习题参考答案

《C语言程序设计》第2版教材习题参考答案 教材 C 语言程序设计 ( 第 2 版 ) 清华大学出版社, 黄保和, 江弋编著 2011 年 10 月第二版 ISBN:978-7-302-26972-4 售价 :35 元 答案版本 本习题答案为 2012 年 2 月修订版本 一 选择题 1. 设已定义 int a, * p, 下列赋值表达式中正确的是 :C)p = &a A. *p = *a B. p = *a C.p = &a D. *p =

More information

<4D F736F F F696E74202D BDE1B9B9BBAFB3CCD0F2C9E8BCC D20D1ADBBB7>

<4D F736F F F696E74202D BDE1B9B9BBAFB3CCD0F2C9E8BCC D20D1ADBBB7> 能源与动力工程学院 结构化编程 结构化程序设计 循环 循环结构 确定性循环 非确定性循环 I=1 sum=sum+i I = I +1 陈 斌 I>100 Yes No 目录 求和 :1+2+3++100 第四节循环的应用 PROGRAM GAUSS INTEGER I, SUM 计数器 SUM = 0 DO I = 1, 100, 1 SUM = SUM + I print*, I, SUM DO

More information

PowerPoint 演示文稿

PowerPoint 演示文稿 The BitCoin Scripting Language 交易实例 交易结构 "result": { "txid": "921a dd24", "hash": "921a dd24", "version": 1, "size": 226, "locktime": 0, "vin": [ ], "vout": [ ], "blockhash": "0000000000000000002c510d

More information

帝国CMS下在PHP文件中调用数据库类执行SQL语句实例

帝国CMS下在PHP文件中调用数据库类执行SQL语句实例 帝国 CMS 下在 PHP 文件中调用数据库类执行 SQL 语句实例 这篇文章主要介绍了帝国 CMS 下在 PHP 文件中调用数据库类执行 SQL 语句实例, 本文还详细介绍了帝国 CMS 数据库类中的一些常用方法, 需要的朋友可以参考下 例 1: 连接 MYSQL 数据库例子 (a.php)

More information

没有幻灯片标题

没有幻灯片标题 第 2 章 C 语言的基本数据类型与表达 式 2. 1 C 语言的语法基础 2. 2 C 语言的基本数据类型 2. 3 常量和变量 2. 4 运算符与表达式 2. 5 数据类型转换 用 第 2 章 C 语言的基本数据类型与表达 2.1 C 语言的语法基础 2. 1. 1 C 语言字符集 式 C 语言的基本符号可分 4 个类, 归纳如下 : (1) 英文字母 : 大小写各 26 个, 共计 52 个

More information

Guava学习之CharSequenceReader

Guava学习之CharSequenceReader CharSequenceReader 类是以 CharSequence 的形式读取字符 CharSequenceReader 类继承自 Reader 类, 除了 remaining() hasremaining() 以及 checkopen() 函数之后, 其他的函数都是重写 Reader 类中的函数 CharSequenceReader 类声明没有用 public 关键字, 所以我们暂时还不能调用这个类

More information

Microsoft Word - 第3章.doc

Microsoft Word - 第3章.doc Java C++ Pascal C# C# if if if for while do while foreach while do while C# 3.1.1 ; 3-1 ischeck Test() While ischeck while static bool ischeck = true; public static void Test() while (ischeck) ; ischeck

More information

基于CDIO一体化理念的课程教学大纲设计

基于CDIO一体化理念的课程教学大纲设计 Java 语 言 程 序 设 计 课 程 教 学 大 纲 Java 语 言 程 序 设 计 课 程 教 学 大 纲 一 课 程 基 本 信 息 1. 课 程 代 码 :52001CC022 2. 课 程 名 称 :Java 语 言 程 序 设 计 3. 课 程 英 文 名 称 :Java Programming 4. 课 程 类 别 : 理 论 课 ( 含 实 验 上 机 或 实 践 ) 5. 授

More information

Chapter 9: Objects and Classes

Chapter 9: Objects and Classes Fortran Algol Pascal Modula-2 BCPL C Simula SmallTalk C++ Ada Java C# C Fortran 5.1 message A B 5.2 1 class Vehicle subclass Car object mycar public class Vehicle extends Object{ public int WheelNum

More information

运算符重载 为什么要 运算符重载 那些运算符可以重载, 哪些不可以 如何实现运算符重载 实现方式 : 成员函数与非成员函数 类型转换 怎样实现对象与基本数据类型数据的运算 2

运算符重载 为什么要 运算符重载 那些运算符可以重载, 哪些不可以 如何实现运算符重载 实现方式 : 成员函数与非成员函数 类型转换 怎样实现对象与基本数据类型数据的运算 2 第十一讲 运算符重载 与类型转换 运算符重载 为什么要 运算符重载 那些运算符可以重载, 哪些不可以 如何实现运算符重载 实现方式 : 成员函数与非成员函数 类型转换 怎样实现对象与基本数据类型数据的运算 2 为什么要运算符重载 预定义的运算符只针对基本数据类型, 若要对类的对象进行类似的运算, 需要重新定义运算符的功能 运算符重载实质就是函数重载 : 对已有的运算符赋予多重含义, 使得同一个运算符作用于不同类型的数据时导致不同的行为

More information

内 容 简 介 本 书 是 一 本 关 于 语 言 程 序 设 计 的 教 材, 涵 盖 了 语 言 的 基 本 语 法 和 编 程 技 术, 其 中 包 含 了 作 者 对 语 言 多 年 开 发 经 验 的 总 结, 目 的 是 让 初 学 的 读 者 感 受 到 语 言 的 魅 力, 并 掌

内 容 简 介 本 书 是 一 本 关 于 语 言 程 序 设 计 的 教 材, 涵 盖 了 语 言 的 基 本 语 法 和 编 程 技 术, 其 中 包 含 了 作 者 对 语 言 多 年 开 发 经 验 的 总 结, 目 的 是 让 初 学 的 读 者 感 受 到 语 言 的 魅 力, 并 掌 语 言 程 序 设 计 郑 莉 胡 家 威 编 著 清 华 大 学 逸 夫 图 书 馆 北 京 内 容 简 介 本 书 是 一 本 关 于 语 言 程 序 设 计 的 教 材, 涵 盖 了 语 言 的 基 本 语 法 和 编 程 技 术, 其 中 包 含 了 作 者 对 语 言 多 年 开 发 经 验 的 总 结, 目 的 是 让 初 学 的 读 者 感 受 到 语 言 的 魅 力, 并 掌 握 语

More information

OOP with Java 通知 Project 2 提交时间 : 3 月 21 日晚 9 点 作业提交格式 学习使用 文本编辑器 cmd, PowerShell (Windows), terminal(linux, Mac)

OOP with Java 通知 Project 2 提交时间 : 3 月 21 日晚 9 点 作业提交格式 学习使用 文本编辑器 cmd, PowerShell (Windows), terminal(linux, Mac) OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 2 提交时间 : 3 月 21 日晚 9 点 作业提交格式 学习使用 文本编辑器 cmd, PowerShell (Windows), terminal(linux, Mac) 复习 面向对象编程 将实际问题分解成不同的对象 不的对象提供不同的服务 对象之间可以传递消息 例子小李深夜

More information

Microsoft Word - 《C语言开发入门》课程教学大纲-2.doc

Microsoft Word - 《C语言开发入门》课程教学大纲-2.doc C 语言开发入门 课程教学大纲 ( 课程英文名称 ) 课程编号 :201409210011 学分 :5 学分学时 :60 学时 ( 其中 : 讲课学时 :37 学时上机学时 :23 学时 ) 先修课程 : 计算机导论后续课程 :C++ 程序设计适用专业 : 信息及其计算机相关专业开课部门 : 计算机系 一 课程的性质与目标 C 语言开发入门 是计算机各专业必修的基础课程, 是数据结构 C++ Java

More information

res/layout 目录下的 main.xml 源码 : <?xml version="1.0" encoding="utf 8"?> <TabHost android:layout_height="fill_parent" xml

res/layout 目录下的 main.xml 源码 : <?xml version=1.0 encoding=utf 8?> <TabHost android:layout_height=fill_parent xml 拓展训练 1- 界面布局 1. 界面布局的重要性做应用程序, 界面是最基本的 Andorid 的界面, 需要写在 res/layout 的 xml 里面, 一般情况下一个 xml 对应一个界面 Android 界面布局有点像写 html( 连注释代码的方式都一样 ), 要先给 Android 定框架, 然后再在框架里面放控件,Android 提供了几种框架,AbsoluteLayout,LinearLayout,

More information

KillTest 质量更高 服务更好 学习资料 半年免费更新服务

KillTest 质量更高 服务更好 学习资料   半年免费更新服务 KillTest 质量更高 服务更好 学习资料 http://www.killtest.cn 半年免费更新服务 Exam : 310-065Big5 Title : Sun Certified Programmer for the Java 2 Platform, SE 6.0 Version : Demo 1 / 14 1. 35. String #name = "Jane Doe"; 36. int

More information

Microsoft PowerPoint - 5. 指针Pointers.ppt [兼容模式]

Microsoft PowerPoint - 5. 指针Pointers.ppt [兼容模式] 指针 Pointers 变量指针与指针变量 Pointer of a variable 变量与内存 (Variables and Memory) 当你声明一个变量时, 计算机将给该变量一个内存, 可以存储变量的值 当你使用变量时, 计算机将做两步操作 : - 根据变量名查找其对应的地址 ; - 通过地址对该地址的变量内容进行读 (retrieve) 或写 (set) 变量的地址称为变量的指针! C++

More information

Microsoft PowerPoint - 8. 运算符重载 Operator Overloading.pptx

Microsoft PowerPoint - 8. 运算符重载 Operator Overloading.pptx 运算符重载 Operator Overloading class Point { public: ; double x_, y_; Why Operator Overloading? Point (double x =0, double y = 0):x_(x),y_(y) { int main(){ Point a(1., 2), b(3,4); Point c = a + b; return 0;

More information

1 4 1.1 4 1.2..4 2..4 2.1..4 3.4 3.1 Java.5 3.1.1..5 3.1.2 5 3.1.3 6 4.6 4.1 6 4.2.6 5 7 5.1..8 5.1.1 8 5.1.2..8 5.1.3..8 5.1.4..9 5.2..9 6.10 6.1.10

1 4 1.1 4 1.2..4 2..4 2.1..4 3.4 3.1 Java.5 3.1.1..5 3.1.2 5 3.1.3 6 4.6 4.1 6 4.2.6 5 7 5.1..8 5.1.1 8 5.1.2..8 5.1.3..8 5.1.4..9 5.2..9 6.10 6.1.10 Java V1.0.1 2007 4 10 1 4 1.1 4 1.2..4 2..4 2.1..4 3.4 3.1 Java.5 3.1.1..5 3.1.2 5 3.1.3 6 4.6 4.1 6 4.2.6 5 7 5.1..8 5.1.1 8 5.1.2..8 5.1.3..8 5.1.4..9 5.2..9 6.10 6.1.10 6.2.10 6.3..10 6.4 11 7.12 7.1

More information

Microsoft PowerPoint - 01_Introduction.ppt

Microsoft PowerPoint - 01_Introduction.ppt Hello, World C 程序设计语言 第 1 章章观其大略 孙志岗 sun@hit.edu.cn http://sunner.cn prf("hello,, world\n"); 超级无敌考考你 : 如何把 hello 和 world 分别打印在两行? 2004-12-19 A Tutorial Introduction 2 hello.c 打印华氏温度与摄氏温度对照表 计算公式 : C=(5/9)(

More information

试卷代号 :1253 座位号 E 口 国家开放大学 ( 中央广播电视大学 )2014 年秋季学期 " 开放本科 " 期末考试 C 语言程序设计 A 试题 2015 年 1 月 E 四! 五 总分! 一 单选题 ( 每小题 2 分, 共 20 分 ) 1. 由 C 语言源程序文件编译而成的目标文件的默

试卷代号 :1253 座位号 E 口 国家开放大学 ( 中央广播电视大学 )2014 年秋季学期  开放本科  期末考试 C 语言程序设计 A 试题 2015 年 1 月 E 四! 五 总分! 一 单选题 ( 每小题 2 分, 共 20 分 ) 1. 由 C 语言源程序文件编译而成的目标文件的默 试卷代号 :1253 座位号 E 口 国家开放大学 ( 中央广播电视大学 )2014 年秋季学期 " 开放本科 " 期末考试 C 语言程序设计 A 试题 2015 年 1 月 E 四! 五 总分! 一 单选题 ( 每小题 2 分, 共 20 分 ) 1. 由 C 语言源程序文件编译而成的目标文件的默认扩展名为 ( ) A. cpp B. c C. exe D. obj 2. 设 x 和 y 均为逻辑值,

More information

Microsoft Word - 第3章.doc

Microsoft Word - 第3章.doc 第 3 章 流程控制语句的应用 语句是程序中最小的程序指令, 即程序完成一次完整正操的基本单位 在 C# 中, 可以使用多种类型的语句, 每一种类型的语句又可以通过多个关键字实现 通过这些语句可以控制程序代码的逻辑, 提高程序的灵活性, 从而实现比较复杂的程序逻辑 本章主要内容 : 选择语句的应用 迭代语句的应用 跳转语句的应用 3.1 选择语句的应用 选择语句也叫作分支语句, 选择语句根据某个条件是否成立来控制程序的执行流程

More information

设计模式 Design Patterns

设计模式 Design Patterns 丁勇 Email:18442056@QQ.com 学习目标 描述 JSP 表达式语言的语法 认识使用 JSP 表达式的优点 在 JSP 中使用表达式语言 表达式语言简介 5 1 EL 为表达式语言 由两个组开发 JSP 标准标签库专家组 JSP 2.0 专家组 JSP 表达式语言的语法 ${EL Expression} JSP EL 表达式用于以下情形 静态文本 标准标签和自定义标签 表达式语言简介

More information

期中考试试题讲解

期中考试试题讲解 一 选择题 ( 一 ) 1. 结构化程序设计所规定的三种基本结构是 C A 主程序 子程序 函数 B 树形 网形 环形 C 顺序 选择 循环 D 输入 处理 输出 2. 下列关于 C 语言的叙述错误的是 A A 对大小写不敏感 B 不同类型的变量可以在一个表达式中 C main 函数可以写在程序文件的任何位置 D 同一个运算符号在不同的场合可以有不同的含义 3. 以下合法的实型常数是 C A.E4

More information

内 容 提 要 将 JAVA 开 发 环 境 迁 移 到 Linux 系 统 上 是 现 在 很 多 公 司 的 现 实 想 法, 而 在 Linux 上 配 置 JAVA 开 发 环 境 是 步 入 Linux 下 JAVA 程 序 开 发 的 第 一 步, 本 文 图 文 并 茂 地 全 程 指

内 容 提 要 将 JAVA 开 发 环 境 迁 移 到 Linux 系 统 上 是 现 在 很 多 公 司 的 现 实 想 法, 而 在 Linux 上 配 置 JAVA 开 发 环 境 是 步 入 Linux 下 JAVA 程 序 开 发 的 第 一 步, 本 文 图 文 并 茂 地 全 程 指 内 容 提 要 将 JAVA 开 发 环 境 迁 移 到 Linux 系 统 上 是 现 在 很 多 公 司 的 现 实 想 法, 而 在 Linux 上 配 置 JAVA 开 发 环 境 是 步 入 Linux 下 JAVA 程 序 开 发 的 第 一 步, 本 文 图 文 并 茂 地 全 程 指 导 你 搭 建 Linux 平 台 下 的 JAVA 开 发 环 境, 包 括 JDK 以 及 集

More information

Learning Java

Learning Java Java Introduction to Java Programming (Third Edition) Prentice-Hall,Inc. Y.Daniel Liang 2001 Java 2002.2 Java2 2001.10 Java2 Philip Heller & Simon Roberts 1999.4 Java2 2001.3 Java2 21 2002.4 Java UML 2002.10

More information

试卷代号 :1075 座位号 rn 国家开放大学 ( 中央广播电视大学 )2015 年秋季学期 " 开放本科 " 期末考试 c+ 十语言程序设计试题 2016 年 1 月 t 问一 Urr-f 斗 士 1 1. 下面的保留字 ( ) 不能作为函数的返回类型 A. void B. int C. new

试卷代号 :1075 座位号 rn 国家开放大学 ( 中央广播电视大学 )2015 年秋季学期  开放本科  期末考试 c+ 十语言程序设计试题 2016 年 1 月 t 问一 Urr-f 斗 士 1 1. 下面的保留字 ( ) 不能作为函数的返回类型 A. void B. int C. new 试卷代号 :1075 座位号 rn 国家开放大学 ( 中央广播电视大学 )2015 年秋季学期 " 开放本科 " 期末考试 c+ 十语言程序设计试题 2016 年 1 月 t 问一 Urr-f 斗 士 1 1. 下面的保留字 ( ) 不能作为函数的返回类型 A. void B. int C. new D. long 2. 在每个 C 十 + 程序中都必须包含有这样一个函数, 该函数的函数名为 ) A.main

More information

模板

模板 制作人 : 张刚 目录 类和对象 面向对象程序设计基本特征 类的声明 构造方法 成员变量和方法 封装 继承 多态 包 访问控制 final static 抽象类和接口 内部类 沈阳工业大学软件学院 Java 课程教研组 Page 2 核心知识点 类 对象 三个基本特征 类的基本结构 成员变量 构造方法 成员方法 类实例 对象创建和操作 沈阳工业大学软件学院 Java 课程教研组 Page 3 1.

More information

Microsoft PowerPoint - 03.Fortran程序设计基础1

Microsoft PowerPoint - 03.Fortran程序设计基础1 简单 Fortran 90 程序的构造形式 : 第二讲 Fortran 程序设计基础 (2) [PROGRAM 程序名 ] [ 声明语句 ] [ 执行语句 ] END [PROGRAM [ 程序名 ]] 程序的书写 (P5) PROGRAM MONEY!calculate balance after interest compounded! 根据利息计算余额 REAL BALANCE, INTEREST,

More information

1.JasperReport ireport JasperReport ireport JDK JDK JDK JDK ant ant...6

1.JasperReport ireport JasperReport ireport JDK JDK JDK JDK ant ant...6 www.brainysoft.net 1.JasperReport ireport...4 1.1 JasperReport...4 1.2 ireport...4 2....4 2.1 JDK...4 2.1.1 JDK...4 2.1.2 JDK...5 2.1.3 JDK...5 2.2 ant...6 2.2.1 ant...6 2.2.2 ant...6 2.3 JasperReport...7

More information

2015年计算机二级(C语言)模拟试题及答案(三)

2015年计算机二级(C语言)模拟试题及答案(三) 2016 年计算机二级 (C 语言 ) 模拟试题及答案 (3) 1.( A ) 是构成 C 语言程序的基本单位 A 函数 B 过程 C 子程序 D 子例程 2.C 语言程序从 ( C ) 开始执行 A 程序中第一条可执行语句 B 程序中第一个函数 C 程序中的 main 函数 D 包含文件中的第一个函数 3 以下说法中正确的是( C ) A C 语言程序总是从第一个定义的函数开始执行 B 在 C 语言程序中,

More information

试卷代号 ~1075 座位号 E 口 国家开放大学 ( 中央广播电视大学 )20]5 年秋季学期 " 开放本科 " 期末考试 C 十十语言程序设计 试题 同二二十斗 2016 年 1 月 巴叫一 1. 下面的保留字 ( ) 不能作为函数的返回类型 A. void B. int C. new D. l

试卷代号 ~1075 座位号 E 口 国家开放大学 ( 中央广播电视大学 )20]5 年秋季学期  开放本科  期末考试 C 十十语言程序设计 试题 同二二十斗 2016 年 1 月 巴叫一 1. 下面的保留字 ( ) 不能作为函数的返回类型 A. void B. int C. new D. l 试卷代号 ~1075 座位号 E 口 国家开放大学 ( 中央广播电视大学 )20]5 年秋季学期 " 开放本科 " 期末考试 C 十十语言程序设计 试题 同二二十斗 2016 年 1 月 巴叫一 1. 下面的保留字 ( ) 不能作为函数的返回类型 A. void B. int C. new D. long 2. 在每个 c++ 程序中都必须包含有这样一个函数, 该函数的函数名为 ( ) A. main

More information

使 用 Java 语 言 模 拟 保 险 箱 容 量 门 板 厚 度 箱 体 厚 度 属 性 锁 具 类 型 开 保 险 箱 关 保 险 箱 动 作 存 取 款

使 用 Java 语 言 模 拟 保 险 箱 容 量 门 板 厚 度 箱 体 厚 度 属 性 锁 具 类 型 开 保 险 箱 关 保 险 箱 动 作 存 取 款 JAVA 程 序 设 计 ( 肆 ) 徐 东 / 数 学 系 使 用 Java 语 言 模 拟 保 险 箱 容 量 门 板 厚 度 箱 体 厚 度 属 性 锁 具 类 型 开 保 险 箱 关 保 险 箱 动 作 存 取 款 使 用 Java class 代 表 保 险 箱 public class SaveBox 类 名 类 类 体 实 现 封 装 性 使 用 class SaveBox 代 表 保

More information

3.1 num = 3 ch = 'C' 2

3.1 num = 3 ch = 'C' 2 Java 1 3.1 num = 3 ch = 'C' 2 final 3.1 final : final final double PI=3.1415926; 3 3.2 4 int 3.2 (long int) (int) (short int) (byte) short sum; // sum 5 3.2 Java int long num=32967359818l; C:\java\app3_2.java:6:

More information

(京)新登字063号

(京)新登字063号 教 育 部 职 业 教 育 与 成 人 教 育 司 推 荐 教 材 Java 程 序 设 计 教 程 ( 第 二 版 ) 沈 大 林 主 编 沈 昕 肖 柠 朴 曾 昊 等 编 著 内 容 简 介 Java 是 由 美 国 SUN 公 司 开 发 的 一 种 功 能 强 大 的, 具 有 简 单 面 向 对 象 分 布 式 可 移 植 等 性 能 的 多 线 程 动 态 计 算 机 编 程 语 言

More information

第 章 程序由语句组成, 语句经常使用数据类型 运算符 表达式等 Java 语言的数据类型 运算符与表达式等是从 C++ 语言简化而来, 更加简洁 高效 2. 1 常量和变量 Java 程序运行时, 值不可修改的数据称为常量, 分为字面常量 ( 常数 ) 与标识符常量两种 变量是程序运行时值发生改变

第 章 程序由语句组成, 语句经常使用数据类型 运算符 表达式等 Java 语言的数据类型 运算符与表达式等是从 C++ 语言简化而来, 更加简洁 高效 2. 1 常量和变量 Java 程序运行时, 值不可修改的数据称为常量, 分为字面常量 ( 常数 ) 与标识符常量两种 变量是程序运行时值发生改变 第 章 程序由语句组成, 语句经常使用数据类型 运算符 表达式等 Java 语言的数据类型 运算符与表达式等是从 C++ 语言简化而来, 更加简洁 高效 2. 1 常量和变量 Java 程序运行时, 值不可修改的数据称为常量, 分为字面常量 ( 常数 ) 与标识符常量两种 变量是程序运行时值发生改变的量 2.1.1 数据类型 Java 是一种强类型语言, 这意味着所有变量都必须先明确定义其数据类型才能使用

More information

OOP with Java 通知 Project 4: 推迟至 4 月 25 日晚 9 点

OOP with Java 通知 Project 4: 推迟至 4 月 25 日晚 9 点 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 4: 推迟至 4 月 25 日晚 9 点 复习 Protected 可以被子类 / 同一包中的类访问, 不能被其他类访问 弱化的 private 同时赋予 package access class MyType { public int i; public double d; public

More information

拦截器(Interceptor)的学习

拦截器(Interceptor)的学习 二 拦截器 (Interceptor) 的学习 拦截器可以监听程序的一个或所有方法 拦截器对方法调用流提供了细粒度控制 可以在无状态会话 bean 有状态会话 bean 和消息驱动 bean 上使用它们 拦截器可以是同一 bean 类中的方法或是一个外部类 下面介绍如何在 Session Bean 类中使用外部拦截器类 @Interceptors 注释指定一个或多个在外部类中定义的拦截器 下面拦截器

More information

第1章 Delphi简介

第1章  Delphi简介 第 2 章 零基础开始学习 Java 基本语法 Java 语言也有自己的一套语法规则, 通过使用这些规则, 能够让程序正确运行, 并且减少错误的发生 本章的实例虽然简单, 却基本涵盖了本篇所讲的内容, 通过这些知识的学习, 将为后面的程序开发奠定坚实的基础 通过本章内容, 可以了解 Java 程序的基本结构 基础语法 ( 包括变量 常量 数据类型 运算符等 ) 以及程序的流程控制 本章要点 ( 已掌握的在方框中打钩

More information

<4D F736F F F696E74202D BDE1B9B9BBAFB3CCD0F2C9E8BCC D20D1A1D4F1>

<4D F736F F F696E74202D BDE1B9B9BBAFB3CCD0F2C9E8BCC D20D1A1D4F1> 能源与动力工程学院 结构化编程 结构化程序设计 选择 结构化编程的三种基本结构 : 顺序结构 I=1 选择 ( 分支 ) 结构 循环结构 sum=sum+i I = I +1 陈 斌 A?=B NO I>100 No YES Yes 目录 第一节逻辑运算 第一节逻辑运算 第二节 I 语句 逻辑运算 算术运算 关系运算 逻辑运算 关系运算符 运算优先级 第三节浮点数及字符的逻辑运算 90 77 功能

More information

Microsoft PowerPoint - 07 派生数据类型

Microsoft PowerPoint - 07 派生数据类型 能源与动力工程学院 目录 派生类型 陈 斌 固有数据类型 数值型 (numerical) 整型 INTEGER 实型 REAL 复数型 COMPLEX 非数值型 字符型 CHARACTER 逻辑型 ( 布尔型 )LOGICAL 自定义数据类型 ( 派生类型, derived type) 派生类型是指用户利用 Fortran 系统内部类型, 如整型 实型 复数型 逻辑型 字符型等的组合自行创建出一个新的数据类型,

More information

CC213

CC213 : (Ken-Yi Lee), E-mail: feis.tw@gmail.com 49 [P.51] C/C++ [P.52] [P.53] [P.55] (int) [P.57] (float/double) [P.58] printf scanf [P.59] [P.61] ( / ) [P.62] (char) [P.65] : +-*/% [P.67] : = [P.68] : ,

More information

任务 3 加法运算练习游戏 019 这就需要用到 C# 语言的基础语法, 如数据类型 运算符和控制语句, 还需要其他的常用控件 在此任务的完成过程中, 读者可以接触到 C# 的数据类型 变量常量 运算符 控制语句等基础语法, 掌握以上知识点及其在软件开发中的应用 3.2 相关知识 预定义

任务 3 加法运算练习游戏 019 这就需要用到 C# 语言的基础语法, 如数据类型 运算符和控制语句, 还需要其他的常用控件 在此任务的完成过程中, 读者可以接触到 C# 的数据类型 变量常量 运算符 控制语句等基础语法, 掌握以上知识点及其在软件开发中的应用 3.2 相关知识 预定义 任务 3 加法运算练习游戏 3.1 情境描述 选择了开发环境并理解了事件驱动机制以后, 要开发项目, 还需掌握 C# 语言的语法 本任务的目标是完成如图 3.1 和图 3.2 所示的小学生加法运算练习游戏 这个小软件的功能是在窗体中的 + 两边出现 2 个 10 以内的随机数, 让用户 ( 适合于小学生 ) 在文本框内输入其和, 然后单击 OK 按钮 若输入的和是正确的, 则跳出一个红色的图片, 同时提示答对了,

More information

Java java.lang.math Java Java.util.Random : ArithmeticException int zero = 0; try { int i= 72 / zero ; }catch (ArithmeticException e ) { // } 0,

Java java.lang.math Java Java.util.Random : ArithmeticException int zero = 0; try { int i= 72 / zero ; }catch (ArithmeticException e ) { // } 0, http://debut.cis.nctu.edu.tw/~chi Java java.lang.math Java Java.util.Random : ArithmeticException int zero = 0; try { int i= 72 / zero ; }catch (ArithmeticException e ) { // } 0, : POSITIVE_INFINITY NEGATIVE_INFINITY

More information

网C试题(08上).doc

网C试题(08上).doc 学习中心 姓名 学号 西安电子科技大学网络与继续教育学院 高级语言程序设计 (C) 全真试题 ( 闭卷 90 分钟 ) 题号一二三总分 题分 60 20 20 得分 一 单项选择题 ( 每小题 3 分, 共 60 分 ) 1.C 语言程序的基本单位是 A) 程序行 B) 语句 C) 函数 D) 字符 2. 下列四组选项中, 均是不合法的用户标识符的选项是 A)A B)getc C)include D)while

More information

计算概论A B03 C++语言的基本成分 - 运算成分(2)

计算概论A B03 C++语言的基本成分 - 运算成分(2) 计算概论 A 程序设计部分 C 语言的构成成分 运算成分 李戈 北京大学信息科学技术学院软件研究所 lige@sei.pku.edu.cn C 语言中的运算符 C 语言的运算符范围很宽 求字节数运算符 : sizeof 下标运算符 [ ] 赋值运算符 = 算术运算符 + - * / % 关系运算符 < > == >= > ~

More information

1intro.pptx

1intro.pptx 欢迎学习 大仕老师 1 第 1 章 JAVA 语言与面向对象的程序设计 1.1 Java 语言简介 1.2 面向对象程序设计 2 Java 语言简介 3 认识 Java Java 历史与发展 5 Java 是最热门的语言之一 数据来源 :IEEE Spectrum 6 6 0.1 Java 的发展历程 Internet 发展中的两次飞跃 : p www p Java Java 的出现 p 1990

More information

Microsoft PowerPoint - 08_OO_CJC.ppt

Microsoft PowerPoint - 08_OO_CJC.ppt C++ 中的 Hello World! C 程序设计语言 第 8 章 OO 与 C++ Java C# 孙志岗 sun@hit.edu.cn http://sunner.cn 兼容 C 语言的 : #include int main() printf("hello,, world!\n"); return 0; 更具 C++ 味道的 : #include int

More information

获取 Access Token access_token 是接口的全局唯一票据, 接入方调用各接口时都需使用 access_token 开发者需要进行妥善保存 access_token 的存储至少要保留 512 个字符空间 access_token 的有效期目前为 2 个小时, 需定时刷新, 重复

获取 Access Token access_token 是接口的全局唯一票据, 接入方调用各接口时都需使用 access_token 开发者需要进行妥善保存 access_token 的存储至少要保留 512 个字符空间 access_token 的有效期目前为 2 个小时, 需定时刷新, 重复 获取 Access Token access_token 是接口的全局唯一票据, 接入方调用各接口时都需使用 access_token 开发者需要进行妥善保存 access_token 的存储至少要保留 512 个字符空间 access_token 的有效期目前为 2 个小时, 需定时刷新, 重复 获取将导致上次获取的 access_token 失效 接入方可以使用 AppID 和 AppSecret

More information

chp6.ppt

chp6.ppt Java 软 件 设 计 基 础 6. 异 常 处 理 编 程 时 会 遇 到 如 下 三 种 错 误 : 语 法 错 误 (syntax error) 没 有 遵 循 语 言 的 规 则, 出 现 语 法 格 式 上 的 错 误, 可 被 编 译 器 发 现 并 易 于 纠 正 ; 逻 辑 错 误 (logic error) 即 我 们 常 说 的 bug, 意 指 编 写 的 代 码 在 执 行

More information

Microsoft PowerPoint - 3. 函数Functionl.ppt [兼容模式]

Microsoft PowerPoint - 3. 函数Functionl.ppt [兼容模式] 函数 Function 如何重用代码 How to reuse code 3 4 = 3*3*3*3 3 4,6 5 : 拷贝 - 粘帖代码 (Copy-paste code) 3 4,6 5,12 10 : 拷贝 - 粘帖代码 (Copy-paste code) Bad! 使用函数 (with a function) 使用函数 (with a function) 使用函数 (with a function)

More information

Mx* Language Reference Manual 2016 年 4 月 7 日 1 用词说明 未定义 指中央还没有表态指语言定义中不涉及的部分, 编译器和运行时环境如何表现是未知的 主要是为了给学生实现语言留下足够的空间, 标准测试集里不会出现涉及未定义部分的内容 例如 : 术语 : 源程

Mx* Language Reference Manual 2016 年 4 月 7 日 1 用词说明 未定义 指中央还没有表态指语言定义中不涉及的部分, 编译器和运行时环境如何表现是未知的 主要是为了给学生实现语言留下足够的空间, 标准测试集里不会出现涉及未定义部分的内容 例如 : 术语 : 源程 Mx* Language Reference Manual 2016 年 4 月 7 日 1 用词说明 未定义 指中央还没有表态指语言定义中不涉及的部分, 编译器和运行时环境如何表现是未知的 主要是为了给学生实现语言留下足够的空间, 标准测试集里不会出现涉及未定义部分的内容 例如 : 术语 : 源程序大小超过 256M 是未定义的 解释 : 我们测试用的源程序大小不会超过 256M 2 程序结构 Mx*

More information

Java 语言程序设计, 安徽水电学院 第一章认识 JAVA Java 开发环境的安装配置 Java 程序的编写与运行 Eclipse 的基本使用 石惠

Java 语言程序设计, 安徽水电学院 第一章认识 JAVA Java 开发环境的安装配置 Java 程序的编写与运行 Eclipse 的基本使用 石惠 Java 语言程序设计, 安徽水电学院 第一章认识 JAVA Java 开发环境的安装配置 Java 程序的编写与运行 Eclipse 的基本使用 石惠 学习目标 3 Java 语言及其特点 了解 重点 Java 程序的编写与运行 1 掌握 Java 开发环境的安装配置 Eclipse 的基本使用 2 目录 1.1 JAVA 简介 点击查看本小节知识架构 1.3 本章小结 知识架构 返回目录 1.2

More information

PowerPoint 演示文稿

PowerPoint 演示文稿 Python 入门 孙栩 xusun@pku.edu.cn 1 课程的整体介绍 目录 contents 2 Python 的介绍及如何安装 Python 3 使用 Python 编写简单小程序 1 课程的整体介绍 课程的整体介绍 Python 入门 1. Python 的介绍与安装 2. 变量与表达式 3. 一些简单的小程序 数据结构 1. 字符串 2. 列表 3. 元组 4. 字典 5. 集合 分支与循环

More information

ExcelUtility 类库使用说明 ( 续 ) 开发 / 设计 : 左文俊 第一个新增功能, 列宽自适应, 当超过 30 个字符则将单元格内容设为换行 任意一个无模板的导出方法均支持该功能, 示例代码如下 : /// <summary> /// 测试方法

ExcelUtility 类库使用说明 ( 续 ) 开发 / 设计 : 左文俊 第一个新增功能, 列宽自适应, 当超过 30 个字符则将单元格内容设为换行 任意一个无模板的导出方法均支持该功能, 示例代码如下 : /// <summary> /// 测试方法 ExcelUtility 类库使用说明 ( 续 ) 开发 / 设计 : 左文俊 第一个新增功能, 列宽自适应, 当超过 0 个字符则将单元格内容设为换行 任意一个无模板的导出方法均支持该功能, 示例代码如下 : 0 /// 测试方法 : 测试将 DataTable 导出到 EXCEL, 无模板 public void TestExportToExcelByDataTable() string excelpath

More information

姓名 : 年级专业 : 学号 : 凡年级专业 姓名 学号错写 漏写或字迹不清者, 成绩按零分记 密 封 线 java 较难 试卷 总分 题号 一 二 三 四 五 题分 得分 D 国际通信协议 4 下面选项中, 不是面向对象的特征的是 ( ) A 封装 B 继承 得分 一 单选题 ( 每题 3 分,

姓名 : 年级专业 : 学号 : 凡年级专业 姓名 学号错写 漏写或字迹不清者, 成绩按零分记 密 封 线 java 较难 试卷 总分 题号 一 二 三 四 五 题分 得分 D 国际通信协议 4 下面选项中, 不是面向对象的特征的是 ( ) A 封装 B 继承 得分 一 单选题 ( 每题 3 分, java 较难 试卷 总分 题号 一 二 三 四 五 题分 D 国际通信协议 4 下面选项中, 不是面向对象的特征的是 ( ) A 封装 B 继承 一 单选题 ( 每题 3 分, 共计 15 分 ) 1 下列修饰符中, 成员内部类被 ( ) 修饰后, 可以被外界访问 C 多态 D 重构 5 下列关于构造方法重载的说法中, 错误的是 () A 不同构造方法中调用本类其它的构造方法时, 需要使用 this([

More information

EJB-Programming-4-cn.doc

EJB-Programming-4-cn.doc EJB (4) : (Entity Bean Value Object ) JBuilder EJB 2.x CMP EJB Relationships JBuilder EJB Test Client EJB EJB Seminar CMP Entity Beans Session Bean J2EE Session Façade Design Pattern Session Bean Session

More information

第二章 Java 语法基础 实验目的 (1) 常量 变量与数据类型 a. 掌握 Java 的常量 变量声明及使用方法 b. 掌握 Java 各种数据类型的使用 c. 掌握基本的输入输出方法 (2) 运算符与表达式 a. 掌握算术运算 关系运算 逻辑运算, 及优先关系 b. 掌握表达式的使用 (3)

第二章 Java 语法基础 实验目的 (1) 常量 变量与数据类型 a. 掌握 Java 的常量 变量声明及使用方法 b. 掌握 Java 各种数据类型的使用 c. 掌握基本的输入输出方法 (2) 运算符与表达式 a. 掌握算术运算 关系运算 逻辑运算, 及优先关系 b. 掌握表达式的使用 (3) 第二章 Java 语法基础 实验目的 (1) 常量 变量与数据类型 a. 掌握 Java 的常量 变量声明及使用方法 b. 掌握 Java 各种数据类型的使用 c. 掌握基本的输入输出方法 (2) 运算符与表达式 a. 掌握算术运算 关系运算 逻辑运算, 及优先关系 b. 掌握表达式的使用 (3) 常用系统类 a. 基本输入输出类的使用 b. 掌握 Math 类的使用 (4) 类及其方法的使用 a.

More information

download.kaoyan.com_2006ÄêÌì½ò¹¤Òµ´óѧ¸ß¼¶ÓïÑÔ³ÌÐòÉè¼Æ£¨409£©¿¼ÑÐÊÔÌâ

download.kaoyan.com_2006ÄêÌì½ò¹¤Òµ´óѧ¸ß¼¶ÓïÑÔ³ÌÐòÉè¼Æ£¨409£©¿¼ÑÐÊÔÌâ 考生注意 : 本试卷共七大题, 满分 150 分 考试时间为 3 小时 ; 所有答案均写在答题纸上 ( 注明题号 ), 在此答题一律无效无效 一 选择题 ( 本题共 20 小题, 每小题 2 分, 满分 40 分 ) 1 char ch 1 2 A 0

More information

1 1 大概思路 创建 WebAPI 创建 CrossMainController 并编写 Nuget 安装 microsoft.aspnet.webapi.cors 跨域设置路由 编写 Jquery EasyUI 界面 运行效果 2 创建 WebAPI 创建 WebAPI, 新建 -> 项目 ->

1 1 大概思路 创建 WebAPI 创建 CrossMainController 并编写 Nuget 安装 microsoft.aspnet.webapi.cors 跨域设置路由 编写 Jquery EasyUI 界面 运行效果 2 创建 WebAPI 创建 WebAPI, 新建 -> 项目 -> 目录 1 大概思路... 1 2 创建 WebAPI... 1 3 创建 CrossMainController 并编写... 1 4 Nuget 安装 microsoft.aspnet.webapi.cors... 4 5 跨域设置路由... 4 6 编写 Jquery EasyUI 界面... 5 7 运行效果... 7 8 总结... 7 1 1 大概思路 创建 WebAPI 创建 CrossMainController

More information

OOP with Java 通知 Project 4: 4 月 19 日晚 9 点

OOP with Java 通知 Project 4: 4 月 19 日晚 9 点 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 4: 4 月 19 日晚 9 点 复习 Protected 可以被子类 / 同一包中的类访问, 不能被其他类访问 弱化的 private 同时赋予 package access class MyType { public int i; public double d; public char

More information