面向对象(PartJava).ppt

上传人:cl****1 文档编号:569294156 上传时间:2024-07-28 格式:PPT 页数:246 大小:1.89MB
返回 下载 相关 举报
面向对象(PartJava).ppt_第1页
第1页 / 共246页
面向对象(PartJava).ppt_第2页
第2页 / 共246页
面向对象(PartJava).ppt_第3页
第3页 / 共246页
面向对象(PartJava).ppt_第4页
第4页 / 共246页
面向对象(PartJava).ppt_第5页
第5页 / 共246页
点击查看更多>>
资源描述

《面向对象(PartJava).ppt》由会员分享,可在线阅读,更多相关《面向对象(PartJava).ppt(246页珍藏版)》请在金锄头文库上搜索。

1、面向对象程序设计面向对象程序设计(For JAVA)教材:教材:JAVA语言导学语言导学出版:机械工业出版社出版:机械工业出版社编著:编著:Mary Campione, Kathy Walrath et. al参考文献参考文献 :JAVA语言程序设计语言程序设计-基础篇基础篇编著:编著:Y. Daniel Liang出版:机械工业出版社出版:机械工业出版社1第11章Java起步2Java简史InternetInternet迅猛发展迅猛发展JavaJava语言正式问世语言正式问世被美国著名杂志被美国著名杂志PC PC MagazineMagazine评为评为19951995年十大优年十大优秀科技

2、产品之一秀科技产品之一James GoslingJames Gosling领领导的导的GreenGreen小组开小组开发了面向数字家发了面向数字家电的电的OakOak语言语言推出了推出了Java2Java2平台。平台。性能上有所提高,性能上有所提高,而且对而且对JavaJava的基的基本模式进行了完本模式进行了完善,并提供了更善,并提供了更为丰富的为丰富的APIAPI 1991199519983Java2版本划分Java 2Java 2J2MEJ2MEJ2SEJ2SEJ2EEJ2EE4Java语言主要特性平台无关性平台无关性简单性简单性面向对象面向对象 分布式、动态性、分布式、动态性、 可靠性

3、、安全性可靠性、安全性 多线程多线程Write OnceRun Anywhere:JVM在语言级支持多线程。在语言级支持多线程。C+必须第三方库必须第三方库取消了多重继承、指针。取消了多重继承、指针。自动地内存垃圾回收。自动地内存垃圾回收。分布式:分布式:Java程序可以在多台程序可以在多台机器上协同计算。(机器上协同计算。(RMI)动态性:动态性:JVM的的Class Loader支持类支持类的动态装载,甚至允许动态地重新的动态装载,甚至允许动态地重新装入已修改的代码,同时应用程序装入已修改的代码,同时应用程序继续执行继续执行。反射机制反射机制可靠性:由于取消指针,内存自动可靠性:由于取消指

4、针,内存自动回收,使得程序更为健壮回收,使得程序更为健壮安全性:安全性:Class Loader将类装载入将类装载入JVM运行前会进行安全检查。用户运行前会进行安全检查。用户可以配置安全策略。可以配置安全策略。5Java程序及其运行机制程序及其运行机制Java虚拟机虚拟机在真实机器中用软件模拟实现的一种抽象的机器。(在真实机器中用软件模拟实现的一种抽象的机器。(Java虚拟机规范)虚拟机规范) ,负责解释和执行,负责解释和执行Java程序编译后产生的程序编译后产生的字节码字节码。Java API是是Java系统提供的预先定义好的软件组件的集合,它们提供许多系统提供的预先定义好的软件组件的集合,

5、它们提供许多可供程序员调用的常用功能可供程序员调用的常用功能丰富的丰富的API为程序员提供了极大的方便,但也是学习的难点为程序员提供了极大的方便,但也是学习的难点 6Java语言规范,API,JDK,IDEJava语言规范(Java Language Specification, JLS)Java语言的语法和语义技术性定义。应用程序接口(Application Program Interface, API)Java预定义类和接口。Java开发工具包(Java Development Kit, JDK)包含软件库、编译器、解释器以及其它工具。集成开发环境(Integrated Developme

6、nt Environment, IDE)在图形界面中,完成编辑、编译、调试和在线帮助等功能。7JavaAPI手册及JDKhttp:/ SE 6 Update 23)http:/ Javaava解释器解释器传统语言的运行机制Java语言的运行机制9Java程序类型Java应用程序(应用程序(Java Application)是独立完整的程序是独立完整的程序在命令行调用独立的解释器软件即可运行在命令行调用独立的解释器软件即可运行主类必须有主类必须有main方法,这也是程序的入口方法,这也是程序的入口 Java小程序小程序Java Applet:是指在浏览器里运行的是指在浏览器里运行的Java小程序

7、,一般来说客户端是指用户小程序,一般来说客户端是指用户所使用的浏览器;所使用的浏览器;Java Servlet:是在服务器端执行的是在服务器端执行的Java程序,为了能够支持程序,为了能够支持Servlet的运行,的运行,服务器端必须安装包含服务器端必须安装包含Java虚拟机的服务器软件(虚拟机的服务器软件(Tomcat, WebLogic, WebSphere ),这种能够支持服务端小程序的服务器一般被称为应用服务器),这种能够支持服务端小程序的服务器一般被称为应用服务器(Servlet容器)。容器)。 10Java开发环境JDK/J2SDK(Java Developer Kit)JDK1.

8、2/J2SDK1.3/J2SDK1.4/JDK5.0(J2SDK1.5) /JDK6.0 Java集成开发环境(IDE)Visual J+(不再支持,不要使用)(不再支持,不要使用)NetBeansJBuilderEclipseWSAD JCreator建议开始不要使用IDE。使用JDK,通过命令行方式编译和运行Java程序。不要过度依赖IDE!11JDK的安装与设置从http:/ 假设假设JDK安装在安装在D:JDK6目录下目录下 set JAVA_HOME=D:JDK6bin set path=%path%; %JAVA_HOME% set classpath=%classpath%; E

9、:ch05bin13Java实用程序简介javac:Java编译器,将Java源代码编译为字节码;java:Java解释器,用来解释执行Java程序的字节码文件.class;appletviewer(小程序浏览器):一种执行HTML文件上的Java小程序类的Java浏览器;javadoc:根据Java源代码及其说明语句生成的HTML文档;jdb:Java调试器,可以逐行地执行程序、设置断点和检查变量;javah:产生可以调用Java过程的C过程,或建立能被Java程序调用的C过程的头文件;Javap:Java反汇编器,显示编译类文件中的可访问功能和数据,同时显示字节代码含义。所有这些命令可以列

10、出帮助信息command-help14编辑源代码一般情况下,我们可以使用系统平台下的任何文本编辑器进行源代码的编辑,在Windows平台下常用的是Window系统自带的“记事本”程序或“写字板”程序Java源码大小写敏感Java源码文件的扩展名:.javaJava源程序文件也不能随意命名,其文件名必须与程序中主类(public)的类名完全相同,包括大小写在内一个一个JavaJava源文件里只能有一个源文件里只能有一个publicpublic类类 15创建、编译和执行Java程序创建/编辑源代码编译源文件执行字节码输出结果源文件(.java文件)字节码(.class文件)编译错误结果错误创建和编

11、辑源代码可以使用任何文本编译器创建和编辑源代码。文件名必须与公共的类名一致,文件后缀为.java编译在控制台中执行:javac 源文件名生成.class字节码文件执行在控制台中执行: java 类名16MyFirstJavaApp.java/注意类名和文件名相同,含main的类必须用public/*Java也支持多行注释*/包含在/*/中的为javadoc注释,在每个包(Package)、每个类、及属性方法前加上javadoc注释可以自动生成基于HTML的帮助文档packageCh11;/defineapackagecontainingalldemosourcecodeofchapter11/

12、*TheMyFirstJavaAppclassimplements*ansimpleapplicationthatdisplayHelloWorldtotheconsole*authorXiwuGu*/publicclassMyFirstJavaApp/*paramargs:commandlinearguments*/publicstaticvoidmain(Stringargs)/JAVA是纯OOP,没有全局函数,main也必须/封装在类里System.out.println(HelloWorld!);/out是System的类变量(静态数据成员),PrintStream类型,println

13、是out的方法17编译JAVA程序设置好path及classpath在JAVA源文件所在目录下输入javacMyFirstJavaApp.java在当前目录下会生成MyFirstJavaApp.class可以指定输出的.class文件保存的目录javacMyFirstJavaApp.java-dbin将输出的.class文件保存在当前目录的子目录bin下18运行Java程序要执行一个Java程序非常简单,只需在字节码文件所在目录下输入下列命令就可以了:java字节码文件名注意不要.class后缀19SuggestionfordevelopingprogrammingskillThe best

14、way to start programming is to have a terminal running an interactive language and a friend sitting nearby who already knows the language and has something else to do but can be interrupted with questions. And you just try stuff, till more and more you get the feel of it. And you find yourself writi

15、ng programs that work. Nelson, Ted, Computer Lib, Microsoft Press, 1987 20第12章面向对象编程21面向对象的编程概念对象和类类和对象的UML表示面向过程vs面向对象消息继承接口22什么是对象对象(软件对象)是变量和相关方法的软件组合,是对现实世界世界对象的抽象对象具有状态和行为同一类对象的行为一致,但状态不同状态(变量)行为(方法)姓名:张三学号:0001身高:1.73学习工作娱乐23什么是类类(class)定义了同一类对象共有的属性和方法。类是对象的模板、蓝图。对象是类的实例。24类的示例下面是一个圆类:class C

16、ircle class Circle double radius = 1.0; double radius = 1.0; Circle() Circle() radius = 1.0; radius = 1.0; Circle(doubleCircle(double r) r) radius = r; radius = r; double double findAreafindArea() () return radius * return radius * radiusradius * * 3.14159;3.14159; 数据字段方法构造函数Circle:c1radius=1.0Circl

17、e:c2radius=10.0Circle:c3radius=15.025类和对象的UML表示类的UML表示类名数据字段方法对象的UML表示对象名:类名数据字段值构造函数26面向过程vs面向对象面向过程以数据结构来描述问题以过程(函数)来描述算法数据结构和算法是分离的,不易维护及软件复用面向对象数据结构及算法描述被封装在类里用类的继承来实现软件复用支持多态只要接口不变,父类的具体实现改变了不会影响子类,易于维护27消息及其组成消息的发送者A及目的对象B希望对象B要执行的方法的名称-ChangeGears方法所需参数:lowerGear汽车汽车驾驶员驾驶员ChangeGears(lowerGea

18、r)单独的一个对象没什么作用。一个复杂的软件系统的功能通过多个对象间的交互实现。对象之间的交互是通过相互发送消息实现的。当对象A(驾驶员)希望对象B(汽车)执行时方法“切换到低档”时,对象A发送消息给对象B。消息包括:消息发送具体实现:在对象A的某个方法里调用对象B的ChangeGears(lowerGear)28继承继承是子类能自动地共享父类中定义的数据和方法的机制29单重继承和多重继承单重继承:一个子类只有一个直接父类多重继承:一个子类可以有多个直接父类JAVA只支持单重继承JAVA继承都是public继承,因此在JAVA术语里只有子类、父类,没有基类和派生类。java.lang.Obje

19、ct类是所有JAVA类的祖先类30接口(interface)接口interface也是一种类,包含常量和方法的定义。无论如何,接口定义的所有变量都是publicstaticfinal类型,所有方法都是publicabstract方法。注意,abstract方法即C+的纯虚函数。因此,接口相当于抽象类。不能拥有对象。接口是无关对象进行彼此交互的协议。无关对象是指在类的层次结构中没有相关性。31接口示例public public interfaceinterface Flyer Flyer void void takeOfftakeOff();(); void land(); void land(

20、); void fly(); void fly(); class Airplane class Airplane implementsimplements Flyer Flyer public void public void takeOfftakeOff() () / / 加速直到离地升空加速直到离地升空 / / 收起起落架收起起落架 public void land() public void land() / / 放下起落架放下起落架 / / 减速并降低副翼直到降落减速并降低副翼直到降落 / / 刹车刹车 public void fly() public void fly() / / 保

21、持引擎运转保持引擎运转 接口方法的访问Flyer f = new AirPlane();f.takeOff();f.fly();f.land();32接口和继承33接口Java类可以实现多个接口,若要能产生实例对象,则必须覆盖定义每个接口的函数。接口中的常量也可以被隐藏。可使用接口名限定访问被隐藏的接口常量。如果类被定义为public的,通常要求存储在与类同名的Java文件。34/Main.java:因classMain为public的interfaceI1ints=1,t=2;/缺省是且只能是publicstaticfinal的intf();/缺省是publicabstractinterfa

22、ceI2ints=1;/缺省是且只能是publicstaticfinal的intf();/缺省是publicabstractclassTestimplementsI1,I2intt;/隐藏了I1,注意这里的t是实例变量publicintf()returnI1.t+I2.s;publicclassMainpublicstaticvoidmain(Stringargs)35Java程序结构Java程序由若干包构成,可以使用无名的缺省包。JAVA里的包作用相当于C+的namespace包由package定义,包由一系列的类和接口构成同一个包不能定义同名类和接口。一个包可以引用其它包定义的公共类和接口

23、(import)包必须和目录名建立对应,而类名则和该目录下的java文件的主文件名对应。程序的入口main函数必须位于一个public类中main,且main函数必须在类中必须是publicstatic的。36第13章 Java语言基础37数据类型:原子类型和引用类型原子类型为值类型38原子类型类型char:采用unicode编码,每字符2字节类型byte:一字节有符号整型类型short:两字节有符号整型类型int:4字节有符号整型类型long:8字节有符号整型类型float:4字节单精度浮点数类型double:8字节双精度浮点数类型boolean:1字节布尔类型,实际使用1位二进制,常量有t

24、rue和false整数常量后带L或l表示long类型,浮点数后带D或d表示双精度浮点数,带F或f表示单精度浮点数。39引用类型相当于C+的指针类型,在堆上上分配内存,但内存分配与回收由java自己完成由class、interface和数组构成的类型都是引用类型与C+指针不同的是,每次访问Java的引用类型变量时,访问的是(相当于C+指针所指向的)存储单元的值或内容。所有引用类型都是从Object对象派生的。引用类型变量必须用new进行实例化,即在堆上分配内存和调用构造函数初始化:Objectx=newObject();原子类型自动在栈上分配内存,只需要初始化。例如,intx=3;40原子类型变

25、量(值类型)和引用类型变量的区别变量表示存储数据的内存单元。原子类型变量存储的是原子类型的值。引用类型变量存储的是对象的引用。当变量未引用任何对象时,它是值为null。1基本类型基本类型 int i = 1int i = 1;i inull引用类型引用类型 Circle c;Circle c;c c对象的引用Circle c = new Circle();Circle c = new Circle();c c41标识符Java中使用标识符(identifier)来命名变量、常量、方法、类、包等实体。标识符命名规则标识符是由字母、数字、下划线(_)、美元符号($)组成的字符序列。标识符必须以字母

26、、下划线(_)、美元符号($)开头。不能以数字开头。标识符不能是保留字。标识符不能为true、false或null。标识符可以为任意长度。42Java保留字abstractcontinuefornewswitchassertdefaultifpackagesynchronizedbooleandogotoprivatethisbreakdoubleimplementsprotectedthrowbyteelseimportpublicthrowscaseenuminstanceofreturntransientcatchextendsintshorttrycharfinalinterfaces

27、taticvoidclassfinallylongstrictfpvolatileconstfloatnativesuperwhile43变量名变量名使用合法的表示符做变量名称。变量名使用合法的表示符做变量名称。变量名可以被对象名、类名中或包名变量名可以被对象名、类名中或包名+类名限定,必要时要多级类名限定,必要时要多级限定。如类变量限定。如类变量System.out,java.lang.Math.PI。这类变量叫。这类变量叫成员成员变量。变量。局部变量局部变量通常指的是函数中声明的变量,其作用范围就是该函数。通常指的是函数中声明的变量,其作用范围就是该函数。成员变量是指类的成员,可分为成员变

28、量是指类的成员,可分为类变量类变量和和实例变量实例变量。实例变量必。实例变量必须依赖于对象的存在而存在。须依赖于对象的存在而存在。在类的函数成员以外访问时,通常需要实例变量是可见的,且需在类的函数成员以外访问时,通常需要实例变量是可见的,且需要使用要使用“对象对象.实例变量实例变量”进行限定访问。进行限定访问。只读变量使用只读变量使用final关键字声明。关键字声明。finaldatatypeCONSTANT_NAME=value;约定:变量名、方法名以小写字母开始,若后面连接单词,则单约定:变量名、方法名以小写字母开始,若后面连接单词,则单词首字母大写,如词首字母大写,如radius, ge

29、tRaidus();常量全部使用大写字母。;常量全部使用大写字母。约定:类名每个单词的首字母大写,如约定:类名每个单词的首字母大写,如AirPlane44运算符(operator)包括单目、双目和三目的。三目运算符为“?:”。算术运算符:算术运算符:单目(+,-, +, -),双目(+,-,*, /, %)单目+,-也分前置和后置,其意义和C+一致但+,-不允许出现在=左边如+i=1;/错关系运算符:关系运算符:, =, ,=, = =, !=逻辑运算符:逻辑运算符:&(与),|(或),! (非)&(与),|(或),(异或)&(条件与):计算p1&p2时,如果p1为false,不用再计算p2&

30、(无条件与):计算p1&p2时,无论如何p1和p2都要计算|(条件或):计算p1|p2时,如果p1为true,不用再计算p2|(无条件或):计算p1|p2时,无论如何p1和p2都要计算只能用于布尔类型移位符:移位符:op1 op2(op1右移op2位,左边填符号位,即最高位)op1op2(op1右移op2位,左边填0)45运算符位逻辑运算符位逻辑运算符:&(按位与),|(按位或),(按位求反),(按位异或)位逻辑运算符只能用于整数类型(byte,short,int,long)赋值及简捷赋值运算符:赋值及简捷赋值运算符:Java要求赋值运算符左边必须是变量(不能是要求赋值运算符左边必须是变量(不

31、能是表达式)表达式)=算术简写:+=,-=,*=,/=, %=位简写:&=, |=,=移位简写:=, =三目运算符三目运算符:? : 数组下标运算符数组下标运算符: 成员访问运算符成员访问运算符: .对象创建运算符:对象创建运算符:newRTTI类型识别运算符:类型识别运算符:instanceof函数调用运算符函数调用运算符: (以逗号分开的参数列表以逗号分开的参数列表 )强制类型转换运算符强制类型转换运算符: (目标类型目标类型)46运算符JAVA里布尔值的结果为true和false,而不像C+是0或1。并且不能将其他类型强制转换为布尔类型instanceof用于判定前一个变量(或对象)是否

32、为后面的类型(类)的实例。47表达式(expression)表达式由字面量(literal)、变量、操作符和方法调用构成,用于求出单个值Java的表达式没有C+的左值概念。表达式求值(evaluate)要注意操作符优先级和结合性(见书P62表20)48语句(statements)语句是完整的可执行单元,以分号结束以下表达式加上;就成为了语句赋值表达式+,-,方法调用对象创建表达式(new表达式)Java的语句基本同C+,除异常可有finally外。无论出现异常与否,都必须执行finally的部分。C+实际上也有,不过保留字为_finally。条件语句:if,ifelse,ifelseif,sw

33、itch循环:while,dowhile,for异常处理:trycatchfinally49第14章 对象基础和简单数据对象50对象的生命周期对象的生命周期:创建、使用、清除。51对象的创建创建:类型名引用变量=new类名(构造器的参数)。必须用new创建。new会自动地调用类的构造函数。一个类可以定义多个重载的构造器。如果new创建的对象没有赋给任何变量,该对象除当时访问外,将无法在以后访问。构造器没有返回类型,构造器的名字与类名相同。即使将返回类型说明为void,也会导致其成为非构造函数。每个Java类(不含接口)都至少有一个构造器,如果类没有显示定义,系统会默认地为该类提供一个不包含任何

34、参数的无参构造器。52对象的创建Circle Circle myCirclemyCirclenew Circle();new Circle();= =1.1.声明一个引用变量声明一个引用变量, ,变量类型为变量类型为Circle,Circle,变量名为变量名为myCirclemyCircle2.2.创建对象创建对象, ,返回对象的引用返回对象的引用3.3.将对象的引用赋值给变量将对象的引用赋值给变量myCirclemyCircle以上语句等价于:CirclemyCircle;/声明一个Circle类型的引用/变量,其值为null,称为空引用myCircle=newCircle();/创建对象,

35、并将/该对象的引用赋给myCircle/使myCircle引用被创建的对象null引用类型引用类型 Circle Circle myCirclemyCircle; ;myCirclemyCircle对象的引用 myCirclemyCircle = new Circle(); = new Circle();myCircle53对象的使用对象的使用有两种方式:操作或者检查它的变量(数据成员)调用它的方法要使用对象的实例成员,首先要得到该对象的引用。对于基于RMI的分布式对象,可以通过名字透明地得到一个运程对象的引用(屏蔽了网络协议、网络地址、端口号等细节)当获得一个对象(不管是本地的还是运程的)的

36、引用后,就可以访问对象的属性或调用其方法54对象的使用类的成员分两种:实例成员与类成员。实例成员包括实例变量与实例方法实例成员依赖于实例(即对象)存在。同一个类的不同实例都拥有一份实例变量的拷贝,对某个实例变量的操作不影响到其它实例变量。实例变量与对象生命周期一样,随对象的创建而创建,随对象的消亡而消亡。必须通过实例的引用来调用实例方法(所谓的依赖)。类成员包括类变量与类方法类成员不依赖于实例存在,无任何对象时也可访问,即值已存在。同一个类的不同实例共享同一个类变量,对类变量的改变会影响所有实例。类变量的生存期不依赖于对象,其它类可以不用通过该类的实例,直接通过类名就访问它们。同样,类方法也可

37、直接通过类名访问。55实例变量与静态变量实例变量(instancevariable):未用static修饰的成员变量,属于类的具体实例(对象),只能通过引用变量访问,即:对象名.变量名。类变量(staticvariable)是用static修饰的变量,在一个类的所有实例间共享,也称类变量。静态变量可以通过类名访问,即类名.变量名(也可用对象名访问)512radiusradiusnumberOfObject- 表示private+ 表示public下划线 表示static二个对象的radius实例变量存储于不同空间,彼此不影响所有对象共享静态成员变量存贮在公共内存某个对象对其修改会影响其他对象5

38、6静态常量静态常量是用staticfinal修饰的变量。例如,Math类中的常量PI定义为:publicstaticfinaldoublePI=3.14159265358979323846;57类方法类方法(staticmethod)是用static修饰的方法。例如:mainMath.random类方法可以通过类名和对象名调用。类方法只能访问类的静态成员,不能访问类中的实例成员。58实例方法实例方法(staticmethod)是没有用static修饰的方法。实例方法只能通过对象名(引用)调用。实例方法可以访问类的静态成员和实例成员。59成员的访问/实例成员通过对象限定使用。/:类成员可通过类名

39、限定使用,也可通过对象限定使用。提倡用第一种使用形式。classTest/等价于classTestextendsObject,即Test继承自Object对象intt;staticintw=3;/定义及初始化wpublicTest(intx)t=x;/在构造函数中初始化w与否根据需要确定/注意JAVA在类的定义结束后不要;/同时JAVA类的方法都必须在类体里实现publicclassMainpublicstaticvoidmain(Stringargs)Testx=newTest(3);inty=x.t;/t的访问权限不是private,是defaulty=x.w;/不提倡以这种形式使用(为什

40、么?)y=Test.w;60Object对象所有类都直接或间接继承自Object类,Object为父类。所有可实例化类的内存管理都是由Object通过引用计数完成的。A继承自B的关系使用extends说明,如classAextendsB。子类在其构造器的第一条语句缺省调用父类无参构造函数。如果子类中没有显式地调用父类的构造函数,编译器会自动地如果子类中没有显式地调用父类的构造函数,编译器会自动地在子类构造函数第一条语句前加上在子类构造函数第一条语句前加上super()也可以显示地调用,必须是子类构造函数的第一条语句也可以显示地调用,必须是子类构造函数的第一条语句。class A extends

41、 B public A()() /some statements class A extends B public A()() super ( ); /some statements 等价于61Object对象Object对象负责了对象内存管理,包括垃圾自动回收。Object类定义了所有类都必须有的基本状态和行为:clone(对象克隆,缺省是浅拷贝,一般需要覆盖),finalize(相当于析构)equals(比较二个对象是否相等),hashCodetoString(将对象转换为字符串)getClass(得到一个对象的类别信息,RTTI)notify,notifyAll以及wait(线程同步)其

42、中clone,finalize的访问权限是protected,其它为public。此外,getClass、notify、notifyAll及wait被定义为final方法,不能被子类覆盖。62classA/Object对象负责管理内存,通过对象的引用次数判定内存可否回收intx;publicA(intx)this.x=x;publicclassMain/注意生命期和作用域的不一致staticAf()Aa=newA(3);Ab=a;Ac=newA(4);returna;/对象2生命起止在f内publicstaticvoidmain(Stringargs)Ax=f();对象1x=3引用计数:1引用

43、a对象1x=3引用计数:2引用a,b对象2x=4引用计数:1引用c对象1x=3引用计数:1引用x对象2x=4引用计数:0对象1x=3引用计数:0f返回,c的作用域结束导致对象2的引用计数为0f返回,a,b的作用域结束,返回值赋给x,导致对象1引用计数为1main返回,x的作用域结束对象1的引用计数=063对象的生命周期由于JAVA在对象的引用计数=0时才清除对象,导致作用域和生命周期的不一致。在上例中,引用a的作用域在f内,但a引用的对象的生命周期在函数f结束后还没结束这是JAVA和C+的一个重要区别:C+:如果函数返回一个引用,该引用了函数内的局部对象。则函数返回的引用引用的是一个失效的对象

44、(编译没问题,但会产生runtimeerror)。JAVA:函数返回一个引用可以引用函数内生成的对象。函数返回后通过该引用还是可以访问该对象,因为该对象的生命周期没结束。64finalize( )方法finalize()方法在Object中有缺省实现。在用户自定义的类中,它可以被覆盖,但一般在最后要调用父类的finalize()方法,来清除对象所使用的所有资源。相当于C+的析构函数。在对对象进行垃圾收集前,Java运行时系统会自动调用对象的finalize()方法来释放系统资源。注意,finalize()是protected,不可在类外通过对象进行调用。在变量的生命期结束时,例如前例中的变量c

45、,系统并不一定马上调用垃圾收集程序,即与C+的析构函数立即执行有所不同。某些情况下,程序员可能需要实现finalize()方法来释放不由垃圾收集器所控制的资源,例如,需要关闭对象打开的文件。考虑到finalize()只在垃圾收集前被调用,而垃圾收集可能被延迟。在急需关闭文件以供其它任务打开时,最好强制系统进行垃圾收集,即调用System.gc()从而引起finalize()执行。一般情况下,如果自定义的对象仅使用了内存,而且不是通过native函数(例如通过C+,Java不自动回收C+分配的内存)申请的,就没有必要自己实现finalize()方法。65对象相等的判定=用来判定是否引用同一个对象

46、,如上例中判定a=b的结果为true。如果存在另一个对象Ad=newA(3),判定a=d,则结果为false。即两个对象的地址不同。Object提供了函数equals()来判断对象是否相等。但其实现是比较二个引用是否指向同一个对象。因此往往需要在自定义类型中覆盖该函数。一般来说,如果自定义类型中包含了引用类型的实例变量(成员),则需要覆盖equals()、hashCode()、clone()(缺省行为是浅拷贝赋值)toString()通常是必须覆盖的,用于将当前对象转化为可打印的字符串。66Object类的equals方法Object是所有Java类的祖先类,它实现了一些有用的方法,如前面介绍

47、的eauals方法publicbooleanequals(Objectobject):检验两个对象是否相等语法:object1.equals(object2)该方法只是比较二个引用变量是否相等,即object1和object2是否指向同一个对象,而不是比较二个引用变量指向的对象的内容是否相等,所以子类很有必要覆盖该方法。classCircleprivatedoubleradius=1.0;Circle()Circle(doubler)radius=r;publicbooleanequals(Objecto)if(oinstanceofCircle)returnthis.radius=(Circ

48、le)o).radius;elsereturnfalse;例如二个例如二个Circle对象,如果半径相等就相等,对象,如果半径相等就相等,我们可以在我们可以在Circle类里覆盖该方法类里覆盖该方法67Object类中的clone方法protectedObjectclone()复制对象(缺省为浅拷贝)对象引用变量的赋值是不能复制对象的,要创建有单独内存空间的新对象,使用clone()方法newobject=someobjet.clone();/newobject!=someobject子类要实现clone方法,首先应该实现Cloneable接口,同时覆盖从Object类继承的clone方法,并

49、将该方法改为public68Object类中的toString方法publicStringtoString()返回代表这个对象的字符串。Object类的默认实现是返回由(包名.)类名hashCode组成。Circlecircle=newCircle();circle.toString();/Circle15037e5Object的toString方法提供的信息不是很有用。因此通常子类应该覆盖该方法,提供更有意义的信息。classCircleprivatedoubleradius=1.0;Circle()Circle(doubler)radius=r;publicStringtoString()

50、return“Acirclewithradius”+radius;/字符串+数值结果为字符串69简单数据对象除了提供原子类型如char、int等原子类型外,Java还提供了这些类型的包装类(Wrapper),如Character、Integer等。作用包装提供了额外的信息,例如通过Integer类型,我们能够获知最小或最大的整数值是多少。由值类型变成了引用类型,用于需要对象引用的场合(比如函数需要引用参数voidswap(inti1,inti2)/callbyvaluevoidswap(Integero1,Integero2)/callbyref这些类还定义了一些有用的方法,用于将值转换为其他

51、类型、转换为字符串等等。BigInteger和BigDecimal从精度上扩展了原始数据类型。 这些类可用于反射(Reflection),反射机制允许程序收集JVM中任何对象或类的信息。70简单数据对象NumberByteShortIntegerLongFloatDoubleCharacterBooleanObjectComparable每个Wrapper类对应一种原子数据类型。每个Wrapper类实现了接口Comparable.BigIntegerBigDecimal71包装类的构造函数构造函数以一个对应的基本数据类型为参数以字符串为参数(除了Character)如Integer类和Doub

52、le类的构造函数如下:publicDouble(doublevalue);publicDouble(Stringvalue);例如DoubledoubleObject=newDouble(5.0);DoubledoubleObject=newDouble(“5.0”);包装类没有无参构造方法72数值包装类的常量每一个数值包装类都有常量MAX_VALUE和MIN_VALUE。MAX_VALUE对应本数据类型的最大值。对Byte,Short,Integer和Long,MIN_VALUE对应最小值对Float和Double,MIN_VALUE对应最小正值如System.out.println(“Th

53、emaximumintegeris”+Integer.MAX_VALUE);System.out.println(“Theminimumpositivefloatis”+Float.MIN_VALUE);73从包装类对象-基本数据类型Byte、Short、Integer、Long、Float、Double类都分别实现方法byteValue、shortValue、intValue、longValue、floatValue、doubleValue。这些方法原型如下publicbytebyteValue()/Byte类实例方法publicshortshortValue()/Short类实例方法pub

54、licintintValue()/Integer类实例方法publiclonglongValue()/Long类实例方法publicfloatfloatValue()/Float类实例方法publicdoubledoubleValue()/Double类实例方法如inti=newInteger(10).intValue();/另外每个类的toString()方法将数值转换成字符串74字符串转换成数值Byte,Short,Integer,Long,Float,DoublepublicstatictypeparseType (Strings)publicstatictypeparseType (S

55、trings,intradix)如inti=Integer.parseInt(“11”,2);/3inti=Integer.parseInt(“12”,8);/10inti=Integer.parseInt(“1A”,16);/26doubled=Double.parseDouble(“3.14”);/3.1475静态方法valueOf该方法创建一个新的对象,并将它初始化为指定字符串的值如: DoubledoubleObject=Double.valueOf(“12.4”);IntegerintegerObject=Integer.valueOf(“12”);76基本类型与包装类之间的自动转换

56、JDK1.5允许基本类型和包装类之间的自动转换。将基本类型的值转换为包装类对象,称为装箱(boxing)将包装类对象转换为基本类型的值,称为开箱(unboxing)IntegerintObject=2;/装箱IntegerintObject1=2,intObject2=3;inti=intObject1;/开箱77数据的格式化、高级算术功能自学78字符、字符串对象JavaAPI提供了三个处理字符数据的类:Character、String、StringBuffer。String是一个finalclass,不能被其它类继承。Character用于处理单个字符。String用于处理字符串常量,而St

57、ringBuffer用于可变字符串的处理。79Character类Character类的作用将char类型的数据封装成对象包含处理字符的方法和常量Character(char):构造函数静态方法isDigit方法判断一个字符是否是数字isLetter方法判断一个字符是否是字母isLetterOrDigit方法判断一个字符是否是字母或数字isLowerCase方法判断一个字符是否是小写isUpperCase方法判断一个字符是否是大写toLowerCase方法将一个字符转换成小写toUpperCase方法将一个字符转换成大写80Character类实例例方法compareTo(Character)

58、比较两个字符对象包含的值,该实例方法返回一个整数值,表示当前对象中的值是大于、等于还是小于参数所包含的值。equals(Object)比较当前对象包含的值与参数对象包含的值,如果两个对象包含的值相等,那么这个实例方法返回true。(Character类覆盖了该方法,因此比较的是对象的内容)toString()这个实例方法将当前对象转换为字符串。charValue()这个实例方法以原始char值的形式返回当前字符对象包含的值。81Character类示例Charactera=newCharacter(a);Characterb=newCharacter(b);intdiff=pareTo(b);

59、/比较大小,-1booleanb1=a.equals(b);/falseStrings=a.toString();/”a”booleanb2=Character.isDigit(a);/falseCharacterc=Character.toUpperCase(b);/Bbooleanb3=Character.isUpperCase(c);/truecharc=a.charValue();82字符串(String)Java提供两个类用于存储和操作字符串String用于其值不能改变的字符串StringBuffer用于可被修改的字符串由于String对象存贮的是不可变的字符串,因此比StringB

60、uffer更高效。对于程序任何位置出现的双引号标记的字符串常量,系统都会自动创建一个String对象,相同字符串常量仅建立一个共享对象。可通过String对象的方法对字符串常量进行操作83String的构造函数很多84创建字符串对象第一种方法是用new运算符如:StringstrRef=newString(stringLiteral);Stringmessage=newString(WelcometoJava);/调用String(String)构造函数,这种方法不推荐第二种方法是直接用赋值形式StringstrRef=stringLiteral;如:Stringmessage=Welcome

61、toJava;但,这二种方法有着本质的不同,第二种方法效率高的多。85二种方式的区别对于程序任何位置出现的双引号标记的字符串常量,系统都会自动创建一个String对象。因此Stringmessage=newString(“WelcometoJava”);实际上创建了另一个完全相同的对象,而且需要调用构造函数。其过程为:(1)以字符串对象“WelcometoJava”为参数调用构造函数String(String),产生新的字符串对象(2)将新对象的引用赋给引用变量messageWelcometoJavaWelcometoJavanewString()message86二种方式的区别Stringm

62、essage=WelcometoJava;由于字符串常量“WelcometoJava”已经是一个字符串对象,因此上面的语句的语义是将对象“WelcometoJava”的引用赋值给message,导致message也该对引用了该对象这个过程中没有调用构造函数产生新的第二个字符串对象WelcometoJavamessage87小测试/代码1Stringsa=newString(Helloworld);Stringsb=newString(Helloworld);System.out.println(sa=sb);/代码2Stringsc=“Helloworld”;Stringsd=Hellowor

63、ld;System.out.println(sc=sd);falsetrue相同字符串常量仅建立一个共享对象。88字符串(String)对任何可包装类型如Character,String等的常量,其常量对象(字面量,Literal)的引用被共享(地址相同)。Characterc1=a;/a为Character类型的字面量(常量)Characterc2=a;System.out.println(c1=c2);/trueIntegerintObj1=1;/1为Integer类型的字面量(常量)IntegerintObj2=1;System.out.println(intObj1=intObj1);

64、/true可通过String对象的方法对字符串常量进行操作。如:inti=“Hello”.length();/589String对象是不可变的abcs新串defg“defg”s+”defg”abcdefgs=s+”defg”以后回收以后回收字符串对象创建之后,其内容是不可修改的。Strings=“abc”;s=s+“defg”;System.out.println(s);程序运行的结果是什么?“abcdefg”s的内容不是改变了吗?不是s的内容变了,而是s引用了新的字符串对象90调用intlength()方法可以获取字符串的长度。Stringmessage=“WelcometoJava”;me

65、ssage.length()返回15charcharAt(index)方法可以获取指定位置的字符。index的值必须在0到length()-1之间。message.charAt(0)返回字符W字符串长度和获取单个字符WelcometoJavamessage.charAt(0)message.charAt(14)0123456789101112131491截取子串substring用于截取字符串的一部分,返回新字符串。publicStringsubstring(intbeginIndex,intendIndex)返回字符串的子串。子串从beginIndex开始,直到endIndex-1publi

66、cStringsubstring(intbeginIndex)返回字符串的子串。子串从beginIndex开始,直到字符串的结尾。WelcometoJava012345678910 11 12 13 14message.substring(0, 11)message.substring(11)Strings=message.subString(0,11);/s内容为”Welcometo”Strings=message.subString(11);/s内容为”Java”92搜索字符或子字符串String类提供的indexOf()和lastIndexOf()方法返回字符串中特定字符或子字符串在字符

67、串中的位置。indexOf():返回字符或字串第一次出现的位置索引值lastIndexOf():返回字符或字串最后一次出现的位置索引值intindexOf(int)intlastIndexOf(int)返回指定的字符第一次(最后一次)出现的位置(参数int是字符的Unicode)intindexOf(int,int)intlastIndexOf(int,int)从指定的索引开始搜索,返回指定的字符第一次(最后一次)出现的位置intindexOf(String)intlastIndexOf(String)返回指定的字符串第一次(最后一次)出现的位置intindexOf(String,int)in

68、tlastIndexOf(String,int)从指定的索引开始搜索,返回指定的字符串第一次(最后一次)出现的位置93搜索字符或字符串indexOf返回字符串中字符或字符串匹配的位置,返回-1表示未找到。WelcometoJava.indexOf(W)/returns0.WelcometoJava.indexOf(x)/returns-1.WelcometoJava.indexOf(o,5)/returns9.WelcometoJava.indexOf(come)/returns3.WelcometoJava.indexOf(Java,5)/returns11.WelcometoJava.in

69、dexOf(java,5)/returns-1.WelcometoJava.lastIndexOf(a)/returns14.WelcometoJava012345678910 11 12 13 1494比较字符串booleanequals(Object)方法用于比较两个字符串是否包含相同的内容:两个字符串内容相同,返回true两个字符串内容不同,返回falsebooleanequalsIgnoreCase(String)忽略大小写比较内容是否相同booleanstartsWith(String)判断是否以某个字符串开始booleanendsWith(String)判断是否以某个字符串结束95

70、比较字符串booleanregionMatch(booleanignoreCase,intthisOffset,Stringother,intotherOffset,intlen)比较部分内容是否相同boolean:是否忽略大小写thisOffset:要比较的this字符串从索引thisOffset开始比较other:要比较的另外一个字符串otherOffset:要比较的other字符串从索引otherOffset开始比较len:要比较的字符数intcompareTo(String)方法用于比较两个字符串的大小,即第一个不同字符的差值。pareTo(s2)的返回值:当两个字符串相同时,返回当s

71、1按字典排序在s2之前,返回小于的值当s1按字典排序在s2之后,返回大于的值96比较字符串 String s0 = Java; String s1 = Welcome to + s0; String s2 = Welcome to Java; String s3 = welcome to java; boolean b = (s1 = s2); /false / =用于比较两个变量是否引用同一个对象用于比较两个变量是否引用同一个对象 b= s1.equals(s2); /true / equals用于比较两个字符串的内容是否相同用于比较两个字符串的内容是否相同 b = s2.equals(s3

72、); /false b = s2.equalsIgnoreCase(s3); /true / equalsIgnoreCase忽略大小写忽略大小写 b = s2.regionMatches(11, s0, 0, 4); /true / regionMatches比较部分字符串比较部分字符串 b = s3.regionMatches(11, s0, 0, 4);/false b = s3.regionMatches(true, 11, s0, 0, 4);/true,忽略大小写忽略大小写 b = s2.startsWith(s0);/false / startsWith判断是否以某个字符串开始判

73、断是否以某个字符串开始 b = s2.endsWith(s0);/true / endsWith判断是否以某个字符串结束判断是否以某个字符串结束 String s4 = abc; String s5 = abe; int i = pareTo(s5); /-2 / compareTo根据字典排序比较两个字符串根据字典排序比较两个字符串97操作字符串字符串连接Stringconcat(String)方法用于连接两个字符串Strings3=s1.concat(s2);/等价于s3=s1+s2使用加号(+)连接两个字符串Strings3=s1+s2;s1+s2+s3等价于s1.concat(s2).

74、concat(s3)concat方法返回新的字符串字符串和其他类型的值相加,其他类型的值会自动转换为String对象“A”+1结果为“A1”classAAa=newA();“H”+a?等价于“H”+a.toString();98操作字符串字符串替换Stringreplace(charoldChar,charnewChar)将字符串中出现的字符oldChar替换为newChar,并返回新的字符串;如果没有发生替换,也要返回新的字符串,但内容和原始字符串一样。Strings6=WelcometoJAVA;Strings7=s6.replace(A,a);/WelcometoJaVa;Strings

75、8=s7.replace(V,v);/WelcometoJava;/ Strings8=s6.replace(A,a).replace(V,v);/Strings8=WelcometoJAVA.replace(A,a).replace(V,v);99操作字符串字符串替换Stringreplace(CharSequencetarget,CharSequencereplacement)CharSequence为接口,定义在java.lang中。String类实现了该接口,因此String对象可以作为该方法的实参。将字符串中出现的子串target替换为新的子串replacement,并返回新的字符串

76、;如果没有发生替换,也要返回新的字符串,但内容和原始字符串一样。Strings8=WelcometoJAVA.replace(JAVA,CPP);返回“WelcometoCPP”100操作字符串StringtoLowerCase()将字符串转换成小写形式StringtoUpperCase()将字符串转换成大写形式Stringtrim()删除两端的空格以上方法都返回新的字符串Strings9=ABCD;Strings10=s9.toLowerCase();/abcd“Strings11=s10.toUpperCase();/ABCDStrings12=JAVA.trim();/JAVA101St

77、ringBuffer类由于String对象是不可改变的,当对字符串的操作(如连接,替换)频繁时,由于不断创建大量的String对象会导致内存开销大因此,Java语言引入了StringBuffer类,用了表示内容可变的字符串102StringBuffer构造器必须用new操作符创建字符串缓冲区实例:Strings=DotsawIwasTod;StringBufferstrBuf=newStringBuffer(s);注意:StringBufferm=“AB”相当于StringBufferm=String类型的对象,由于等号两边的类型不同,不能将一个String类型的对象“AB”的引用赋值给Str

78、ingBuffer类型的变量m。故这样实例化StringBufferm=“AB”是错误的。StringBuffer的实例方法lengh()可得到strBuf的长度。103StringBuffer容量(Capacity)StringBuffer的intlength()方法返回当前字符串缓冲区中包含了多少个字符StringBuffer的intcapacity()方法返回当前字符串缓冲区能容纳多少个字符ABC012345678910 11 12 13 14 1516 1718length()=3capacity()=19StringBufferbf=newStringBuffer(ABC);intl

79、=bf.length();/3intc=bf.capacity();/19104StringBuffer容量(Capacity)只要字符串缓冲区所包含的字符序列的长度没有超出此容量,就无需分配新的内部缓冲区数组。如果内部缓冲区溢出,则此容量自动增大。StringBuffer类是线程安全的(实现了同步机制,因此多个线程读写同一个StringBuffer是安全的)。从JDK5开始,为该类补充了一个单个线程使用的等价类,即StringBuilder。与StringBuffer相比,通常应该优先使用StringBuilder类,因为它支持所有相同的操作,但由于它不执行同步,所以速度更快。105Stri

80、ngBuffer方法和String类一样,StringBuffer支持以下方法:intlength()/返回所包含字符串长度charcharAt(intindex) /返回指定下标位置的字符Stringsubstring(intstart)/取从start开始的子串Stringsubstring(intstart,intend);/取子串start,end-1/课本是基于jdk1.3,因此说不支持以下方法/但JDK1.6支持intindexOf(Stringstr)/搜索字串intindexOf(Stringstr,intfromIndex);/搜索字串intlastIndexOf(Strin

81、gstr)/搜索字串intlastIndexOf(Stringstr,intfromIndex)/搜索字串这些方法的使用同String类106修改字符串(见表30)可以将各种类型的数据追加到StringBuffer中/将str追加到字符缓冲区,并返回新的引用publicStringBufferappend(Stringstr)StringBufferbf=newStringBuffer();/emptybufferStringBuffernbf=bf.append(“Welcome”);booleanb=(bf=nbf);/true/说明nbf和bf引用同一个StringBuffer对象/St

82、ringBuffer类所有返回StringBuffer引用的方法都具有此性质bf.append(“”);/空格bf.append(“to”);bf.append(“Java”);/bf.append(“1”).append(“2”).append(“3”)System.out.println(bf.toString();/WelcometoJava107修改字符串(见表30)可以将各种数据插入缓冲区 /将字符c插入到缓冲区里指定索引的位置publicStringBufferinsert(intposition,charc)/将字符串str插入到缓冲区里指定索引的位置publicStringBu

83、fferinsert(intposition,Stringstr)StringBufferbf=newStringBuffer(“WelcometoJAVA”);bf.insert(11,”CPPand”)/WelcometoCPPandJAVA108修改字符串(见表30)可以从缓冲区删除字符/删除start,end-1范围的字符publicStringBufferdelete(intstart,intend)/删除索引为index的字符publicStringBufferdeleteCharAt(intindex)可以替换缓冲区里的字符/将start,end-1位置的字符替换为strpubl

84、icStringBufferreplace(intstart,intend,Stringstr)可以设置指定索引index处的字符为新字符cpublicStringBuffersetCharAt(intindex,charc)可以倒转缓冲区内容publicStringBufferreserve()可转换为String对象publicStringtoString()109修改字符串(见表30)newStringBuffer(“WelcometoJAVA”).delete(8,11);/WelcomeJavanewStringBuffer(“WelcometoJAVA”).deleteCharAt

85、(8);/WelcomeoJavanewStringBuffer(“WelcometoJAVA”).reverse();/AVAJotemocleWnewStringBuffer(“WelcometoJAVA”).replace(11,15,“CPPandJAVA”); /WelcometoCPPandJAVAnewStringBuffer(“WelcometoJAVA”).setCharAt(0,w);/welcometoJAVA110声明数组引用变量语法datatypearrayRefVar;例如:doublemyList;或者datatypearrayRefVar;/Notrecomme

86、nded,C+Style例如:doublemyList;数组变量是引用类型的变量,声明数组变量并不分配内存空间。111创建数组使用new操作符创建数组。arrayRefVar=newdatatypearraySize;例如:myList=newdouble10;声明和创建在一条语句中。datatypearrayRefVar=newdatatypearraySize;或者datatypearrayRefVar=newdatatypearraySize;例如:doublemyList=newdouble10;或者doublemyList=newdouble10;112数组初始化新创建的数组对象,其

87、元素根据类型被设置为默认的初始值。数值类型为0字符类型为u0000布尔类型为false引用类型为null数组可以在声明后的花括号中提供初始值。doublemyList=1.9,2.9,3.4,3.5或者doublemyList;/nullreferencemyList=newdouble1.9,2.9,3.4,3.5113访问数组一个数组的大小在创建这个数组之后不能被改变。可以用以下语法进行访问数组的长度:arrayRefVar.length/注意没括号例如:myList.length的值为10。数组元素通过索引进行访问。元素的索引从0开始,范围从0到length-1。arrayRefVari

88、ndex例如:myList0表示数组的第一个元素myList9表示数组的最后一个元素114增强的for循环(JDK1.5)JDK1.5引入一个新的for循环,可以不用下标就可以依次访问数组元素。语法:for(elementTypevariable:arrayRefVar)例如for(inti=0;imyList.length;i+)sum+=myListi;for(doublevalue:myList)sum+=value;115复制数组直接使用赋值语句不能实现数组的复制,结果是两个数组引用变量指向同一个数组对象。复制数组的方法使用循环来复制每个元素使用System.arraycopy方法使用

89、数组的clone方法x: 1y : 1copy基本类型x : refy : refcopyarray数组类型inty=xintx=1,2;inty=x;116复制数组复制数组的方法使用循环来复制每个元素intsource=2,3,1,5,10;/inttarget=source;不是复制inttarget=newintsource.length;for(inti=0;isource.lenght;i+)targeti=sourcei;使用System.arraycopy方法System.arraycopy(source,0,targetArray,0,sourc.length);使用数组的cl

90、one方法117数组变量为引用可以作为函数参数(callbyreference)可以作为函数返回值118Arrays类java.util.Arrays类包括各种静态方法,其中实现了数组的排序和查找排序doublenumbers=6.0,4.4,1.9,2.9;java.util.Arrays.sort(numbers);二分查找 intnumbers=2,4,7,10,11,45,50;intindex=java.util.Arrays.binarySearch(list,11);119二维数组声明数组引用变量dataTyperefVar;创建数组并将引用赋值给变量refVar=newdata

91、TyperowSizecolSize;在一条语句中声明和创建数组dataTyperefVar=newdataTyperowSizecolSize;或者dataTyperefVar=newdataTyperowSizecolSize;120二维数组示意图int matrix = new int45matrix21 = 7int array = 1, 2, 3,/line1 4, 5, 6,/line2 7, 8, 9,/line3 10, 11, 12;0 1 2 3 401230 1 2 3 4701230 1 212345678901210 11 123121二维数组的长度二维数组X的每个元

92、素Xi是一个一维数组(代表一行)。数组X的长度是数组X的元素的个数(行数),可由X.length得到。元素Xi是一个一维数组,其长度可由Xi.length得到。X00X10X20X0X1X2XX01X11X21X02X12X22X03X13X23X.length is 3 X0.length is 4 X1.length is 4 X2.length is 4 注意Xi为引用,Xij是否为引用取决于元素类型122不规则数组二维数组的每个元素(数组)的长度可以不同。创建二维数组时,可以只指定第一下标。例如:int x = new int5;x0 = new int5;x1 = new int4;

93、x2 = new int3;x3 = new int2;x4 = new int1;x.length=5x2.length = 4x4.length = 1x00x10x20x0x1x2xx01x11x21x02x12x22x03x13x04x30 x31x40x3x4引用锯齿数组(raggedmatrix)123第15章 类和继承124声明类基本语法:基本语法: class extends implements * 其中:其中: “”表示括号中的内容可出现表示括号中的内容可出现1次或次或0次。次。 “*”表示括号中的内容可出现任意次,且花括表示括号中的内容可出现任意次,且花括弧中每个弧中每个

94、“ *”的出现位置随意互换。的出现位置随意互换。一个类只能继承一个直接父类JAVA继承不分私有、保护或公有继承。或者说都是公有继承。一个类可以实现多个接口125类的修饰符类的修饰符包括:类访问控制符(Accessibility)public:公有类,任何源代码都可以访问。一个java源文件里只能有一个公有类,且该源文件名前缀=公有类名。缺省(default):只有在同一个包(package)内访问。这里讨论的是类的访问控制,而不是类的成员的访问控制。C+里只有对类的成员的访问控制Java里有类访问控制和类成员访问控制二个不同的控制级别126类的访问控制符类访问控制符(Accessibility

95、)public:公有类,任何源代码都可以访问。一个java源文件里只能有一个公有类,且该源文件名前缀=公有类名。缺省(default):只有在同一个包(package)内访问。这里讨论的是类的访问控制,而不是类的成员的访问控制。C+里只有对类的成员的访问控制Java里有类访问控制和类成员访问控制二个不同的控制级别P1publicclassAclassB/缺省packageP2;/包的声明,表示该源文件定义的类都属于P2importP1.*;/引入P1包,可以直接使用A类型。否则必须写P1.ApublicclassCvoidf(Aa)/okvoidg(Bb)/error/由于B类在package

96、P1里声明为缺省,因此在另外一/个包P2里不允许出现B类型Aa=newA();/*Ok*/Bb=newB();/errAB=newA();/OK.Why?127类的访问控制及类的成员访问控制类A的访问控制是控制能否使用A作为数据类型。如果不可以使用,就无法创建A的对象,更谈不上访问A的成员。类A的成员访问控制的是在允许创建A对象(或者允许访问A类型)的前提下,进一步控制是否有权限访问对象成员(或类成员)。允许访问A类型不一定可以创建A对象。(为什么)构造函数可以是私有的128类的修饰符类的修饰符还包括:abstract:被该修饰符修饰的类为抽象类,不能被实例化抽象类不能有对象或实例化。子类如果

97、继承了抽象类,必须覆盖定义父类(抽象类)中的所有abstract(纯虚)函数的函数体才能成为具体类(可有对象)。final:被该修饰符修饰的类不能被继承abstract和final不能同时使用。final表示不能再用于定义子类,而abstract需要定义子类具体化。PublicabstractfinalclassA/Error129类的构造函数构造器可重载声明多个,必须无任何返回类型,否则被当做普通方法。构造器不能多态,不被视作成员方法。publicclassAvoidA()/由于定义了返回值(即使是void),被当做普通函数System.out.println(普通函数);intA(inti

98、)/由于定义了返回值,被当做普通函数return1;A()/构造函数System.out.println(构造函数);A(inti)/重载的构造函数publicstaticvoidmain(Stringargs)newA().A();/先调用构造函数,再调用普通函数130类的其他定义规则类中的方法(不包括构造器、static方法)缺省为类似C+的虚函数,因而是多态的。(final方法相对于父类是虚函数,相对子类则不是)父类用extends说明,由于Java的单继承,只能有一个父类。如无extends出现,表示父类缺省为Object。通常需要覆盖Object类的某些方法,但finalize()方

99、法可根据需要实现。接口可出现多个,必须在类中实现每个接口定义的函数才能成为具体类。只要有一个接口函数没有被实现,该类就是抽象类(必须加abstract修饰)。类中的成员(变量和方法)可定义两种:类成员和实例成员。实例成员必须通过对象名(对象引用)访问类成员可以通过类名或对象名访问(推荐使用类名)类成员函数只能访问类变量实例成员函数也可以访问类变量,但这时只能用类名限定,不能用this和super限定131类的成员访问控制注意不要把package用于说明类、变量和方法,package只能用来说明包。类的成员访问控制适用于实例成员和类成员变量、方法和构造器可以指定四个访问级别:public,pro

100、tected,package,private。如不指定,缺省为package内使用,即只能被当前包内的代码访问。如果为private的,则只能被类自己的成员函数访问。如果为protected的,则可以被当前类及其子类以及同一包中的代码访问。如果为public的,则可以被任何代码访问。132类的成员访问控制成员修饰符同一个类相同包中的类子类不同包中的类publicprotected*无(package)private*子类类体中可以访问从父类继承来的protected成员。但如果子类和父类不在同一个包里,子类里不能访问父类实例的protected成员。133成员访问控制P2P1BaseSubBS

101、ubAC134成员访问控制packagePA;publicclassBase privateStringpriString=PrivateString; protectedStringproString=ProtectedString;StringdefaultString=DefaultString; publicStringpubString=PublicString; publicvoidfBase()Stringstr=this.priString;/OKstr=this.proString;/OKstr=this.defaultString;/OKstr=this.pubString

102、;/OK/在类体内的代码可以访问类的所有成员,包括本类其它对象所有成员str=newBase().priString;/OK135成员访问控制packagePA;publicclassSubAextendsBase publicvoidfSubA()Stringstr=this.pubString;/子类可以访问从父类继承来的公有成员str=this.proString;/子类可以访问从父类继承来的保护成员str=this.defaultString;/子类和父类位于同一包,因此可以访问继承来/的缺省成员/str=this.priString;/子类不能访问从父类继承来的私有成员str=new

103、Base().pubString;/可以访问对象的公有成员str=newBase().proString;/可以访问同一个包里类的对象的保护成员str=newBase().defaultString;/可以访问同一个包里类的对象的缺省/成员/str=newBase().priString;/不能访问另一个类实例的私有成员136成员访问控制packagePA;publicclassC publicvoidfC()Baseb=newBase();Stringstr=b.pubString;/可以访问对象公有成员str=b.defaultString;/可以访问同一个包中类的对象的缺省成员str=b

104、.proString;/C和Base同一个包,因此可以访问对象的保护成员/str=b.priString;/不能访问另一个类的对象的私有成员137成员访问控制packagePB;importPA.*;publicclassSubBextendsBase publicvoidfSubB()Stringstr=this.pubString;/可以访问继承来的公有成员str=this.proString;/可以访问继承的保护成员/str=this.defaultString;/SubB和Base不在一个包,因此无法访问继承/的缺省成员/str=this.priString;/无法访问继承的私有成员B

105、aseb=newBase();str=b.pubString;/可以访问对象的公有成员/str=b.proString;/SubB和Base不在一个包,因此无法访问另一个父/类对象的保护成员/str=b.defaultString;/不在一个包里,所以无法访问对象的缺省成员/str=b.priString;/无法访问对象的私有成员138声明成员变量变量修饰符类型变量名=变量初始化表达式;变量修饰符包括:成员访问控制符staticfinaltransientvolatile139声明成员变量变量修饰符包括:static:声明这是一个类变量而不是实例变量final:声明这是成员变量是常量trans

106、ient:声明这个变量是瞬态变量,不能被串行化(serialized)volatile:声明这个变量是挥发变量final和volatile不能同时使用初始化表达式必须是可以求值且类型兼容的表达式classApublicIntegerintObj=newInteger(1);140声明成员方法方法修饰符返回类型方法名(参数列表)throws异常列表/方法实现方法修饰名包括:成员访问控制符static:类方法abstract:抽象方法final:不能被子类覆盖synchronized:确保线程同步native:本地方法(比如C语言实现,在JAVA里被调用)throws异常列表:如果方法里会抛出异常

107、并且没有在方法里被catch,则必须在方法声明里声明异常(即自己不想处理的异常必须声明)141方法声明的基本规则abstract只能和public、protected一起使用,除不能和其他修饰符一起使用(为什么不能是private、static、final的?)synchronized方法可以是实例方法也可以是类方法synchronized可以和static,native,final同时使用publicsynchronizedstaticnativefinalvoidf3();/在Java类中声明本地方法时不能定义函数体(因为函数体必/须用本地语言如C实现)函数f3的声明说明synchroni

108、zed、static、native、final这四个修饰符的任意组合都是合法的。142方法声明的基本规则构造器没有必要表现多态特性,因此不能是abstract的,不能继承到子类中,也不能被子类覆盖。构造器也不能是native、final或synchronized或static的。如果一个类包含abstract函数,则要同时将该类定义为abstract的。143类定义实例/abstract类应包含未实现的接口或abstract函数abstractclassAvolatileintx=0;transientinty=0;/非序列化成员protectedfinaltransientintz=0;pu

109、blicabstractintf();/由子类实现nativesynchronizedintg();/由其它语言实现A();/尽管有构造器,但抽象类不能有实例144this引用在实例方法或者构造器中,this引用当期对象,也就是被调用的方法或构造器所属对象通过this关键字,可以引用当前对象的任何成员(为什么)使用this原因:方法或构造器的形参隐藏了对象的成员变量。classAinti=0;voidf(inti)/形参i作用域小于实例成员i,因此隐藏了/实例成员ithis.i=i;145this引用一个类的构造器可以在第一条语句调用同一个类的其它构造函数。要使用“this(实参)”而不是“t

110、his.构造函数”,因构造函数不被认为是成员方法。要防止递归调用构造函数。语法为:this(actualParameterListopt)publicclassCircle privatedoubleradius; publicCircle(doubleradius) this.radius=radius; publicCircle() this(1.0);/调用另一个构造函数Circle(double)/必须出现在构造函数的第一条语句中146类的成员变量的初始化声明类的成员变量时可以用初始化表达式初始化变量修饰符类型变量名=变量初始化表达式;但这样做有几个限制:必须用=。但有时不能满足需求,

111、如需要根据条件赋予不同初值(需要if-else)初始化表达式(可能包含方法调用)不能抛出异常如果抛出异常,无法catch这时必须把成员初始化放到其他位置对于实例变量可以放到构造函数体中但类变量放到哪?静态初始化块。(也可以在构造函数里访问类变量,比如对其赋值。但这不是初始化。因为只有在实例化对象时才会调用构造函数,而此时类变量早初始化好了)如果声明的成员变量没有显式初始化,编译器会自动地加上初始化值,例如int型成员被初始化为0,引用成员被缺省地初始化为null147静态初始化块(StaticInitializationBlock)静态初始化模块是由static修饰的语句块,里面只能访问类成员

112、(变量和方法),并且在JVM的ClassLoader将类装入内存时调用。(类的装入和类的实例化是两个不同步骤,首先是将类装入内存,然后再实例化类的对象)。public class A /类的成员变量声明(实例变量和类变量) /类的方法 /构造器 static /静态初始化模块 /只能访问类成员(变量和方法) /可以用任意语句,可以catch异常 148静态初始化块示例publicclassStaticInitBlock staticPrintWriterpw; static/类变量最好放在staticinitializeblock里,因为构造/PrintWriter对象可能会抛出异常 try

113、pw=newPrintWriter(newFile(C:test.txt);catch(FileNotFoundExceptione)e.printStackTrace();类中可以定义多个静态初始化块,执行顺序按定义顺序149非静态初始化块与静态初始化块对应,JAVA支持非静态初始化块(不用static修饰的语句块)。非静态初始化块的执行发生在对象构造时,但先于构造函数体执行(类似C+构造函数的成员初始化列表)作用:如果多个构造方法共享一段代码,并且每个构造方法不会调用其他构造方法,那么可以把这段公共代码放在非静态初始化块中。一个类可以有多个非静态初始化块,按照在类中出现的顺序执行150非静

114、态初始化块非静态初始化模块可以简化构造方法的代码publicclassBookprivatestaticintnumOfObjects;privateStringtitleprivateintid;publicBook(Stringtitle)this.title=title;publicBook(intid)this.id=id/可以访问实例和类变量numOfObjects+;publicclassBookprivatestaticintnumOfObjects;privateStringtitleprivateintid;publicBook(Stringtitle)numOfObject

115、s+;this.title=title;publicBook(intid)numOfObjects+;this.id=id等价151非静态初始化块示例publicclassInitBlockServerSocketsrvSocket;/实例变量除了可在构造函数初始化外,也可/以在非静态初始化块里初始化/非静态初始化块 trysrvSocket=newServerSocket();/在服务器创建一个Listensocketcatch(IOExceptione)e.printStackTrace();类中可以定义多个非静态初始化块,执行顺序按定义顺序152初始化模块执行顺序第一次使用类时装入类如果

116、父类没装入则首先装入父类,这是个递归的过程,直到继承链上所有祖先类全部装入装入一个类时,类的静态数据域和静态初始化模块按它们在类中出现的顺序执行实例化类的对象首先构造父类对象,这是个递归过程,直到继承链上所有祖先类的对象构造好构造一个类的对象时,按在类中出现的顺序执行实例数据域的初始化及实例初始化模块执行构造函数函数体153初始化模块执行顺序如果声明类的实例变量时具有初始值,如doubleradius=5.0;变量的初始化就像在实例初始化模块中一样,即等价于doubleradius;radius=5.0;如果声明类的静态变量时具有初始值,如staticintnumOfObjects=0;变量的

117、初始化就像在静态初始化模块中一样,即等价于staticintnumOfObjects;staticnumOfObjects=0;154初始化顺序classTeststaticStringx=“ab”;/执行顺序static/静态初始化块,可以处理异常tryx=cd;/执行顺序catch(Exceptionex)Testt=null;/执行顺序(变量m.t),(变量n.t)publicTest()/执行顺序(变量m),(变量n)publicclassMainpublicstaticvoidmain(Stringargs)Testm=newTest();/执行顺序Testn=newTest();/

118、执行顺序155同类型实例变量的初始化publicclassListNodeListNodenext;/JAVA里类可以包含相同类型的成员,因为ListNode是引用/C+里类只能包含相同类型的指针或引用成员/ListNodenext=newListNode();/同样会递归调用ListNode()next=newListNode();/会造成递归调用toString();publicstaticvoidmain(Stringargs)newListNode();类变量及实例变量都可以在定义时初始化,但和当前类型相同的实例变量的初始化最好应在类体外进行,否则容易产生无穷递归引起栈溢出。如果是在类

119、体外new一个ListNode对象,然后让next引用该对象,就不会有这个问题156JAVA类的继承JAVA只支持单继承,Object类是所有类的祖先类如果定义一个类时没有用extends关键字指定父类,Objects则缺省地作为其父类Java继承相当于C+里的公有继承即父类成员的访问控制修饰符到了子类保持不变子类从父类继承所有变量和方法,但能否访问取决于访问控制修饰符。(比如私有成员被继承了,但子类无法访问)构造函数不是成员,所以不能被继承。但子类可以通过super关键字调用父类构造函数(如果访问控制符允许)157方法的重载(overload)和C+一样,JAVA的重载必须通过参数表的差异来

120、完成。差异指参数个数不同或至少一个参数类型不一样158方法的覆盖(override)如果子类的一个实例方法与父类的一个实例方法有完全相同的方法签名和返回类型,称子类方法覆盖了父类的方法。方法标记:返回类型方法名(参数列表)子类不能覆盖父类中的final方法子类必须覆盖父类的abstract方法,否则子类就是抽象的由于非static方法和非构造函数自动是多态的(虚函数),因此子类覆盖父类的方法可以表现出多态性子类可以覆盖父类的protected方法,并将可见性提高到public。但是,子类不能降低父类中定义的方法的可见性。比如,如果一个方法在父类中定义为public,在子类也必须定义为publi

121、c159方法覆盖和多态性(Polymorphism)示例classAvoidf()System.out.println(“A”);/自动多态classBextendsAvoidf()System.out.println(“B”);/自动多态classCextendsB finalvoidf()System.out.println(“C”);/再也不能被子类覆盖classDextendsC/finalvoidf()System.out.println(“D”);/再不能被覆盖publicclassPolymorphism publicstaticvoidmain(Stringargs)Ao=ne

122、wA();o.f();/Ao=newB();/父类引用直接指向子类对象o.f();/Bo=newC();o.f();/Co=newD();o.f();/C160覆盖(override)与重载(overload)public class Test public class Test public static void public static void main(Stringmain(String argsargs) ) A a = new A(); A a = new A(); B b = new B(); B b = new B(); b.p(10);/b.p(10);/访问的是父类的访

123、问的是父类的p p a.p(10);/ a.p(10);/访问的是子的是子类的的p p (B)(a).p(10);/ (B)(a).p(10);/父父类的的p p class B class B public void public void p(intp(int i) i) class A extends B class A extends B /override /override public void public void p(intp(int i) i) super.p(isuper.p(i); /); /可以通可以通过supersuper调父父类的的/方法或属性方法或属性 Sys

124、tem.out.println(iSystem.out.println(i);); public class Test public class Test public static void public static void main(Stringmain(String argsargs) ) A a = new A(); A a = new A(); a.p(10);/ a.p(10);/父父类的的p p a.p(10.0);/ a.p(10.0);/子子类的的p p class B class B public void public void p(intp(int i) i) cl

125、ass A extends B class A extends B /overload /overload public void public void p(doublep(double i) i) System.out.println(iSystem.out.println(i);); 161方法隐藏(hiding)如果子类中定义的类方法与父类中的类方法具有相同的标记。就称为子类方法隐藏了父类的方法。但被隐藏的父类方法依然存在。下面看隐藏和覆盖的区别162隐藏和覆盖packageCh15;classBase publicstaticvoidhide()System.out.println(

126、HidemethodinBase); publicvoidoverride()System.out.println(OverridemethodinBase);classSubextendsBase publicstaticvoidhide()System.out.println(HidemethodinSub); publicvoidoverride()System.out.println(OverridemethodinSub);publicclassHiding publicstaticvoidmain(Stringargs)/不管类方法是否被隐藏,/总是可以使用类名来访问Sub.hid

127、e();/CallHidemethodinSubBase.hide();/CallHidemethodinBase/如果通过对象Subsub=newSub();Basebase=(Base)sub;/将子类对象强制转成父类对象base.hide();/这时可以调用父类被隐藏的方法,base.override();/调用的是子类的override,多态隐藏方法的调用是编译时决定,隐藏方法的调用是编译时决定,由由base的声明类型决定调用的是的声明类型决定调用的是Base.hide()覆盖方法的调用是运行时决定,覆盖方法的调用是运行时决定,由由base引用对象的实际类型决定调用引用对象的实际类型决

128、定调用Sub.override隐藏方法是静态方法,因此没有多态性隐藏方法是静态方法,因此没有多态性163隐藏和覆盖下表给出了子类方法和父类方法具有相同的方法签名时的影响超类实例方法超类静态方法子类实例方法覆盖(还必须有相同的返回类型)编译报错子类静态方法编译报错隐藏164方法的隐藏与覆盖标记即函数原型,包括函数返回类型、函数名和函数参数性。不允许仅返回类型不同,而标记其它部分相同。如果子类中定义的类方法与父类中的类方法具有相同的标记,就称为子类方法隐藏了父类方法。如果子类定义的实例方法与父类的实例方法具有相同的标记,就称为子类实例方法覆盖了父类方法。子类方法的访问权限不能比被隐藏与覆盖的父类方

129、法的权限更低,即不能缩小访问权限范围。如父类方法的访问权限为public,则子类方法的访问权限不能为private。注意和C+的区别子类方法抛出的异常类型不能是被隐藏与覆盖的父类方法抛出的异常类型的父类,即不能扩大可抛出异常的类型范围。例如,父类方法抛出Exception类型的异常,则隐藏与覆盖它的子类方法不能抛出Object类型的异常。如果子类不覆盖或不实现父类声明为abstract的方法,则子类仍然是抽象类,即不能创建实例对象。对于父类中被声明为final的方法,子类不能覆盖final方法。165方法的隐藏与覆盖classExextendsException/Ex是异常Exception的

130、子类classAintf()return1; /f,g缺省访问权限是package内使用staticintg()throwsExceptionreturn1;classBextendsApublicfinalintf()return2;/f访问权限是public,覆盖protectedstaticintg()throwsExreturn2;/g为protected,隐藏父类方法。注意protectedpackage不能扩大异常范围166隐藏成员变量子类中的成员变量隐藏父类中的同名变量(因为子类的作用域更小)可以通过super关键字访问父类被隐藏的实例成员变量(如果访问权限允许)。可以通过“父类

131、.成员名”访问父类被隐藏的类成员变量(如果访问权限允许)。子类变量的访问权限可与父类成员的访问权限完全不同,可以扩大或缩小访问权限。子类成员是否为final或volatile也可与父类成员完全不同。子类方法的访问权限不能较父类同标记方法的访问权限缩小。注意和C+的区别:C+恢复访问权限是将派生类继承的恢复访问权限是将派生类继承的基类成员的访问权限基类成员的访问权限复原复原成和该成员在基类定义时的访问成和该成员在基类定义时的访问权限一样权限一样, 而不是而不是改变改变成其它的访问权限。成其它的访问权限。167变量的隐藏classAvolatileintx=3;staticfinalinty=5;

132、publicvoidh()classBextendsApublicfinalintx=4;/隐藏A的x,扩大访问权限privatestaticinty=6;/隐藏A的y,缩小访问权限intf()returnx+this.x+super.x;/this和super只能用于实例函数/super.x访问父类的x,也可用super.方法名调用父类的方法/但调用父类构造函数必须用super(参数);staticintg()returny+B.y+A.y;/注意类成员A.y用“类名.”限定/静态成员方法只能访问静态成员/privatevoidh()/错误:缩小了访问权限168super关键字单继承只有一个

133、父类,对父类对象的引用可使用super关键字。子类的构造器会在第一条语句缺省用“super()”调用父类无参构造函数,除非代码用“super(实参)”显式调用带参数的父类构造函数(如果显式调用,也必须是构造函数体的第一条语句)。对父类的实例成员可以使用“super.成员名”访问。对当前类的实例成员可以使用“this.成员名”访问。对父类和当前类的类成员用“类名.成员名“限定访问。也可以用super或this访问(不推荐不推荐)169调用父类构造函数子类在其构造器的第一条语句缺省调用父类无参构造函数。如果子类中没有显式地调用父类的构造函数,编译器会自动地如果子类中没有显式地调用父类的构造函数,编

134、译器会自动地在子类构造函数第一条语句前加上在子类构造函数第一条语句前加上super()也可以显式地调用,必须是子类构造函数的第一条语句也可以显式地调用,必须是子类构造函数的第一条语句。如果调用的父类构造函数必须带实参,则必须在子类里显式调用class A extends B public A()() /some statements class A extends B public A()() super ( ); /some statements 等价于170调用父类构造函数classAinti=0;/classA只定义了带参数的构造函数/因此,classA没有不带参数的构造函数A(inti

135、)this.i=i;/由于父类没有不带参数的构造函数,B必须自定义构造函数。和C+一样classBextendsAB(intx) super(x);/必须显式地调用父类构造函数,因为编译器无法自/动加上这条语句171类的嵌套嵌套类是一个类中定义的类型成员。嵌套类的函数可以访问宿主类的所有成员包括private成员。嵌套类也可以选择不同的访问权限说明嵌套类。嵌套类可以被声明为abstract或final的。接口也可以被声明为嵌套类。嵌套类也分为静态的或非静态的。静态的嵌套类简称为嵌套类,非静态的嵌套类简称为内部类。静态的嵌套类不能在其函数中直接访问被嵌套类的实例变量,但可以直接访问其静态类变量。

136、172类的嵌套classHost privateinti=0; publicintj=1; publicclassInner privateintk=0; publicintf()/f是内部类Inner的方法 returni+j;/内部类里可以访问宿主类的所有成员/Inner作为一种数据类型可以被使用 publicInnerg()returnnewInner();/g是Host类的实例方法 publicinth()Innerins=newInner(); returnins.f()+ins.k;/宿主类里也可以访问内部类里的所有成员(包括私有)publicclassInnerClassDemo

137、/由于Inner是public,Host.Inner可以在Host外作为数据类型被使用/如果Inner是private,在Host类外是无法使用Inner作为数据类型的Host.Innerin=newHost().g(); inti=in.f();173第第16章章 接口和包接口和包174接口Java只支持单重继承,程序中类的层次结构是树状结构,在处理复杂问题时,单重继承显得力不从心。面对多重继承时,可以选定一个做父类,将其它类作为新类的成员对象实现(代理模式,见C+部分)。在JAVA里,可以通过接口实现类间多重继承的功能175接口(Interface)接口是一个方法声明和常量定义集合,没有给

138、出方法的实现接口是由常量和抽象方法组成的特殊类。无论如何使用关键字,接口定义的所有变量都是publicstaticfinal类型,所有方法都是publicabstract方法(纯虚函数)。接口也是一种class,因此可以作为数据类型使用接口是无关对象进行彼此交互的协议。无关对象是指在类的层次结构中没有相关性。176接口由于接口的变量和方法说明已经固定,使用其它关键字如transient,volatile,synchronized,private,protected均无效。接口不可以实现任何方法,即在接口类中定义函数体。类可以实现多个接口,但只能继承一个父类。177接口和继承178接口定义接口包

139、括接口声明和接口体两部分:接口包括接口声明和接口体两部分: public interface extends publicstaticfinal = ; publicabstract () throws 异常异常列表列表; 下面的下面的MAXSIZE是是public static final的的 public interface MyInterface int MAXSIZE = 1024; int myMethod(String name); 同同class一样,若一样,若interface使用使用public说明,则必须在说明,则必须在单独同名的单独同名的Java文件中定义。文件中定义。

140、179接口定义规则接口可通过关键字接口可通过关键字extends继承一个或多个父接口,它们继承一个或多个父接口,它们之间用逗号分隔,形成父接口列表。之间用逗号分隔,形成父接口列表。如果实现某接口的类是如果实现某接口的类是abstract的抽象类,则在类的定义的抽象类,则在类的定义部分可以不实现接口中的所有方法;否则必须具体实现部分可以不实现接口中的所有方法;否则必须具体实现接口中的所有方法。接口中的所有方法。一个类在实现某个接口的抽象方法时,必须使用同接口一个类在实现某个接口的抽象方法时,必须使用同接口方法完全相同的方法完全相同的标记标记声明声明;同类继承中的覆盖一样,实现时不能缩小接口中定义

141、的同类继承中的覆盖一样,实现时不能缩小接口中定义的方法的访问范围。接口中的抽象方法访问修饰符为方法的访问范围。接口中的抽象方法访问修饰符为public,所以,类在实现方法时必须使用修饰符,所以,类在实现方法时必须使用修饰符public。同如前所述的类继承中的覆盖一样,实现时不能扩大接同如前所述的类继承中的覆盖一样,实现时不能扩大接口中方法抛出异常的类型范围。口中方法抛出异常的类型范围。180接口定义及实现classMyExceptionextendsExceptioninterfaceI1 voidf1()throwsException;/等价于publicabstractvoidf();不能

142、给出函数的实现 intC1=1;/等价于publicstaticfinalintC1=1;/接口I2里有二个抽象方法f1,f2和二个常量C1和C2interfaceI2extendsI1/接口可以继承 voidf2()throwsException; intC2=2;/A只实现了一个抽象方法f1,因此必须是抽象类abstractclassAimplementsI2 publicvoidf1()throwsMyException/必须是公有的,不能缩小访问范围/接口方法实现时不能抛出更大范围的异常/由于B继承了A,因此继承了A定义的f1.B自己又实现了f2,因此B是具体类classBextend

143、sAimplementsI2 publicvoidf2()throws MyException /必须是公有的,不能缩小访问范围必须是公有的,不能缩小访问范围181将接口作为类型使用接口一旦定义,就可以作为类型使用。可以用接口类型的引用指向具体实现类的对象,这样才能发挥接口的作用(多态)。接口类型属于引用类型,接口类型的变量可以存储:空引用(null)任何实现该接口的类的实例的引用接口中的方法通过“接口类型的引用变量.方法名”访问,但接口类型的引用变量必须指向实现了该接口的类的实例对象接口中的常量名通过“接口名.常量名”访问或”引用名.常量名”访问。注意接口定义的常量是静态的182public

144、 interface Flyer void takeOff(); void land(); void fly();public class Airplane implements Flyer public void takeOff() / 加速直到离地升空 / 收起起落架 public void land() / 放下起落架 / 减速并降低副翼直到降落 / 刹车 public void fly() / 保持引擎运转 接口方法的访问Flyerf=newAirPlane();f.takeOff();f.fly();f.land();183接口示例publicinterfaceI1publicvoi

145、dm1();publicinterfaceI2extendsI1publicvoidm2();publicinterfaceI3publicvoidm3();publicclassAimplementsI2,I3publicvoidm1()/implementspublicvoidm2()/implementspublicvoidm3()/implementsI1.javaI2.javaI3.javaA.java当一个类实现多个接口时,这个类可以是多种类型的实例。如下列表达式都返回trueAa=newA();ainstanceofI1ainstanceofI2ainstanceofI3ains

146、tanceofObject或者可以写I1o1=newA();I2o2=newA();I3o3=newA();o1.m1();/o1只能调m1o2.m1();/o2可以调m1,m2o2.m2();o3.m3();/o3可以调m3184接口的作用考虑这样一个场景能否定义一个MAX类,实现一个静态方法max,对于任意类型的二个对象,比较其大小似乎是不可能的任务:因为类型不确定使用接口就可以实现这个目标利用java.lang.Comparable接口185Comparable接口有时需要比较二个对象,但不同类型对象的比较具有不同的含义,因此Java定义了Comparable接口。因此,任何需要比较对象

147、的类,都要实现该接口。该接口定义如下:packagejava.lang;publicinterfaceComparablepublicintcompareTo(Objecto);CompareTo判断this对象相对于给定对象o的顺序,当this对象小于、等于或大于给定对象o时,分别返回负数、0或正数186Comparable接口有了Comparable接口,我们可以实现很通用的类来比较对象,例如实现一个从两个对象中找出最大者的方法。注意max方法的参数类型和返回类型都是Comparable因为Comparable引用可以指向任何实现了Comparable接口的对象,因此任何实现Compara

148、ble接口的对象都可以作为max方法的实参。这些对象有自己的具体实现,但Max.max与这些对象的具体实现无关。这就是接口的好处。另外要注意的是:o1.CompareTo(o2)调用是动态绑定(多态)publicclassMaxpublicstaticComparablemax(Comparableo1,Comparableo2)if(pareTo(o2)0)returno1;elsereturno2;187实现可比较的Circle类对于Circle的两个对象c1和c2,我们直接调用Max.max(c1,c2)找出最大的对象对于实现了Comparable接口任何类的二个对象(不管其具体实现是什

149、么)a1和a2,我们都可以调用Max.max(a1,a2)找出最大的对象publicclassCircleimplementsComparable privatedoubleradius=1.0;Circle()Circle(doubler)radius=r; publicintcompareTo(Objecto)if(this.radius(Circle)o).radius)return1;elseif(this.radius(Circle)o).radius)return-1;elsereturn0;188Java语言的包引入package的原因:容易找到和使用类避免名称冲突控制访问包的定

150、义:包是一个相关的类和接口的集合,它可以提供访问保护和名称空间管理。包是一种松散的类的集合,通常把需要在一起工作的类(功能上相关并互相访问)放入一个包。系统自动建立“无名包”,也就是默认包或缺省包。无名包中的类不能被其它包中的类所引用(imports)。Imports只能引入包中public的类。一个包可由若干个Java文件构成,包内非private的成员可以被包内所有函数访问。189有名包创建有名包的格式为:package;Java使用文件系统来存储包,包的名称必须和程序文件所在目录名完全一样(大小写敏感)。如果定义了一个多级结构的包,其结构也必须和文件系统的目录结构完全一致。通常类只能引用

151、import一个包中的公共类。可以引用一个包中所有的类(使用.*),或仅仅一个类(直接使用类名)。可以直接用“包名.类名”限定类名进行访问。使用Javac工具时,要设置类路径setclasspath=190创建和加入包在一个JAVA源文件的顶部放置package语句packagemyPackage;myPackage包就会自动被创建。该JAVA文件里包含的所有class和interface都被加入到该包里(类似于将该文件里的所有class和interface都被限定在名字空间myPackage里)不同的java源文件里包含的class和interface可以加入到同一个包。191创建和加入包p

152、ackagehust.cs.idc;/声明一个包/该文件里的所有类/和接口被加入包hust.cs.idcpublicclassA1classB1interfaceI1/其他类或接口的定义A1.javapackagehust.cs.idc;/声明一个包/该文件里的所有类/和接口也被加入包hust.cs.idcpublicclassA2classB2interfaceI2/其他类或接口的定义A2.javapackagehust.cs.idcQuestion:一个包里可以有多个公有类或接口吗?192包的命名使用组织的internet域名的反序形式命名包。一个组织内部发生的命名冲突需要由组织内部的约定

153、来处理,通常在组织名称后面包含项目名称。包名必须与目录结构一一对应,目录名之间用.分隔。组织组织World Wide Web Consortium 域名域名www.w3c.org 包名包名org.w3c.dom(用于解析用于解析xml文档)文档) org.w3c.dom.bootstrap org.w3c.dom.events组织组织The Apache Software Foundation域名域名www.apache.org 包名包名org.apache.tools.ant(工程管理的开源工具)(工程管理的开源工具) org.apache.lucene(开源的搜索引擎框架开源的搜索引擎框架

154、) org.apache.lucene.index org.apache.lucene.search193包的目录Java要求包名与文件系统的目录结构对应。对于名为hust.cs.idc的包必须创建对应的目录结构一个典型的JAVA工程目录结构应该包含src、bin、doc子目录。假如工程目录位于D:JavaDemo下Example:hust/cs/idc/Hello.java 194包的目录假设要创建一个java源文件Hello.javapackagehust.cs.idc;publicclassHellopublicstaticvoidmain(Stringargs)System.out.p

155、rintln(Hello);Example:hust/cs/idc/Hello.java 如果要把该文件放到工程目录D:JavaDemo下,其目录结构应该为195包的目录示例196包的目录包里源文件被编译后,产生的.class文件也会位于对应的目录结构中编译:在hust的上级目录下(D:JavaDemo)运行javachustcsidcHello.java-d.bin“-d”选项指定将生成的.class文件放到指定目录注意生成的Hello.class所在目录,位于D:JavaDemobinhustcdidc下197包的目录为了使Java知道包在文件系统的位置,必须修改环境变量classpath

156、setclasspath=%classpath%;d:JavaDemobin/注意只要指定到d:JavaDemobin这一级/运行时会自动根据包名按对应目录去找Hello.class运行:在任何目录下(设定好classpath后)javahust.cs.idc.Hello198使用包中的类用简单名称引用包成员。代码与要使用的成员位于相同的包成员所属的包已经被导入用完全限定名称引用包成员使用另一个包中的成员,而且此包未被导入导入多个包时,出现命名冲突要将特定成员导入到当前文件中,必须package语句之后,类和接口定义之前,加入import语句。要导入某个包中的所有的类和接口,应使用具有通配符(

157、*)的import语句。Java默认导入包java.lang199包中类的引入importjava.math.*;/引入java.math中的所有公有类importjava.lang.System;/仅仅引入java.lang包中的System类publicclassMainpublicstaticvoidmain(Stringargs)BigIntegerx=newBigInteger(“1”);x=x.add(newjava.math.BigInteger(“2”);/包名限定类System.out.printf(x.toString();/已引入,无包名限定200第17章 异常处理201

158、为什么要异常处理对于任何语言的程序设计而言,错误的发生总是不可避免的,这是每一个程序设计开发人员应该牢记的金科玉律之一。为了加强程序的健壮性(Robust),程序设计时,必须充分考虑错误发生的可能性,并建立相应的处理机制。202什么是异常异常(Exception)又称为例外,是指在程序运行过程中发生的非正常事件,这些事件的发生会影响程序的正常执行。以下的例子都是一些程序设计中常见的异常:进行数学中“无意义”的运算,例如除数为零、对负数求对数平方根等。对数组进行操作时,超出了数组的最大下标。对变量的赋值超出了其类型所能表达的范围。程序所需进行的I/O操作不能正常执行,例如所需访问的文件不存在或所

159、需访问的外设不能正常工作等。203异常与错误在Java语言中,异常只是所谓程序错误的一个子集。一般而言,异常代表的是程序级的错误,这些错误可以由程序本身进行处理。另一些系统级的底层错误,如内存耗尽,JVM崩溃,硬件错误、底层库调用错误等也会引起程序的执行中断。但这类错误一般和应用程序无关,应用程序也不需要处理。在Java语言中,这类错误被称为“错误(Error)”。204异常对象在Java语言中,我们用异常对象来表示不同的异常。所谓Java异常对象就是一个存放着相关错误信息的对象,如果方法运行时产生了异常,该方法就可以抛出一个异常对象,而不是通过方法的返回值来指示异常的发生。为了表示不同类型的

160、异常,Java语言中定义了相关的异常类,这些异常类及其子类的实例就是异常对象。205常见的异常类ArrayIndexOutOfBandsExceptionIOExceptionFileNotFoundExceptionNullPointerExceptionNumberFormatExceptionOutOfMemoryException206Java中的异常类在Java语言中,任何异常对象都是Throwable类的直接子类或间接子类的实例。Java的类库已经提供了一些常见的异常类,如果这些异常类不能够满足要求,用户也可以创建自己的异常类。通常不要处理Error类型的异常。RuntimeExc

161、eption也不是程序的“必检异常”,不应将异常定义为RuntimeException的子类,不catch“非必检异常”编译也不会报错。程序定义自己的异常时最好从“必检异常”Exception继承。207必检异常和非必检异常对于除RuntimeException类及其子类以外的所有Exception的子类而言,这类异常必须catch。如果可能产生这类异常的代码不做任何异常捕获的话,程序将不能正常编译。所以,这类异常也被称为“检查类型的异常”或“必检异常”。相对的,RuntimeException和Error类及其子类被称为“非必检异常”RuntimeException是由于程序设计和实现错误引

162、起的异常,不能按字面意思理解成“运行时产生的异常”,因为所有异常都是程序运行时产生的。RuntimeExcetion都是可以通过精心设计的代码避免的208异常的捕获和处理JAVA利用try/catch/finally语句来捕获或处理异常try/包含可能抛出异常的语句catch(ExceptionType1e1)/异常e1的处理代码catch(ExceptionType2e2)/异常e2的处理代码finally/可选/无论是否发生异常,finally块始终会被执行/通常在这里放置清理工作的代码/如关闭打开的文件209异常的捕获和处理示例publicpublic classclass Except

163、ionDemoExceptionDemo void voidf()f()PrintWriterPrintWriterpw=pw=nullnull; ; try try /如果如果C:C:text.text.txttxt不是可写的文件,会抛出不是可写的文件,会抛出FileNotFoundExceptionFileNotFoundException异常异常pw=pw=newnew PrintWriter(PrintWriter(newnew File(C:text.txtFile(C:text.txt););Stringlines=Line1,Line2,Line3;Stringlines=Lin

164、e1,Line2,Line3;/下标越界,会抛出下标越界,会抛出ArrayIndexOutOfBoundsExceptionArrayIndexOutOfBoundsException对象对象 forfor( (intinti=0;ii=0;ilines.lengthlines.length+1+1;i+);i+)pw.println(linesipw.println(linesi);/);/将字符串写入文件将字符串写入文件C:C:text.txtcatchcatch( (FileNotFoundExceptionFileNotFoundExceptione)e)System.System.o

165、utout.println(File.println(FilenotFound!);notFound!);catchcatch( (ArrayIndexOutOfBoundsExceptionArrayIndexOutOfBoundsExceptione)e)System.System.outout.println(Index.println(Indexoutofbounds!);outofbounds!);finallyfinally/finally/finally块始终会被执行块始终会被执行 if if(pw(pw!=!=nullnull)pw.closepw.close();(); 21

166、0方法的调用栈Java程序在执行的过程中,实际上形成了这样一个先进后出的调用堆栈,各方法之间依照调用先后的不同,由先至后的进入调用堆栈,堆栈的最上层即是当前被调用执行的方法。main方法方法A方法B方法C方法调用次序211Question下面的函数返回值是什么intJ()trySystem.out.println(Try);return1; finallySystem.out.println(Finally);return0;212方法的调用栈中异常对象的传递当某一方法中抛出一个异常对象时,如果该方法中没有处理该异常对象的语句,那么这个异常对象就会按照与调用方向相反的方向在调用堆栈中的方法间依

167、次传递,直到某一方法中处理该异常为止。如果该异常被传递至主方法,而主方法中仍然没有处理该异常的语句,则异常将会被抛至系统,程序中断。main方法方法A方法B方法C方法调用次序异常对象递213异常catch的顺序如果try块中没有任何的异常抛出,则所有的catch子句将会被跳过;如果有finally块,则执行其中的语句;否则执行try/catch的下一语句当try块中的某条代码抛出异常时:首先,try块中的剩余语句将被跳过不予执行;其次,程序执行catch子句进行异常捕获,异常捕获根据异常对象的类型进行匹配,执行与所抛出的异常对象类型相对应的catch子句中的异常处理代码。因此,如果异常被一个c

168、acth子句截获,其他catch子句就没有机会执行。如果try块中所抛出的异常对象类型与所有的catch子句中的所声明的异常类型都不匹配,则方法会立即中止,并将该异常对象继续抛出,沿方法调用堆栈传递到下一个方法。214异常catch的顺序因此,catch子句的次序很重要,要根据可能抛出的异常类型的继承关系,将子类异常的catch子句放在前面,父类放在后面。trycatch(Exceptione)catch(IOExceptione)catch(ArrayIndexOutOfBoundsExceptione)异常类型匹配:1:异常对象正好是参数类型的对象2:异常对象是属于参数类型的子类对象因此,

169、这个例子里下面二个catch子句永远没有机会执行215声明方法的异常一个方法不处理它产生的异常,而是沿着调用堆栈向上传递,由调用它的方法来处理这些异常,则称为声明异常。声明异常的方法: Modifier returnType methodName(paramlist) throws exceptionList例如public void f( ) throws IOExeption, FileNotFoundException/在在f内可以不内可以不catch IOExeption和和FileNotFoundException当一个方法声明了异常,意味着它声明的异常类型在方法内部可以不处理,留给

170、该方法的调用者处理可以声明抛出多个异常216声明方法的异常方法f的调用者(Invoker)有二个选择第一个选择:截获并处理异常,这时Invoker不必再声明这二个异常public void g( ) try f() catch (IOExeption e) catch (FileNotFoundException e) 217声明方法的异常方法f的调用者(Invoker)有二个选择第二个选择:Invoker也可以不处理这二个异常,这时Invoker必须也声明这二个异常public void g( ) throws IOExeption, FileNotFoundException f( );

171、218抛出异常在Java程序中,只有某些代码抛出异常,才可以捕获异常;任何代码都可以抛出异常,不管是什么代码抛出的异常,都是通过throw语句完成的。抛出异常:不是出错产生,而是人为地抛出。throw语句需要一个参数:一个可以抛出的对象,格式如下:throwThrowableObject/ThrowableObject为异常对象引用219抛出异常/如果在方法里throw一个Exception对象且没有catch,则在方法声明中/必须声明throwsExceptionvoidh()throwsExceptionthrownewException();/如果在方法里throw一个Exception

172、对象且catch了,则在方法声明中/不必声明throwsExceptionvoidI()trythrownewException();catch(Exceptione)220基本规则Java的异常是纯面向对象的,抛出的异常必须是对象的引用。因此,不像C+异常可以是原子类型,Java异常类型不得为原子类型或接口类型。实际抛出的异常类型必须是继承自“Java中的异常类”的可实例化的类。声明的异常类型可以是继承自“Java中的异常类”的抽象类。221基本规则声明异常类型使用throws,可声明抛出多个异常类型。抛出异常类型的对象引用,使用throw或使用thrownew类名(实参表)。方法必须通过t

173、hrows说明它可能抛出而自己并未捕获的所有“必检异常”。子类方法抛出的异常类型不能是被隐藏与覆盖的父类方法抛出的异常类型的父类,即不能扩大可抛出异常的类型范围。例如,父类方法抛出Exception类型的异常,则隐藏与覆盖它的子类方法不能抛出Object类型的异常。222子类异常对象的类型范围classREextendsExceptionintx=0;classAvoidk()throwsREvoidh()throwsExceptionclassBextendsA/以下函数k使用RE的父类Exception表示扩大了异常对象的类型范围/函数k可抛出Exception及其子类的对象引用,因此,扩

174、大了异常对象的类型范围voidk()throwsExceptionthrownewException();/错:扩大了异常对象类型范围voidh()throwsRE/未扩大异常对象的类型范围publicclassMainpublicstaticvoidmain(Stringargs)Aa=newB();trya.k();catch(REre)/a.k()抛出的异常对象类型为Exception,传给re被当作RE类型的对象处理System.out.println(re.x);/实际上,抛出的类型为Exception的异常对象中没有实例变量x223异常catch的顺序异常的catch顺序同C+。不

175、同的是java没有catch()的形式。但是,可以使用catch(Objectob)代替,因为Object是所有可实例化类的父类。需要注意的是C+还可以抛出不是实例化对象的异常,例如,抛出int类型的异常,而java的所有异常必须是对象。Java的finally同C+的_finally,该部分的语句无论是否发生异常都会执行。异常的处理极其耗费资源,不要动不动就使用异常。224第18章 线程225多线程线程是程序内的一个单一的顺序控制流程,也被称为“轻型进程(lightweight process)”或“执行上下文(executioncontext)”同一个进程多个线程可以“同时”运行,并且在一

176、个程序内执行不同的任务。利用Timer和TimerTask可以实现同一个任务的定时重复运行。有两种方法可以实现同一个或多个线程的运行run:(1) 定义Thread类的子类并覆盖run方法;(2)定义Applet的子类实现接口Runnable的run方法。226使用Timer和TimerTaskimportjava.util.Timer;importjava.util.TimerTask;classtestTimertimer;publictest(ints)timer=newTimer();timer.schedule(newRemTask(),s*1000);classRemTaskext

177、endsTimerTaskpublicvoidrun()System.out.println(Timesup!);timer.cancel();publicvoidmain(Stringargs)newtest(5);System.out.println(TaskScheduled.);227定义Thread类的子类并覆盖runclasssimextendsThreadpublicsim(Stringname)super(name);publicvoidrun()/覆盖runfor(inti=0;i10;i+)System.out.println(i+getName();trysleep(in

178、t)(Math.random()*1000);catch(InterruptedExceptione)System.out.println(Done!+getName();publicclassMainpublicstaticvoidmain(Stringargs)newsim(Jamaica).start();newsim(Fiji).start();228线程的生存周期创建消亡运行阻塞就绪休眠等待start()sleep()wait()I/OCPU可用任务完成yield()229线程状态的转换条件进入不可运行状态条件进入不可运行状态条件返回可运行状态条件返回可运行状态条件挂起(调用挂起(调

179、用suspend()方法)方法) 调用调用resume()方法方法睡眠(调用睡眠(调用sleep()方法)方法)sleep()方法的指定时间结束方法的指定时间结束阻塞(请求阻塞(请求I/O操作)操作)I/O操作结束操作结束等待(调用等待(调用wait()方法)方法)调用调用notfiy()或或notifyAll()方法方法230线程知识正常终止:run()方法运行完毕。强行中止:(1)调用线程对象的stop()方法;(2)创建线程对象的上级线程停止。线程优先级范围从110,数字越高越能被优先执行。高优先级的线程执行完毕,低优先级的才能执行。通过线程对象的setPriority()方法设置优先级

180、。缺省优先级为5,即NORM_PRIORITY。Thread定义了三个常数:MAX_PRIORITY、MIN_PRIORITY、NORM_PRIORITY。231时间片策略“自私线程”如果不放弃CPU(如run函数中有死循环),同优先级的线程就很难得到机会运行。Java本身并不直接支持时间片,部分平台(如Windows)的JRE针对“自私线程”采用了时间片轮换策略。将CPU的运行时间划分成段,相同优先级的线程分别占用一段轮流执行。执行的顺序和轮换的时间不可预测。不要编写“自私的”线程:适当的时候通过yield()方法放弃CPU,由运行状态变为就绪状态。低优先级的线程仍得不到CPU运行,高级和同

181、级的可以得到CPU。232正确理解优先级和调度在多任务环境中,如果完全依赖低优先级线程可能永远得不到运行机会,称为线程“饿死”线程调度程序可能为低优先级线程分配CPU,以防止线程“饿死”优先级只是用来提高多线程的调度效率,并不保证高优先级线程永远优先运行不要依赖线程的优先级来设计对调度敏感的算法如何调度最终取决于JRE运行平台233调度测试publicclassPTextendsThreadpublicPT(Stringname)super(name);publicvoidrun()while(true)inti;for(intj=0;j100000;j+)for(intk=0;k1000;k

182、+)i=j+k;if(getPriority()NORM_PRIORITY)System.out.println(getName()+isrunning.);234调度测试/低优先级线程也有机会运行publicstaticvoidmain(Stringargs)PTt1=newPT(Higerprioritythread);PTt2=newPT(Lowerprioritythread);t2.setPriority(4);t1.start();t2.start();235线程的调度当多个线程对同一个对象进行读写操作的时候,如果完全不加控制,可能会出现难以预料的结果比较典型的是“生产者/消费者”

183、问题一个线程(生产者)修改数据另一个线程(消费者)读取数据希望生产者修改的数据都能被消费者读取,并且不会重复读取236生产者/消费者消费者问题/CubbyHole类类:用于保存生产者的修改,用于保存生产者的修改,/并能让消费者读取并能让消费者读取publicclassCubbyHoleprivateintdata;publicintget()returnthis.data;publicvoidput(intdata)this.data=data;237生产者/消费者问题/Producer类类:生产生产0-9,存入,存入CubbyHole对象中对象中public class Producer e

184、xtends Thread private CubbyHole res; public Producer(CubbyHole res) this.res = res; public void run() for (int i=0; i10; i+) res.put(i); System.out.println(“Producer put:” + i); try sleep(int)(Math.random()*100); catch (InterruptedException e) 238生产者/消费者问题/Consumer类:读取CubbyHole对象中的数据publicclassConsu

185、merextendsThreadprivateCubbyHoleres;publicConsumer(CubbyHoleres)this.res=res;publicvoidrun()for(inti=0;i10;i+)System.out.println(Consumerget:+res.get();trysleep(int)(Math.random()*100);catch(InterruptedExceptione)239生产者/消费者问题/ProdConsTester类:测试运行程序publicclassProdConsTestpublicstaticvoidmain(Stringar

186、gs)CubbyHoleres=newCubbyHole();Producerpro=newProducer(res);Consumercon=newConsumer(res);pro.start();con.start();240生产者/消费者问题分析运行结果可发现两个问题分析运行结果可发现两个问题生产者产生的资源可能没有被消费者获取生产者产生的资源可能没有被消费者获取生产者产生的资源可能被消费者重复取得生产者产生的资源可能被消费者重复取得造成这些问题的原因是两个线程在访问同一个造成这些问题的原因是两个线程在访问同一个对象时没有考虑同步的控制。对象时没有考虑同步的控制。当消费者尚未取得资源时

187、,生产者可能已经当消费者尚未取得资源时,生产者可能已经产生新的资源,使消费者错过一些资源产生新的资源,使消费者错过一些资源当生产者尚未产生新资源时,消费者再次执当生产者尚未产生新资源时,消费者再次执行行get( )方法,造成资源被重复获取方法,造成资源被重复获取241引入同步机制解决前述问题的关键是引入线程之间的同步控制逻辑解决前述问题的关键是引入线程之间的同步控制逻辑通过通过synchronized关键字锁定共享对象关键字锁定共享对象方法名前加方法名前加synchronized关键字标记关键字标记用用synchronized(Object) 标记锁定的代码标记锁定的代码当线程执行由当线程执行

188、由synchronized关键字控制的代码时,关键字控制的代码时,就锁定了共享对象。其他线程如果需要操作该对象,就锁定了共享对象。其他线程如果需要操作该对象,就必须等待该线程执行完受保护的代码。就必须等待该线程执行完受保护的代码。使用使用synchronized要注意防止死锁。要注意防止死锁。使用使用wait方法使线程进入等待状态。方法使线程进入等待状态。使用使用notify、notifyAll唤醒正在等待的线程唤醒正在等待的线程242新的CubbyHolepublicclassCubbyHoleprivateintdata;privatebooleangetable=false;public

189、synchronizedintget()while(getable=false)trywait();catch(InterruptedExceptione)getable=false;notifyAll();returnthis.data;243新的CubbyHolepublicsynchronizedvoidput(intdata)while(getable=true)trywait();catch(InterruptedExceptione)this.data=data;getable=true;notifyAll();244创建线程组线程组可以将多个线程集中在一个对象中,并同时操作这些线

190、程(比如全部挂起或杀死)所有的Java线程都是某个线程组的一员。当Java应用程序开始执行时,虚拟机会创建一个名为main的线程组如果程序没有显式地创建其他线程组并将线程设置其中,则所有的线程都是main线程组的成员可利用ThreadGroup类创建线程组对象:ThreadGroupmyGroup=newThreadGroup(“mygroup”);Threadthread1=newThread(myGroup,“threadone”);Threadthread2=newThread(myGroup,“threadtwo”);245管理线程组使用线程组对象管理多个线程:publicintactiveCount()返回线程组中活动线程的估计数publicfinalvoidstop()停止线程组中的所有线程publicfinalvoidsuspend()挂起线程组中的所有线程publicfinalvoidresume()唤醒线程组中所有被挂起的线程publicfinalvoidsetMaxPriority(intpri)设置线程组的最大优先级,组中的线程优先级不得高于该值246

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 高等教育 > 研究生课件

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号