JAVA程序设计基础

java 专栏收录该内容
3 篇文章 0 订阅

目录

1引言... 5

1.1 程序设计语言的分类... 6

1.2 Java的发展过程(网络咖啡)... 7

1.3 Java程序的类型... 7

1.4 编译和执行Java程序... 8

2 JAVA2介绍... 9

21 什么是JAVA.. 9

22选择JAVA的原因... 9

23Java 程序的运行环境... 10

24与平台(CPUOS类型)无关性... 10

25“一次编译,到处运行的原理... 11

26通过在不同的平台上安装对应的Java 虚拟机,可以实现平台无关。... 11

27Java虚拟机介绍... 11

28Java2类库的版本和主要应用领域... 12

2.8.1 Java2类库的版本类型... 12

2.8.2 Java2主要应用领域... 12

29 实时编译器... 12

210  Java 运行时环境... 12

211 选择Java开发工具... 13

212Java Application程序基本结构... 13

213第一个JAVA例程... 13

214Java API 文档... 13

3Java语言基础... 14

31Java的基本语法... 14

32Java中的常量... 15

33 变量的概念与作用... 16

34 Java中的数据类型... 16

3.4.1数据类型种类... 16

3.4.2 Java中数据类型的转换规则:... 16

3.4.3 数据类型转换必须满足如下规则... 17

35Java中的运算符与表达式... 17

3.5.1 主要的类型... 17

3.5.2 特点... 17

3.5.3 几个常用的操作符在应用时所应该注意点... 18

3.5.4 引用型变量只支持有限的逻辑判断... 18

3.5.5 运算符的优先次序... 18

3.5.6 格式化输... 18

36Java中的语句及控制流... 19

3.6.1 主要的语句... 19

3.6.2 特点... 19

38数组... 27

3.8.1 数组初始化和匿名数组... 27

3.8.2 拷贝数组... 27

3.8.3 命令行参数... 28

3.8.4 对数组排序... 28

3.8.5多维数组... 29

3.8.6 不规则数组... 30

39 Java中的注释方式... 31

310 JAVA的健壮的体现... 31

311 JAVAC++在语言上的差别... 31

4类的基本架构... 32

41认识类... 32

411类的基本概念... 32

412类的声明... 33

41创建新的对象... 34

414   使用类来设计完整的程序... 36

415   同时创建多个对象... 36

4有关方法的使用... 36

421 定义与使用方法... 36

42数据成员的访问方式... 37

42在类定义的内部调用方法... 37

4参数的传递... 38

43调用方法并传递参数... 38

43传递多个参数... 38

4方法的返回值... 38

44没有返回值的方法... 38

44有返回值的方法... 39

4公有成员与私有成员... 39

45创建私有成员... 39

45创建公有成员... 39

45数据的封装... 39

45省略publicprivate. 40

5类的其它功能... 40

5函数成员(方法)的重载... 41

511 ... 41

512 使用重载常犯的错误... 43

52 构造函数... 43

521 构造函数的基本认识... 43

522 构造函数的调用时机... 43

523 构造函数的重载... 44

524 从某一构造函数调用另一构造函数... 44

525 构造函数的公有与私有... 44

526 构造函数的省略... 44

5.2.7 初始化块... 45

53 类变量与类方法... 46

531 实例变量与实例方法... 46

532 类变量... 46

533 类方法... 47

534 “类方法使用的限制... 47

54 类类型的变量... 47

541 赋值给类类型的变量... 47

542 以类类型的变量传递参数... 48

543 由方法返回类类型的变量... 48

544 对象析构和finalize方法... 48

55 利用数组来保存对象... 49

551 创建对象数组的范例... 49

552 传递数组到方法里... 50

56 内部类... 50

561 内部类的编写... 51

562 匿名内部类... 52

6继承... 54

60类之间的关系... 54

61 继承的基本概念... 54

611 简单的继承范例... 55

612 调用父类中特定的构造函数... 56

6.1.3 使用构造函数常见的错误... 57

62 由子类访问父类的成员... 58

63 覆盖... 58

631 覆盖父类的方法... 58

632 以父类的变量访问子类的成员... 58

64 再谈super()this()58

65 终止继承... 59

66 类之源--Object... 59

7抽象类接口... 60

71 抽象类... 60

711 定义抽象类... 60

712 抽象类的实现... 61

713 用抽象类类型的变量来创建对象... 61

714 使用抽象类的注意事项... 61

72 接口的使用... 61

73 接口的扩展... 63

74 ... 64

7.4.1 文件的分割... 64

7.4.2 定义包... 64

7.4.3 理解类路径(classpath)65

7.4.4 一个简单的例子... 65

7.4.5 访问保护... 66

7.4.6 一个访问的例子... 67

7.4.7 引入包... 69

8常用类库、集合类... 70

81有关字符串的类库... 71

82 StringBuffer类库... 72

8.3 wrapper class72

8.4使用math... 73

8.5日期类... 74

8.6随机数类 Random.. 75

87 向量类Vector. 75

88 Class类与Runtime... 76

89常用集合类... 77

8.9.1集合(Set... 78

8.9.2列表(List... 79

8.9.3Map. 80

8.10 Java规则表达式... 81

9异常处理... 82

91 异常... 83

9.1.1 异常的基本概念... 83

9.1.2 引起异常的原因:... 83

9.1.3 实例... 84

9.1.4 异常的分类... 84

9.1.5 Java编程语言提供几种预定义的异常,最常见的异常描述:... 84

9.1.6 为何需要异常处理... 85

92异常的处理... 85

9.2.1 简单的异常范例... 85

9.2.2 常规的异常处理范例... 85

9.2.3 异常处理机制的回顾... 91

9.2.4 一个非常实用的异常处理程序... 91

93异常类的继承架构... 92

94声明异常... 93

95 抛出异常... 95

96 自己编写异常类(用户自定义定义异常)... 97

97 IOException异常类的使用... 101

98调用机制... 102

10IO输入/输出... 102

101 ... 102

10.1.1 什么是数据流... 102

10.1.2 数据流的分类... 103

10.1.3 管道流... 104

10.1.4 对象流... 106

10.2 文件... 109

10.2.1 文件I/O.. 109

10.2.2 文件的输入/输出... 112

10.2.3 随机存取文件... 119

10.2.4 串行化... 120

10.3 补充... 122

10.3.1 关于IO的根类... 122

10.3.2 从键盘读... 119

10.3.3 持久性和序列化的概念... 123

11线程... 121

11.1 简介... 121

11.1.1 什么是进程与线程... 124

11.1.2 线程与进程的区别... 124

11.1.3 线程的优势... 124

11.1.4 线程的模型... 125

11.2 线程的实现... 126

11.2.1 创建线程的方式... 126

11.2.2 线程状态... 129

11.2.3 线程的基本控制... 131

11.2.4 线程的调度... 140

11.2.5 同步和死锁... 140

11.2.6 Thread类的重要方法... 148

11.2.12 优先级... 156

11.2.8 线程同步... 157

11.2.9线程间通信... 158

12常用修饰词... 161

121 instanceof163

12.2 transient修饰符... 163

12.3 strictfp关键字... 163

12.4 native修饰符... 164

12.5 Volatile修饰符... 164

附录1Java文档注释... 164

13.1如何插入注释... 164

13.3 方法注释... 165

13.4字段注释... 165

13.5通用注释... 165

13.6包和概述注释... 166

13.7如何提取注释... 166

 

1章 引言

本章内容:

l  程序设计语言的分类

l  Java的发展过程

l  Java程序的类型

l  编译和执行Java程序

1.1 程序设计语言的分类

    程序设计语言是学习计算机技术的基础,它经历了较长的发展过程,有许多不同的分类方法。

    1.按发展过程分类:

    a.机器语言:是以二进制代码的形式组成的机器指令集合,不同的机器有不同的机器语言。这种语言编制的程序运行效率极高,但程序很不直观,编写很简单的功能就需要大量代码,重用性差,而且编写起来效率比较低,很容易出现错误。

    b.汇编语言:比机器语言直观,它将机器指令进行了符号化,并增加了一些功能,如宏,符号地址等,存储空间的安排由机器完成,编程工作相对进行了极大的简化,使用起来方便了很多,错误也相对减少。但不同的指令集的机器仍有不同的汇编语言,程序重用性也很低。

    c.高级语言:高级语言是与机器不相关的一类程序设计语言,读写起来更接近人类的自然语言,因此,用高级语言开发的程序可读性较好,便于维护。同时,由于高级语言并不直接和硬件相关,其编制出来的程序可移植性也要好得多。常见的高级语言Pascal,c,c++,Basic...Java就是高级语言的一种。

    d.第四代语言(4GL)

    具有一定的智能,更接近于日常语言,它对语言的概括更为抽象,从而使语言也更为简洁。

2.按执行方式分类

a.编译执行的语言

通过工具软件将源代码经过目标代码转换成机器代码,即可执行程序,然后直接交操作系统执行。这类程序语言的优点是执行速度比较快,缺点是编译器与机器之间存在一定的依赖性,不同操作系统需要的编译器可能不同,另外,在一个系统上编译的程序到另一系统上不一定正确运行。如C++Pascal...

    b.解释执行的语言

    解释执行是程序读入一句执行一句,而不需要整体编译链接,这样的语言与操作系统的相关性相对较小,但运行效率低,而且需要一定的软件环境来做源代码的解释器。当然,有些解释执行的程序并不是使用源代码来执行的,而是需要预先编译成一种解释器能够识别的格式,再解释执行。如Java

    3.按思维模式分类

  用计算机处理问题是个复杂的过程。需要许多设想,小心规划,逻辑的精度和细节的考虑。另一方面,他又具有挑战性,令人振奋,为个人的创造力和表达能力提供了令人满意的空间。

    程序设计语言总是需要以某种思维方式进行设计和实现,因此不同的语言可能有不同的思维方式。目前存在两种思维方式。

    a.面向过程的程序设计语言

    过程化的解决问题的方法是把它分裂成几个小部分,然后再求解每个较小的问题。最后,汇总这些方案来求解整个问题。这种方法的缺点是模块间互相依赖,难以升级,而且互用性差,解决的方法是使用面向对象的程序设计模型。

    b.面向对象的程序设计语言

    OOP(面向对象程序设计)的设计思想:

    现实世界有对象组成,这些对象被分成类,对象是类的一个特例。OOP根据现实世界的对象来描述软件系统。考察一个公司的职员,他是一个对象,它包含了某些属性:姓名,年龄,家庭住址,电话号码......他也包含了某些行为:允许用户输入或者现实职员的这些属性。OOP的好处是逼真地建模,并且对变动有弹性。

  Java是一种纯面向对象的编程语言。

1.2 Java的发展过程(网络咖啡)

  Java的诞生需追溯到1991年。当时,在Sun公司内,有一个称为Green的项目,这个项目的工程师受命设计一种小型的计算机语言,用于机顶盒、家电控制芯片等消费类设备。由于这些设备功能有限、内存较小,所以这种语言必须尽可能地小,而且应能生成非常紧凑的代码,最重要的一点是,这些设备所采用的处理芯片多种多样,要使这个语言有强大的生命力,就绝不能够受限于任何一种体系结构。

    为了实现这一目标,项目组准备尝试一种古老的模型:设计出一种“可移植”的语言。在执行前,生成一个“中间码”,在任何一种机器上安装了特定的解释器,就可以运行这个“中间码”。

    这样“中间码”一定会非常小,而解释器也不大,就可以达到这个目标。

    这个小组大部分员工都熟悉C++语言,使得这个新语言走向了“面向对象”的道路。这个语言原来的名字是“Oak“(项目组领导办公室窗外的橡树名),但后来由于“Oak”这一名字己被占用,所以选用“Java”一种咖啡的名字作为新语言的名字,所以现在说到Java语言,大家都会想到那杯热气腾腾的咖啡!也许,Sun公司希望Java语言能够象咖啡一样被人们接受、喜爱吧!

    然而Java语言并未被Sun公司和消费类家电公司所接受。接着该小组又开始了许多其它方面的尝试,均惨遭失败,不得不在1994年解散。

    而此时,Internet上的www服务越来越普遍,人们需要一个好用的浏览器,以便更好地阅读Web页面。这时Green项目组的旧部意识到可用Java语言写一个浏览器。这个最终发展成目前的HotJava浏览器。

    HotJava这个试验品在19955月召开的SunWorld大会上首次亮相,从而引起了遍及全球、至今未衰的Java热。

    1995年秋NetScape决定让其浏览器支持Java,并在1996年初发布了支持Java的版本。这意味着Java语言开始大流行,后来,IBM等许多著名IT公司都注册了Java使用许可证,而且视Sun公司为敌的微软公司也在IE中提供了对Java的支持,并在Window中提供了Java虚拟机。

    Java语言的初露锋芒,让Sun公司意识到它的价值,于是在1996年初发布了Java的第一个版本。几个月后,又发布了Java1.02版,但该版本的Java语言并不是十分完备,不适合用于正规的应用程序的开发。但仅过了一小段时间,Sun公司就发布了令人满意的Java1.1版,它实现了绝大部分目标。

  在1998年召开的JavaOne大会上,Sun公司发布了Java1.2版,这就是Java 2.

    [注:JavaScript是网景公司发明的,原名LiveScript]

1.3 Java程序的类型

  Java程序有二类:应用程序和小应用程序(applicationsapplets)

  应用程序是可在任何操作系统提示下执行的程序。单独的应用程序可以是基于窗口的应用程序或是控制台应用程序。基于窗口的应用程序有图形用户界面,而控制台应用程序是基于字符的应用程序,没有图形用户界面。单机的应用程序使用单机的资源。网络应用程序可使用网上资源。分布式应用可访问网上跨许多计算机执行的对象。

  允许应用在本地计算机上读入和写出文件。单独应用程序驻留在本地机器的硬盘上。当需要执行此应用程序时,把它从硬盘装入到内存,并执行之。

  小应用程序是在Web页面内执行的Java程序。因此,不象应用程序,小应用程序需要Java使能的浏览器,如:Microsoft Internet Explorer4.0或以上版本、Netscape Navigator4.0或以上版本、或HotJava。小应用程序在用户通过Web浏览器装入Web页面时被装入和执行。当包含小应用程序的Web页面被显示时,用户与此小应用程序交互。

  Applet使用简单,因为用户启动applet必须做的所有事是访问Web页面。另一方面,应用程序必须在执行它之前下载到本地计算机。Applets有图形用户界面。Applets的安全特权比应用程序差。Applet只可访问宿主机上资源,他不可访问被下载计算机上的文件。

  与应用程序相反,小应用程序可以驻留在远程计算机上。当本地机器需要执行applet时,applet从远程系统被装入到本地机计算机的内存。一旦本地机计算机可用时,由浏览器解释此applet,并与本地提供的库资源链接起来,然后执行之。

1.4 编译和执行Java程序

  有两种方法可以编译和执行Java程序:使用命令行或其他程序,如集成开发环境或者文本编辑器。

  (1)使用命令行

  (a)用记事本写一源文件并保存为".java"文件,例hello.java

    (b)设置环境变量pathclasspath(可设置系统变量,以便以任何用户身份登录都能使用

       —我的电脑|属性|高级|环境变量)

         path: d:\jdk1.3.1_07\bin;

       classpath: d:\ jdk1.3.1_07\lib;

  (c)javac hello.java  //javac程序是Java的编译器。它把hello.java文件编译成hello.class文件

  (d)java welcome  //java程序是Java解释器,它负责解释执行编译器生成到class文件中的字节码。

  (2)使用集成开发环境

    如果有一个Java集成开发环境,就不需要做上面这些繁琐的事情了。集成开发环境不仅可以提供一个能够识别Java语法的编辑器,使读者简单地编译和执行Java程序,还可以建立应用程序或者小程序的框架。并调试程序,将做好的程序打包成可执行文件等等。即集成开发环境集成了编辑、编译、跟踪调试、执行及打包Java应用程序的一切工具,如Microsoft公司出品的Visual J++Borland公司出品的JBuilder,它们对系统资源的侵占非常大,通常建议用户有256MB内存,目前流行的IDE开发工具为eclipse

    初学阶段,不需要编写很复杂的Java程序,对调试方面的需求也并不迫切,使用文本编辑器和JDK就足够了。

  (3)从文本编辑器中编译和运行程序

JCreator,FreeJavaJava开发环境的一个图形用户界面。

 

2 JAVA2介绍

本章内容:

          了解Java及其发展过程

          了解 Java 的特点和优点

          了解 Java 的运行机制

          了解 Java 程序的基本结构

          掌握如何编译和运行 Java 程序

21 什么是JAVA

         Java 完全面向对象的编程语言。

         Java 可用来生成两类程序:应用程序、小应用程序( Applet

         Java 既是一种解释执行的语言,也是一种编译执行的语言。

211 JAVA语言的版本

Java 1.0 小型版本

Java 1.1 1997年,改进了用户界面

Java 1.2  1998年,改进图形用户界面,数据库连接

Java 1.3  2000年,增加新的核心功能

Java 1.4  2002年,安全性的改进,对COBRA的支持等

22选择JAVA的原因

          面向对象

²  Java 中任何东西都是对象,因此,重点集中在数据以及应用程序中的数据操作方法。  

          易于学习

²  Java的风格类似于C++,因而对C++程序员而言非常容易掌握Java编程技术;

²  Java摒弃了C++中容易引发程序错误的地方,如指针操作和内存管理;

          平台无关性

²  Java 程序被编译成一种与体系结构无关的字节代码

²  只要安装了Java运行时系统Java程序可以在任意的处理器上运行

          分布式

        Java提供了包容广泛的例程库,可处理像HTTPFTP这样的TCP/IP协议。Java应用程序可通过一个特定的URL来打开并访问对象,就像访问本地文件系统那样简单。

          健壮性

²  Java在编译和运行程序时都要对可能出现的问题进行检查

²  它提供自动垃圾收集来进行内存管理

²  面向对象的异常处理机制

          安全

²  Java语言提供的安全

²  编译器提供的安全

²  字节码校验器

²  类加载器

          可移植性

²  源代码级

²  目标代码级

          解释执行

²  编译器 javac 将处理.java源文件生成类文件

²  类文件可通过名称为 java 的加载实用程序解释执行,将Java 字节码转换为机器可执行代码。

          高性能

        通过JIT编译器首先将字节码编译成固有代码,将结果缓存下来,然后在需要时调用它们。

          多线程

²  Java语言内置支持多线程的功能

²  Java 类库提供了Thread

          动态

        Java自身的设计使它适合于一个不断发展的环境。在Java类库中可以自由地加入新的方法和实例变量而不会影响用户程序的执行。

23Java 程序的运行环境

Java是解释执行的高级编程语言

编译型源程序

Java虚拟机

源程序

字节码程序

Java源程序

目标程序

编译

连接

编译

执行

解释

执行

.java

.class

 

 

 

24与平台(CPUOS类型)无关性

q  一次编程到处执行,Write Once, Run AnyWhere

q  而传统的计算机应用程序需要针对不同的应用平台进行开发

 

25“一次编译,到处运行的原理

 

 

 

26通过在不同的平台上安装对应的Java 虚拟机,可以实现平台无关。

 

所以… Write once , run anywhere!

27Java虚拟机介绍

         Java虚拟机是一种利用软件方法实现的抽象化的计算机,基于下层的操作系统和硬件平台,可以在上面执行Java的字节码程序。

         Java虚拟机将在内部创建一个运行时系统,帮助以下列方式执行代码:

        加载 .class 文件

        管理内存

        执行垃圾收集

28Java2类库的版本和主要应用领域

2.8.1 Java2类库的版本类型

q  J2MEJava2 Micro Edition),针对嵌入式设备的编程技术

q  J2SEJava2 Standard Edition),针对桌面计算机开发

q  J2EEJava2 Enterprise Edition),针对企业级的分布式应用,主要涉及JDBCRMIEJBServletJSP以及Web ServiceXML等方面的技术

2.8.2 Java2主要应用领域

q  嵌入式技术(如嵌入式设备、移动通讯设备、手持式设备、测试仪器等);

q  基于Application/AppletJavaBeanPC应用;

q  基于动态网站的ServletJSP 应用,实现Web应用程序等

q  基于EJBJ2EE企业级分布式应用等,主要是解决企业级异构环境(不同主机、不同的操作系统)下的编程。 

 

 

29 实时编译器

         使用实时 (Just In Time)” 编译器,也称为JIT编译器。可以提高 Java 代码的执行速度。

         SUN公司在Java 2 发行版中提供了JIT编译器,JIT编译器是Java虚拟机的一部分。

         JIT 的主要用途是将字节码指令集转换为针对某一特定微处理器的机器代码指令。

210  Java 运行时环境

编译时

.java

(源代码)

.class

(字节码)

运行时

类加载器

字节码校验器

解释器

JIT代码

生成器

硬件

网络

211 选择Java开发工具

         JDK是有助于程序员开发Java 程序的 Java 开发工具包包括:

        类库

        编译器

        调试器

        Java 运行时环境( JRE

         JDK 提供的常用工具

Ø  javacJava编译器,将Java源代码换成字节代码

Ø  javaJava解释器,直接从类文件执行Java应用程序代码

Ø  appletviewer(小程序浏览器):一种执行HTML文件上的Java小程序类的Java浏览器

Ø  javadoc:根据Java源代码及其说明语句生成的HTML文档

Ø  jdbJava调试器,可以逐行地执行程序、设置断点和检查变量

Ø  javah:产生可以调用Java过程的C过程,或建立能被Java程序调用的C过程的头文件

Ø  JavapJava反汇编器,显示编译类文件中的可访问功能和数据,同时显示字节代码含义

212Java Application程序基本结构

 

import语句

主类名称

方法体

213第一个JAVA例程

步骤:a.编写   b.编译和运行

214Java API 文档

    Java API 文档是一种非常有用的文档,描述许多 java 的内置功能,包含类、包、接口等的帮助。

加载 API 文档的步骤:

  1. 打开 Internet Explorer
  2. 键入下列 URL http://java.sun.com/j2se/1.4.1/docs/api/index.html
  3. 通过单击相关主题,可以浏览网页。

 

 

总 结

         Java 是一种面向对象的编程语言,主要用于 Internet 应用程序。

         Java 可用来生成两类程序:应用程序、小应用程序( Applet

         Java 既是一种解释执行的语言,也是一种编译执行的语言。

         Java 的特点包括:

²  -面向对象

²  -易于学习

²  -平台无关性

         Java 虚拟机是 Java 编程语言的核心。

         Java 运行时环境 (JRE) 包含一方面与硬件交互、另一方面又与程序交互的 JVM

         Java 开发工具包有助于开发 Java 程序。

         JDK 提供多种工具,这些工具位于 JDK bin 目录下,具体如下:

        javac

        java

        appletviewer

 

 

 

 

 

 

3Java语言基础

本章内容:

l  Java的基本语法

l  数据类型

l  运算符

l  流程控制语句

l  数组

l  注释

31Java的基本语法

代码主要是放入类中

Java是严格区分大小写的

w  Java是一种自由格式的语言

      程序代码分为结构定义语句和功能执行语句。

     功能执行语句的最后必须用(;)结束。

w  Java中的标识符

      java中的包、类、方法和变量的名字。可由任意顺序的大小写字母、数字、下划线(_)和美元符号($)组成,但标识符不能以数字开头,不能是关键字。

如:_userName$usernameusernameuser_nameclass100.1Hello World

Java中的关键字

abcstract

eo

implement

privste

this

boolean

double

import

proterted

throw

break

else

instanceof

public

throws

byte

extend

int

return

transient

case

false

interface

short

true

catch

final

long

static

try

char

finally

native

strictfp

void

f

float

new

super

volatile

f

for

null

seitch

while

default

if

package

 

synchronized

32Java中的常量

w  整型常量

              十进制(18),十六进制(0x18),八进制(018)

w  长整型常量

              13L

w  单精度浮点数

              5.1f4f2e3f0f

w  双精度浮点数

              5.142e-30d

w  布尔常量

              truefalse

w  字符常量

       ‘a’ ‘ 6’, ‘\u0001’Unicode值)

       ‘\r’表示接受键盘输入,相当于按下了回车键

       ‘\n’是换行

       ‘\t’是制表符,相当于table

       ‘\b’是退格键,相当于Back Space;

       ‘\ ‘’是单引号

       ‘\”’是双引号

       ‘\\’反斜杠

w  字符串常量

              “Hello World”, “7431”, “noise \nXXX”

w  null常量

              null常量只有一个值,用null表示,表示对象的引用为空

33 变量的概念与作用

w  变量就是系统为程序分配的一块内存单元,用来存储各种类型的数据。根据所存储的数据类型的不同,有各种不同类型的变量。变量名代表这块内存中的数据。

w  Int x=0,y;

              y=x+3;

              执行这两条语句的过程中,内存分配及变化情况。X在被取值前必须被初始化。

34 Java中的数据类型

       3.4.1数据类型种类

q  基本数据类型(intfloatcharbooleanbyte等),Java中没有unsigned类型

q  引用数据类型(数组、类、接口等)。

基本数据类型

引用数据类型

数据类型

数值型

字符型(char)

布尔型(boolean)

** if(x=0)

(class)

接口(interface)

数组

整数类型(byte,short,int,long)

浮点类型(float,double)

 

3.4.2 Java中数据类型的转换规则:

q  不同类型的数据混合运算时,系统自动将数据从低级转换到高级(域小的自动转化为域大的数据类型)

q  高级的数据类型转换到低级时,必须强制进行数据类型转换。

                            Char

                                      

 

Byte          short           int              long

 

 

 

                            Float             double

 

 

                    数字类型间的合法转换

 

3.4.3数据类型转换必须满足如下规则

q  不能对boolean类型进行类型转换。

q  不能把对象类型转换成不相关类的对象。

q  转换过程中可能导致溢出或损失精度

q   int i = 8;  byte b=(byte)i;

q   (byte)255 == -1       (byte)0x5634 == 0x34

q   浮点数到整数的转换是通过舍弃小数得到,而不是四舍五入

(int)23.7 == 23       (int)-45.89f == -45

35Java中的运算符与表达式

3.5.1主要的类型

q  算术运算符: +,―,*/%++,――

q  关系运算符: ><>=<===!=

q  布尔逻辑运算符: !&&||

q  位运算符: >><<>>>&|^~

q  赋值运算符: =,及其扩展赋值运算符如+=,―=*=/=等。

q  条件运算符:  ?

q  其它

ü  分量运算符    ·

ü  下标运算符    []

ü  实例运算符   instanceof

ü  内存分配运算符new

ü  强制类型转换运算符 (类型)

ü  方法调用运算符()

3.5.2特点

       基本上与C/C++相同,但不同的是:

q  新增 instanceof(关系运算符):识别某对象是否为某类的对象,即测试它左边的对象是否是它右边的类的实例,返回boolean类型

void Cook(Person man)

{     if(man instanceof Chinese)

{//如果识别是中国人则按照中国人的习惯来Cook

}

else if(man instanceof American)

{//如果识别是美国人则按照美国人的习惯来Cook

}

}

q  改造“+”运算符:使其能够适应字符串运算:System.out.println("How Are"+"You !");

3.5.3几个常用的操作符在应用时所应该注意点

q  +

ABCD+EFGHàABCDEFGH

ABCD+E+F   àABCDEF

A+Bà两个字符的内码相加

q  字符串赋值

        String X=ABCD;

   String Y=X;

   X=EFGH;

此时Y仍然为“ABCD”,因为每进行一次字符串赋值,系统将重新构造出一个新的字符串内存地址,然后将字符串内容拷贝

可以采用StringBuffer来代替,以免产生大量的字符串空间的垃圾

3.5.4引用型变量只支持有限的逻辑判断

1)相等判断(是否同一个对象的引用)

q  运算符==  !=

q  示例

       theObject == null      

       otherObject != theObject

2)类型判断(是否是每个类的实例)

q  运算符instanceof

q  示例

       theObject instanceof Object

       “” instanceof String

3.5.5运算符的优先次序

1     . , [] , ()                                  9      &

2   ++ , -- , ! , ~,instanceof          10      ^

3     new (type)                            11      |

4     * , / , %                                 12      &&

5     + , -                                       13      ||

6     14      ?:

7     > , < , >= , <=               15  = , += , -= , *= , /= , %= , ^=

8     == , !=                       16&= , |= ,<<= , >>= , >>>=

3.5.6格式化输出

可以使用语句System.out.print(x) 向控制台打印数字x。这条命令将按照x类型所允许的最多非零数字位数打印x。如:

x=10000.0/3.0;

System.out.println(x);

打印结果是:

3333.3333333333335

如果想显示的是美元、美分等内容,这么做有些问题,可以通过控制显示格式处理输出。Java.text包中的NumberFormat类有三个方法可以产生下列数据的标准格式化器:

l  数字

l  货币

l  百分数

10000.0/3.0在这三种格式中打印如下:

3,333.333          NumberFormat.getNumberInstance()

$3,333.33          NumberFormat.getCurrencyInstance()

333,333%          NumberFormat.getPercentInstance()

/*

           double x=10000.0/3.0;

    //     NumberFormat formatter=NumberFormat.getNumberInstance();

           NumberFormat   formatter=NumberFormat.getCurrencyInstance(Locale.US);

    //     formatter.setMaximumFractionDigits(4);

    //     formatter.setMinimumIntegerDigits(6);

           String s=formatter.format(x);

           System.out.println(s);

*/

36Java中的语句及控制流

3.6.1主要的语句

q  分支语句:if-else, switch

q  循环语句:while, do-while, for

q  与程序转移有关的其它语句:break, continue, return

q  例外处理语句:try-catch-finally, throw

3.6.2特点

基本上类同于C/C++语言,但不同的是

1)无goto 语句。

2)改造了continue语句,跳转到括号指明的外层循环中。

3)改造了break语句,跳转到该语句标号处,从此再执行该语句。

注意:

       breakcontinue的标准功能继续保留!

块作用域

块或复合语句是用一对花括号括起的任意数量的简单Java语句。块定义了变量的作用范围。块可以嵌套在其他块中。下面是一个块嵌套在main方法中的例子:

   {

            int n;

            ...

            {

                     int k;

                     int n;

                     ...

            }

   }

If语句

w  条件语句是程序设计语言中最基本的流程控制语句,几乎任何一门程序设计语言的条件语句都用到了if关键字,因而条件语句也被称为if语句。条件语句分简单语句和复合语句

语法:if(Boolean){statement1}

         else{statement2}

 

public class ifElse

{

public static void main(String args[])

{

int month;

String season="  ";

for(month=1;month<=13;month++)

{

if(month==3||month==4||month==5)

season="春季(Spring)";

else if(month==6||month==7||month==8)

season="夏季(Summer)";

else if(month==9||month==10||month==11)

season="秋季(Autumn)";

else if(month==12||month==1||month==2)

season="冬季(Winter)";

else season="不合理月份(Bogus)";

System.out.println(month+"月是"+season);

}

}

}

 

public class ExampleIf

{

public static void main(String[] args)

{

boolean a=false;

boolean b=false;

if(a==b)

{

System.out.println("a等于b");

}

else

{

System.out.println("a不等于b");

}

}

}

switch

w  switch是条件语句的一个变种 ,运用switch语句时,首先需要计算括号内表达式的值,然后把这个值与case后面的常量比较。执行第一个匹配的分支语句;若无匹配则执行最后一个default分支,如果同时缺省default项,则不执行任何语句。

 

switch(expression)

{

case 1:

statement 1;

break;

case 2:

statement 2;

break;

..........

default:

statement 3;

break;

}

 

public class SwitchCaseDefault

{

public static void main(String[] args)

{

int a=2;

switch(a)

{

case 1:

System.out.println("a="+a);

break;

case 2:

System.out.println("a="+a);

break;

default:

System.out.println("I  DON'T  KONW");

}

}

}

for循环结构

w  for语句是最标准的循环语句,也是功能最强的一种循环结构。for语句的功能是循环执行一段语句,直到某个条件为假时才结束。

 

public class sample2

{

public static void main(String args[])

{

int i;

for(i=0;i<=10;i++)

{

System.out.println("\nThe line "+i);

}

}

while循环结构

w  while语句是Java最基本的循环语句。当它的控制表达式是真时,while语句重复执行一个语句或语句块;直到控制表达式为假时才结束。

 

public class WhileExample

{

public static void main(String args[])

{

int n = 10;

while(n > 0)

{

System.out.println("tick " + n);

n--;

}

}

}

do… while循环结构

w  如果while循环一开始条件表达式就是假的,那么循环体就根本不被执行。然而,有时需要在开始时条件表达式即使是假的情况下,while循环至少也要执行一次。Java就提供了这样的循环:do-while循环。do-while循环总是执行它的循环体至少一次,因为它的条件表达式在循环的结尾。

public static void main(String args[])

{

int n = 10;

do

{

System.out.println("tick " + n);

n--;

} while(n > 0);

}

}

breakcontinue

w  在循环语句的主体内(statement),可以使用break语句和continue来控制循环流程。

w  break:跳出循环,不再执行剩余部分。

w  continue:停止当次循环,回到循环起始处。continue语句之后的语句将不在执行。

w  无穷循环:

第一种形式:for(;;)

第二种形式:while(true)

3.7 大数字

编译运行下面这个程序:

public class Test{

    public static void main(String args[]){

        System.out.println(0.05+0.01);

        System.out.println(1.0-0.42);

        System.out.println(4.015*100);

        System.out.println(123.3/100);

    }

};

结果是:

0.060000000000000005

0.5800000000000001

401.49999999999994

1.2329999999999999

Java中的简单浮点数类型floatdouble不能够进行运算。不光是Java,在其它很多编程语言中也有这样的问题。在大多数情况下,计算的结果是准确的,但是多试几次(可以做一个循环)就可以试出类似上面的错误。

解决的方法是使用java.math包中的两个类,BigIntegerBigDecimal。这两个类可以操作任意长的数字。BigInteger类实现了任意精度的整数运算,而BigDecimal实现了任意精度的浮点运算。

BigDecimal一共有4个够造方法,我们不关心用BigInteger来够造的那两个,那么还有两个,它们是:

BigDecimal(double val) 

BigDecimal(String val) 

如果需要精确计算,非要用String来够造BigDecimal不可!

使用double构造BigDecimal将导致精度不准确,原因是double本身就不精确。

BigDecimal bd=new BigDecimal(.1);

结果bd的值将会是0.1000000000000000055511151231257827021181583404541015625。必须用字符串来构造BigDecimal才能显示定义其精确度,

BigDecimal bd2=new BigDecimal(".1");

然而,不能使用熟悉的数学操作符,如+*等来操作大数字。要实现这些功能,必须使用大数字类中addsubtractmultiplydividemod等方法。

l  BigInteger add(BigInteger other)

l  BigInteger subtract(BigInteger other)

l  BigInteger multiply(BigInteger other)

l  BigInteger divide(BigInteger other)

l  BigInteger mod(BigInteger other)

 

 [示例源文件Arith.java]

import java.math.BigDecimal;

/**

 * 由于Java的简单类型不能够精确的对浮点数进行运算,这个工具类提供精

 * 确的浮点数运算,包括加减乘除和四舍五入。

 */

public class Arith{

//默认除法运算精度

    private static final int DEF_DIV_SCALE = 10;

    //这个类不能实例化

    private Arith(){

    }

    /**

     * 提供精确的加法运算。

     * @param v1 被加数

     * @param v2 加数

     * @return 两个参数的和

     */

    public static double add(double v1,double v2){

        BigDecimal b1 = new BigDecimal(Double.toString(v1));

        BigDecimal b2 = new BigDecimal(Double.toString(v2));

        return b1.add(b2).doubleValue();

    }

    /**

     * 提供精确的减法运算。

     * @param v1 被减数

     * @param v2 减数

     * @return 两个参数的差

     */

    public static double sub(double v1,double v2){

        BigDecimal b1 = new BigDecimal(Double.toString(v1));

        BigDecimal b2 = new BigDecimal(Double.toString(v2));

        return b1.subtract(b2).doubleValue();

    } 

    /**

     * 提供精确的乘法运算。

     * @param v1 被乘数

     * @param v2 乘数

     * @return 两个参数的积

     */

    public static double mul(double v1,double v2){

        BigDecimal b1 = new BigDecimal(Double.toString(v1));

        BigDecimal b2 = new BigDecimal(Double.toString(v2));

        return b1.multiply(b2).doubleValue();

    }

    /**

     * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到

     * 小数点以后10位,以后的数字四舍五入。

     * @param v1 被除数

     * @param v2 除数

     * @return 两个参数的商

     */

    public static double div(double v1,double v2){

        return div(v1,v2,DEF_DIV_SCALE);

    }

    /**

     * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指

     * 定精度,以后的数字四舍五入。

     * @param v1 被除数

     * @param v2 除数

     * @param scale 表示表示需要精确到小数点以后几位。

     * @return 两个参数的商

     */

    public static double div(double v1,double v2,int scale){

        if(scale<0){

            throw new IllegalArgumentException(

                "The scale must be a positive integer or zero");

        }

        BigDecimal b1 = new BigDecimal(Double.toString(v1));

        BigDecimal b2 = new BigDecimal(Double.toString(v2));

        return b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();

    }

    /**

     * 提供精确的小数位四舍五入处理。

     * @param v 需要四舍五入的数字

     * @param scale 小数点后保留几位

     * @return 四舍五入后的结果

     */

    public static double round(double v,int scale){

        if(scale<0){

            throw new IllegalArgumentException(

                "The scale must be a positive integer or zero");

        }

        BigDecimal b = new BigDecimal(Double.toString(v));

        BigDecimal one = new BigDecimal("1");

        return b.divide(one,scale,BigDecimal.ROUND_HALF_UP).doubleValue();

    }

};

38数组

假设在程序中需要定义100个整数变量,并且要求计算出它们相加的结果。这在程序中应该怎么去描述呢?至少要定义100个整数变量,一次定义100个整数变量,再把它们相加,这是一件很吃力的事情。

w  数组(array)是相同类型变量的集合,可以使用共同的名字引用它。数组可被定义为任何类型,可以是一维或多维,数组中的一个特别要素是通过下标来访问它。

w  数组一旦被创建,就不能再改变它的大小(尽管能够改变数组元素)。如果在程序运行过程中经常需要扩展数组,则应该使用另一种数据结构,数组列表。

w  定义(声明)数组时,既可使用语法int[] a; 也可使用语法int a[]

w  使用new操作符创建数组如:int[] a=new int[100];

w  给数组中的每个元素赋值,如:

int[] a=new int[100]

for(int i=0;i<100;i++) i<100也可写为i<a.length

a[i]=I;

3.8.1数组初始化和匿名数组

Java中有个创建数组对象并同时赋与其初始值的简写形式,如:

Int[] smallPrimes={2,3,5,7,11,13}; 没有使用new操作符

Java,还可以初始化一个匿名数组:new int[]{17,19,23,29,31,37};

3.8.2拷贝数组

可以把一个数组变量拷贝给另一个,这时,两个变量都指向相同的数组:

a.完全拷贝

int[] luckyNumbers=smallPrimes;

       2

       3

       5

       7

       11

       13

    mallPrimes=

 

  luckyNumbers=

                                                 拷贝数组变量

luckyNumbers[5]=12;//现在smallPrimes[5]的值也是12

b.部分拷贝

如果实际上只是想一个数组中的值拷贝给另一个变量,那么需要使用System类中的arraycopy方法,其语法如下:

System.arraycopy(from, fromIndex, to, toIndex, count);//to数组必须有足够的空间来容纳拷贝的元素。

              int[] smallPrimes={2,3,5,7,11,13};

              int[] luckyNumbers={1001,1002,1003,1004,1005,1006,1007};

              System.arraycopy(smallPrimes,2,luckyNumbers,3,4);

              for(int i=0;i<luckyNumbers.length;i++)

              System.out.println(i+": "+luckyNumbers[i]);)

 

 

       2

       3

       5

       7

       11

       13

    mallPrimes=

 

 

      1001

      1002

      1003

       7

       11

       13

  luckyNumbers=

                      数组间拷贝值

3.8.3命令行参数

每个Java程序都有一个main方法,它带有String[] args参数。这个参数表示main方法接收了一个字符串数组,也就是命令行参数。

3.8.4对数组排序

对数组中的数字排序,可以Arrays类中的一种sort方法:

int[] a=new int[10000];

Arrays.sort(a);

[示例LotteryDrawing.java]

import java.util.*;

import javax.swing.*;

public class LotteryDrawing{

       public static void main(String[] args){

              String input=JOptionPane.showInputDialog("How many numbers do you need to draw?");

              int k=Integer.parseInt(input);

              input=JOptionPane.showInputDialog("What is the highest number you can draw?");

              int n=Integer.parseInt(input);

              int[] numbers=new int[n];

              for(int i=0;i<numbers.length;i++)

              numbers[i]=i+1;

              int[] result=new int[k];

              for(int i=0;i<result.length;i++)        {

                     int r=(int)(Math.random()*n);

                     result[i]=numbers[r];

                     numbers[r]=numbers[n-1];

                     n--;

              }

              Arrays.sort(result);

              System.out.println("Bet the following combination. It'll make you rich!");

              for(int i=0;i<result.length;i++)

              System.out.println(result[i]);

              System.exit(0);

       }

}

3.8.5多维数组

Java中,多维数组(multidimensional arrays)实际上是数组的数组。定义多维数组变量要将每个维数放在它们各自的方括号中。例如,下面语句定义了一个名为more的二维数组变量。

int more[][] = new int[4][5]; 

 

 

 

 

public class MoreArray

{

 

public static void main(String args[])

{

int twoD[][]= new int[4][5];

int i, j, k = 0;

for(i=0; i<4; i++)

for(j=0; j<5; j++)

{

twoD[i][j] = k;

k++;

}

for(i=0; i<4; i++)

{

for(j=0; j<5; j++)

System.out.print(twoD[i][j] + " ");

System.out.println();

}

}

}

    当给多维数组分配内存时,它只需指定第一个(最左边)维数的内存即可。也可以单独地给余下的维数分配内存。例如,下面的程序在数组more被定义时给它的第一个维数分配内存,对第二维则是手工分配地址。

int more[][] = new int[4][];

more [0] = new int[5];

more [1] = new int[5];

more [2] = new int[5];

more [3] = new int[5];

3.8.6不规则数组

    当手工分配内存时,不需要给每个维数相同数量的元素分配内存。多维数组实际上是数组的数组,每个数组的维数在它的控制之下。例如,下列程序定义了一个二维数组,它的第二维的大小是不相等的。

 

public class MoreArray2

{

public static void main(String args[])

{

int more[][] = new int[4][];

more[0] = new int[1];

more[1] = new int[2];

more[2] = new int[3];

more[3] = new int[4];

int i, j, k = 0;

for(i=0; i<4; i++)

for(j=0; j<i+1; j++)

{

more[i][j] = k;

k++;

}

for(i=0; i<4; i++)

{

for(j=0; j<i+1; j++)

System.out.print(more[i][j] + " ");

System.out.println();

}

}

}

39 Java中的注释方式

1、在Java程序中主要可以采用如下几种注释方式

  C 语言的方式:  

  /* ..... */ 适用于多行注释文字

  Java中的/*  */注释并不嵌套使用。即,当一段代码本身含有*/符号时,无法简单地在这段代码的首尾分别加上/**/就把它注释掉。

  C++语言的方式:

  // 适用于单行注释文字

  Java语言的方式:

/**.....*/适用于多行文档注释文字

2、举例

1

              // 单行注释,简单的解释语句含义.

 

2

       /* 多行注释,用来说明更多的内容,包括算法等.

              ……

       */

3

       /** Java文档注释,可以通过javadoc

     * 成类和接口的HTML格式的帮助文档.

         * 这种注释有其特殊的格式(参见相关文档)

  */

310 JAVA的健壮的体现

w  1JAVA是强类型语言———每种类型的数据必须赋与对应的数据值;

w  2JAVA没有指针——— 不能强行访问指定的内存空间;

w  3JAVA进行自动内存回收;

w  4JAVA鼓励用接口而不要类。

311 JAVAC++在语言上的差别

w  1)不再包括预处理器的功能

w  2)不再有structureuniontypedef

w  3)取消了函数而改称为方法

w  4)取消了多重继承

w  5)取消了goto

w  6)不再支持指针

 

 

 

 

 

 

4类的基本架构

本章重点:

l  认识类

l  方法的使用

l  参数的传递

l  方法的返回值

l  公有成员、私有成员及缺省访问修饰成员

    随着商业逻辑的复杂化,外界对程序设计的要求日趋复杂,面向对象的概念也就跟着孕育而生。类为面向对象程序设计最重要的内容。

41认识类

    Java是纯面向对象的程序设计语言,每一个Java程序均少不了类,Java程序都至少存在一个或一个以上的类。Java中所有的变量和函数都必须写在类(class)里,

411类的基本概念

类的发展,是为了让程序语言更能清楚地描述出日常生活的事物。

    例如,长方形(rectangle)是几何形状中常见的一种,它的边长有宽(width)和高(height)两个属性,根据这两个属性便可求出它的面积(area)和周长(perimeter)如何利用Java的类来描述长方形,使得它可以保存长方形的信息(宽与高),并能利用此类计算出面积与周长呢?作法是:定义一个长方形类。一般来说,类是由数据成员与函数成员封装而成的,它们的基本概念是:

数据成员

    每一个长方形,不论尽寸的大小,均具有宽与高这两个属性,而这两个属性也就是长方形类的数据成员,当然,长方形类还可能有其他的数据,如颜色等。

函数成员

对于长方形类而言,除了宽与高这两个数据成员以外,还想计算其面积和周长。因此可以把计算面积与周长这两个函数纳入长方形类里,变成类的函数成员。Java称这种封装于类内的函数为方法

因此,所谓的是把事物的数据与相关功能封装在一起,形成一种特殊的结构,用以表达真实事物的一种抽象概念。

width

height

数据成员

 

函数成员

Area=width*height

Perimeter=2(width+height)

长方形类

Area()

Perimeter()

Width

height

数据成员,field

函数成员,method

412类的声明

  使用类之前,必须先定义它,然后才可利用所定义的类

来声明变量,并创建对象。类定义的语法如下:

 [格式4-1   类的定义]

 class 类名称

 {

     数据类型  field 名称;

      …

     返回值的数据类型  方法名称(参数1,参数2

    {

       程序语句;

       Return 表达式;

        }

         …

 }

以前述的范例为例,可定义如下的长方形类:

Class CRrectangle

{

    int width;

    int height;

 

    int area()

   {

    return width*height;

   }

    int perimeter()

   {

    return 2*(width+height);

   }

}

413  创建新的对象

先打造一个长方形的模板(定义类),再以此模板制造长方形(创建对象)

由类所创建的对象,即为类的instance”

Width

height

Area()

Perimeter()

Width=12

Height=5

Width=6

Height=10

长方形类

4-3  由类创建新的对象

长方形类所建立的对象(1

长方形类所建立的对象(2

§声明与创建对象

  要创建属于某类的对象,可通过下面两个步骤来达成

  (1)声明指向由类所创建的对象的变量。

  (2)利用new创建新的对象,并指派给先前所创建的变量。

  例如:要创建长方形类的对象,可用下列的语法来创建:

    CRectangle rect1;

  rect1=new CRectangle();

  以上两句可缩减成一行:

  CRectangle rect1=new CRectangle();

§指向对象的变量

    变量rect1并不是基本类型的变量,它所存放的并非对象的实体,而是指向对象实体的一个参考地址。

Ox1000

4-4 创建对象,并让变量rectl指向它

Ox1000

rect1

对象CRectangle

参考地址

①先建立指向对象参考地址的变量

②配置存储CRectangle对象所需的内存空间

§访问对象的内容

     如果要访问到对象里的某个变量(field)时,可以通过下面语法:

        [格式3-2  访问对象中某个变量]

            对象名称.特定的field

  例如,对象rect1的宽和高可通过下列的语法来指定:

  rect1.width;

  rect1.height;

  public static void main(String args[])

  {

   CRectangle rect1;

   rect1=new Crectangle();

 

   rect1.width=12;

           rect1.height=5;

           …

  }

3-5  对成员(或field进行访问的操作

Width

height

Area()

Perimeter()

Width=12

Height=5

长方形类CRectangle

Crectangle类所建立的对象rectl

 

Rectl.width=12

Rectl.height=5

414   使用类来设计完整的程序

415   同时创建多个对象

4有关方法的使用

421 定义与使用方法

   [格式3-3  声明方法,并定义其内容]

    返回值类型  方法名称(类型  参数1,类型  参数2

   {

      程序语句;

            Return表达式;

      }

    对象要调用封装类里的方法时,只要用下列的语法即可:

    对象名称.方法名称(参数1,参数2

    注意,若不需要传递参数到方法中,只要将左右括号写出,不必填入任何内容。

Class app4_3

{

      …

      rect1.area();

      …

      rect1.perimeter();

      …

}

Class Crectangle

{

      …

      int area()

      {

            return width*height;

      }

      int perimeter()

      {

            return 2*(width+height);

      }

}

4-6  方法的运行过程

4.2.2  再看一个简单的范例

App3_04

 

Pi

radius

Pi

radius

3.14

2.0

3.14

2.0

4-3 不同对象的数据成员在内存中的分配情形

422  数据成员的访问方式

       main()方法内如果需要访问field(如radius,pi)时,可通过:

   指向对象的变量.field名称来进行。

         cir1.radius=2.0;

              cir1.pi=3.0;

    然而,如果是在类声明内部使用这些field,则可直接取用field的名称,而

不需加上调用它的对象名称(事实上,在编写类的定义时,我们根本不知道

哪一个对象要调用它)

   总而言之,在类声明之外需要用到field名称时,则必须指明是哪一个对象

变量,也就是用指向对象的变量.field名称的语法来访问。相反,若是在类声

明的内部使用这些field时,由于此时己是站在对象的角度来看待field,所以也

就不必指出field之前的对象名称了。

§  This 关键字

           如果要刻意强调对象本身的Field”的话,也可在field前面加上this这个关键字,即“this.field名称。引时的this即代表取用此 field的对象。

423  在类定义的内部调用方法

   到目前为止所学过的方法均是在类定义的外部被调用,其所采用的语法为:

      对象名称.方法名称(参数1,参数2

      例:cir1.show_area();

    事实上在类定义的内部,方法与方法之间也可以相互调用。

   在同一类的定义里面,某一方法仍然可以直接调用其他方法,而不需加上    “指向对象的变量名称

  同field一样,如果要刻意强调对象本身的方法的话,也可在方法前面加上this这个保留字,即“this.方法名,此时的this即代表取用此一方法的对象。

4参数的传递

   当方法不需要传递任何参数时,方法的括号内什么也不用填,如 show_area()--没有传递任何参数的methods,事实上,方法也可加上各种数据类型的参数,以应付各种不同的计算需求。

431  调用方法并传递参数

  调用方法并传递参数时,参数是置于方法的括号内来进行传递。括号内的参数可以是数值、字符串,甚至是对象。

432  传递多个参数

  如果要传递两个以上的参数,只要将所有的参数置于方法

[格式4-4  方法的定义格式]

 

   返回值类型  方法名称(类型  参数1,类型  参数2

  {

      程序语句;

      Return 表达式;

   }

  如果方法有返回值,则在方法一开始声明时便必须写上返回值类型,至于要返回的值,则把它置于return语句后面。通常return置于方法所有语句的最后面。

4方法的返回值

使用方法时,不仅可以传递参数到方法内;也可以让方法返回特定的值到调用端的程序,这个返回来的值称为返回值。返回值可以是数值、字符串,或者是一个对象。值得注意的是,对任何一个方法而言,Java允许在程序中传递多个参数到方法内,但方法的返回值却只能有一个。

[格式4-4  方法的定义格式]

返回值类型  方法名称(类型  参数1,类型  参数2

  {

      程序语句;

      Return 表达式;

   }

如果方法有返回值,则在方法一开始声明时便必须写上返回值类型,至于要返回的值,则把它置于return语句后面。通常Return置于方法所有语句的最后面。

441  没有返回值的方法

   有些方法不必传递任何数据到调用端程序,因此没有返回值。若方法本身没有返回值,则必须在方法定义的前面加上关键字voidvoid的本意是空无所有之意。

   当方法没有返回值,则方法最后的return语句可以省略,也可以加上,但不接任何的表达式。若方法本身没有返回值,则必须在前面加上void

Void show_area()  {     //show_area()方法,显示出圆面积

        System.out.println(“area=“+pi*radius*radius)

  return;

}                

因没有返回值,故可在方法最后加上return语句,但不接任何的表达式,其执行结果与没有该语句的执行结果相同

442  有返回值的方法

  double getRadius(){

   return radius;

  }

由于返回值的数据类型为double,因此getRadius()方法之前要冠上double

4公有成员与私有成员

  在类外部更改的数据成员,虽然对程序员来说非常方便,但从某个层面来说,却是隐藏着潜在的危险。

  总之,从类外部访问数据成员时,如果没有一个机制来限定访问的方式,则很可能导致安全上的漏洞,而让bug进驻程序代码中。

45创建私有成员

  若数据成员没有一个机制来限定类中field的访问,则很可能会造成错误的输入。为了防止这种情况发生,Java提供了私有成员的赋值即在field声明的前面加上private,这样便无法从类以外的地方赋值或读取到类内部的成员,因此可达到数据保护的目的。

  到目前为止,我们所编写的Java程序均是在同一个文件里中,事实上,如果同一个文件包含有多个类,则这些类将被视为在同一个package内。

452  创建公有成员

  既然类外部无法访问到类内部的私有成员,则Java就必须提供另外的机制,使私有成员得以通过这个机制供外界访问。解决此问题的的方法是创建公有成员。即,在类的外部可对类内的公有成员做访问的操作,因此可通过公有成员的方法来对私有成员做处理。

453  数据的封装

  在OOP的术语里,所谓的封装,就是把属性和方法依功能划分为私有成员公有成员,并且包装在一个类内来保护私有成员,使得它不会直接受到外界的访问。

454  省略publicprivate

  publicprivate是用来赋值公有与私有成员的标识符。标识符是可以略去不写的。

  如果类的成员之前省略publicprivate标识符的话,则表示这个成员只能在同一个package(包)里被访问。这种访问方式被称为友好访问。

到目前为止,我们所编写的Java程序均是在同一个文件里中,事实上,如果同一个文件包含有多个类,则这些类将被视为在同一个package

 

**每个类都可以有一个main方法。这在对类进行单元测试时是一个很方便的技巧。

 

 

 

 

 

 

5类的其它功能

本章内容:

l  函数成员(方法)的重载

Ø  重载

Ø  使用重载常犯的错误

l  构造函数

Ø  构造函数的基本认识

Ø  构造函数的调用时机

Ø  构造函数的重载

Ø  从某一构造函数调用另一构造函数

Ø  构造函数的公有与私有

Ø  构造函数的省略

l  类变量与类方法

Ø  实例变量与实例方法

Ø  类变量

Ø  类方法

Ø  类方法使用的限制

l  类类型的变量

Ø  赋值给类类型的变量

Ø  以类类型的变量传递参数

Ø  由方法返回类类型的变量

Ø  释放内存

l  利用数组来保存对象

Ø  创建对象数组的范例

Ø  传递对象数组到方法里

l  内部类

Ø  内部类的编写

Ø  匿名内部类

5函数成员(方法)的重载

  所谓的重载是指相同的方法名称,如果参数个数不同,或者是参数个数相同、类型不同的话,方法便具有不同的功能

511 重载

class CCircle

{

      private String color;

      private double pi=3.14;

      private double radius;

     

      public void setColor(String str)

      {

             color=str;

      }

      public void setRadius(double r)

      {

             radius=r;

      }

      public void setAll(String str,double r)

      {

             color=str;

             radius=r;

      }

      public void show()

      {

             System.out.println("color="+color+",Radius="+radius);

             System.out.println("area="+pi*radius*radius);

      }

}

public class Test

{

      public static void main(String[] ar)

      {

             CCircle cir1=new CCircle();

            

             cir1.setColor("Red");

             cir1.setRadius(2.0);

             cir1.show();

            

             cir1.setAll("Blue",4.0);

             cir1.show();

      }

}

其中的set Color()setRadius()setAll()均是在赋值对象的数据成员,如同是家里分别买了冷气机、暖气机和除湿机一样,不但占空间,且维护上也不怎么方便。Java的重载功能,恰可补足这方面的缺憾。利用重载编写的代码如

class CCircle

{

       private String color;

       private double pi=3.14;

       private double radius;

      

       public void setCircle(String str)

       {

              color=str;

       }

       public void setCircle(double r)

       {

              radius=r;

       }

       public void setCircle(String str,double r)

       {

              color=str;

              radius=r;

       }

       public void show()

       {

              System.out.println("color="+color+",Radius="+radius);

              System.out.println("area="+pi*radius*radius);

       }

}

public class Test

{

       public static void main(String ar[])

       {

              CCircle cir1=new CCircle();

             

              cir1.setCircle("Red");

              cir1.setCircle(2.0);

              cir1.show();

             

              cir1.setCircle("Blue",4.0);

              cir1.show();

       }

}

  所谓重载是指相同的方法名称,可根据其参数的不同(可能是参数个数、类型或顺序不同)来设计不同的功能。

    通过方法的重载,只需有一个方法名称,却可拥有不同的功用,使用起来相当的方便。

    编译器通过匹配具体的方法调用中所使用的值的类型和多个方法头中的参数类型挑选出正确调用的方法。如果编译器找不到匹配的参数或者找到多个可能的匹配,则会产生一个编译时错误(这个过程称为重载解析。)

512 使用重载常犯的错误

方法名相同,但方法的特征不同(包括方法参数的个数、类型或顺序),这样的一组方法称为方法重载,只有返回类型不同的一组方法不能被称为方法重载。例: 

    public void setCircle(double radius)

    public int setCircle(double radius)

    这两个方法的参数个数和类型完全相同,但返回类型不同,不能称为方法重载。如果尝试编译这样的方法,编译器会出现错误信息。

52 构造函数

  之前所创建的对象,其数据成员均是在对象创建之后,才由方法来赋值。实际上,Java也可以在创建对象的同时,一并赋值它的数据成员。方法是利用构造函数。

521 构造函数的基本认识

  构造函数的功能是帮助新创建的对象赋初值。构造函数可视为一种特殊的方法,它的语法如下:

[格式4-1 构造函数的定义方式]

构造函数的名称必须和类名称相同

标识符  类名称(类型1 参数1,类型2 参数2

        程序语句;

           构造函数没有返回值

 构造函数包含初始化类的成员数据的代码。当类的对象创建时,它自动执行。因此,不管谁创建类对象,构造函数被激活及成员数据被初始化。

构造函数的规则:

  (1)构造函数和声明它的类必须具有相同的名字。

  (2)构造函数没有返回类型。

  (3)不能显式地调用构造函数。

  (4)一个类可以有多个构造函数。

522 构造函数的调用时机

  静态构造函数和对象构造函数分别有不同的调用时机。如

class CCircle

{

       private double pi=3.14;

       private double radius;

       static

       {

              System.out.println("Hello");

       }

       public CCircle(double r)

       {

              radius=r;

       }

       public void show()

       {

              System.out.println("radius="+radius+",area"+pi*radius*radius);

       }

}

 

public class Test

{

       public static void main(String[] args)

       {

              CCircle cir1=new CCircle(4.0);

              cir1.show();

              CCircle cir2=new CCircle(10);

              cir2.show();

       }

}

523 构造函数的重载

  构造函数重载是方法重载的一个应用。

524 从某一构造函数调用另一构造函数

  为了某些特定的运算,Java允许从某一构造函数内调用另一个构造函数。利用这个方法,可缩短程序代码,减少程序开发的时间。

  从某一构造函数内调用另一构造函数,是通过this()这个关键字来调用的。

在某一个构造函数调用另一构造函数时的注意事项:

(1)必须使用this关键字来调用,不能以构造函数直接调用,否则编译器出现错误。

(2)this关键字必须写在构造函数内的第一行的位置,放错了地方也无法编译。

(3)可通过this有参数和无参数的构造函数

  this()_调用无参数的构造函数

  this(参数列表)_调用有参数的构造函数

525 构造函数的公有与私有

  构造函数也有publicprivate之分。Public构造函数可以在程序的任何地方被调用,在任何地方创建的对象均可自动调用它。如果构造函数被设成private,则无法在该构造函数所在的类以外的地方被调用。

526 构造函数的省略

  如果构造函数省略,Java会自动调用默认的构造函数。默认的构造函数的格式如下:

[格式5-2 默认的构造函数。如果没有事先定义好构造函数,则Java会自动调用默认的构造函数]

Public Ccircle()

{

}

    默认的构造函数并没有任何的参数,也不做任何事情。事实上,也就是因为有默认构造函数的这种设计,才使得即使在程序代码里没有编写任何的构造函数,也可以创建对象。

默认的构造函数是没有任何参数的构造函数。如果自行设计了一个没有参数的构造函数,则在创建对象时会调用自选设计的构造函数,而不会调用默认的构造函数。这是Java的覆写所致。

5.2.7初始化块

实际上,Java有三种初始化数据字段的方法:

l  在构造函数中设置值(默认构造函数和非默认构造函数)

l  在声明中赋值

l  通过初始化块设置值

5.2.7.1 在声明中赋值分为默认字段初始化和显式字段初始化

默认字段初始化:如果在构造函数中没有显式地给某个字段赋值,那么它会被自动赋为默认值:数字变量为0,布尔变量为false; 如果是对象引用,那么是null。但是靠这种方式给变量赋值不是一种好的编程习惯。

显式字段初始化:可以在类的定义中简单地把值赋给任何字段,如:

class Employee{ …private String DeptNo=”D002”;}

5.2.7.2初始化块

初始化块又分为初始化块和静态初始化块

由于数据字段存在多种初始化的手段,如果要列出构造过程的所有可能路径极易造成混淆。因此下面给出调用构造函数后详细的执行过程:

l  初始化所有数据字段为默认值(0falsenull)。

l  按照在类声明中出现的次序依次执行所有字段初始化语句和初始化块

l  如果构造函数的第一行代码调用了另一个构造函数,则执行被调用的构造函数主体。

l  执行构造函数主体。

[示例]

//ConstructorTest.java

import java.util.*;

 

public class ConstructorTest{

      public static void main(String[] args){

             Employee[] staff=new Employee[3];

             staff[0]=new Employee("Harry",40000);

             staff[1]=new Employee(60000);

             staff[2]=new Employee();

             for(int i=0;i<staff.length;i++){

                    Employee e=staff[i];

                    System.out.println("name="+e.getName()+",id=" +e.getId()+",salary="+e.getSalary());

             }

      }

}

 

class Employee{

      public Employee(String n,double s){

             name=n;

             salary=s;

      }

      public Employee(double s){

             this("Employee #"+nextId,s);

      }

      public Employee(){

      }

      public String getName(){

             return name;

      }

      public double getSalary(){

             return salary;

      }

      public int getId(){

             return id;

      }

      {

             id=nextId;

             nextId++;

      }

      static{

             Random generator=new Random();

             nextId=generator.nextInt(10000);

      }

      private String name="";

      private double salary;

      private int id;

      private static int nextId;

}

53 类变量与类方法

  类变量、类方法与实例变量、实例方法意思相近,但功能却截然不同。

531 实例变量与实例方法

实例变量

  每个对象都有自己保存属性的地方,而不与其他对象共享,如果改变了一个对象中某个变量(数据成员)的值,其他对象对应的数据成员并不受影响,因为这些变量各自独立,且存于不同的内存之内。具有特性的变量,Java称之为实例变量

实例方法

  类里有些方法必须通过对象来调用,即必须先创建对象再利用对象来调用它。无法直接调用方法而不通过对象。具有此特性的方法,Java称之为实例方法。

532 类变量

  实例变量是属于个别对象所有,彼此之间不能共享。除了实例变量之外,Java也提供了另一种变量——“类变量。与实例变量不同的是,它可由所有的对象来共享,也就是说每一个对象的类变量均相同,更改了某个对象的类变量,其他对象的类变量也将随之更改。

  类变量实例变量变量一样,都必须在类中声明。不同的是,如要把变量声明为类变量,必须在变量之前加上“static”这个标识符。

例如,假设Ccircle类里的变量pi,如要把它改为类变量,可将它声明成:

private  static  double  pi=3.14;

    这样声明的目的是把这个变量分享给每一个对象。所有对象的pi值相同,因此没有必要让每一对象都保有自己的pi值。

pi声明成static,则由Ccircle类所创建的对象均可共享它

使用类变量可节省可观的内存空间,尤其是大量创建对象的时候。类变量使用的另一个时机是基于程序的需要。

533 类方法

  前面所有的方法均是通过对象来调用。通过对象来调用方法,某些场合里不正确,如在没有对象产生的情况下无法通过对象调用方法。

  解决的方法就是把方法声明成类方法。其作法是在方法之前加上static标识符:

 public static void count();

 在使用时,直接用类来调用它:

main()方法与static标识符

public static void main(String[ ] args)

  main()之前加上了一个static标识符,使得main()变 成是一个类方法。原因main()方法是程序的入口函数,很显然,调用main()方法的是类而不是类的对象。因此在main()方法之前加上static是理所当然的。此外,main()方法均是在类之外被调用的,因此前面还得加上public标识符。

534 “类方法使用的限制

  类方法的特性虽然可解决一些问题,但这些特性本身也带来了一些限制。   

类方法访问的限制

  类方法与任何特定的对象都没有特定的关系,因此在没有对象产生的情况下,类方法依然可以被调用。基于这个原故,类方法内部无法对实例变量实例方法进行访问,

  在类方法内部不能使用this关键字。因为this是代表调用该方法的对象,类方法既然己不需要对象来调用,this也自然不应存在于类方法内部。

54 类类型的变量

  变量可分为基本类型的变量非基本类型的变量值类型变量引用类型变量)两种。所谓基本类型的变量是指由intdouble等关键字所声明而得的变量;而由类声明而得的变量称之为类类型的变量,它是属于非基本类型的变量的一种。

例下面的语法声明了radius为基本类型的变量:

private  double  radius

下列则声明了cir1CCircle类类型的变量:

CCircle  cir1

cir1=new CCirlcle()

  事实上,因cir1是指向CCircle类产生的对象,所以称cir1指向对象的变量,这只是从cir1变量功能的角度而称呼。

541 赋值给类类型的变量

  即使不用new产生新的对象,依然可对类类型的变量赋值。

在本例中,只产生了一个对象,但通过赋值,可将两个不同名称的变量cir1cir2指向同一个对象,所以通过任一变量对对象做变更,另一变量所指向的对象内容也会随着更改。

542 以类类型的变量传递参数

  如果想传递类类型的变量到方法里,只要在定义方法时,把类名称加到参数之前即可。传递类类型变量到方法的语法格式为:

[格式5-3 以类类型的变量传递参数]

void  compare(CCircle  obj){

}

  在Java中,方法调用总是使用传值调用,这也意味着方法得到的只是所有参数值的拷贝。因此,方法不能修改传递给它的任何参数变量的内容。

543 由方法返回类类型的变量

  如要由方法返回类类型的变量,只要在方法声明的前面加上欲返回的类即可,其语法如下:

[格式5-4 由方法返回类类型的变量]

   返回类型为CCircle类的变量

CCircle compare(CCircle obj){

            参数类型为CCircle

}

544 对象析构和finalize方法

  一些面向对象的程序设计语言,尤其是是C++,具有显式的析构方法,放置对象不再被使用时可能用到的清除代码。析构函数中最常见的活动就是回收分配给对象的内存。而由于Java能自动进行垃圾收集,不需要人工的内存回收,所以,Java并不支持析构函数  当然,有些对象除内存外,还使用了其他资源,如文件或是指向使用了系统资源的其他对象的句柄。这些情况下,在不再需要时,回收和再利用资源非常重要。

  我们可以把finalize方法添加到任何类中。finalize方法会在垃圾收集器清除对象之前被调用。但在实际操作中,不要依赖finalize方法回收任何短缺资源--因为我们很难知道这个方法具体什么时候才被调用。

1:垃圾回收有两种启动方式:

a 当其感觉到内存不足时;

b 程序通过System.gc()强迫启动。

2finalize()这个方法的名字是语言内定的,但是其内容可以在类的定义时由程序员给出。

它也有两种启动方式:

a 当垃圾回收动作启动时会自动调用这个函数;

b 程序通过System.runFinalizersOnExit(true)强迫启动,但此方法不安全,不鼓励。

垃圾回收是一个优先级非常低的线程,什么时候启动完全依赖JVM的实现算法.调用System.gc()是建议垃圾回收,但是不一定会回收,不过会加大回收的可能性.垃圾回收时会调用finalize方法,但是因为不知道什么时候垃圾回收,所以不要在finalize方法释放资源.垃圾回收随时都有可能运行,并不是说内存不足才启动的.

  如果需要在资源使用完毕后立即关闭此资源,那就需要对它进行手工管理,对需要清除的资源使用dispose方法(java.awt.Window)。重要的是,如果你作用的类含有dispose方法,你需要在对象操作完成后调用这个方法

简单的做法是:

  用new来创建新的对象后,Java便会分配内存空间给它。如果此对象不再使用了,可把指向该对象的变量设为null即可,例

   CCircle cir1=new CCircle;

   

  cir1=null;

 一经赋值为null,该变量便不指向任何对象,Java便会释放原先被该对象所占据的内存块,但如果两个类类型的变量指向同一个对象,把其中一个变量设为null,因另一个变量还是指向它,因而Java的搜集残余内存机制并不会释放它。

55 利用数组来保存对象

  对象可以用数组来存放,但必须有下面两个步骤:

   1.声明类类型的数组变量,并用new分配内存空间给数组。

   2.new产生新的对象,并分配内存空间给它。

 例如,欲创建三个CCircle类类型的数组元素,

 CCircle cir[];  声明CCirlce类类型的数组变量,

  cir=new CCircle[3]; 并用new分配内存空间

 创建好数组元素之后,便可把数组元素指向由CCircle类所产生的对象:

        cir[0]=new CCircle();   

    cir[1]=new CCircle();    new产生新的对象,并分配内存空间给它

    cir[2]=new CCircle(); 

  即欲使用对象数组,至少需要用两次new运算符来分配内存空间,一次是分配内存空间给类类型的数组另一次是分配内存空间给数组中的对象元素。

551 创建对象数组的范例

class CCircle

{

       private static double pi=3.14;

       private double radius;

      

       public CCircle(double r)

       {

              radius=r;

       }

       public void show()

       {

              System.out.println("area="+pi*radius*radius);

       }

}

public class Test

{

       public static void main(String args[])

       {

              CCircle cir[];

              cir=new CCircle[3];

              cir[0]=new CCircle(1.0);

              cir[1]=new CCircle(4.0);

              cir[2]=new CCircle(2.0);

             

              cir[1].show();

              cir[2].show();

       }

}

552 传递数组到方法里

传递数组时,在括号内填上数组名即可

class CCircle

{

       private static double pi=3.14;

       private double radius;

      

       public CCircle(double r)

       {

              radius=r;

       }

       public static double compare(CCircle c[])

       {

              double max=0.0;

              for(int i=0;i<c.length;i++)

                     if(c[i].radius>max)

                            max=c[i].radius;

              return max;

       }

}

public class Test

{

       public static void main(String[] args)

       {

              CCircle cir[];

              cir=new CCircle[3];

              cir[0]=new CCircle(1.0);

              cir[1]=new CCircle(4.0);

              cir[2]=new CCircle(2.0);

             

              System.out.println("Largest radius="+CCircle.compare(cir));

       }

}

56 内部类

   在类的内部也可以定义另一个类。如果在类A的内部再定义一个类B,此时类B称为内部类,而类A则称为外部类。内部类也可声明成publicprivate。当内部类声明成publicprivate时,其访问的限制与数据成员与方法完全相同。

 

定义内部类的语法格式:

[格式 5. 5 定义内部类别]

标识符 class 外部类的名称

{

       //外部类的成员

       标识符 class 内部类的名称

       {

       //内部类的成员

       }

}

561 内部类的编写

1

public class Test

{

       public static void main(String args[])

       {

              Caaa aa=new Caaa();

              aa.set_num(5);

       }

      

       static class Caaa

       {

              int num;

              void set_num(int n)

              {

                     num=n;

                     System.out.println("num="+num);

              }

       }

}  

2

public class Test

{

       public Test()

       {

              Caaa aa=new Caaa();

              aa.set_num(5);

       }

      

       public static void main(String args[])

       {

              Test obj=new Test();

       }

      

       class Caaa

       {

              int num;

              void set_num(int n)

              {

                     num=n;

                     System.out.println("num="+num);

              }

       }

}

562 匿名内部类

  所谓的匿名内部类,是指可以利用内部类创建不具名称的对象,并利用它访问到类里的成员。创建匿名内部类并访问成员的语法如下:

       [格式 5. 6 创建匿名内部类,并执行所定义的方法]

       (

 new 类名称()

        {

         方法名称(参数1,参数2,参数n

          {

           语句;

          }

         }

       ).方法名称(参数1,参数2,参数n

  创建匿名内部类的用意,主要是用来补充内部类里没有定义到的方法,并可有效地简化程序代码。例

public class Test

{

       public static void main(String[] args)

       {

              (

                     new Caaa()

                     {

                            void set_num(int n)

                            {

                                   num=n;

                                   System.out.println("num="+num);

                            }

                     }

              ).set_num(5);

       }

       static class Caaa

       {

              int num;

       }

}

内部类的对象能访问外部类的成员。外部类通过内部类对象能访问内部类成员。你也能在方法内创建类。内部类方法能访问在方法(包含内部类)内规定的变量。内部类必须说明在方法变量后以致变量能进入内部类。

综合例题 TestInnerClass.java

class OuterClass

{

       private int OutNumber;

       private String OutString;

       public OuterClass(int OutNumber,String OutString)

       {

              this.OutNumber=OutNumber;

              this.OutString=OutString;

       }

       public class InnerClass

       {

              public int InnerNumber;

              public String InnerString;

              public InnerClass(int InnerNumber,String InnerString)

              {

                     this.InnerNumber=InnerNumber;

                     this.InnerString=InnerString;

              }

              public void OutPutValue()

              {

                     System.out.println(OutNumber);

                     System.out.println(OutString);

              }

       }

}

public class TestInnerClass

{

       public static void main(String[] args)

       {

              OuterClass obj=new OuterClass(333333,"cccccc");

              OuterClass.InnerClass obj1=obj.new InnerClass(222222,"bbbbbb");

              System.out.println(obj1.InnerNumber);

              System.out.println(obj1.InnerString);

              System.out.println("");

              System.out.println("");

              obj1.OutPutValue();

       }

}

 

 

 

 

 

6继承

本章内容:

 

         继承的基本概念

Ø  简单的继承范例

Ø  调用父类中特定的构造函数

Ø  使用构造函数常见的错误

         由子类访问父类的成员

         覆盖

Ø  覆盖父类的方法

Ø  以父类的变量访问子类的成员

Ø  再谈super()this()

Ø  终止继承

Ø  类之源----Object

Ø  再谈super()this()

Ø  终止继承

Ø  类之源----Object

 

OOP的程序而言,它的精华在于类的继承。继承可以使我们以既有的类为基础,进而派生出新的类通过这种方式,便能快速地开发新的类,而不需再编写相同程序代码。(就像不需要从头制造新型电视机)

60类之间的关系

类与类之间最常见的关系有:

l  依赖

l  聚合

l  继承

依赖关系(“use-a”):是最明显也最常见的关系。如果一个类的方法操作了另一个类中的对象,那么这个类就依赖于另一个类。

聚合关系(“has-a”):当类A的对象包含类B的对象时,我们称类A和类B为聚合关系。

继承关系(“is-a”):用来表示更特殊的和更一般的类之间的关系。

61 继承的基本概念

  以设计一个硬币类CCoin类为例。可用来创建各种不同半径(radius)与不同币值(value)的硬币。由于CCircle类里己包含了piradius成员与setRadius()show()等方法,所以在创建硬币类CCoin时,便可通过继承的方式来利用这些数据成员与方法。即只要针对硬币类Ccoin要新增的数据成员与方法编写程序代码即可,而不必在Ccoin里编写相同的程序代码,这就是继承的基本思想。

611 简单的继承范例

  以既有类为基础,进而派生出另一类,Java称之为类的扩展

  Java类的继承,可用下面的语法来表示:

[格式 6. 1 类继承的格式]

Class CCircle

{

 private double pi=3.14;

 private double radius;

    CCircle类里的方法

}

Class CCoin extends CCircle

{

 private int value;

 CCoin类里的方法

}

  Ccoin是通过关键字extends,概括继承CCircle的所有非私有成员,并加入新的成员以符合类所需。即由Ccoin所创建的对象,除了本身的成员之外,同时也拥有CCircle类里所定义的各种非私有成员,包括数据成员、方法与构造函数,这种特性在Java里称为继承

   此时原有的类称为父类(super class),因继承而产生的新类则称为子类(sub class)。父类与子类之间的成员继承关系可用图5-1来说明。

 

pi

radius

setRadius()

show()

pi

radius

value

setRadius()

show()

setValue()

Circle类(父类)

CCoin类(子类)

继承

继承

子类本身的数据成员

继承自父类的method

子类本身的method

6. 1 类的继承

继承自父类的数据成员

class CCircle

{

       private double pi=3.14;

       private double radius;

      

       public CCircle(){

              System.out.println("CCircle() constructor called");

       }

       public void setRadius(double r){

              radius=r;

              System.out.println("radius="+radius);

       }

       public void show(){

              System.out.println("area="+pi*radius*radius);

       }

}

class CCoin extends CCircle

{

       private int value;

       public CCoin(){

              System.out.println("CCoin() constructor called");

       }

       public void setValue(int t){

              value=t;

              System.out.println("value="+value);

       }

}

public class Test

{

       public static void main(String[] args)

       {

              CCoin coin=new CCoin();

              coin.setRadius(2.0);

              coin.show();

              coin.setValue(5);

       }

}

上例说明了类继承的基本概念、运作模式以及使用方法等

继承中的几点重要概念:

1.通过extends关键字,可将父类的成员(非私有数据成员与方法)继承给子类。如要调用这些继承过来的成员时,使用过去惯用的语法即可。

2.Java的继承中,执行子类的构造函数之前,会先调用父类中没有参数的构造函数,其目的是为了要帮助继承自父类的成员做初始化的操作。

612 调用父类中特定的构造函数

  即使没有明确地指定构造函数,子类还是会先调用父类中没有参数的构造函数,以便进行初始化的操作。问题是,如果父类有数个构造函数,要如何才能调用父类中特定的构造函数呢?其作法是,在子类的构造函数中,通过super()这个关键字来调用。

class CCircle

{

       private double pi=3.14;

       private double radius;

      

       public CCircle(){

              System.out.println("CCircle() constructor called");

       }

       public CCircle(double r)

       {

              System.out.println("CCircle(double r) constructor called");

              radius=r;

       }

       public void show(){

              System.out.println("area="+pi*radius*radius);

       }

}

class CCoin extends CCircle

{

       private int value;

       public CCoin(){

              System.out.println("CCoin() constructor called");

       }

       public CCoin(double r,int v){

              super(r);

              value=v;

              System.out.println("CCoin(double r,int v) constructor called");

       }

}

public class Test

{

       public static void main(String args[])

       {

              CCoin coin1=new CCoin();

              CCoin coin2=new CCoin(2.5,10);

              coin1.show();

              coin2.show();

       }

}

这里有几点要提醒:

1.如果省略了super(r)语句,则父类中没有参数的构造函数还是会被调用的。

2.调用父类构造函数的super()语句必须写在子类构造函数的第一行,不能置于它处,否则编译时将出现错误信息。

3.super()可以重载,也就是说,super会根据参数数目和类型,执行相应的父类的构造函数。

6.1.3使用构造函数常见的错误

  Java在执行子类的构造函数之前,如果没有用super()来调用特定父类的构造函数,则会先调用父类中没有参数的构造函数。因此,如果父类中只定义了有参数的构造函数,而在子类的构造函数里又没有用

    Super()来调用父类中特定的构造函数,则编译时将发生错误,因为Java在父类中找不到没有参数的构造函数可供执行。

This()super()

  构造函数可用this关键字来调用同一类内的其他构造函数。Super关键字也有类似的功能,但基本上两者的使用时机并不相同,this()是在同一类内调用其他的构造函数,而super()则是从子类的构造函数调用其父类的构造函数。

虽然使用的时机不同,但this()super()还是有其相似之处:

 1.当构造函数有重载时,this()super()均会根据所给予的参数类型与个数,正确地执行相对应的构造函数

 2.this()super()均必须编写在构造函数内的第一行,也就是因为这个原因,this()super()无法同时存在于同一个构造函数内。

62 由子类访问父类的成员

  父类的radius成员是声明成private,因此只能在CCircle类内访问,或都是通过构造函数或公有方法来达成访问的目的。若在子类内直接访问private的数据成员,会出现错误。

  要想使父类开放权限,使得子类也能访问到父类的数据成员,可以在父类中把数据成员声明成protected(保护成员),而非private

  把成员声明成protected最大的好处是:可同时兼顾到成员的安全性与便利性,因它只能供父类与子类的内部来访问,而外界则无法更改或读取它。(一个包的除外)

63 覆盖                             

  覆盖的概念与重载相似,它们均是Java“多态的技术之一。

631 覆盖父类的方法

  Java既是以类为基础,而继承又是OOP语言里常用的把戏,因此在父类与子类里定义了名称、参数的数据类型与个数均完全相同的方法是很有可能的事,尤其是父类与子类是分别交由不同的人来编写时更容易发生。

  当父类与子类均同时拥有名称、参数的个数与数据类型均相同的情形在OOP的技术里称为覆盖。通过覆盖技术,在子类中可定义和父类里的名称、参数个数与数据类型均完全相同的方法,用以取代父类中原有的方法。

  覆盖重载的比较

  覆盖重载均是Java“多态的技巧之一。这两个技术对初学者而言很容易混淆,重载与覆盖的区别有5点:

 1.方法的覆盖是子类和父类间的关系,而方法的重载是同一类内部多个方法间的关系。

       2.方法的覆盖一般是两个方法间的,而重载时可能有多个重载方法。

  3.覆盖的方法有相同的方法名和形参表,而重载的方法只能有相同的方法名,不能有相同的形参表。

       4.覆盖时区分方法的是根据调用它的对象,而重载是根据形参表来决定调用的是哪个方法。

       5.覆盖的英文名称为overriding,重载的英文名称为overloading

632 以父类的变量访问子类的成员

通过父类的变量访问子类的成员,只限于“覆盖”的情况发生时。即父类与子类的方法名称、参数个数与类型必须完全相同,才可通过父类的变量调用子类的方法。

**一个对象变量可以指向多种实际类型的现象被称为“多态”。而在运行时自动选择正确的方法进行调用的现象被称为“动态绑定”。

  Java中,对象变量是多态的。一个类型为Employee的变量既可以指向类型为Employee的对象,又可以指向Employee类的任何子类的对象。

64 再谈super()this()

    通过super关键字可用来调用父类的方法,事实上super后面也可加上数据成员(即变量)的名称。

  this关键字用法与super类似。This除了可用来调用同一类内的其他构造函数之外,如果同一类内实例变量局部变量的名称相同时,也可利用它来调用同一类内的实例变量

65 终止继承

  覆盖有其便利性,但在设计类时,如果基于某些因素,父类的方法不希望子类的方法来覆盖它,便可在父类的方法之前加上“final“关键字。如此该方法便不会被覆盖。

  final的另一个功用是把它加在数据成员变量前面,则该变量就变成一个常量,如此便无法在程序代码的任何地方再做修改。

 例static  final  double  pi=3.14;

66 类之源--Object

  前几节中的范例中,己学到类的基本继承方法,例如,Ccoin类继承自Circle类,因而Ccoin称为子类,而CCircle则为父类,有趣的是,CCircle也有其父类虽然没有很明确地指定它。事实上在Java里,如果某一类没指定其父类的话,则Java会自动赋值该类继承自Object这个类,而成为它的子类(Object是置于java.lang类库里的一个类)。

  Object类可说是类之源,所有的类均直接或间接继承它,如图6-2所示。

 

Object

CCircle

CCoin

间接继承Object

的成员

如果没有指定父类,则新建的

类会以Object类做为它的父类

CCoin继承自的CCircle,

时它也会继承Object的成员

6-2 所有的类均继承自Object这个类

直接继承Object

的成员

 

功能说明

方法名称

将调用toString()的对象转成字符串

String toString()

两个类变量所指向的是否为同一个对象

Boolean equals(Object obj)

取得调用getClass()的对象所属的类

Class getClass()

 

 

 

 

 

 

 

 

equals()方法的使用

  equals()方法可用来比较两个类变量是否指向同一个对象。如果是,则返回true,否则返回false

  所有的类均是Object类的子类,因此所有类都继承了Object类的equals()方法,因此都可以使用它。

  toString()方法的使用

  toString()方法的功能是将对象的内容转换成字符串,并返回其内容。例如,若变量a是指向由类Caaa所创建的对象,则下面的语句会调用toString()方法,并输出所指向对象的内容:

  System.out.println(a);

上面的语句是以类类型的变量a当成println()的参数,此时Java会先用变量a来调用toString()方法,再把结果当成println()的参数。也可以用下面的语法来编写出相同功用,且更容易了解的语句:

  System.out.println(a.toString());

    toString()的返回值不太具有意义,且很少有人看得懂它。因此如果要用toString()返回对象的内容,建议覆盖toString()方法,以符合需要。

 

 

 

 

 

7抽象类接口

本章内容:

 

    抽象类

Ø  定义抽象类

Ø  抽象类的实现

Ø  用抽象类类型的变量来创建对象

Ø  使用抽象类的注意事项

    接口的使用

    多重继承

    接口的扩展

   

    “抽象类接口是类概念的扩展。通过继承扩展出子类,加上覆盖的应用,抽象类可以一次创建并控制多个子类。接口则是Java里实现多重继承的重要方法。灵活地运用抽象类接口知识,将编写出更精巧的Java程序。

71 抽象类

   通过继承,可以从原有的类派生出新的类。原有的类称为基类(base class)或父类,而新的类则称为派生类(derived class)或子类(sub class)。通过这种机制派生出的新类不仅可以保持原有类的功能,同时也可以拥有新的功能。

   Java也可以创建专门的类用来当作父类,这种类称为抽象类”(abstract class)。抽象类有点类似模板的作用,其目的是要依据它的格式来修改并创建新的类。但是并不能直接由抽象类创建对象,只能通过抽象类派生出新的类,再由它来创建对象。

711 定义抽象类

  抽象类是以abstract关键字为开头的类。定义抽象类的语法如下:

[格式 7.1 抽象类的定义格式]

Abstract  class  类名称 //定义抽象类

{

 声明数据成员;

 返回值的数据类型 方法名称(参数…)

 {

   

  }

 abstract  返回值的数据类型  方法名称(参数…);

}

 注意在抽象类定义的语法中,方法的定义可分为两种:一种是一般的方法,它和先前介绍过的方法没有什么区别;另一种是抽象方法,它是以abstract关键字为开头的方法,此方法只声明了返回值的数据类型、方法名称与所需的参数,但没有定义处理的方式,即没有方法体。

712 抽象类的实现

  抽象类的目的是要依据它的格式来修改并创建新的类,因此抽象类里的抽象方法并没有定义具体的处理方式,而是要保留给从抽象类派生出的新类来定义。

  假设想设计一个图形(shape)的父类Cshape,依据此类可用来派生出圆形(circle)、长方形(rectangle)与三角形(triangle)等具体图形的类。可以把父类与派生类之间的关系绘制成如图7-1所示。

 

Cshape

CRectangle

Circle

Ctriangle

父类

衍生类

7.1 父类与派生类之间的关系图

     假设这些几何形状均具有颜色”(color)这个属性,可以把color这个数据成员,以及赋值color的方法均设计在父类里,另外,如果想为每一个几何形状的类设计一个area()方法,用来显示几何形状的面积,因每种几何形状的面积计算方式并不相同,所以把area()方法的处理方式设计在父类里并不恰当,但每一个由CShape父类所派生出的子类又都需要用到这一个方法,因此可以在父类里只声明area()方法,而把area()方法处理的方法留在子类里来定义,也就是说,把area()声明成抽象方法,然后在各个子类里明确地定义他们即做覆盖的操作。

713 用抽象类类型的变量来创建对象

  利用父类的变量也可以访问子类的内容,这种情况同样适用在抽象类与其派生类身上。也就是说,利用抽象类类型的变量也可访问派生类的对象。         

714 使用抽象类的注意事项

   使用抽象类时,需要注意:抽象类不能用来直接产生对象。其原因在于它的抽象方法只有声明,而没有明确地定义,因此如果用它来创建对象,则对象根本不知要如何使用这个抽象方法。

72 接口的使用

  接口(interface)Java所提供的另一种重要功能,它的结构和抽象类非常相似。接口本身也具有数据成员与抽象方法,但它与抽象类有下列两点不同:

  1.接口的数据成员必须初始化。

  2.接口里的方法必须全部都声明成abstract,也就是说,接口不能像抽象类一样保有一般的方法,而必须全部是抽象方法

接口定义的语法如下:

[格式7-2 接口的定义格式]

Interface  接口名称

 final  数据类型  成员名称=常量;//数据成员必须赋值初值

 abstract  返回值的数据类型  方法名称(参数…);//抽象方法

  接口与一般类一样,本身也具有数据成员与方法,数据成员必须是public static final修饰的,这是被系统默认的,所以写不写public static final修饰符效果都一样,但一般都写出final。方法系统默认为public abstract的,一般不写修饰符。

   这些方法是公共的抽象方法,没有方法体,所以后面不接大括号,就以分号结束。

下例说明了接口定义的方式。

Interface  iShape2D

{

 final double pi=3.14;

 abstract void area();

}

  既然接口里只有抽象方法,它只要声明而不用定义处理方式,于是自然可以联想到接口也没有办法像抽象类一样,用new运算符直接产生对象。相反的,必须利用接口的特性来打造一个新的类,再用它来创建对象。利用接口打造新的类的过程,称之为接口的实现(implementation)。格式如下:

[格式 7-3 接口的实现]

Class 类名称  implements  接口名称 //接口的实现

  … …

}

   接口的运作方式和抽象类非常类似,不同的是,接口里的数据成员必须设为常量,且所有的方法必须声明成abstract。抽象类则限制较少,它的数据成员不必设初值,且允许一般的方法与抽象方法共存

   不能直接由接口来创建对象,而必须通过由实现接口的类来创建。虽然如此,仍可以声明接口类型的变量(或数组),并用它来访问对象。

7.3 多重继承

   有时候,我们会希望一个子类同时继承自两个以上的父类,以便使用每一个父类的功能,但很不幸的是,Java并不允许多个父类的继承,原因是多重继承容易引发歧义性。

 

 

 

 

   通过接口的机制,多重继承的处理还是可以实现作法是将类实现两个或两个以上的接口,如此一来,类里的方法必须明确定义每一个接口里的方法,因而可达到多重继承的目的。

父类1

父类2

父类3

子类

类实现两个或两个以上的接口的语法如下:

[格式7-4 实现两个或两上以上的接口]

Class 类名称  implements  接口1,接口2…{

  … …

73 接口的扩展

  接口可通过扩展(extends)的技术来派生出新的接口。原来的接口称为父接口,派生出的接口称为子接口(sub interface)。这种机制,派生接口不仅可以保有父接口的成员,

同时也可以加入新的成员以满足实际问题的需要。

Circle

Circle

Circle

Circle

iShape2D接口

iShape3D接口

iShape2D接口

父接口

父接口

父接口

extends

implements

7-5 接口的扩展

接口的扩展(或继承)也是通过关键字extends。值得注意的是,一个接口可以继承自多个接口,这与类的继承有所不同。下列为接口扩展的语法:

[格式 7-5 接口的扩展]

Interface  子接口名称  extends  父接口名称1,父接口名称2

{

 … …

}

74

Java里,可以将大型程序内的类独立出来,分门别类地存到不同文件中,再将这些文件一起编译执行,如此的程序代码将更具亲和性且易于维护。

7.4.1文件的分割

在开发大型程序时,为了工作上的需要,程序代码的开发通常是由一些人,或者是几个小组同时进行。每个参与的小组或成员分别负责某些类,并将所编写的类分开保存在各自的文件中,直到所有的类均开发好,再分别编译与执行。这种概念是利用文件分割的方式,将大型程序分开成独立的类,以利于程序的开发与维护。

Java里怎样实现文件分割和分别编译呢?示例如下:

//CCircle.java

class CCircle

{

    public void show(){

     System.out.println("show() method called");

    }

}

//Test.java,

public class Test

{

    public static void main(String args[])

    {

     CCircle cir=new CCircle();

     cir.show();

    }

}

  两个文件分别创建好后,分别编译产生CCircle.classTest.class文件,再运行Test文件即可。

使用package

  当一个大型程序交由数个不同的组别或人员开发时,用到相同的类名称是很有可能的事。这时需要使用package关键字。

(package)是类的容器,用来保存划分的类名空间以防类的名称冲突。包以分层方式保

存并被明确的引入新的类定义。前述每个例题类名从相同的名称空间获得。为了避免名称冲突,即为了确保你选用的类名不和其他程序员选择的类名相冲突,可以使用Java提供的把类名空间划分为更多易管理的块的机制,这种机制就是包。包既是命名机制也是可见度控制机制。我们可以在包内定义类,使在包外的代码能或不能访问该类。

7.4.2定义包

package的使用方法是在类或接口的最上面一行加上package的声明(package必须Java源文件的第一句)。该文件中定义的任何类将属于指定的包。

package语句定义了一个存储类的名字空间。如果省略package语句,类名被输入一个默认的没有名称的包。

package声明的通用形式为:

package MyPackage;

java 用文件系统目录来存储包。目录名必须和包名严格匹配。Package声明仅仅指定了文件中定义的文件属于哪一个包。它不拒绝其他文件的类成为相同包的一部分,即多个文件可以包含相同package声明。

我们还可以创建包层次。一个多级包的声明的通用形式为:

package pkg1[.pkg2][.pkg3];

7.4.3理解类路径(classpath)

Java编译器考虑的特定位置作为包层次的根被类路径控制。在命令行键入类名编译源文件和运行Java解释器解释类,并得到结果。这种情况下它能够正确地工作是因为默认的当前工作目录(.)通常在类路径环境变量中被设置。然而,当有包参与时,事情就不这么简单了。

假如在一个test包中创建了一个名为PackTest的类,因为目录结构必须与包相匹配,创建一个名为test的目录并把PackTest.class被存放在test目录下。此时PackTest类被保存在test包中,不再能简单用PackTest来引用。必须通过列举包层次来引用该类。引用包层次时用点号将包名隔开。该类现在必须叫做test.PackTest

7.4.4一个简单的例子

package MyPack;

class Balance

{

    String name;

    double bal;

    Balance(String n,double b)

    {

       name=n;

       bal=b;

    }

    void show()

    {

       if(bal<0)

       System.out.println("-->");

       System.out.println(name+":$"+bal);

    }

}

class AccountBalance

{

    public static void main(String args[])

    {

       Balance current[]=new Balance[3];

       current[0]=new Balance("K.J.Fielding",123.23);

       current[1]=new Balance("Will Tell",157.02);

       current[2]=new Balance("Tom Jackson",-11.33);

       for(int i=0;i<3;i++)

       current[i].show();

    }

}

该文件名为AccountBalance.java,把它存放在MyPack目录中。接着,编译文件。确信结果文件.class同样在MyPack目录中。然后用下面的命令行执行AccountBalance类:

java MyPack.AccountBalance

注释:此例中AccountBalanceMyPack包的一部分。这意味着它不能自己执行。即不能用下面的命令行:

java AccountBalance;(因为AccountBalance必须和它的包名一起使用。)

 

7.4.5访问保护

Java提供很多级别的保护以使在类、子类和包中有完善的访问控制。包是封装类和下级包的容器,而类就像是数据和代码的容器。

一个类(或接口)只可能有两个访问级别:默认的或是公共的。如果一个类声明成public,它可以被任何其他代码访问。如果该类是默认访问控制符,它仅可以被相同包中的其他代码访问。

 

由于类和包的相互影响,Java将类成员的可见度分为四种:。

l  相同包有继承关系的类

l  相同包无继承关系的类

l  不同包有继承关系的类

l  不同包无继承关系的类

类成员的访问控制符有四类:三个访问控制符:publicprivateprotected和缺省,

它们之间的相互作用如下表:

访

Private成员

默认的成员

Protected成员

Public成员

同一类中可见

同一个包中对子类可见

同一个包中对非子类可见

不同包中对子类可见

不同的包中对非子类可见

说明:任何声明为public的内容可以被从任何地方访问。被声明成private的成员不能被该类外看到。如果一个成员不含有一个明确的访问说明,它对于该包中的子类或其他类是可见的。这是默认访问。如果希望一个元素在当前包外可见,但仅仅是元素所在类的子类直接可见,把元素定义成protected

 

成员与构造函数所使用的修饰符

修饰符

说明

没有修饰符 

成员或构造函数只能被同一个package内的程序所访问

public

如果所属的类也声明成public,则成员或构造函数可被不同package内所有的类所访问。若所属类不是声明成public,则成员或构造函数只能被同一个package内的程序所访问。

private

成员或构造函数只能被位于同一个类内的程序所访问

protected

成员或构造函数只能被位于同一package内的类以及它的子类为访问

 

7.4.6一个访问的例子

//Protection.java

package p1;

public class Protection

{

    int n=1;

    private int n_pri=2;

    protected int n_pro=3;

    public int n_pub=4;

    public Protection()

    {

       System.out.println("base constructor");

       System.out.println("n="+n);

       System.out.println("n_pri="+n_pri);

       System.out.println("n_pro="+n_pro);

       System.out.println("n_pub="+n_pub);

    }

}

//Derived.java

package p1;

class Derived extends Protection

{

    Derived()

    {

       System.out.println("derived constructor");

       System.out.println("n="+n);

       //System.out.println("n_pri="+n_pri);

       System.out.println("n_pro="+n_pro);

       System.out.println("n_pub="+n_pub);

    }

}

//SamePackage.java

package p1;

class SamePackage

{

    SamePackage()

    {

       Protection p=new Protection();

       System.out.println("same package constructor");

       System.out.println("n="+p.n);

//     System.out.println("n_pri="+p.n_pri);

       System.out.println("n_pro="+p.n_pro);

       System.out.println("n_pub="+p.n_pub);

    }

}

//Protection2.java

package p2;

class Protection2 extends p1.Protection

{

    Protection2()

    {

       System.out.println("derived other package constructor");

       //System.out.println("n="+n);

       //System.out.println("n_pri="+n_pri);

       System.out.println("n_pro="+n_pro);

       System.out.println("n_pub="+n_pub);

    }

}

//OtherPackage.java

package p2;

class OtherPackage

{

    OtherPackage()

    {

       p1.Protection p=new p1.Protection();

       System.out.println("other package constructor");

       System.out.println("n="+p.n);

       System.out.println("n_pri="+p.n_pri);

       System.out.println("n_pro="+p.n_pro);

       System.out.println("n_pub="+p.n_pub);

    }

}

//Demo.java

package p1;

public class Demo

{

    public static void main(String args[])

    {

       Protection ob1=new Protection();

       Derived ob2=new Derived();

       SamePackage ob3=new SamePackage();

    }

}

 

package p2;

public class Demo

{

    public static void main(String args[])

    {

       Protection2 ob1=new Protection2();

       OtherPackage ob2=new OtherPackage();

    }

}

7.4.7引入包

包的存在是划分不同类的好的机制。所有的标准类都存储在不同的包中,可以通过被访问的package名称.类名称的语法来访问位于不同package里的类。

利用这种方法有些麻烦,因为每次都要指明被访问的package名称。简单的方法是直接把被访问的package导入程序代码中,这样它们就相当于位于同一个文件内,于是被访问的package名称.”就可以省略了,即一旦类所属的包被引入,类就可以被直呼其名地引用。

import语句对于程序员是很方便的而且在技术上并不需要编写完整的Java程序。如果在程序中将要引用若干个类,那么用import语句将会节省很多打字时间。

import声明的通用形式:

import pkg1[.pkg2][.pkg3].(classname|*);

说明:

l  除非是文件系统的限制,不存在对于包层次尝试的实际限制。

l  Import java.io.*;可能会增加编译时间—特别是在引入多个大包时。因此,明确的命名想要用到的类而不是引入整个包是一个好的方法。然而,星号形式对运行时间、性能和类的大小绝对没有影响。

l  编译器为所有程序隐式引入java.lang.*

l  如果在用星号形式引用的两个不同包中存在具有相同类名的类,编译器将保持沉默,除非试图运用其中的一个。此时,将产生一个编译错误要求用明确的命名指定包中的类。

l  任何用到类名的地方,可以使用它的全名,全名包括它所有的包层次。

l  当一个包被引入,仅仅是该包中声明成public的项目可以在引入代码中对非子类可用。

 

 

 

 

 

8常用类库、集合类

本章内容:

 

    Java常用的类库

Ø  有关字符串的类库

Ø  StringBuffer类库

Ø  Wrapper class

Ø  使用math

Ø  日期类

Ø  随机数类Random

Ø  向量类Vector

Ø  Class类与Runtime

Ø  常用集合类

 

   Java己经把功能相近的类分门别类到不同的类库中,例常用的String类是置于java.lang类库里,而BufferedReader则是置于java.io类库内。可通过帮助文档查看Java所提供的类库的全貌。

  下图演示了类库、子类库、类与接口之间的层次关系。

  其中实线是类库与子类库之间的连接,而虚线则连接类库或子类库里所提供的类或接口。

java.applet

java.awt

java.beans

…  …

java.awt.color

java.awt.event

java.awt.font

java.awt. …

ColorSpace

…  …

ActionEvent

ContainerEvent

FocusEvent

MouseListener

…  …

Button

Canvas

CheckBox

…  …

类库的层次关系图

 Java提供的类库很多,但常用的只有几个,下表列出了Java常用的类库以及它们所包含类的主要功能,这些类库在稍后的章节里将会一一介绍。

       

 

 

 

 

Java常用的类库

 类库名称

         所包含类的主要功能

java.applet

applet相关的类,applet是嵌在网页里的小程序,用来执行特定的功能

java.awt

java早期窗口组件设计有关的类

java.awt.event

与事件(event)触发相关的类

java.lang

Java最基本的类,此类会自动加载

java.io

与输入/输出相关的类

java.net

与网络组件相关的类

java.util

java utility相关的类

   如果想使用某个类库里的某个类,可用类库名称.类名称,但如果想省略类库名称,只使用类名称,则必须将整个类库导入。

        例要导入java.awt类库里的多个类,可用以下语法

   import  java.awt.Button;

   import  java.awt.Canvas;

   如果要导入类库里的所有类时,可以通过通配符 来导入:

   import  java.awt.*;

    需要注意的是,当用通配符导入类库里的所有类时,该类库底下的子类库里的类并不会自动导入。也就是说,如果导入java.awt.*,则ActionEventColorSpace等类并不会被导入。因此如果要导入Java.awt.event下的所有类可用下列的语法:

   import  java.awt.event.*;

81有关字符串的类库

  String类是放置在java.lang类库内。Java.lang类库里所有的类均会自动加载,因此当使用到String类时,不需利用import命令来加载它。

  创建字符串对象(String object)

1.可以利用String这个标识符来声明属于字符串的变量,然后再赋值给它,如: 

String str=“abc”;

  2.字符串可视为由字符数组组成,因此也可利用字符数组来产生字符串,如:

         char  data[ ]={a,b,c};

    String str=new String(data);

  3.直接利用String构造函数来创建:

     String str=new String(abc);

   String类构造函数的格式

构造函数格式

       主要功能

String()

没有参数的String()构造函数

String(byte[ ] bytes

byte数组创建字符串

String(byte[] bytes,int offset,int length)

取出byte数组,从数组的第offset位置开始,长度为length来创建字符串

String(char[] value)

利用字符数组来产生字符串对象

String(char[] value,int offset,int count)

取出字符数组,从数组的第offset位置开始,长度为count来创建字符串

String(String original)

利用初始字符串(original string)来产生字符串对象

字符串类所提供的方法

  String类提供了相当多的方法来做字符串的处理,如字符串的查找、转换大小写等。下表列出常用方法

    方法

     主要功能

char charAt(int index)

取得index位置的字符

boolean equals(String str)

测试字符串是否与str相同

int indexOf(char ch)

根据字符ch找出第一个在字符串出现的位置

int length()

取得字符串的长度

String substring(int index)

取出index之后的子字符串

String substring(int ind1,int ind2)

取出位于ind1ind2之间的字符串

Boolean starsWidth(String prefix)

测试字符串是否以prefix字符串为开头

String toLowerCase()

将字符串转换成小写

String toUpperCase()

将字符串转换成大定

82 StringBuffer类库

  由于String类只提供了一些查找与测试的方法,如果要用字符串做连接或修改,String类就没有提供,因此如果要修改字符串,则必须使用StringBuffer类来声明字符串,并用此类所提供的方法来进行字符串的修改。

  与String类一样,StringBuffer类也是置于java.lang类库里,这个类库里的类会自动加载,因此不用特别去加载它。

  下表列出了StringBuffer类常用的一些方法。

StringBuffer类常用的方法

                   方法

                 主要功能

StringBuffer append(char c)

将字符c放到字符串缓冲区之后

StringBuffer append(String str)

将字符串str放到字符串缓冲区之后

StringBuffer deleteCharAt(int index)

删除字符串缓冲区里第index位置的字符

StringBuffer insert(int k,char c)

在字符串缓冲区的第k个位置插入字符c

StringBuffer insert(int k,String str)

在字符串缓冲区的第k 个位置插入字符串str

int length()

取得缓冲区字符串的长度

StringBuffer replace(int m,int n,String str)

将字符串缓冲区里第mn之间以字符串str取代

StringBuffer reverse()

将字符串缓冲区里的字符串反向排列

String toString()

将字符串缓冲区里的字符串转换成String

8.3 wrapper class

  原始数据类型(primitive)byteshortcharintlongfloatfloatdouble等均不被看成是对象。虽然如此,Java还是提供了一些特殊的类,让原始数据类型在使用上尤如对象一般,这些特殊的类称之为 wrapper class

  所谓的wrap就是把东西包装起来之意。因此 wrapper class便是把原始数据类型包装起来,并且额外提供相关的功能。Wrapper class所提供的变量均属 类变量,所提供的方法均是类方法。例如前面提到的各种类型的最大值与最小值代码均属于类变量,且定义在它们相对应的wrapper class里。

  下表列出了原始数据类型与相对应的 wrapper class

下表原始数据类型与相对应的wrapper class

    原始数据类型

  wrapper class

boolean

Boolean

byte

Byte

char

Character

short

Short

int

Integer

long

Long

float

Float

double

Double

wrapper class所提供的方法常被用在数据类型的转换上。下表列出了各种类常用的转换方法            

各种类常用的转换函数

  方法

主要功能

Byte

static byte parseByte(String s)

将字符串s转换成byte类型的值

Byte

static String toString(byte b)

byte类型的数值b转换成字符串

Character

static String toString(char c)

将字符c转换成字符串

Short

static short parseShort(String s)

将字符串s转换成短整数

Short

staic String toString(short s)

将短整数s转换成字符串

Integer

static int parseInt(String s)

将字符串s转换成整数

Integer

static String toString(int I)

将整数I转换成字符串

Long

static long parseLong(String s)

将字符串s转换成长整数

Long

static String toString(Long i)

将长整数i转换成字符串

Float

static float parseFloat(String s)

将字符串s转换成浮点数

Float

static String toString(float f)

将浮点数f转换成字符串

Double

static double parseDouble(String s)

将字符串s转换成双精度浮点数

Double

static String toString(double d)

将双精度浮点数d转换成字符串

8.4使用math

  Math类是置于java.lang类库里的一个类,它所提供的方法可用来计算相关的数学函数。与wrapper class一样,Math类所提供的变量也是属于类变量,所提供的方法则是属于类方法。即不需要产生对象,即可通过Math类来调用它所提供的变量与方法。

      下表列出了Math类所提供的类变量

      Math类所提供的类变量

方法

主要功能

public static final double E

尤拉常量

public static final double PI

圆周率,∏

 Math类里的类方法多达20余个,下表例举了常用的几个

方法

主要功能

public static souble sin(double a)

正弦函数 sin(a)

public static double cos(double a)

余弦函数 cos(a)

public static double tan(double a)

正切函数 tan(a)

public static double asin(double a)

反正弦函数 sin(a)

public static double acos(double a)

反余弦函数 cos(a)

public static double atan(double a)

反正切函数 tan(a)

public static double exp(double a)

自然指数函数 exp(a)

public static double log(double a)

自然对数函数 log(a)

public static double sqrt(double a)

开根号函数 sqrt(a)

public static double ceil(double a)

产生一个大于a的最小整数

public static double floor(double a)

产生一个小于a的最大整数

public static souble pow(double a,double b)

计算ab次方

public static int round(float a)

返回最接近a的整数

public static double random()

返回0.0-1.0之间的随机数

public static type abs(type a)

计算a的绝对值,其中type可为intlongfloat或是double

public static int max(int a,int b)

找出ab中较大者

public static int min(int a,int b)

找出ab中较小者

8.5日期类

    Java提供了4个常用的日期类:DateCalendarDateFormatGregorianCalendar.在程序中,对日期的处理主要是如何获取、设置和格式化,Java的日期类提供了很多方法以满足程序员的各种需要.其中,Date主要用于创建日期对象并获取日期,Calendar可获取和设置日期,DateFormat主要用来创建日期格式化器,由格式化器将日期转换为各种日期格式串输出,GregorianCalendar类用我们熟悉的日历符号表示日期,它是对更一般的Canlendar类的扩展. Java是网络语言,程序必须注意到各个国家对日期格式的不同理解.

 例DateTest.java演示了日期的获取、设置和格式化.

GregorianCalendar类可使得编写日历程序十分简单,如下例:

//CalendarTest.java

import java.util.*;

public class CalendarTest{

      public static void main(String[] args){

             GregorianCalendar d=new GregorianCalendar();

             int today=d.get(Calendar.DAY_OF_MONTH);

             int month=d.get(Calendar.MONTH);

             d.set(Calendar.DAY_OF_MONTH,1);

             int weekday=d.get(Calendar.DAY_OF_WEEK);

             System.out.println("Sun Mon Tue Wed Thu Fri Sat");

             for(int i=Calendar.SUNDAY;i<weekday;i++)

             System.out.print("    ");

             do{

                    int day=d.get(Calendar.DAY_OF_MONTH);

                    if(day<=10)

                    System.out.print(" ");

                    System.out.print(day);

                    if(day==today)

                    System.out.print("*");

                    else

                    System.out.print("  ");

                    if(weekday==Calendar.SATURDAY)

                    System.out.println();

                    d.add(Calendar.DAY_OF_MONTH,1);

                    weekday=d.get(Calendar.DAY_OF_WEEK);

             }while(d.get(Calendar.MONTH)==month);

             if(weekday!=Calendar.SUNDAY)

             System.out.println();  }

}

8.6随机数类 Random

使用Math中的Random类可产生一个0到1之间的伪随机数,为了适应网络时代编程

对随机数的需要,JavaRandom类中提供了更多的功能,Random的实例化对象可使用一个48位长的种子数来生成随机数.如果两个Random对象使用同样的种子数,并以同样的顺序调用生成方法,仍然可以保证得到两个不同的32位伪随机数.为了使Java程序有良好的可移植性,应该尽可能使用Random类来生成随机数.

  Random有两个构造方法:Random()Random(long seed).前者使用系统时间作为种子数,后者使用指定的种子数.构造方法只是创建了随机数生成器,必须调用生成器的方法才能产生随机数.Random具有nextBooleannextDoublenextInt等方法。

 RandomTest.java演示了用Random生成各种类型的随机数。

87 向量类Vector

  大多数编程语言中的数组是固定长度的,即数组一经建立就不能在使用过程中改变其长度.有些程序可能要解决数组长度不断改变的问题,这对于无法预见使用多长的数组更合适的程序员来说是一件头痛的事情,Java引入的Vector类较好地解决了这个问题.

  Vector被设计成一个能不断增长的序列,用来保存对象引用.在创建Vector对象时可以指定初始容量和增量,每次添加元素都将使向量长度按增量自动增长.

  Vector类似于可变长数组,但功能更加强大,任何类型的对象都可以放入Vector.通过调用Vector封装的方法,可以随时添加或删除向量元素,以及增加或缩短向量序列的长度.

Vector的常用方法如下所示.

Vector(int initcapacity,int increment)//构造方法,指定初始容量和增量

Void addElement(Object obj)//在尾部添加元素,元素个数自动增1

Boolean removeElement(Object obj)//删除第一个与obj相同的元素

Void setElementAt(Object obj,int index)//替换指定位置上的元素

Object elementAt(int index)//返回指定位置的元素

Int indexOf(Object obj)//返回指定元素obj在向量中的位置

Int size()//返回元素个数

Int capacity()//返回向量的长度

Void trimToSize()//按当前元素个数缩减向量长度

Enumeration elements()//生成一个向量元素的枚举

Vector包含在java.util包中,例子VectorTest.java演示了Vector的应用

/**

Enumeration接口定义了可以对一个对象的类集中的元素进行枚举(一次获得一个)的方法。这个接口尽管没有被摈弃,但已经被Iterator所替代。Enumeration对新程序来说是过时的。然而它仍被几种从以前版本遗留下来的类(例如VectorProperties)所定义的方法使用,被几种其他的API类所使用以及被目前广泛使用的应用程序所使用。

   

Enumeration指定下面的两个方法:

boolean hasMoreElements()

Object nextElement()

执行后,当仍有更多的元素可提取时,hasMoreElements()方法一定返回true。当所有元素都被枚举了,则返回falsenextElement()方法将枚举中的下一个对象做为一个类属 Object的引用而返回。也就是每次调用nextElement()方法获得枚举中的下一个对象。调用例程必须将那个对象置为包含在枚举内的对象类型。

对于Enumeration可以以Vector为例

Vector里有很多对象,如果你要查看其中的所有对象,一个办法是用Vectorget(int index)方法,不过这样效率比较低,另外一个方法是用Vectorelements()方法返回一个Enumeration对象,用EnumerationhasMoreElements()方法向下移动并判断当前位置是否有对象,有则用nextElement()方法返回这个对象

 

例如, 打印 vector v中的所有对象:

Enumeration e = v.elements()

while(e.hasMoreElements() )

{

System.out.println(e.nextElement());

}

 

另外还有个Iterator接口,和Enumeration是差不多的,不过名称比较短,通常推荐用Iterator

Iterator

对集合进行迭代的迭代器。迭代器代替了 Java Collections Framework 中的 EnumerationCollections 接口中定义了 iterator 方法,用于返回在此 collection 的元素上进行迭代的迭代器。

*/

88 Class类与Runtime

   通过Class类与Runtime类中的方法可以获得运行时的信息,如当前类名、超类名、包名、内存空间以及操作系统名称等.

以下程序演示了如何获取运行时信息.

//ClassTest.java

public class ClassTest

{

       public static void main(String args[])

       {

              String str=new String("");

              System.out.println("本类名 ="+str.getClass().getName());

              System.out.println("超类名 ="+str.getClass().getSuperclass().getName());

              System.out.println("包名   ="+str.getClass().getPackage().getName());

              System.out.println("操作系统 ="+System.getProperty("os.name"));

              System.out.println("Java版本 ="+System.getProperty("java.vm.version"));

              System.out.println("内存总量 ="+Runtime.getRuntime().totalMemory());

              System.out.println("剩余空间 ="+Runtime.getRuntime().freeMemory());

       }

}

System.getProperty(String name)方法用于得到系统的属性.下面是该方法的常用参数。-------------------------------------------------

java.versionjava.version                        Java运行环境版本 

java.vendorjava.vendor                         Java运行环境卖主 

java.vendor.urljava.vendor.url               Java卖主的URL  

java.homejava.home                               Java的安装目录  

java.vm.specification.version                 Java虚拟机规范版本  

java.vm.specification.vendor                 Java虚拟机规范供应商 

java.vm.specification.name                    Java虚拟机规范名称  

java.vm.versionjava.vm.version              Java虚拟机执行版本  

java.vm.vendorjava.vm.vendor               Java虚拟机实现供应商  

java.vm.namejava.vm.name                    Java虚拟机实现名称  

java.specification.version                    Java运行时环境规范版本 

java.specification.vendor                    Java运行时环境规范供应商  

java.specification.name                       Java运行时环境规范名称  

java.class.versionjava.class.version      Java类格式版本号 

java.class.pathjava.class.path              Java