1 / 76

接口与包

接口与包. 什么是包( package ). 包的作用类似于 C 的函数库,但是 C 的函数库很容易出现重名的问题,包在一定程度上解决了这个问题。 一个包通常包含很多个功能相近的类。 JDK 里面已经包含了 N 个功能强大的包,所以当你要实现一个功能,首先记得去查一下 JDK 的文档,看看有否对应功能的包。 编程语言的优劣都是相对而言的,大家觉得 Java 功能很强大,其实是因为 SUN 公司的 JDK 免费赠送了众多功能强大的包。. 包- 1. 包的命名 SUN 公司建议用域名的逆序,因为域名是唯一的。如: cn.edu.xmu 包的结构

loring
Download Presentation

接口与包

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 接口与包

  2. 什么是包( package ) • 包的作用类似于C的函数库,但是C的函数库很容易出现重名的问题,包在一定程度上解决了这个问题。 • 一个包通常包含很多个功能相近的类。 • JDK里面已经包含了N个功能强大的包,所以当你要实现一个功能,首先记得去查一下JDK的文档,看看有否对应功能的包。 • 编程语言的优劣都是相对而言的,大家觉得Java功能很强大,其实是因为SUN公司的JDK免费赠送了众多功能强大的包。

  3. 包-1 • 包的命名 • SUN公司建议用域名的逆序,因为域名是唯一的。如:cn.edu.xmu • 包的结构 • 一个包被映射为一个目录,包里面的每个类,则映射到目录中的某一个文件,如:com.prenhall.mypackage这个包被展开后,就是

  4. 包-2 • 打包 • package packagename; • 上面这一行一般出现在Java源文件的第一行,表示这个文件的所有类,都将被打包到packagename中。 • 不同源文件的packagename可以相同,Java会根据packagename将相同包的class压缩在一起,生成一个扩展名为jar的包。 • 顺便说一下,jar包其实是zip格式的压缩文件

  5. 包-3 • 使用包 • 要使用一个包里的某个类,必须首先导入 • import javax.swing.JOptionPane; • 如果要使用一个包里的多个类,用这个 • import javax.swing.*; • *表示导入这个包的所有类 • 注意包之间没有嵌套关系,例如java.awt和java.awt.geom是两个完全独立的包。这个其实很好理解,因为java.awt这个包,仅仅包含java/awt这个目录下的class文件,而java/awt/geom则是另外一个目录了。

  6. 包的命名规范 • 包的名称一般全部采用小写 • 包名的前缀一般是域名单词序列的逆序 • 实例 • com.sun.eng • com.apple.quicktime • edu.cmu.cs.bovik.cheese

  7. 文件的命名规范 • 必须与该文件中public类的类名同名 • 后缀必须是 .java

  8. 类/接口的命名规范 • 类名一般是名词/名词词组:每个单词的首字母大写,其它字母小写 • 类名应当尽量简单,而且其含义能够尽量准确地刻画该类的含义 • 一般采用全称—尽量少用缩写词 (除非该缩写词被广泛使用) • 实例 • Clock • Time • ImageSprite

  9. 方法的命名规范 • 方法名一般是 • 动词/动词性词组 • 首字母小写 • 中间单词的首字母大写,其它字母小写 • 尽量应用简单的、常用的单词 • 实例 • run( ); • getBackground( ); • getTime( );

  10. 变量的命名规范 • 变量名的首字母小写 • 中间单词的首字母大写,其它字母小写 • 变量名的首字母尽量不要用字符: _或$ • 变量名应当简短、有含义、且便于记忆 • 变量名常常由表示其所扮演的角色与数据类型组成 • 实例 • int i; char c; double widthBox; • Point startingPoint, centerPoint; • Name loginName;

  11. 常量的命名规范 • 常量名一般全大写,单词之间用下划线分隔 (“_”) • 实例: • static final int MIN_WIDTH = 4; • static final int MAX_WIDTH = 999;

  12. 源文件编辑 • 尽量不要用TAB排版 • 行数/每行的字符数不宜过多 • 缩排方式(Indentation) • 多用空白符/行 • 友情提示:通常Java的开发环境都会提供源代码的格式化/重排功能,如果使用记事本编辑源代码,建议去下载一个叫astyle的源代码重排工具(DOS命令行工具)。UltraEdit有捆绑这个工具,并提供了GUI界面的调用方式。

  13. 文件组织 • 源程序文件一般采用如下的组织顺序: • 最开始一般是注释 • package 和 import 语句 • 类和接口的定义

  14. 搞一个复杂一点的例子先热身 • 我们想做一个类Circle表示平面直角坐标系上的一个圆(解析几何还记得吧...)。 • 然后我们想做一个求面积的成员函数。 • 我们还要假定所有的Circle的成员变量都是private的,所有的成员函数都是public的,下面开始动手设计这个类。。。

  15. 操作步骤 1 • 先搭一个空架子总是没有问题的: class Circle{ }

  16. 操作步骤 2 • 设计一下成员变量,这里三个足够了: class Circle{ private double r; //r是半径 private double x,y; //(x,y)是圆心坐标 }

  17. 操作步骤 3 • 然后是构造函数,这里先搞两个: class Circle{ private double r; private double x,y; public Circle() { x=0; y=0; r=1; } public Circle(double x, double y, double r) { this.x=x; this.y=y; this.r=r; //一不小心用到了this,阿门,this是啥知道不? } }

  18. 操作步骤 4 • 然后是面积函数: class Circle{ private double r; private double x,y; public Circle() { x=0; y=0; r=1; } public Circle(double x, double y, double r) { this.x=x; this.y=y; this.r=r; } public double getArea() { return (Math.PI*r*r); } }

  19. Eclipse测试程序 • 综合一下以上的所有步骤: • 先建一个工程,名字随意,再建一个类,名字叫Circle,然后把Circle的代码贴上; • 再建一个类,名字随意,例如叫Test,勾上自动生成main函数的选项; • 现在你的工程里应该有两个java源文件,两个类,其中一个是用来测试的类,所以它有main函数,Circle类由于没有main函数,所有它是不能运行的。因此启动运行的主类是Test。

  20. Test类的写法 public class Test{ public static void main(String args[]) { Circlec1 = new Circle(); System.out.println(c1.getArea()); Circlec2 = new Circle(3.0,4.0,5.0); System.out.println(c2.getArea()); } }

  21. Circle的改进 • Circle类显然不完善,例如你在Test类里,如果想知道c1的x,y,r三个成员分别是多少,可能吗?如果你想在Test类里,随时修改c1的半径,可能吗? • 答案显然不行,所以,Circle其实还有很多的成员函数需要添加。 • 这里就不演示了,自己动手试试。。。

  22. 研究一下对象的引用 • 一个类的成员函数(static除外),是不是一定要new才能被使用呢?例如GUI编程中经常有这种语句: • Container container = getContentPane(); container.add(new Button("hello button")); • 这个container好像就没有new对吧?怎么就可以抓来用呢?难道随便get一下也能用? • 其实这个container已经被new过了,只是你没有看见而已。get一下相当于做对象的引用。这么说好像很复杂,看一个例子:

  23. 对象的引用 I 这个例子的输出是:a.x=1 b.x=10 a.x=10 a.x=100 b.x=100 • class A{ • public int x; • } • public class Test{ • public static void main(String args[]) { • A a = new A(); • a.x = 1; System.out.println("a.x="+a.x); • A b = a; //注意这里b不是new出来的 • b.x = 10; System.out.println("b.x="+b.x); • System.out.println("a.x="+a.x); • a.x = 100; System.out.println("a.x="+a.x); • System.out.println("b.x="+b.x); • } • }

  24. 对象的引用 II • 总结一下,如果一个对象不是new出来的(例子中的b),而是直接用一个已经new出来的对象(例子中的a)给它赋值,那么这个对象b就叫做a的引用。 • 引用的对象之所以可以使用,不需要new,其实是因为它所指向的对象和赋值给它的对象是同一个!!!也就是它只是给原来的对象又起了一个名字而已。 • 顺便再提一次,对象作为函数参数的时候,实参都是通过引用传递的方式传给形参的。

  25. 再谈多态性

  26. 当继承发生的时候,多态性就来了 • 运行时多态性是面向对象程序设计代码重用的一个最强大机制。 • 动态性的概念也可以被说成“一个接口,多个方法”。 • Java实现运行时多态性的基础是动态方法调度,它是一种在运行时而不是在编译期调用重载方法的机制。 • 顺带提一下,静态的多态性就是函数重载。

  27. 方法覆盖 • 当子类和超类定义了同样的函数时,就产生了方法覆盖,如: public class Circle extends GeometricObject { /** Override the toString method defined in GeometricObject */ public String toString() { return super.toString() + "\nradius is " + radius; } } • 当覆盖发生时,在子类中,超类中的函数会被自动屏蔽;如果非要调用超类的函数,只能借助于super关键字。

  28. 覆盖和重载是不同的 • 下图中的函数p,左边是覆盖,右边是重载 • 覆盖的特征:函数名相同,参数列表相同 • 重载的特征:函数名相同,参数列表不同

  29. 来一个例子 class A{ protected int x; public void f() { x--; } } class B extends A{ int x; void f() {//覆盖了超类的f() this.x++; //这里用的是自己的x,也可以直接写x++ super.x = 100; //这里用的是超类继承来的x super.f(); //这里用的是超类继承来的f() } void f(int i) { //重载了继承来的函数f(),注意二者的参数列表不同 i++; } }

  30. 有个复杂点的例子 class A { void fun() { System.out.println(“A”); } } class B extends A { void fun() { System.out.println(“B”); } } class C extends B { void fun() { System.out.println(“C”); } } class Test { public static void main(String[] args) { A a; B b = new B(); C c = new C(); a=b; a.fun(); // 此处将输出Ba=c; a.fun(); // 此处将输出C } }

  31. 解释一下 • Java 的动态多态性这种机制遵循一个原则:当超类(父类)对象的引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法; • 当然,这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。你可能会问,如果这个方法没有被覆盖呢?Good question……

  32. 还有个例子 class A { void fun() { System.out.println(“A”); } } class B extends A { void funB() { System.out.println(“B”); } } class C extends B { void funC() { System.out.println(“C”); } } class Test { public static void main(String[] args) { A a; B b = new B(); C c = new C(); a=b; a.fun(); // 此处将输出Aa=c; a.fun(); // 此处将输出A } }

  33. 从编程角度来看抽象类

  34. 抽象类 • 假设我们用GeometricObject这个类作为几何形状的基类,以便可以在GeometricObject的基础上派生出Circle类、Rectangle类等等具体的几何图形。 • 显然,GeometricObject这个类应该包含几何形状的一些共性:颜色、是否填充、创建日期、toString()函数、可以被求周长、可以被求面积…… • 下面我们来设计这个类:

  35. 面积和周长的困惑 • 颜色、是否填充、创建日期等函数不难写出相应的Java代码。但是这里有两个函数是没法写代码的,这就是面积和周长函数。这是因为面积和周长跟具体形状有关,在没有具体的形状之前,这两个属性没法计算。 • 现在的问题是,作为一个基类GeometricObject,如果没有设计getArea和getPerimeter函数,显然属于重大设计失误。但是这个函数又确实没法写,怎么办呢?Java说,好办,你可以把那两个函数做成抽象函数嘛。

  36. 抽象函数 • 抽象函数是一种特殊函数,它只定义了函数原型,但是不需要实现这个函数。抽象函数用关键字abstract修饰。 • 包含抽象函数的类叫做抽象类,也必须用abstract修饰。 • 我们现在知道,抽象类只是一个半成品,所以Java中,抽象类不可以被new出来。

  37. GeometricObject类的部分代码 1 public abstract class GeometricObject { 2 private String color = "white"; 3 private boolean filled; 4 private java.util.Date dateCreated; 5 6 /** Construct a default geometric object */ 7 protected GeometricObject() { 8 dateCreated = new java.util.Date(); 9 } ... 43 /** Abstract method getArea */ 44 public abstract double getArea(); 45 46 /** Abstract method getPerimeter */ 47 public abstract double getPerimeter(); 48 } 注意这两个抽象函数是没有函数体的。

  38. 抽象类的派生类 • 刚才看到,抽象类有几个函数其实是没有实现的;于是Java规定,凡是抽象类的派生类,一定要完成父类的未竟事业。 • 例如从GeometricObject类派生两个类,一个叫做Circle,一个叫做Rectangle。那么这两个类都一定要覆盖(实现)getArea和getPerimeter函数。换句话说,这两个类可以没有其它成员,但是一定要实现这两个成员函数。

  39. Circle类的部分代码 3 public class Circle extends GeometricObject { 4 private double radius; ... 23 /** Return area */ 24 public double getArea() { 25 return radius * radius * Math.PI; 26 } ... 33 /** Return perimeter */ 34 public double getPerimeter() { 35 return 2 * radius * Math.PI; 36 } ... 43 } 注意这两个GeometricObject的抽象函数在这里一定要被实现。

  40. Rectangle类的部分代码 3 public class Rectangle extends GeometricObject { 4 private double width; 5 private double height; ... 35 /** Return area */ 36 public double getArea() { 37 return width * height; 38 } 39 40 /** Return perimeter */ 41 public double getPerimeter() { 42 return 2 * (width + height); 43 } 44 } 注意这两个GeometricObject的抽象函数在这里一定要被实现。

  41. 抽象类小结 • 抽象函数只能在抽象类中出现,这些抽象函数在派生类中一定要被实现。 • 抽象类不能被new出来。 • 抽象类可以没有抽象函数,但是即便如此,它也不能被new出来。 • 即使父类不是抽象类,子类也可以定义成抽象类。 • 父类中的非抽象函数,可以被子类覆盖,并修改成抽象函数。 • 抽象类不能被new,但是抽象类是一个合法的数据类型。如: GeometricObject[] objects = new GeometricObject[10];

  42. 一个测试程序 1 public class TestGeometricObject { 2 /** Main method */ 3 public static void main(String[] args) { 4 // Declare and initialize two geometric objects 5 GeometricObject geoObject1 = new Circle(5); 6 GeometricObject geoObject2 = new Rectangle(5, 3); 7 8 System.out.println("The two objects have the same area? " + 9 equalArea(geoObject1, geoObject2)); 10 11 // Display circle 12 displayGeometricObject(geoObject1); 13 14 // Display rectangle 15 displayGeometricObject(geoObject2); 16 } 17 18 /** A method for comparing the areas of two geometric objects */ 19 public static boolean equalArea(GeometricObject object1, 20 GeometricObject object2) { 21 return object1.getArea() == object2.getArea(); 22 } 23 24 /** A method for displaying a geometric object */ 25 public static void displayGeometricObject(GeometricObject object) { 26 System.out.println(); 27 System.out.println("The area is " + object.getArea()); 28 System.out.println("The perimeter is " + object.getPerimeter()); 29 } 30 }

  43. 特殊抽象类:接口

  44. 接口-1 • 当一个抽象类没有成员变量,并且其全部函数都被做成抽象函数时,Java会建议你使用interface这个关键字来替换class。所以,本质上,接口其实是一个抽象类,它的全部函数都是抽象函数并且没有成员变量(可以有静态常量)。 • 接口由于不带有成员变量,所以它特别适合用来描述一种通用的操作。例如同一个类的两个实例A、B比较大小这个操作,无论A、B是什么类的实例,你都可以定义A>B时,取正值;A=B时,取零;A<B时,取负值。因此比较大小就特别适合被设计成接口。

  45. 接口-2 • 设计接口 public interface Comparable { public int compareTo(Object o); } • 上面的interface约等于abstract class。 • 这里的函数compareTo是抽象函数,所以Comparable并不需要把这个函数具体实现(当然也没法实现)。

  46. 接口-3 • 我们知道,抽象类在被继承时,派生类必须实现所有的抽象函数。类似的规定在接口上也是如此。 • 接口当然不能被继承,但是它可以被实现implements。当一个接口被实现时,所有它定义的函数都必须被实现(也就是新类必须覆盖这些函数)。 • 由于对象基本上都有可比性,其实很多Java类其实都实现了Comparable接口。

  47. 接口-4 • String类和Date类的部分源代码: • 可见String的不同引用之间是可比的。注意下面的取值都是true,所以很大程度上,实现一个接口,跟继承一个类,没有什么区别。

  48. 接口-5 • 实现一个接口,下面是一个可比较大小的矩形类: 1 public class ComparableRectangle extends Rectangle 2 implements Comparable { 3 /** Construct a ComparableRectangle with specified properties */ 4 public ComparableRectangle(double width, double height) { 5 super(width, height); 6 } 7 8 /** Implement the compareTo method defined in Comparable */ 9 public int compareTo(Object o) { 10 if (getArea() > ((ComparableRectangle)o).getArea()) 11 return 1; 12 else if (getArea() < ((ComparableRectangle)o).getArea()) 13 return -1; 14 else 15 return 0; 16 } 17 }

  49. 接口-6 • 上例中,ComparableRectangle类继承了Rectangle类,并实现了Comparable接口。 • 在Java中,由于仅仅支持单一继承,所以每次只能extends一个类(这样就大大限制了OOP的功能);为了能够实现C++中多重继承的功能,Java允许你一次实现多个接口。

  50. 抽象类与接口小结

More Related