1、Java程序设计系列讲座2 OOP之封装性,黄绍辉 厦门大学计算机科学系 E-mail: ,面向对象程序设计基本思想,对象的基本概念 对象是系统中用来描述客观事物的一个实体,它是构成系统的一个基本单位。一个对象由一组属性和对这组属性进行操作的一组服务组成。 对象是问题域或实现域中某些事物的一个抽象,它反映该事物在系统中需要保存的信息和发挥的作用;它是一组属性和有权对这些属性进行操作的一组服务的封装体。 客观世界是由对象和 对象之间的联系组成。,类的基本概念,类是具有相同属性和服务的一组对象的集合。分类的原则是抽象。 在面向对象的编程语言中,类是一个独立的程序单位,它应该有一个类名并包括属性说明
2、和服务说明两个主要部分。 类与对象的关系 就如模具和铸件 的关系,类的实 例化结果就是对 象,而对一类对 象的抽象就是类。,关于OOPObject Oriented Programming,对象是基本编程单位。与此对应,Java使用类(class)表示对象,一个class就是一个类; 对象由一组属性和一组服务两部分组成(两部分不必都存在)。与此对应,Java使用一组成员变量(member variable)表示属性,一组成员函数(member method)表示服务。,OOP的例子,例如我们要表示一个遥控器对象,它起码有两个属性:外壳,按钮。它起码有两个功能:换台,调音量。使用Java来设计这
3、个对象,它看起来就是这个样子: class 遥控器 外壳成员按钮成员换台函数()调音量函数() ,OOP的基本特征,封装性 把对象的属性和服务结合成一个独立的相同单位,并尽可能隐蔽对象的内部细节。 继承性 从一个已有的类来构造新类,除了保留原有的类的特性,还增加了一些新特性。已有的类称为父类superclass,新类称为子类subclass。 多态性 在父类中定义的属性或服务被特殊类继承之后,可以具有不同的数据类型或表现出不同的行为。,OOP-Part I 封装性,从C语言开始出发,任务:在程序中实现对时间的一些操作 思路:时间是一个整体,包括时/分/秒,为了在程序中表示一个具体的时间,这三个
4、部份缺一不可。 最自然的,可以采用三个整型变量来存储这三个部份,例如: int hour; int minute; int second;,C程序示例,void main() int hour,minute,second;hour=12;minute=12;second=12;printf(“Time is %d:%d:%d”,hour,minute,second); ,如果有很多个时间,void main() int hour1,minute1,second1; int hour2,minute2,second2; int hour3,minute3,second3; printf(“Tim
5、e1 is %d:%d:%d”,hour1,minute1,second1); printf(“Time2 is %d:%d:%d”,hour2,minute2,second2); printf(“Time3 is %d:%d:%d”,hour3,minute3,second3); ,如果用数组,void main() int hour100,minute100,second100; for(int i=0;i100;i+) printf(“Time%d is %d:%d:%d”,(i+1),houri,minutei,secondi); 问题1:万一数组不够大怎么办? 问题2:如果hour/
6、minute/second的下标弄混了怎么办?,解决方案,问题1只能通过动态分配内存解决,使用静态分配是不能彻底解决问题的。 问题2的解决可以求助于结构,这样就可以将三个量作为一个整体来处理。 struct MyTime int hour;int minute;int second; ;,C语言的增强版,void main() struct MyTime time1; time1.hour=12;time1.minute=12;time1.second=12;printf(“Time is %d:%d:%d”,time1.hour,time1.minute,time1.second); 注意这
7、里其实struct MyTime已经可以算是一个对象了,因为它拥有自己的成员;所以每一个这种结构的变量,例如time1,都是这个对象的实例;如果还有一个time2,那么time1和time2的成员是互不相关的。,强行改造成Java程序,struct MyTime int hour;int minute;int second; ; void main() struct MyTime time1; time1.hour=12;time1.minute=12;time1.second=12;printf(“Time is %d:%d:%d”,time1.hour,time1.minute,time1
8、.second); ,public class Main public static void main(String args),class MyTime int hour; int minute; int second; ,MyTime time1=new MyTime();,System.out.print(“Time is “ + time1.hour+”:”+time1.minute+”:”+time1.second);,新的任务,现在要添加一个函数用来处理时间,我们希望调用这个函数后,能够得到一个新的时间,如: AddOneHour;/小时数加1 为了实现上述的函数,C中可以选择的
9、设计方案有2个: 生成新时间对象,然后覆盖到旧对象上,此时需要函数能够返回新对象: struct MyTime AddOneHour(struct Mytime t) 在旧对象上修改,此时需要将旧对象的指针传入,但是不再需要返回值: void AddOneHour(struct Mytime* t),C语言版本的AddOneHour,struct MyTime AddOneHour(struct Mytime t) struct MyTime temp = t;temp.hour +;return temp; void AddOneHour(struct Mytime* t) t-hour +
10、; ,使用示例: struct MyTime t; t = AddOneHour(t);,使用示例: struct MyTime t; AddOneHour(,关于上面两个函数的探讨,前面说过,struct MyTime已经可以看成是对象了,从上面的两个函数可以看出,为了修改对象的属性,比较好的作法,是(通过指针)直接修改对象本身的属性,代码简单,效率也高。 但是C毕竟是面向过程的语言,实现 “对象的加工”或者叫“对象的服务”,需要借助struct MyTime之外的函数AddOneHour完成。 OOP语言的第一个方便之处,就是把属性和服务整合到同一个对象中,这给编程带来了极大的方便,例如:
11、,Java版本的AddOneHour,class MyTime int hour; int minute; int second; void AddOneHour() hour+; ,使用示例: MyTime t=new MyTime(); t.AddOneHour();,把属性和服务整合到同一个对象内部的最大好处,是你不再需要将一个结构作为一个参数传入到外部的处理函数,也不需要考虑将处理后的结果从函数传出,甚至贴代码的时候,都不用担心会少贴一个函数,看出封装的好处了吗?,C的结构可以存储数据,但是不能包含函数,因此必须在其它的地方定义函数。这个做法可能导致以下后果: 调用不方便,参数要传入传
12、出毕竟是比较麻烦的 可能和别人写的函数产生冲突(例如重名) 代码的重用和修改不方便 Java可以将数据和处理函数一起封装,因此数据以及对数据的加工都可以在同一个对象当中完成。,总结一下封装性,OOP中,对象就是对一组变量和相关方法的封装,其中变量表明了对象的状态,方法表明了对象具有的行为。 简单一点说,封装性指的是:属性和函数都放在同一个对象的内部。 对Java而言就是:一个class可以内部可以有多个成员变量和多个成员函数;成员函数可以直接访问成员变量,并对其进行修改。 通过对象的封装,实现了模块化和信息隐藏。,跑步进入OOP有个例子,从简单对象入手,对象不是很深奥的东西,一个对象在Java
13、里面就是一个class而已。例如随便写一个对象: class Point /首字母大写是Java建议的对象命名规则int x,y; 这个Point对象可以用来表示平面上的一个点。注意Java并不要求对象一定要有成员函数,所以这个对象已经很完整,只是还不够好用而已,接下来我们会逐步完善它。,对象的用法,要使用一个已有的对象不是太难,但是你要做足以下的两件事情: 使用对象的名字声明一个变量 创建对象的一个实例 用上面的Point做例子,就是这样: 使用对象的名字声明一个变量 Point p; 创建对象的一个实例 p = new Point(); 接下来这个p就拥有了Point的全部资源,例如它有两
14、个成员变量,叫x,y,而且还是整型的。,对象的用法,对象的声明和创建可以合成一步: Point p = new Point(); 当然也可以隔了很多步: Point p; /* 中间有很多很多很多行,但是和p无关 */ p = new Point(); 只有一个准则:对象在创建之前是不能使用的,所以new很重要。,对象与实例,前面说到,对象必须创建才能使用,所以你必须这样创建p:Point p = new Point(); 这里引入几个术语:Point 是类名,也是对象名,或者干脆叫对象p 是对象的实例,也就是Point的实例 你可以理解对象是一个图章,它每盖章一次(new一次)生成一个p,p
15、就是一个实例。,一个完整例子:Test.java,class Point int x,y; public class Test public static void main(String args) Point p = new Point();System.out.println(“p.x=”+p.x+“ p.y=”+p.y);p.x = 1; p.y = 2;System.out.println(“p.x=”+p.x+“ p.y=”+p.y); ,插播一下:数组,使用对象,我们刚刚学会两招: 招式一:对象可以同时拥有属性和函数; 招式二:对象要new以后才能使用 下面我们来用这两招对付Ja
16、va的一个常用对象:数组。 和C语言不同,Java把数组设计成了对象,所以根据我们的招式二,Java的数组必须先new才能使用。如果你能够意识到这一点,那么恭喜你,OOP入门成功,数组入门1,1 声明一个数组 dataType arrayRefVar; 或 dataType arrayRefVar; 例如:double myList; 2 创建一个数组 arrayRefVar = new dataTypearraySize; 如:myList = new double10;,数组入门2,声明并创建一个数组 dataType arrayRefVar = new dataTypearraySize
17、; 或 dataType arrayRefVar = new dataTypearraySize; 例如:double myList = new double10;,数组入门3,数组在创建之后,Java会为这个数组自动分配空间。(注意声明数组的时候并没有分配数组元素空间,因为那时候不知道数组多大),数组入门4,任何一个数组,都可以通过属性length得到数组的长度。如: myList.length 数组创建的时候会自动赋零值,对于数值型的,就是0或者0.0或者0,对于布尔型的,就是false 任何一个数组,其元素的最小下标是0,最大下标是length-1 数组下标越界会引发异常,数组入门5,数
18、组元素的引用使用 ,以下两个方法可以打印出数组myList的每一个元素值: for (int i = 0; i myList.length; i+) System.out.print(“ “+myListi); 或者(JDK1.5以上版本才支持的) for (int val : myList) System.out.print(“ “+val); ,数组入门6,数组在创建的时候可以顺便初始化,如: double myList = 1.9, 2.9, 3.4, 3.5; 它相当于: double myList = new double4; myList0 = 1.9; myList1 = 2.9
19、; myList2 = 3.4; myList3 = 3.5;,数组例程1/3,用100以内随机数初始化数组 for (int i = 0; i myList.length; i+) myListi = Math.random() * 100; ,数组例程2/3,数组求和 double total = 0; for (int i = 0; i myList.length; i+) total += myListi; ,数组例程3/3,求数组中的最大值 double max = myList0; for (int i = 1; i max) max = myListi; ,数组作为函数参数1,数
20、组作为参数,Java采用的是引用传递方式,基本数据类型(int,char等)作为参数,采用值传递方式。 简单一点说,引用传递方式,实际参数和形式参数,操作的其实是同一个对象;值传递方式,实际参数和形式参数,操作的是不同对象,仅仅是这两个对象的值相等而已。,数组作为函数参数2,public class Test public static void main(String args) int x = 1; / x represents an int valueint y = new int10; / y represents an array of int valuesm(x, y); / In
21、voke m with arguments x and ySystem.out.println(“x is “ + x); / x值不变,还是1System.out.println(“y0 is “ + y0); / y0值已改变,本来是0的public static void m(int number, int numbers) number = 1001; / Assign a new value to numbernumbers0 = 5555; / Assign a new value to numbers0 ,数组作为函数参数3,1 public class TestPassArra
22、y 2 /* Main method */3 public static void main(String args) 4 int a = 1, 2;56 / Swap elements using the swap method7 System.out.println(“Before invoking swap“);8 System.out.println(“array is “ + a0 + “, “ + a1 + “);9 swap(a0, a1); /值传递,返回后不改变原值 10 System.out.println(“After invoking swap“); 11 System
23、.out.println(“array is “ + a0 + “, “ + a1 + “); 12 13 / Swap elements using the swapFirstTwoInArray method 14 System.out.println(“Before invoking swapFirstTwoInArray“); 15 System.out.println(“array is “ + a0 + “, “ + a1 + “); 16 swapFirstTwoInArray(a); /引用传递,返回后原值可能改变 17 System.out.println(“After in
24、voking swapFirstTwoInArray“); 18 System.out.println(“array is “ + a0 + “, “ + a1 + “); 19 ,数组作为函数参数4,20 /值传递,返回后不改变原值 21 /* Swap two variables */ 22 public static void swap(int n1, int n2) 23 int temp = n1; 24 n1 = n2; 25 n2 = temp; 26 27 /引用传递,返回后原值可能改变 28 /* Swap the first two elements in the arra
25、y */ 29 public static void swapFirstTwoInArray(int array) 30 int temp = array0; 31 array0 = array1; 32 array1 = temp; 33 34 ,多维数组1,二维数组的定义 dataType arrayRefVar; 或 dataType arrayRefVar; 例如: int matrix; 或 int matrix; 注意,这里仅仅说明matrix是一个二维数组,但是这个数组的大小还未知,所以还不能存储元素。,多维数组2,二维数组有行和列的概念,如:,多维数组3,二维数组可以在声明的时
26、候赋初值,也可以在new之后再一个一个慢慢赋值。其实二维数组行和行之间是相互独立的,每一行都相当于一个一维数组。,由于二维数组的每一行是一个一维数组,所以这些一维数组的大小不必相等,如:此时:triangleArray.length = 5, /因为数组有5行triangleArray0.length = 5, triangleArray1.length = 4, triangleArray2.length = 3, triangleArray3.length = 2, triangleArray4.length = 1.,多维数组4,二维数组例程1,随机数填充 for (int row =
27、0; row matrix.length; row+) for (int column = 0; column matrixrow.length; column+) matrixrowcolumn = (int)(Math.random() * 100); 这里matrix是二维数组。过一遍二维数组必须用两层的循环,一般用i循环行,j循环列;如果英文好一点,可以用row循环行,column或者col循环列。,二维数组例程2,打印数组元素 for (int row = 0; row matrix.length; row+) for (int column = 0; column matrixro
28、w.length; column+) System.out.print(matrixrowcolumn + “ “);System.out.println(); ,二维数组例程3,数组求和 int total = 0; for (int row = 0; row matrix.length; row+) for (int column = 0; column matrixrow.length; column+) total += matrixrowcolumn; ,杨辉三角的例子,public class Yanghui public static void main(String args)
29、 int a=new int1010;for(int i=0;i10;i+) for(int j=0;j=i;j+) if(j=0|i=j) aij=1; if (i9) ai+1j+1=aij+aij+1; System.out.print(aij+“ “);System.out.println(); ,研究一下构造函数,创建对象,前面说到,对象必须创建才能使用,那么创建对象Point是不是只有一种方法呢? 默认的显然是只有一种,所以你只能这样创建p:Point p1 = new Point(); 但是实际生活里,我们必须有多种途径来创建对象,这样用起来才方便。例如你可能希望这样创建: Po
30、int p2 = new Point(1,2); 然后期待着p2的x和y成员分别是1和2,如何做到这一点呢?,什么是构造函数,可不可以换一种方式创建Point?当然可以,不过你要自己加构造函数,例如: class Point int x,y;Point(int xx, int yy) x=xx;y=yy; 注意上面那个函数,它有两大神奇之处: 没有定义返回值,连void都没有 名字和类的名字完全一样 这种写法奇怪的函数就是传说中的:构造函数,默认构造函数,现在你终于可以这样创建一个p了:Point p = new Point(1,2); 不过你现在反而不能这样创建一个p了: Point p =
31、 new Point(); why?,关于构造函数 I,构造函数和类的名字完全一样,并且没有返回值,它的作用是提供类的创建方式,所以,根据类必须先创建再使用的原则,构造函数是每一个类一定要具备的东西。 或许你注意到第一个版本的Point没有构造函数,是的,不过Java是这样聪明的一个语言,所以它会帮你补充一个不带参数,没有函数体的构造函数,例如Point类,它偷偷加的代码是:class Point int x,y;Point() /这一行尽管你没看见,但是它确实存在 ,关于构造函数 II,Java自动提供构造函数仅仅当你自己没有提供构造函数的时候才这么干,如果你提供了哪怕只有一个的构造函数,它
32、决不自作多情帮你加一个。所以第二个版本的Point最好改成下面: class Point int x,y;Point() /由于Java罢工,这个函数只好自己加 Point(int xx, int yy) x=xx;y=yy; ,关于成员变量对内篇,每一个类都可以有自己的成员变量,例如Point类的x和y;对于Point类的内部而言,x,y相当于全局变量;如果脱开Point类,那么就要看x,y是怎么声明的了。 class Point Point() x=10; y=10; Point(int xx, int yy) x=xx;y=yy;int x,y; ,x,y的作用范围 从这里开始,x,y的
33、作用范围 到这里结束,关于成员变量对外篇,成员变量有四种修饰,它们决定了外部访问成员变量的权限: private,default,protected、public 对于private,实际上是不允许外部访问的,所以如果你的Point这样定义: class Point private int x,y; 然后有 Point p = new Point(); 那么这两个语句是错的:p.x=10; p.y=10;,关于成员变量对外篇,如果这个时候你想从外面修改x,y的值怎么办?答案是:没法改!除非Point类另外提供函数给你用,例如: class Point private int x,y;publi
34、c int getX() return x; /这就是传说中的getterpublic void setX(int xx) x=xx; /传说中的setter 上帝保佑,现在你终于可以从外面改x的值了: Point p = new Point(); p.setX(100); System.out.print(p.getX();,关于成员函数 I,成员函数的访问权限和成员变量完全一样,所以这里不重复了。 成员函数的调用,也要通过对象的实例才能够调用(当然静态函数除外,后面再讲),不会忘记什么叫对象的实例了吧?Oh, my God! 还是加一个函数试试,例如我们给Point加一个成员函数:publ
35、ic double test(double s) return x*y*s;,关于成员函数 II,现在我们来调这个函数(这里是在外部调,如果在类的内部调,那就直接调用就好了) Point p = new Point(3,4); System.out.print(p.test(5.0); /通过p调用test函数 还是不太放心,演示一下对象内部怎么调test: class Point private int x,y;public double f() return test(5.0);public double test(double s) return x*y*s; ,关于成员函数 III,总
36、结一下,一个类的成员函数: 对于类的内部而言,是直接抓过来随便爱怎么调用都行(多大公无私啊),不需要借助任何东西; 对于类的外部而言,如果是private的成员函数,那显然是不能用的;如果遇到public的,那运气还不错,可以通过创建一个类的实例(也就是new一下),来间接调用这个成员函数。 成员变量的结论与此类似。 顺便说一下,在一个类的外部,每次new一个实例的时候,相当于调用一次构造函数;至于调用的是哪一个构造函数,那要看你给的参数了。,关于静态方法和静态成员 I,如果一个class有一个static的成员,那使用的时候要小心一点。给一个例子: class A public int x;
37、public static int y;public static void f() System.out.print(x+y); 还记得怎么调用f,怎么使用x,y么?很简单: A a = new A(); /看到这句不要晕掉,好多a. a.x = 10; a.y = 10; a.f(); /调用函数f,不要参数,关于静态方法和静态成员 II,下面来一段复杂的测试代码,测试A这个类: class Test public static void main(String args) A a = new A();a.x = 10; a.y = 10;A b = new A();b.x = 100;
38、b.y = 100;System.out.print(“a.x=“+a.x+“a.y=“+a.y+“b.x=“+b.x+“b.y=“+b.y); 运行上面程序,你会发现一个奇怪的现象: a.x不等于b.x,可是a.y居然等于b.y 前一半比较好理解,因为a和b本来就是不同的实例,虽然它们是一个模子造出来的;可是,那y怎么回事?,关于静态方法和静态成员 III,其实,静态成员是如此特殊的一个东西,因为它是所有实例共享的!也就是无论你new多少个实例出来,静态成员自始至终就是一个而已。而且,就算是你没有new过实例,它就已经存在了,神奇吧? 所以,java鼓励你这样直接使用静态成员: A.y =
39、100; A.f(); /注意这里没有实例噢,A亲自上阵了 所以,静态成员变量在java里一般被用作共享的常数,例如Math.PI,所以你可以常常看到final和static一起混的情形;静态成员函数一般被用作全局函数,供任意类调用,例如Math.sin(),再次啰唆一下类成员的访问控制,类成员总共有四种访问模式:private,protected,public以及default,它们的访问控制如下:确定访问控制权限的原则 基于安全性的考虑,在能满足设计要求的前提下,访问权限越严格越好,一个例子,class Root private int a;protected float b;public
40、 double c;char d;void fun() a+; b+; c+; d+; /类的内部,成员变量相当于全局/变量,在任何函数中均可直接用 假设有一个类的实例 Root r = new Root(); 那么 r.a+就是错误的,因为这相当于在类的外部使用变量; 但是 r.c+是正确的,因为c是public,支持外部访问。,关于封装的进一步探讨,Java的封装属性有四个(按开放程度排序): private,default,protected以及public。 一般来说,封装越严格越好,因此尽量不要将成员变量设置为public。 当把成员变量设置为private时,为了能够提供外部类对这
41、些成员变量的存取,一般要设置一堆的public的get方法和set方法。例如对hour成员: public int getHour() return hour; public void setHour(int h) hour = h; ,方法重载,方法重载是指 多个方法享有 相同的名字, 但是这些方法 的参数必须不 同,或者是参 数的个数不同,或者是参数类型不同。 返回类型不能 用来区分重载 的方法。,再次啰唆一下构造方法, 构造方法是一个特殊的方法。Java 中的每个类都有构造方法,用来初始化该类的一个对象。 构造方法具有和类名相同的名称,而且不返回任何数据类型。 重载经常用于构造方法。 构
42、造方法只能由new运算符调用。,关于构造方法以及new,要生成一个对象的实例,必须使用new运算符。每次new的时候,对象的构造方法会被自动调用。 构造的方法可能有很多个,new的时候调用哪一个取决于new的时候带的参数。 例如: MyTime t1=new MyTime(); /调用无参数的构造方法 MyTime t2=new MyTime(hour, second, minute); /调用使用三个参数的构造方法,注意三个参数的类型 /要和构造方法声明的参数类型保持一致,一个练习,写一个类Person表示一个人,用name和age表示其姓名和年龄,并提供以下三个构造函数:Person();
43、 /默认名字为 noname,年龄为0Person(String name); /年龄默认为0Person(String name, int age); 在main函数中使用以上三种构造方式创建3个Person对象,然后输出。,异常,异常和异常类型,异常是程序运行时发生的错误。当异常发生时,常常导致程序崩溃,从而丢失尚未保存的数据。 用户输入非法是异常的主要来源,例如:,异常是可以捕捉的,Java允许你自己去捕捉异常,从而避免程序崩溃。例如上例可以修改为:,异常处理(I),异常分类: 受检异常(编程时一定要检测的异常) 非受检异常 异常的层次:,异常处理(II),异常产生: 运行时(被动)产生
44、 主动抛出throw 异常处理捕获 try catch( ExceptionName1 e ) catch( ExceptionName2 e ) finally ,异常处理(III), try 捕获例外的第一步是用try选定捕获例外的范围,由try所限定的代码块中的语句在执行过程中可能会生成例外对象并抛弃。 catch 每个try代码块可以伴随一个或多个catch语句,用于处理try代码块中所生成的例外事件。catch语句只需要一个形式参数指明它所能够捕获的例外类型,这个类必须是Throwable的子类,运行时系统通过参数值把被抛弃的例外对象传递给catch块。,异常处理(IV), catc
45、h 语句的顺序: 捕获例外的顺序和catch语句的顺序有关,当捕获到一个例外时,剩下的catch语句就不再进行匹配。因此,在安排catch语句的顺序时,首先应该捕获最特殊的例外,然后再逐渐一般化。也就是一般先安排子类,再安排父类。 finally 捕获例外的最后一步是通过finally语句为例外处理提供一个统一的出口,使得在控制流转到程序的其它部分以前,能够对程序的状态作统一的管理。不论在try代码块中是否发生了异常事件,finally块中的语句都会被执行。,异常处理(V),当你不希望自己处理异常时,可以尝试将其抛出,让上一级代码去catch这个棘手的问题;当然如果整个程序都没有能catch到
46、这个异常,那这个程序只好死定了 抛出异常的两种做法 让方法抛出异常,方法内不处理 public static void main(String args) throws IOException,IndexOutOfBoundsException 主动抛出 IOException e=new IOException(); throw e;,受检异常的例子,Java在控制台下的输入: 注意要 import java.io.*; BufferedReader in=new BufferedReader(new InputStreamReader(System.in); try String data
47、 = in.readLine(); catch (IOException e) 这里如果没有trycatch,那么编译不能通过,非受检异常的例子,字符串转成整数 这里假设s是String类型,并且由用户输入: int i; try i = Integer.parseInt(s); catch(Exception e)e.printStackTrace(); 这里不要trycatch也可以,但是当用户输入非法的时候,你的程序只好自求多福了,关于异常的总结,其实现在你只要知道如何捕捉一切异常就可以,其它的高级主题等你长大了就慢慢明白了:) try /把可能引发异常的语句统统放在这里, /只要不超出try范围就可以。 catch(Exception e)/异常产生后,java会自动转向这里,如果 /你不知道此时该做点什么,放空好了。 ,包,包,包的作用类似于C的函数库,但是C的函数库很容易出现重名的问题,包在一定程度上解决了这个问题 一个包通常包含很多个功能相近的类 导入包 import 包名.类名; /导入这个包的特定类 import 包名.*; /导入这个包的所有类 注意包之间没有嵌套关系,例如java.awt和java.awt.geom是两个完全独立的包 创建包 package 包名; /此句必须是源文件的第一条语句,