Java 2面向对象程序设计基础(中)ppt.ppt_第1页
Java 2面向对象程序设计基础(中)ppt.ppt_第2页
Java 2面向对象程序设计基础(中)ppt.ppt_第3页
Java 2面向对象程序设计基础(中)ppt.ppt_第4页
Java 2面向对象程序设计基础(中)ppt.ppt_第5页
已阅读5页,还剩264页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Java 2面向对象程序设计基础(中),第5章 包、接口和异常 第6章 线程 第7章 输入与输出 第8章 Java的网络编程,第5章 包、接口和异常,(时间:3次课,6学时),第5章 包、接口和异常,教学提示:Java将相关的类组成包,有助于大型软件系统的管理。接口的设计和调用也是Java程序设计的重要技术。对各种异常的处理是程序设计中要解决的重要问题。Java采用了一种特殊的机制来处理异常。 本章主要介绍:和类相关的两个概念:包、接口,同时在最后还将介绍Java对异常的处理方法。,第5章 包、接口和异常,5.1 包 5.2 接口 5.3 异常及其处理 5.4 创建自定义的异常 5.5 课后练习,5.1 包,5.1.1 Java 包的用途 5.1.2 访问包成员 5.1.3 源文件的布局 5.1.4 classpath参数,5.1 包,尽管类具有强大的功能,但仅仅一个类或少量的几个类根本无法满足应用程序的需要。通常的应用程序可能需要几十个类,复杂的应用程序需要上百个、甚至更多的类。Java将相关的类组成包,有助于大型软件系统的管理。,5.1.1 Java 包的用途,1. 包的建立方法 Java包通过package 语句建立,基本语法是: package.子包名; 例如: package .jmu.graphics; 在定义类或接口的源文件开始处,通过package 语句,可以将相应的类或接口放入package所声明的包里。包是相关类和接口的集合,提供了访问级别控制和命名空间管理。,5.1.1 Java 包的用途,2. 包的使用方法 每个源文件只能有一个包声明,并且一定要求放在源文件的开始位置。如果一个源文件没有包语句,则源文件所定义的类或接口被系统放入默认包中。包有层次,层次间由点分隔。 通过创建和使用包,程序员不仅可以很容易地确定哪些类和接口是相关的,也可以很容易地找到提供特定功能的类和接口,例如,与画图有关的类和接口在graphics 包中。,5.1.1 Java 包的用途,3. 包的访问权限 通过声明类的访问级别,可以控制对类的访问权限。 类的访问级别分为默认级和public 级。定义类的默认访问级别不需要任何关键字,被声明为默认级的类只对同一个包中的类是可视的。也就是说,只有同一个包内的类实例可以访问这个类,外界不能访问它。如果用关键字public 定义类,不但同一个包里的其他类可以访问这个类,其他包中的类也可以访问它。换句话说,同一个包中的类,相互之间有不受限制的访问权限。而在不同包中,只有public 类可被访问。,5.1.1 Java 包的用途,除public修饰符之外,用于访问控制的修饰符还有protected和tected或private仅用来定义方法或变量的访问控制级别。protected方法或protected变量仅对同一个包内的类或不同包中的子类来说是可视的。private方法和private变量对外部定义的类均不可视。表5-1表示了访问控制修饰符的使用范围和相应访问级别。需要说明的是,通常不建议采用默认方式定义方法或成员变量的访问级别。,5.1.1 Java 包的用途,表5-1 访问控制表,5.1.1 Java 包的用途,4. 包的命名方式 包提供了新的命名空间,既使所定义的类使用与其他包中的类相同的名字,只要同名类所属的包不同名,就不会引起冲突。原因是这些类的全程限定名称不同。类的全程限定名包含了类的各层包名。这实质上是应用了面向对象的概念,将类封装入包中。 Java建议反转Internet域名为包名。如域名为:,包命名可以.jmu开始,例如建立包.jmu.timer,创建类.jmu.timer.Time。,5.1.2 访问包成员,对于包来说,只有它的public 成员可被外界访问。若外界想要访问包的public 类,有3种方式可以实现。 (1) 通过类的全程限定名访问该类。 (2) 通过import 语句导入该类。 (3) 通过import 语句导入整个包。 如果某个类只被访问一次,那么可以直接使用类的全名。,5.1.2 访问包成员,虽然可以用import 语句导入一个包,但是否可以访问包中的类,还取决于该类的访问级别。需要再次强调的是,只有包的public 成员才对外界是可视的,参见表5-1。 为了方便程序员开发,Java 运行时系统自动导入3个完整的包: (1) 默认包,默认包没有名字。 (2) java.lang包。 (3) 当前所用。,5.1.3 源文件的布局,Java平台依靠分级文件系统管理Java源文件和字节码文件。Java源文件是以.java为扩展名的文本文件。每个源文件称为一个编译单元,每个编译单元中有一个或者多个类,但至多只能有一个public 类,并且源文件必须与public 类同名。源文件存放的文件目录映射着相应类所属的包。例如:类Rectangle 的文件名 Rectangle.java,这个文件将被放置在名为graphics 的子目录下。graphics 目录可以存在于文件系统的任意位置,如图5.1所示。,5.1.3 源文件的布局,图5-1 Rectangle.java源文件的目录,5.1.3 源文件的布局,如果依据Java 的习惯,以反转的域名定义包的名字,如:.jmu.graphics.Graphic类,源文件名为Graphic.java,其源文件目录如图5-2所示。 源文件经编译之后产生字节码文件,与源文件同名,扩展名为.class,如图5-3所示。 按照好的编程习惯,应该将.java文件和.class文件分列在两个相互对应的目录结构下,如图5-4所示。,5.1.3 源文件的布局,图5-2 Graphic.java源文件的目录,5.1.3 源文件的布局,图5-3 .java文件和.class文件,5.1.3 源文件的布局,图5-4 java文件和.class文件的布局,5.1.3 源文件的布局,以这样的模式组织源文件和字节码文件,有益于Java的编译器和解释器确定用户程序中所用到的类和接口的位置。编译器在编译程序时必须能够准确找到程序中出现的类,并对这些类进行类型匹配和语法检查等工作。同样的道理,解释器在运行程序时也必须能够确定相应的字节码文件的位置,以调用这些类的方法。通过设置classpath 环境变量可以帮助编译器和解释器确定类或接口的源文件以及字节码文件的位置。,5.1.4 classpath参数,classpath参数用于设置Java包所存在的顶级目录。也就是说,通过classpath可以为Java包建立所依存的目录列表。编译和运行Java程序时,系统会自动从classpath 环境变量中读取包所在的目录。文件所在目录由系统通过classpath 中的目录和package 语句定义的目录合成。,5.1.4 classpath参数,【例5.2】下面通过一个完整的实例程序说明classpath参数的作用,同时展示源文件和字节码文件的一种建议布局模式。本例包含两个类Time 和TestTime,TestTime 为主类。程序对用户输入的表示小时、分钟和秒钟的3个整型数,经过数据正确性验证,分别转换为通用时间表示格式和标准时间表示格式,通过对话框显示给用户,运行结果如图5-5所示。,5.1.4 classpath参数,图5-5 运行结果(例5.2),5.1.4 classpath参数,文件布局方式如下:创建D:JavaSample目录存放Java程序,创建D:JavaSamplesrc子目录存放源文件,创建D:JavaSampleclass 子目录存放编译后的字节码文件。具体实现方式是将文件TestTime.java 存放在D:JavaSamplesrc 子目录下,为使src 子目录的结构与class 子目录结构相对应,创建D:JavaSamplesrccnedujmutimer 子目录存放 Time.java 文件。类Time 和类TestTime 的布局如图5-6所示。,5.1.4 classpath参数,图5-6 Time类和TimeTest类的布局,5.1.4 classpath参数,Time.java中的语句: package .jmu.timer; 将Time类放入包.jmu.timer中,源文件Time.java存放在与包相对应的目录结构下。在TestTime.java中通过语句: import .jmu.timer.Time; 导入Time 类。 需要设置classpath 参数,规定程序中的包.jmu.timer 编译所依存的顶级目录为D:JavaSampleclass,如: set classpath = D:JavaSampleclass; 编译: javac d d:JavaSampleclass d:JavaSamplesrccnedujmutimerTime.java javac d d:JavaSampleclass d:JavaSamplesrcTestTime.java 运行: java TestTime,5.1.4 classpath参数,用户输入的时、分、秒数,分别转换为通用时间格式和标准时间格式,通过对话框显示给用户,运行结果如图5-5所示。现给出这两个Java文件的源代码。 / Time.java package .jmu.timer; import java.text.DecimalFormat; /用于数据格式化 / 24小时制时间 public class Time extends Object private int hour; / 0 23 private int minute; / 0 59 private int second; / 0 59 / Time类构造器,初始化每个实例对象的值为0 / 确保每个Time对象处于一致的状态,5.1.4 classpath参数,public Time() setTime( 0, 0, 0 ); / 数据的正确性验证,对无效数据取0值 public void setTime( int h, int m, int s ) hour = ( ( h = 0 ,5.1.4 classpath参数,/将标准时间格式转换为字符串 public String toString() DecimalFormat twoDigits = new DecimalFormat( “00“ ); return ( (hour = 12 | hour = 0) ? 12 : hour % 12 ) + “:“ + twoDigits.format( minute ) + “:“ + twoDigits.format( second ) + ( hour 12 ? “ AM“ : “ PM“ ); / TestTime.java / TestTime类需要导入Time类 import Javax.swing.JOptionPane; import .jmu.timer.Time; / import 语句导入Time类 public class TestTime public static void main( String args ) ,5.1.4 classpath参数,Time t = new Time(); t.setTime( 13, 27, 06 ); String output = “Universal time is: “ + t.toUniversalString() + “nStandard time is: “ + t.toString(); JOptionPane.showMessageDialog( null, output, “Packaging Class Time1 for Reuse“, JOptionPane.INFORMATION_MESSAGE ); System.exit( 0 ); ,5.1.4 classpath参数,特别要强调的是,如果没有正确设置classpath 参数,那么TestTime.java在编译时会出现找不到Time 类的错误。Java编译器严格按照classpath和包名所指定的路径寻找包。只要指定的路径下没有所需要的文件,即使该文件就在当前目录,Java也会报告文件找不到的错误。这点对初学者来说需要特别注意。,5.2 接 口,5.2.1 接口能够解决的问题 5.2.2 接口的定义 5.2.3 Comparable 接口 5.2.4 回调,5.2 接 口,接口是和类很相似而又有区别的一种结构,接口的设计和调用也是Java程序设计的重要技术。从结构上,接口有如下特点。 (1) 接口用关键字interface来定义,而不是用class。 (2) 接口中定义的变量全是最终的静态变量。 (3) 接口中没有自身的构造方法,而且定义的其他方法全是抽象方法,即只提供方法的定义,而没有提供方法的实现语句。 (4) 接口采用多重继承机制,而不是采用类的单一继承机制。,5.2.1 接口能够解决的问题,当类只从一个类继承时称为单继承,Java 语言只允许单继承。如果需要多继承关系,可以通过Java语言中的接口实现。 抽象方法只有方法定义,没有方法体。如果一个类包含任何抽象方法,包括直接的和继承所得,则这个类必须是抽象的。如果一个类是抽象的,则不能创建此类的对象。抽象类强制要求它的所有子类重写自己的方法以覆盖抽象方法。 如果存在超类Animal,显然Cat 同时具有Animal 和Pet 的特性。就是说Cat 不只是一个Animal,还是一个Pet,除需要继承Animal 类,还期望能实现Pet 类。因为Java 只允许单继承,Cat 不能既继承Animal 又继承Pet。解决问题的方法是定义接口,并通过接口实现多继承。,5.2.2 接口的定义,1. 接口声明 一个完整的接口声明如下: public interface interfaceName extends listOfSuperInterface . 在接口声明中,只有关键字interface和接口名是必需的,其他内容是可选的。public用来指明任意一个类都可以调用此接口。如果一个接口声明中没有关键字 public,那么只有那些与本接口在同一个包中的类才能访问它。,5.2.2 接口的定义,2. 接口体 接口体包含常量定义和方法定义两部分。常量定义和变量定义在形式上类似,但常量名要求用大写字母表示。其格式如下: type NAME =value; 其中,type可以是Java的任何一种数据类型,接口中的常量全都默认为是final和static属性,即使常量定义中没有注上这两个字也是如此。但有些程序中为了使可读性好,仍然写明final static属性。,5.2.2 接口的定义,方法的定义格式如下: returnType methodName(para1, para2, .).; 其中,returnType表示返回值类型,如果没有返回值,则用关键字void指明。后面方括号中的内容是可选的,用来列出入口参数。入口参数可能有多个,之间用逗号隔开;如果没有入口参数,则可省去这部分内容。 由于接口中都用抽象方法,即只对方法作定义而不提供方法的实现语句,所以,接口的方法定义中没有方法体。接口中所有的方法都默认为是abstract属性。,5.2.2 接口的定义,上述程序段定义了一个接口Bigable,内含一个常量SUM,另外包含3个方法,每个方法都指明了其返回值类型,但接口的方法中没有方法体。 【例5.5】对接口中定义的方法进行实现(例子详见书141143页) 运行结果如图5-8所示。,5.2.2 接口的定义,图5-8 运行结果(例5.5),5.2.3 Comparable 接口,经常需要对数据集合中的对象进行比较,若要比较对象,需要实现java.lang.Comparable 接口。Comparable 接口具有惟一的方法compareTo(Object),其定义为: public interface Comparable public int compareTo(Object o); ,5.2.3 Comparable 接口,实现Comparable 接口的类的所有实例对象之间应该互相可比较,并具备全序关系。这种全序关系采用类的自然排序规则。 在Java 2中有包括Character、File、Long、ObjectStreamField、Short、String、Float、Integer、Byte、Double、BigInteger、BigDecimal、Date、CollationKey 在内的23 个类实现了Comparable 接口。表5-2为其中8种基本数据类型的自然排序规则。虽然有一些类共用同一种自然排序规则,但只有相互可比较的类才能进行排序。,5.2.3 Comparable 接口,表5-2 基本数据类型的自然排序规则,5.2.4 回调,软件模块之间总是存在着一定的接口,通过接口形成相互调用关系。习惯上,常把调用者称为客户方,被调用者称为服务方。从调用方式上,可以把它们分为三类:同步调用、回调和异步调用,如图5-9所示,其中A 表示客户方,B表示服务方。,5.2.4 回调,图5-9 软件接口间的三种调用方式,5.2.4 回调,同步调用是一种阻塞式调用,客户方要等待服务方执行完毕才返回,它是一种单向调用。回调是一种双向调用模式,也就是说,服务方在被调用时也会调用客户方。 异步调用是一种类似消息或事件的机制,不过它的调用方向刚好相反,服务方在收到某种信息或发生某种事件时,会主动通知客户方,即通过接口调用客户方。,5.3 异常及其处理,5.3.1 什么是异常 5.3.2 异常的层次结构 5.3.3 异常的处理,5.3 异常及其处理,对各种异常的处理是程序设计中要解决的重要问题。Java采用了一种特殊的机制来处理异常。本节讲述这种机制的思想,并结合具体例子讲解Java怎样用try-catch-finally语句实现这种机制。最后,简要说明怎样自定义异常类型以扩充Java系统的异常处理功能。,5.3.1 什么是异常,用任何一种计算机语言设计的程序在运行时都可能出现各种错误,常见的错误如除数为0、文件不存在、文件不能打开、数组下标超过界限、内存不够用等。对于这种在运行中出现的错误,计算机系统中通常有两种处理办法。 由计算机系统本身直接检测程序错误,遇到错误时使程序终止运行。 由程序员在程序设计中兼顾错误检测、错误信息显示和出错处理。 在Java程序中,异常一般由以下两种原因引起。 (1) 程序中存在非法操作,最简单的例子就是除数为0的除法操作,这种原因常常是程序员出于无意或大意造成的,所以称为隐式异常。 (2) 程序员在程序中使用了throw语句引起的异常。,5.3.2 异常的层次结构,Java程序中的异常以类的层次结构组织。Throwable是所有异常类的父类,它是Object的直接子类。Exception和Error是Throwable的直接子类,而RuntimeException是Exception的子类。 在系统包java.lang、java.util、java.io和中声明的异常类是标准异常类。这些标准异常类分为两种:一种是RuntimeException子类,因为RuntimeException是不可检测的异常类,所以这些标准异常类也是不可检测的异常类;另一种是除了RuntimeException子类以外的其他Exception的子类,这些异常类是可检测的。,5.3.2 异常的层次结构,1. 不可检测的标准异常类 (1) java.lang中的标准异常类 ArithmeticException:表示遇到了异常的算术问题,例如被0整除。 ArrayStoreException:试图把与数组类型不相符的值存入数组。 ClassCastException:试图把一个对象的引用强制转换为不合适的类型。 IndexOutOfBoundsException:下标越界。 NullPointerException:试图使用一个空的对象引用。 SecurityException:检测到了违反安全的行为。,5.3.2 异常的层次结构,(2) java.util中的标准异常类 EmptyStackException:试图访问一个空堆栈中的元素。 NoSuchElementException:试图访问一个空向量中的元素。 2. 可检测的标准异常类 (1) java.lang ClassNotFoundException:具有指定名字的类或接口没有被发现。 CloneNotSupportedException:克隆一个没有实现Cloneable接口的类。 IllegalAccessException:试图用给出了完整的路径信息的字符串加载一个类,但是当前正在执行的方法无法访问指定类,因为该类不是public类型或在另一个包中。,5.3.2 异常的层次结构,InstantiationException:试图使用Class的newInstance 方法创建一个对象实例,但指定的对象没有被实例化,因为它是一个接口、抽象类或者一个数组。 InterruptedException:当前的线程正在等待,而另一个线程使用了Thread的interrupt方法中断了当前线程。 (2) java.io IOException:申请I/O操作没有正常完成。 EOFException:在输入操作正常结束前遇到了文件结束符。 FileNotFoundException:在文件系统中,没有找到由文件名字符串指定的文件。 InterruptIOException:当前线程正在等待I/O操作完成,而另一个线程使用Thread的interrupt方法中断了当前线程。,5.3.2 异常的层次结构,当一个异常被抛出(即产生了异常)时,应该如何处理呢?在Java语言中,使用语句try-catch-finally 进行异常处理,程序流程从引起异常的代码转移到最近的try语句的catch子句,语句如下所示: try ./被监视的代码段 , 一旦发生异常 , 则交由其后的catch代码段处理 catch (异常类型 e) . catch (异常类型 e) . . finally./最终处理,5.3.2 异常的层次结构,把可能会发生异常情况的代码放在try语句段中,利用try语句对这组代码进行监视。如果发生了异常,就交由catch语句处理。catch语句在执行前,必须识别抛出的异常是否为catch能够捕获的异常。如果catch语句参数中声明的异常类与抛出的异常类相同,或者是它的父类,catch语句就可以捕获任何这种异常类的对象。,5.3.2 异常的层次结构,如果希望在异常发生时能确保有一段代码被执行,那么应该使用finally子句。这样即使发生的异常与catch所能捕获的异常不匹配也会执行finally子句。 【例5.8】使用finally举例,程序如下。 public class CatchDemo public static void main(String args) System.out.println(“这是一个异常处理的例子n“); try int i=10; i/=0; catch (ArithmeticException e) System.out.println(“异常是:“+e.getMessage(); finally System.out.println(“finally语句被执行“); ,5.3.2 异常的层次结构,在JCreator中运行结果如图5-10所示。 图5-10 运行结果(例5.8),5.3.3 异常的处理,1. 异常的捕获 在5.3.2节的例子中,已经用了try-catch-finally语句块,下面介绍try-catch-finally语句各部分的用法。 捕获异常是通过try-catch-finally语句实现的。 try. catch( ExceptionName1 e ). catch( ExceptionName2 e ). finally. (1) try 捕获异常的第一步是用try选定捕获异常的范围,由try所限定的代码块中的语句在执行过程中可能会生成异常对象并抛出。 (2) catch 每个try代码块可以伴随一个或多个catch语句,用于处理try代码块中所生成的异常事件。catch语句只需要一个形式参数指明它所能够捕获的异常类型,这个类必须是Throwable的子类,运行时系统通过参数值把被抛出的异常对象传递给catch块。,5.3.3 异常的处理,(3) finally 捕获异常的最后一步是通过finally语句为异常处理提供一个统一的出口,使得在控制流转到程序的其他部分以前,能够对程序的状态作统一的管理。不论在try代码块中是否发生了异常事件,finally块中的语句都会被执行。,5.3.3 异常的处理,2. 异常的抛出 在异常发生时,可以对它进行捕获,也可以将它继续抛出。 【例5.11】对异常进行抛出举例,程序如下。 class CatchDemo2 Public static void main(String args) throws ArithmeticException, ArrayIndexOutOfBoundsException int a=args.length; System.out.println(“na=“+a); int b=42/a; int c=1; c42=99; ,5.4 创建自定义的异常,Java提供的一些异常有时候不能满足编程的需求,比如规定用户输入数据的范围在20到30之间,但是Java并没有这个方面的异常。这个时候就可以应用自定义的异常来规范化客户的数据输入。 在Java中进行自定义异常时,自定义异常类必须是Throwable的直接或间接子类。下面的例子是关于自定义异常的。它通过继承Exception类而继承Throwable,即间接继承Throwable。,5.4 创建自定义的异常,【例5.12】自定义异常举例,程序如下。 class OutBoundsException extends Exception OutBoundsException(String mes) /调用超类的构造函数 super(mes); class check String ChecktheNum(int n) throws OutBoundsException Integer N=new Integer(n); if(n30|n20) throw new OutBoundsException(“the number is out of bound!“); else return “the number“+N.toString()+“is in the bound!“; class Test,5.4 创建自定义的异常, public static void main(String args) try check c=new check(); System.out.println(“以下是合法的数据的报告!“); System.out.println(c.ChecktheNum(25); System.out.println(“以下是非法的数据的报告!“); System.out.println(c.ChecktheNum(5); catch(OutBoundsException e) System.out.println(e.toString(); 运行结果如图5-13所示。,5.4 创建自定义的异常,图5-13 运行结果(例5.12),5.5 课 后 练 习,1. 填空题 (1) 声明接口的保留字是_。 (2) 类的声明“public class Test extends Applet implements Runable”中,定义的类名是_,其父类是_;实现了_接口。这个类的源程序必须保存为_(写出包括扩展名的文件名)。 2. 选择题 (1) 接口是Java面向对象的实现机制之一,以下说法正确的是( )。 A. Java支持多重继承,一个类可以实现多个接口 B. Java只支持单重继承,一个类可以实现多个接口 C. Java只支持单重继承,一个类只可以实现一个接口 D. Java支持多重继承,但一个类只可以实现一个接口 (2) 关于接口的定义和实现,以下描述正确的是( )。 A. 接口定义中的方法都只有定义没有实现 B. 接口定义中的变量都必须写明final和static C. 如果一个接口由多个类来实现,则这些类在实现该接口中的方法时应采用统一 的代码 D. 如果一个类实现一个接口,则必须实现该接口中的所有方法,但方法未必声明 为public.,5.5 课 后 练 习,3. 判断题 (1) 对每个异常,只能进行捕获。 ( ) (2) 接口中定义的方法可以是private型的。 ( ) 4. 简答题 (1) 按照Java习惯,程序员应该如何组织和命名包?如何布局源文件? (2) 设置classpath 参数的作用是什么? 5. 操作题 (1) 根据如图5-14所示的UML图设计完成编码TestAnimal.java,使输出结果符合样例: Fish swim in their tanks all day. Fluffy likes to play with string. Spiders catch flies in their webs to eat. This animal walks on 8 legs. This animal walks on 0 legs. Fish, of course, cant walk; they swim.,5.5 课 后 练 习,(2) 编写MyPoint.java,定义点的数据结构为两个双精度数x,y并重写toString 方法,通过字符串连接运算,使输出点形式为: start point is startx,y end point is endx,y (3) 编写TestMyPoint.java,实例化MyPoint 为start 和end,输出start 和end 的状态。 (4) 编写方法distance()计算两点间的距离。,5.5 课 后 练 习,图5-14 UML图,Q & A? Thanks!,第6章 线程,(时间:3次课,6学时),第6章 线程,教学提示:计算机世界要想真正地反映现实世界,必须解决事情的同步问题,即解决程序实现多线程的问题。 因此可编写有几条执行路径的程序,使得程序能够同时执行多个任务,借此实现多线程运行。Java语言的一大特点就是内置对多线程的支持。 本章主要介绍:Java中的线程作用机制 、线程的实现方法、线程的控制和线程的同步与死锁 。,第6章 线程,6.1 线程简介 6.2 线程的实现方法 6.3 线程的控制 6.4 Java的多线程实例 6.5 线程的同步与死锁 6.6 ThreadLocal问题 6.7 课后练习,6.1 线程简介,6.1.1 程序、进程和线程 6.1.2 线程的生命周期 6.1.3 线程的优先级及其调度6.1.4 线程组,6.1 线程简介,对于许多编程人员来说,线程并不是那么的陌生。但是在Java中,线程的作用机制又是如何工作的呢?本节将重点介绍Java中的线程作用机制。,6.1.1 程序、进程和线程,程序是由若干条语句组成的语句序列,是一段静态代码。 进程是程序的一次动态执行过程。 需要特别指出的是,进程不仅包括程序代码,还包括系统资源。即一个进程既包括其所要执行的指令,也包括执行指令所需的任何系统资源,如CPU、内存空间等。不同进程所占用的系统资源相对独立。,6.1.1 程序、进程和线程,线程又是一个抽象的概念,它包含了一个计算机执行传统程序时所做的每一件事情。线程是一种在CPU上调度的程序状态,它在某一瞬时看来只是计算过程的一个状态。一个进程中的所有线程共享该进程的状态,它们存储在相同的内存空间中,共享程序的代码和数据。所以当其中一个线程改变了进程的变量时,那么其他线程下次访的将是改变后的变量。 多线程是指同一个应用程序中有多个顺序流同时执行。在一个程序中可以同时运行多个不同的线程来执行不同的任务,各个线程并行地完成各自的任务。浏览器就是一个典型的多线程例子。,6.1.2 线程的生命周期,每个Java程序都有一个默认的主线程。对于应用程序,主线程是main()方法执行的路径。图6-1说明线程的生命周期及其状态转换。,6.1.2 线程的生命周期,图6-1 线程的状态转换,6.1.2 线程的生命周期,从图6-1中可以看出:一个线程从创建到消亡的整个生命周期中,总是处于下面5个状态中的某个状态。 1. 新建状态 通过new命令创建一个Thread类或其子类的线程对象时,该线程对象处于新建状态。创建一个新的线程对象可以用下面的语句实现: Thread thread=new Thread(); 该语句是最简单的创建线程的语句,但该语句创建的线程是一个空的线程对象,系统还未对这个线程分配任何资源。,6.1.2 线程的生命周期,2. 就绪状态 该状态又可称为可运行状态。处于新建状态的线程可通过调用start()方法启动该线程。Start()方法产生了线程运行需要的系统资源。启动后的线程将进入线程就绪队列排队等待CPU服务,此时线程已经具备了运行的条件,一旦它获得CPU等资源时就可以脱离创建它的主线程而独立运行。,6.1.2 线程的生命周期,3. 运行状态 当处于就绪状态的线程被调度并获得CPU资源时,使进入运行状态。每个线程对象都有一个重要的run()方法,run()方法定义了该线程的操作和功能。当线程对象被调度执行时,它将自动调用其run()方法并从第一条语句开始顺次执行。,6.1.2 线程的生命周期,4. 阻塞状态 又称不可运行状态。当发生下列情况之一时,线程就进入阻塞状态。 (1) 等待输入输出操作完成。 (2) 线程调用wait()方法等待一个条件变量。 (3) 调用了该线程的sleep()休眠方法。 (4) 调用了suspend()挂起方法。,6.1.2 线程的生命周期,5. 消亡状态 消亡状态又称死亡状态,当调用run()方法结束后,线程就进入消亡状态,这是线程的正常消亡。另外线程还可能被提前强制性消亡。不管何种情况,处于消亡状态的线程不具有继续运行的能力。,6.1.3 线程的优先级及其调度,线程被创建之后,每个Java线程的优先级都在Thread.MIN_PRIORITY(常量1)和Thread.MAX_PRIORITY(常量10)的范围之内。每个新建线程的默认优先级都为Thread.NORM_PRIORITY(常量5)。可以用方法int getPriority()来获得线程的优先级,同时也可以用方法 void setPriority(int p)在线程被创建后改变线程的优先级。 一个线程将始终保持运行状态,直到出现下列情况:由于I/O(或其他一些原因)而使该线程阻塞;调用sleep、wait、join 或yield 方法也将阻塞该线程;更高优先级的线程将抢占该线程;时间片的时间期满而退出运行状态或线程执行结束。,6.1.3 线程的优先级及其调度,【例6.3】综合使用线程的方法来控制线程的工作举例,程序如下。 /一个实现Runnable接口的SimpleRunnable类 class SimpleRunnable implements Runnable protected String message; protected int iterations; public SimpleRunnable(String msg, int iter) message = msg; iterations = iter; public void run() for (int i=0; iiterations; i+=1) System.out.println(message); try Thread.sleep(100); catch (InterruptedException e) System.out.println(e); ,6.1.3 线程的优先级及其调度,/ThreadExample类运行这个线程 public class ThreadExample public static void main(String args) Thread t1, t2; t1 = new Thread(new SimpleRunnable(“Thread 1“, 10); t2 = new Thread(new SimpleRunnable(“Thread 2“, 15); System.out.println(“T1 p is: “ + t1.getPriority(); System.out.println(“T2 p is: “ + t2.getPriority(); t2.setPriority(7); System.out.println(“T2 after set p is: “ + t2.getPriority(); t2.yield(); System.out.println(“T2 after yield p is: “ + t2.getPriority(); t1.start(); t2.start(); ,6.1.3 线程的优先级及其调度,运行结果如下: T1 p is: 5 T2 p is: 5 T2 after set p is: 7 T2 after yield p is: 7 Thread 2 Thread 1 Thread 2 Thread 1 Thread 2 Thread 1,6.1.3 线程的优先级及其调度,Thread 2 Thread 1 Thread 2 Thread 1 Thread 2 Thread 1 Thread 2 Thread 1 Thread 2 Thread 1 Thread 2 Thread 1 Thread 2 Thread 1 Thread 2 Thread 2 Thread 2 Thread 2 Thread 2,6.1.4 线程组,线程组(Thread Group)允许把一组线程统一管理。例如,可以对一组线程同时调用interrupt()方法,中断这个组中所有线程的运行。创建线程组的构造方法为: ThreadGroup(String groupName); 线程组构造方法的字符串参数用来标识该线程组,并且它必须是独一无二的。线程组对象创建后,可以将各个线程添加到该线程组,在线程构造方法中指定所加入的线程组: Thread(groupName, threadName);,6.1.4 线程组,线程组下可以设子线程组,默认创建线程组将成为与当前线程同组的子线程组。当线程组中的某个线程由于一个异常而中止运行时,ThreadGroup类的uncaughtException (Threadt,Throwable e)方法将会打印这个异常的堆栈跟踪记录。,6.2 线程的实现方法,6.2.1 继承Thread类 6.2.2 实现Runnable接口,6.2 线程的实现方法,要使用多线程实现程序的流程控制,需要首先创建线程,生成线程实例,然后需要控制线程的调度。线程的创建分两步:定义线程体和创建线程对象。线程体决定线程的功能,它由run()方法实现,系统通过调用线程的run()方法实现线程的具体功能。Java中可通过继承Thread类或实现Runnable接口这两种途径来构造线程的run()方法。,6.2.1 继承Thread类,可通过创建Thread类的子类并重写其中的run()方法来定义线程体以实现线程的具体功能,然后创建该子类的对象以创建线程。 Thread类在包java.lang中,它定义了Java程序中一个线程需要拥有的属性和方法。如表6-1和表6-2所示。,6.2.1 继承Thread类,表6-1 Thread类的属性,6.2.1 继承Thread类,表6-2 Thread类的构造方法和关键方法,6.2.2 实现Runnable接口,另一种实现多线程的方法是实现Runnable接口。Runnable接口只定义了一个方法run()方法,该方法是一个抽象方法,所有实现Runnable接口的类都必须具体实现这个方法,为它提供方法体并定义具体操作。Runnable接口中的 run()方法与Thread 类中的run()方法一样,是被系统自动识别执行的。,6.2.2 实现Runnable接口,通过实现Runnable接口实现多线程的一般步骤如下。 (1) 创建实现Runnable接口的类ClassName。它的一般格式为: class ClassName implements Runnable public void run() /编写代码 (2) 创建Runnable类ClassName的对象。一般格式为: ClassName RunnableObject=new ClassName(); (3) 用带有Runnable参数的Thread类构造方法创建线程对象,对象RunnableObject作为构造方法的参数,作为新建线程的目标对象为线程提供 run()方法。如用表 8-2 中列出的构造方法Thread(Runnable target)创建线程对象: Thread ThreadObject=new Thread(RunnableObject);,6.2.2 实现Runnable接口,Thread 类中除了上述构造方法带有Runnable 参数外,还有下面3个构造方法也带有Runnable参数。 Thread(Runnable target,String name) Thread(ThreadGroup group,Runnable target) Thread(ThreadGroup group ,Runnable target,Stri

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论