• 1. 第五章 继承、接口与泛型李林国 2009.11.1
  • 2. 5.1 子类与父类——继承继承(Inheritance)是两个类之间的一种泛化关系(一般->特殊关系),是一种由已有的类创建新类的机制。利用继承,可以先创建一个拥有共同属性的一般类,根据该一般类再创建具有特殊属性的新类。 由继承而得到的类称为子类(Subclass),被直接或间接继承的类称为父类(Father Class),或叫超类(Superclass)或基类(Base Class)。子类继承父类的状态和行为,同时也可以修改父类的状态或重写父类的行为,并添加新的状态和行为。
  • 3. Java仅支持单继承,而不支持多重继承,即每个子类只允许有一个父类,而不允许有多个父类。但是可以从一个父类中生成若干个子类。 继承不改变成员的访问权限,父类中的公有成员、保护成员和默认成员,在子类中仍然是公有成员、保护成员和默认成员。Java中的多继承将通过接口方式来实现。
  • 4. 5.1.1 创建子类在类的声明中加入extends子句来创建一个类的子类: class <子类名> [extends父类名] { //只允许一个直接父类 [子类中的新增成员] //子类体 } 说明: ①关键字class用来定义类,关键字extends用来指出该类的直接父类,若没有extends子句,则表示该类的默认父类为java.lang.Object类。 ②若“父类”又是某个类的子类,则“子类”同时也是该类的(间接)子类。 ③“子类”可以继承“父类”中访问权限设定为public、protected和default的成员变量和成员方法,但是不能继承访问权限为private的成员变量和成员方法。例如:
  • 5. 5.1.1 创建子类(续一)class A { // 类A继承类java.lang.Object int a1 = 10; private int a2 = 20; public int getA2() { return a2; } } class B extends A {// 类B继承类A,间接继承类 //java.lang.Object // 类B继承父类A的非私有成员:a1和getA2(),而私有成员a2不能被继承 int b1 = 100; private int b2 = 200; public int getB2() { return b2; } }
  • 6. 5.1.2 继承的传递性类的继承是可以传递的。 类继承的传递性是指若类B继承类A,类C又继承类B,则类C应包含类A和类B的非私有成员,以及类C自身的成员。
  • 7. 5.2 子类的继承性由于子类受到父类成员的访问控制符和包的限制,子类并不能继承父类中所有的成员变量和成员方法。public 成员protected 成员默认 成员private 成员父子类在相同包中(同包)○○○父子类在不同包中(跨包)○○是否继承成员控制符包域表4-3 子类继承父类的规则注:①父类的构造方法不能被子类继承,只能通过super( )来调用。 ②父类中成员的访问权限在子类中是不变的。
  • 8. 1.父子类在同一包中的继承规则若子类和父类在同一个包中,则子类继承父类中的public、protected和默认成员(成员变量和成员方法),将其作为子类的成员,但不能继承父类的private成员。 P81 例5-1
  • 9. 2.父子类在不同包中的继承规则(续一)若子类和父类不在同一个包中,则子类继承了父类中的public和protected成员变量和成员方法,将其作为子类的成员,但不能继承父类的默认和private成员。
  • 10. 5.3 子类对象的构造过程由于子类中包含有父类的非私有成员。在创建子类对象时,不仅要对自身成员变量初始化,而且还要对继承的父类中成员变量初始化,但是只有继承的一部分子类对它才有访问权限。 对象的初始化是通过调用构造方法来实现的。 构造方法不能被继承,但可以在子类的构造方法中调用父类的构造方法。 为了方便使用,系统规定用this()表示当前类的构造方法,用super()表示直接父类的构造方法。 除Object类没有父类外,其他类的构造方法中应包含自身类的构造语句和直接父类的构造方法(如果没有显示调用,则使用父类的不带参数构造方法)。 课本p82 例5-2
  • 11. 构造方法的特性①自动提供默认构造方法:若一个类中没有定义任何构造方法时,则系统自动提供一个没有参数的默认构造方法;若类中已定义任何构造方法,则系统将不提供默认构造方法。 ②允许构造方法重载:一个类中可根据需要定义多个重载的构造方法,不同构造方法的选择是根据参数的个数、类型和顺序进行匹配。 ③支持this()和super():在构造方法定义中,可使用this()来调用本类的其他构造方法,使用super()来调用父类的构造方法。若this和super都不用时,系统会自动调用super(),即父类的默认构造方法。 ④调用this()或super()的构造方法的语句必须放在第一条语句,并且this()和super()最多只可调用其中一条。
  • 12. 5.4 成员变量的隐藏和方法的重写5.4.1 成员变量的隐藏 子类可以隐藏继承的成员变量:子类中定义的成员变量与父类中的成员变量同名时(不必类型相同),子类就隐藏了继承的成员变量,。 课本p83 例5-3
  • 13. 5.4.2 多态性(方法的重写)多态性(Polymorphism)是面向对象方法的三个重要特性之一。多态是指在一个程序中相同的名字可以表示不同的实现。Java的多态性主要表现在三个方面: ①方法重载:指可在一个类中定义多个名字相同而实现不同的成员方法,它是一种静态多态性,或称编译时多态; ②方法覆盖(重写):即子类可以隐藏与父类中的同名成员方法,它是一种动态多态性,或称运行时多态; ③变量覆盖:即子类可以隐藏与父类中的同名成员变量。 多态性大大提高了程序的抽象性和简洁性。从静态与动态的角度可将多态分为编译时多态(静态多态)和运行时多态(动态多态)。
  • 14. 多态性的概念(1)编译时多态(静态多态) 编译时多态是指在编译阶段,编译器根据实参的不同来静态确定具体调用相应的方法,Java中的方法重载属于静态多态。 (2)运行时多态(动态多态) 运行时多态是指运行时系统能根据对象状态不同来调用其相应的成员方法,即动态绑定。Java中的方法覆盖属于动态多态。 由于子类继承了父类的非私有属性,所以子类对象可以作为父类对象使用(即上转型对象)。程序中凡是使用父类对象的地方,都可以用子类对象来代替。可以通过引用子类的实例来调用子类的方法。
  • 15. 方法重载方法重载(Method Overloading)是指Java 的同一个类中的两个或两个以上的方法可以使用同一个名字,而它们的参数声明(个数、类型和顺序)不同。 例如:add( )方法可能有以下三个版本: public int add(int x, int y) { return x + y; } public double add(double x, double y) { return x + y; // 参数类型不同重载 } public double add(double x, double y, double z) { return x + y + z; // 参数个数不同重载 }
  • 16. 方法重载(续一)重载方法并存于程序中,当一个重载方法被调用时,javac编译器会根据实参的个数、类型和顺序来表明实际调用的重载方法的版本。 因此,每个重载方法的参数个数、类型和顺序必须是不同的。虽然每个重载方法可以有不同的返回类型,但返回类型并不足以区分所使用的是哪个方法。 方法重载属于Java的静态多态,当Java在编译阶段调用一个重载方法时,形参与调用实参匹配的方法才被调用。 方法重载的目的不是代码复用,它可以提高成员方法的抽象程度,对外提供一致简洁的使用接口。 课本p84 例5-4
  • 17. 构造方法重载构造方法重载是指Java的同一个类可以有多个名字与类名相同,但参数不同的构造方法。
  • 18. 方法覆盖方法覆盖(Method Override)是指子类对父类中相同方法头的方法重新定义,又称为方法重写。这时子类和父类具有方法头(包括修饰符、方法名、参数表和返回值类型)相同,但方法体不同的同名方法。 方法重写的原则是: ①改写后的方法不能比被重写的方法有更严格的访问权限。 ②改写后的方法不能比被重写的方法产生更多的异常。 调用重写方法的原则是: ①对子类的一个实例,若子类重写了父类的方法,则运行时系统调用子类的方法; ②对子类的一个实例,若子类继承了父类的方法(未重写),则运行时系统调用父类的方法。
  • 19. 方法覆盖(续一)调用重写方法的格式是: ①一般地,在子类中通过“方法名”所调用的成员方法包括子类中的重写方法、继承自父类的方法(未重写)和子类新增的方法三种。 ②若要访问父类中被覆盖的成员方法,则必须使用super关键字来表示父类对象,其一般调用格式是: super.<方法名>([实参表]); 通过方法覆盖,可以在子类中修改父类中同名方法的功能。方法覆盖实现了在运行时的多态性,通过运行时的对象状态来调用其相应的成员方法,从而提高程序的简洁性和灵活性。 课本p85 例5-5
  • 20. 方法覆盖与方法重载的异同(1)相同点: ①二者都要求方法名相同; ②二者都可以用于抽象方法和非抽象方法。 (2)不同点: ①方法覆盖要求参数签名必须一致,而方法重载要求参数签名必须不一致; ②方法覆盖只能用于子类覆盖父类的方法,方法重载用于同一个类的所有方法(包括从父类中继承而来的方法); ③方法覆盖对方法的访问权限和抛出的异常有特殊的要求,而方法重载在这方面没有任何限制; ④方法覆盖要求返回类型必须一致,而方法重载对此不做限制; ⑤父类的一个方法只能被子类覆盖一次,而一个方法在所在的类中可以被重载多次。
  • 21. 变量覆盖变量覆盖(Variable Override)是指在子类中对从父类继承过来的成员变量重新定义,即变量名相同而其类型相同或不同,又称为成员变量的隐藏。 成员变量覆盖的处理规则是: ①在子类中将继承父类的同名变量“隐藏”了。 ②若子类执行继承父类的操作,则使用继承自父类的变量;若子类执行自己定义的操作时,则使用自己定义的变量。 若在子类中引用父类中继承的变量,则可使用super关键字来表示父类对象,其一般格式是: super.<父类变量名> ; 1.使用super调用父类的构造方法;例 5-6 2.使用super操作被隐藏的成员变量和方法;例 5-7
  • 22. 5.5 上转型与下转型对象1.子类对象和父类对象之间的转换 Java允许父类对象和子类对象在一定条件下相互之间转换。其相互转换规则是: ①隐式(自动)转换:子类对象直接赋值给父类对象时,子类对象可自动转换为父类对象,称该父类对象是子类对象的上转型对象; ②强制转换:父类对象赋值给子类对象时,必须将父类对象强制转换为子类对象,称该子类对象是父类对象的下转型对象; ③没有继承关系的类类型,即不在继承树的同一个继承分支上,则不允许进行类型转换。
  • 23. 2.上转型对象设类A是类B的父类,若有: A a; B b=new B(); a=b; //a是b的上转型对象(子类对象可自动转型成父类对象) 则称父类对象a是子类对象b的上转型对象。若有: B b; A a=new A(); b = (B)a; //b是a的下转型对象(父类对象须强制转型成子类对象) 则称子类对象b是父类对象a的下转型对象。
  • 24. 2.上转型对象(续一)对象的上转型对象的实体由子类负责创建,上转型对象会失去原对象的一些属性和功能。上转型对象的特性是: ①上转型对象不能操作子类新增的成员变量和成员方法。 ②上转型对象可以操作子类继承或重写的成员变量和成员方法。 ③若子类重写了父类的某个方法,则上转型对象调用该方法时一定是调用被子类重写的方法。 ④若子类隐藏了父类的某个变量,则上转型对象访问该变量时,访问的是父类中被隐藏的变量。 ⑤可以将对象的上转型对象再强制转换成子类对象,则该子类对象又具备了子类所给的所有属性和功能。 课本p89 例5-8
  • 25. 3.对象引用变量的动态绑定引用变量是指类的对象的引用型变量,其绑定规则如下: (1)对于一个引用类型的变量,Java编译器按照它声明的类型来处理. (2)对于一个引用类型的变量,运行时Java虚拟机按照它实际引用的对象来处理。例如下转型对象的引用虽然编译可以通过,但运行时会抛出ClassCastException运行时异常。 (3)在运行时环境中,通过引用类型变量来访问所引用对象的方法和属性时,Java虚拟机采用以下绑定规则: ①实例方法与引用变量实际引用的对象的方法绑定,这种绑定属于动态绑定,因为是在运行时由Java虚拟机动态决定的。 ②静态方法与引用变量所声明的类型的方法绑定,这种绑定属于静态绑定,因为实际上是在编译阶段就已经做了绑定。 ③成员变量(包括静态变量和实例变量)与引用变量所声明的类型的成员变量绑定,这种绑定属于静态绑定。因为实际上是在编译阶段就已经做了绑定。
  • 26. 5.6 继承的使用方法(1)继承树的层次不宜太多,尽量保持在两到三层(不包括顶层的Object类),以降低复杂度,提高可扩展性。 (2)继承树的上层应尽可能为抽象层(如接口和抽象类)。 (3)使用一棵继承树上的类时,尽可能把引用变量声明为继承树的上层类型,以提高系统之间的松耦合。若继承树上有接口类型,则尽可能把引用变量声明为继承树上层的接口类型。
  • 27. 5.6 继承的使用方法(续一)(4) 精心设计用于被继承的类 ①提供良好的文档说明,以便开发人员正确安全地创建该类的子类。 ②尽可能封装父类的实现细节,而把代表实现细节的属性和方法定义为private类型。 ③将不允许子类覆盖的方法定义为final方法。 ④父类的构造方法中不允许调用可被子类覆盖的方法,以防运行时出错。 ⑤若不是专门为了继承而设计的类,随意继承它是不安全的,可以采取以下两种措施来禁止继承:把类声明为final类;把此类的所有构造方法声明为private类型,然后通过一些静态方法来负责构造自身的实例。 继承与多态:例5-9
  • 28. 5.7 继承与组合区别组合关系和继承关系相比,前者最主要的优势是不会破坏封装。 若类A与类C之间为组合关系,由类C封装实现,则其仅向类C提供接口;若类A与类C之间为继承关系,则类C会向类A暴露部分实现细节。组合关系虽然不会比继承关系减少编码量,但组合关系会降低系统的耦合性,提高了系统的可维护性。 组合关系的缺点是比继承关系要创建更多的对象。 对于组合关系,创建整体类的实例时,必须创建其局部类的实例;而对于继承关系,创建子类的实例时,无需创建父类的实例。
  • 29. 5.8 抽象类与抽象方法抽象类是Java支持多态性的一种重要机制,通过抽象类可以统一处理一个类层次中的同名方法。 在很多实际应用中,类层次的顶层类并不具备下层子类的一些功能实现,而将这些方法声明为抽象方法,在Java中含有抽象方法的类就叫抽象类。 抽象类和抽象方法是被abstract修饰的类和方法。
  • 30. 5.8.1 抽象类其定义方法如下: abstract class <类名>{ …… //类体 } 抽象类的特点是不能被实例化,只是一种概念类。 使用抽象类的优点是可以充分利用它所具有的公共属性来提高程序的开发效率。 抽象类一定有它的子类,而子类通常不是抽象类,若其子类是抽象类,则该抽象类一定会有不是抽象类的子类。抽象类与其子类的关系是继承关系。 虽然抽象类不能定义对象,但是抽象类可以有构造方法,并能被其子类所调用。
  • 31. 5.8.1 抽象类(续一)使用抽象类注意以下四点: ①abstract抽象类不能创建对象,必须通过子类继承后,由子类来创建对象,即不能使用new操作符。 ②abstract抽象类中,可以没有抽象方法,也可以有一个或多个抽象方法。 ③若一个类中含有abstract抽象方法,则该类必须被声明为抽象类。 ④abstract抽象类不能被同时声明为final类。
  • 32. 5.8.2 抽象方法抽象方法(Abstract Method)是指被abstract修饰的成员方法。抽象方法是一种仅有方法头,而无方法体的一种特殊方法。抽象方法不能实现任何操作,而只能作为所有子类中重载该方法的一个统一接口。抽象方法定义的一般格式是: abstract class <类名> { //抽象类 abstract <返回类型> <方法名>([<参数表>]) ; //抽象方法 } 在该定义中,使用了一个分号来替代方法体。 存在抽象方法的类一定是抽象类,但抽象类不一定存在抽象方法。子类继承了父类的抽象方法后,使用不同的实现(即方法体)来重载它。于是便形成了名字、返回值类型和参数表都相同,而方法体不同的覆盖方法。
  • 33. 5.8.2 抽象方法(续一)使用抽象方法的好处是: 抽象方法在不同类中覆盖,隐藏具体的实现细节信息,在程序中只需给出抽象方法名,而不必知道具体调用哪个方法。 抽象方法对外提供了一个共同的接口,该抽象类的所有子类都可以使用该接口来实现该功能。 使用抽象方法要注意以下两点: ①abstract抽象方法只允许方法声明,不允许实现; ②abstract抽象方法不能被同时声明为final方法。 抽象类的应用举例:例5-10
  • 34. 5.9 接口接口(Interface)是Java中支持多态性的另一种重要机制。接口允许多继承,是对类继承的扩充。 一方面引进接口是为了弥补Java不支持多继承带来的不足,由于Java只支持单重继承而不支持多重继承,其类层次结构是树状结构,降低了复杂性,为了解决现实世界中存在的多继承问题,引进接口则可以实现类似于多重继承的网状层次结构。 另一方面,从软件工程的角度看,引进接口可有效地将软件设计和实现分离,使开发者分工明确,提高了软件质量,在面向对象程序设计语言的发展中具有里程碑意义。
  • 35. 5.9.1 接口的概念Java中的接口有两种意思: ①概念性接口,指系统对外提供的所有服务,类的所有能被外部使用者访问的方法构成了类的接口。 ②接口类型:指用interface关键字定义的接口,它明确地描述了系统对外提供的所有服务,清晰地把系统的实现细节与接口分离。 Java中的接口类型(简称接口)实际上是由常量和抽象方法构成的特殊类。 在接口定义中,仅规定了实现某些功能的对外接口和规范,但是不包含具体的实现,这些功能是在“实现”了该接口的各个类中完成的,在这些类中具体定义了接口中各个抽象方法的方法体。
  • 36. 5.9.2 接口的定义接口由接口首部和接口体两部分构成,定义接口的一般格式: [public] interface <接口名> [extends <父接口名列表> ] { [public][static][final]<类型><常量名>=<常量值>; //常量说明 [public][abstract][native]<类型><方法名>([参数表]) [throws 异常列表]; //抽象方法说明 } 其中,interface是定义接口的关键字。接口名的首字母大写,若多个单词组成接口名则每个单词的首字母大写。 1) 一个接口可以继承若干个父接口。 2) 一个类可以“实现”若干个接口,从而获得多个“父类”,实现多重继承。 3) 接口体由接口常量和抽象方法两部分组成,因此,接口是若干个常量和抽象方法的集合。
  • 37. 接口的主要特性接口与抽象类在表面上有些相似,如两者都不能被实例化。但接口对其中的成员变量和成员方法增加了许多限制,其主要特性有: ①接口的访问控制符有public和默认两种,被public修饰的是公有接口,可被所有类和接口使用。没有访问控制符(默认访问控制)的接口只能被同一个包中的其他类和接口使用 ②接口中的成员变量必须是被public、static和final修饰的,默认都是“public static final”类型的变量,且必须被显式初始化。 ③接口中的成员方法必须是被public和abstract修饰的,默认都是“public abstract”类型的方法。若接口中存在native方法(方法体使用非Java语言编写),则该接口需加native修饰符.
  • 38. 接口的主要特性(续一)④接口中只能包含“public static final”类型的成员变量和“public abstract”类型的成员方法。 ⑤接口没有构造方法,不能被实例化。 ⑥接口具有继承性。一个接口可以通过关键字extends来继承多个其他接口,从而实现“多重继承”。但接口不能实现另一个接口。 ⑦接口必须通过类来实现它的方法。一个类只能继承一个直接父类,但可以用implements关键字来实现多个接口。 ⑧与子类继承抽象父类相似,当类实现某个接口时,它必须实现接口中所有的抽象方法,否则这个类必须被定义为抽象类。 ⑨不允许创建接口的实例,但允许定义接口类型的引用变量,该引用变量能引用这个接口类的实例。
  • 39. 5.9.3 接口的实现接口的实现是指接口被类实现,在它的实现类中具体给出接口中抽象方法的方法体。类中实现接口的一般方法是: ①在类的头部,使用关键字implements说明该类要实现的接口。 ②若实现接口的类是非抽象类,则该类体内必须实现接口中所有的抽象方法,并且方法头应与接口中定义的完全一致。 ③若实现接口的类是抽象类,则可以不实现接口中的所有方法,但是在抽象类的非抽象子类中对其父类所实现的接口中所有抽象方法都必须实现。 ④一个类在实现某接口的抽象方法时,必须使用完全相同的方法头。若在类中实现方法时,则方法必须显式地被public修饰,因为接口的抽象方法均是显式或隐式的public方法。 接口的实现举例:例5-11
  • 40. 5.9.4 接口与抽象类的比较1.接口与抽象类的异同 接口常常被用来为具有相似功能的一组类,对外提供一致的服务接口,这一组类可以是相关的,也可以是不相关的,而抽象类则是为一组相关的类提供一致的服务接口。所以,接口往往比抽象类具有更大的灵活性。 (1)相同点: ①抽象类与接口都位于继承树的上层,代表系统的抽象层; ②都不能被实例化; ③都能包含有抽象方法。
  • 41. 5.9.4 接口与抽象类的比较(续一)(2)不同点: ①在抽象类中可以为部分方法提供默认的实现,避免在子类中重复实现它们,提高了代码的可重用性,这是抽象类的优势所在;而接口中只能包含抽象方法,不可以有任何实现代码。 ②抽象类与其子类之间存在泛化(层次/从属)关系,而接口与实现它的类之间则不存在任何层次关系,只存在实现关系。 ③抽象类只能被单继承,而接口可以被多继承;一个类只能继承一个直接的父类,这个父类可以是抽象类,但一个类可以同时实现多个接口,这是接口的优势所在。 ④在接口中只能声明公有的类常量字段,而抽象类中可以声明任何字段。 ⑤抽象类能够尽可能地为它的相关子类提供公共特征性,包括属特征和行为特征,而接口则为一组子类仅提供一组对外的服务接口,即抽象方法。 ⑥接口是在编译时处理,而抽象类是在运行时处理,接口比抽象类能节省运行开销。
  • 42. 2.使用接口的优势①对于已经存在的类继承树,可以方便地从类中抽象出新的接口,但是从类中抽象出抽象类却不那么容易,因此接口更有利于软件系统的维护与重构。 ②使用接口,可以方便地对已经存在的系统进行自下而上的抽象。对于任意两个类,不管它们是否属于同一个父类,只要它们存在相同的功能,就能从中抽象出一个接口类型。接口中定义了系统对外提供的一组相关服务,接口并不能强迫它的实现类在语义上是同一种类型。
  • 43. 3.使用接口和抽象类的原则①接口是系统中最高层次的抽象类型,要用接口作为系统与外界交互的窗口。 ②接口本身必须十分稳定,一旦制定,不要随意修改,否则会对外界使用者及系统内部都造成影响。 ③可用抽象类来定制系统中的扩展点。
  • 44. 5.10 接口的回调 在讲述继承与多态时,我们通过子类对象的上转型体现了继承的多态性,即把子类创建的对象的引用放到一个父类的对象中时,得到该对象的一个上转型对象,那么这个上转型对象在调用方法时就可能具有多种形态,不同对象的上转型对象调用同一方法可能产生不同的行为。 1.接口回调 接口回调是多态的另一种体现,接口回调是指:可以把使用某一接口的类创建的对象的引用赋给该接口声明的接口变量中,那么该接口变量就可以调用被类实现的接口中的方法,当接口变量调用被类实现的接口中的方法时,就是通知相应的对象调用接口的方法,这一过程称作对象功能的接口回调。 接口回调例题:例5-12; 接口作为参数:例5-13;
  • 45. 5.11 面向接口面向接口也可以体现程序设计的“开-闭”原理(Open-Closed Principle),即对扩展开放,对修改关闭。将自己经常需要变化的细节分割出来,作为接口中的abstract方法,然后面向接口来设计类。 例子5-14
  • 46. 5.12 内部类内部类 1.内部类的定义与优势 一个类的内部也可以包含(嵌套)另一个类,称这个被包含的类为内部类(Inner Class)或嵌入类(Nested Class)。相应地,把包含内部类的类称之为外部类(Outer Class)。 与一般的类相同,内部类也具有自己的成员变量和成员方法。通过建立内部类的对象,可以存取其成员变量和调用其成员方法。 内部类应用举例:例5-15
  • 47. 1.内部类的定义与优势Java中内部类定义是非常灵活的,其规则是: ①在另一个类声明一个类,即成员类(Member Class); ②在一个方法中声明的一个类,即局部类(Local Class); ③类的可嵌套深度没有限制。 内部类的主要优势有: ①内部类对象能够访问其所在外部类的全部属性,包括私有属性。 ②内部类能够被隐藏起来,不被同一包中的其他类所见。 ③使用内部类编写事件驱动程序时非常方便。
  • 48. 2.内部类的类型与引用方式内部类又分为定义在方法体外的成员类和定义在方法体内的局部类两种: 成员类进一步分为静态成员类(Static Member Class)或称类成员类(Class Member Class)和非静态成员类(Nonstatic Member Class)或称实例成员类(Instance Member Class)。 局部类又分为静态局部类(Static Local Class)、非静态局部类(Nonstatic Local Class)和匿名类(Anonymous Class)。
  • 49. 3.内部类的主要特性①内部类一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称。名称不能与包含它的类名相同。 ②可以访问它所在的外部类的静态成员变量和实例成员变量,也可以访问它所在方法的局部变量。 ③可以定义为abstract类型。 ④可以声明为public、protected、default、private类型。 ⑤若被声明为static类型,则变成了“顶层类”,不能再使用局部变量。 ⑥若想在内部类中声明任何static成员,则该内部类必须声明为static类型。
  • 50. 4.使用内部类的注意事项①Java程序中,对于内部类的继承没有限制,与普通的类一样可被单继承。同时对内部类的嵌套层次也没有限制。 ②内部类同一般类一样,可以实现接口。 ③非静态内部类可视为外部类中的实例(普通)方法,它对整个类具有访问权限,可以访问其所在的外部类,包括private成员变量在内的所有成员变量。但是非静态内部类中不能包含static 类型的成员变量。非静态的内部类允许被四种不同的访问修饰符修饰,也可以被其他外部类的成员方法访问。 ④静态内部类可视为外部类中的static方法,它只能访问被声明为static类型的成员变量,以及调用static型的方法。 ⑤局部(方法体中)内部类,视为方法中的局部变量,它可以访问外部类中的所有成员变量,包括private私有成员变量。它可以访问其所在方法中被声明为final类型的局部常量。
  • 51. 5.13 匿名类 1.和类有关的匿名类 当使用类创建对象时,程序允许我们把类体与对象的创建组合在一起,也就是说,类创建对象时,除了构造方法还有类体,此类体被认为是该类的一个子类去掉类声明后的类体,称作匿名类。匿名类就是一个子类,由于无名可用,所以不可能用匿名类声明对象,但却可以直接用匿名类创建一个对象。假设Hello是类,那么下列代码就是用Hello的一个子类(匿名类)创建对象:
  • 52. 5.13 匿名类 new Hello () { 匿名类的类体 } 匿名类可以继承类的方法也可以重写类的方法。我们使用匿名类时,必然是在某个类中直接用匿名类创建对象,因此匿名类一定是内部类,匿名类可以访问外嵌类中的成员变量和方法,匿名类不可以声明static成员变量和static方法。匿名类的主要用途就是向方法的参数传值。下面的例子展示了匿名类的用法。
  • 53. 5.13 匿名类 2.和接口有关的匿名类 假设Computable是一个接口,那么,Java允许直接用接口名和一个类体创建一个匿名对象,此类体被认为是实现了Computable接口的类去掉类声明后的类体,称作匿名类。下列代码就是用实现了Computable接口的类(匿名类)创建对象: new Computable() { 实现接口的匿名类的类体 }
  • 54. 5.13 匿名类 如果某个方法的参数是接口类型,那么我们可以使用接口名和类体组合创建一个匿名对象传递给方法的参数,类体必须要实现接口中的全部方法。 下面的例子17展示了和接口有关的匿名类的用法。 程序的运行结果如下:返回
  • 55. 5.14 常见错误和异常处理6.2.1 什么是异常处理 异常指的是程序运行时出现的非正常情况,又称为差错、违例等。当异常现象发生时,会造成程序运行中断、系统死机等问题。Java程序可以用特定语句来处理异常并继续程序执行,而不让程序中断。Java语言提供一个异常处理类Exception类,专门处理程序执行期间的错误。每当Java程序运行过程中发生一个可识别的运行错误时,即该错误有一个异常类与之相对应时,系统都会产生一个相应的该异常类的对象,即产生一个异常。Java语言异常处理机制有以下一些优点: (1)Java语言通过面向对象的方法进行异常处理,把各种不同的异常事件进行分类,体现了良好的层次性,提供了良好的接口,这种机制对于具有动态运行特性的复杂程序提供了强有力的控制方式。 (2)Java语言的异常处理机制使得处理异常的内容和程序本身内容分开,降低了程序的复杂性,增强了程序的可读性。 (3)由于把异常事件当成对象来处理,利用类的层次性可以把多个具有相同父类的异常统一处理,也可区分不同的异常分别处理,使用非常灵活。
  • 56. 5.14.1 Exception类 Java语言的异常类是处理运行时错误的特殊类,每一种异常类对应一种特定的运行错误。所有的Java异常类都是系统类库中的Exception类的子类 。 Exception类有若干子类,每一个子类代表了一种特定的运行时错误。这些子类有些是系统事先定义好并包含在Java类库中的,称为系统定义的运行异常。 系统定义的运行异常通常对应着系统运行错误。由于这种错误可能导致操作系统错误甚至是整个系统的瘫痪,所以需要定义异常类来特别处理。 常见的系统定义异常如下: (1)ArithmeticException:数学错误。 (2)ArrayIndexOutOfBoundsException:数组下标越界使用。 (3)ClassNotFoundException:未找到欲使用的类。 (4)FileNotFoundException:未找到指定的文件或目录。
  • 57. (5)InterruptedException:线程在睡眠、等待或其他原因暂停时被其他线程打断。 (6)IOException:输入、输出错误。 (7)MalformedURLException:URL格式错误。 (8)NullPointerException:引用空的尚无内存空间的对象。 (9)SecurityException:安全性错误,如Applet欲读写文件。 (10)UnknownHostException:无法确定主机的IP地址。 系统定义的异常主要用来处理系统可以预见的较常见的运行错误,对于某个应用程序所特有的运行错误,则需要编程者根据程序的特殊逻辑在应用程序中自行创建自定义的异常类和异常对象。这种用户自定义异常主要用来处理用户程序中特定的逻辑运行错误。
  • 58. 5.14.2 异常处理的过程在Java语言中,异常处理最常用的是try-catch-finally语句结构,以及throw和throws关键字。下面我们将一一进行介绍。 1.try-catch-finally 通过使用try-catch-finally语句来捕获一个或多个异常,基本格式为: try { 语句体 } catch( 异常错误类型 变量名 ) { 语句体 } finally { 语句体 }
  • 59. 其中,catch语句可以有一个或多个,而finally语句可以没有,但是,至少要有一个catch语句或finally语句。 try语句中的语句体是程序中有可能会产生一个或多个异常的语句。 catch语句的参数类似于方法的参数,其包括一个异常类型和一个异常对象。异常类型必须为Exception类的子类,它指明了catch语句所处理的异常类型。在程序运行时,当try语句体中产生异常时,系统会通过catch语句捕获这个异常,然后执行catch语句中的语句体对该异常进行处理。 catch语句可以有多个,分别处理不同类的异常。Java运行时系统从上到下分别对每个catch语句处理的异常类型进行检测,直到找到类型相匹配的catch语句为止。这里,类型匹配指catch所处理的异常类型与生成的异常对象的类型完全一致或者是它的父类。因此,catch语句的排列顺序应该是从特殊到一般。 也可以用一个catch语句处理多个异常类型,这时它的异常类型参数应该是这多个异常类型的父类,程序设计中要根据具体的情况来选择catch语句的异常处理类型。 在捕捉异常时,还可以使用finally语句。在try-catch之后接上finally语句,表示执行try-catch描述后,无论有无异常,最后必须执行finally语句中的语句体。
  • 60. 2.关键字throws和throw 在有些情况下,程序并不需要使用try-catch-finally处理它所生成的异常,而是由调用它的方法来处理这些异常,这时就要用到throws子句,它包含在方法的声明中。其格式如下: 返回类型 方法名( 参数 ) throws 异常类名 { 语句体 } 其中,异常类名可以是多个,但需要用逗号隔开。 一般这种抛出异常的语句应该被定义为在满足一定条件时执行,例如:把throw语句放在if语句的判断分支中,只有当if条件得到满足,即用户定义的逻辑错误发生时才执行。 含有throw语句的方法,应该在方法头定义中增加如下部分: throws 异常类名 这样做主要是为了通知所有欲调用此方法的方法。由于该方法包含throw语句,所以要准备接受和处理它在运行过程中可能会抛出的异常。如果方法中的throw语句不止一个,定义方法时的异常类名也不止一个,应该包含所有可能产生的异常。
  • 61. 5.14.3 自定义异常处理Java类库中定义的异常主要用来处理系统可以预见的比较常见的运行错误。如果某个应用程序有特殊的要求,则可能出现系统不能识别的运行错误,这时就需要用户自己创建异常和异常类,使系统能够识别这种错误并进行处理,增强用户程序的健壮性和容错性,从而使系统更加稳定。 用户自定义的异常类一般都是Exception类的直接或间接子类。 创建自定义异常的基本步骤如下。 (1)定义一个新的异常类,其必须继承Exception类、Exception类的子类或用户自定义的其他异常类。定义的格式如下: class 自定义异常类名 extends 父异常类名 { 语句体 }
  • 62. (2)为新的异常类定义属性和方法,或重载父类的属性和方法,使之能够体现出程序中出现这种异常的信息。 例如: public class NumberRangeException extends Exception { public NumberRangeException() { super(); } public NumberRangeException( String s ) { super( s ); } }
  • 63. 上面的程序定义了一个名为NumberRangeException的异常类,它是Exception类的子类。该类具有两个构造方法,第一个构造方法使用super();语句直接调用父类的没有参数的构造方法,第二个构造方法使用super( s );语句直接调用父类参数为字符串类型的构造方法。 当然,我们也可以自己编写构造方法的具体内容,以便完成更复杂的操作。 (3)抛出用户自定义的异常。用户自定义异常不可能依靠系统自动抛出,而必须通过throw语句抛出异常,通常是通过条件判断确定是否抛出这个异常类的新对象。 抛出用户自定义异常格式如下: 返回类型 方法名( 参数1, 参数2,… ) throw 自定义异常类 { if( 条件判断 ) throw ( new 自定义异常类名( this ) ); …… }
  • 64. 5.15 泛型类泛型(generics)是sun公司在SDK1.5中推出的,其主要目的是可以建立具有类型安全的集合框架,如链表、散列映射等数据结构。 泛型声明 class 名称<泛型列表> 如:class A A是泛型类的名称,E是其中的泛型.其中E可以是任何对象或接口,但不能是基本类型数据. 可以不使用E表示泛型,任何其他的合理标示符都可以,但最好和我们熟悉的类型名称有所区别. 泛型列表给出的泛型可以作为类的成员变量的类型、方法的类型以及局部变量的类型。
  • 65. 2. 使用泛型类声明对象 使用泛型类声明对象时,必须要指定类中使用的泛型的具体实际类型。 例如: class Chorus{ void makeChorus(E person,F yueqi){ yueqi.toString(); person.toString(); } } Chorus model; model = new Chorus();
  • 66. 5.16 this、Super和修饰符5.16.1 this引用 每当创建一个对象时,JVM就会给它分配一个引用自身的指针this,因此所有对象默认的引用都可使用this。在程序中使用this关键字的情形有以下几种: ①在类的构造方法中,可通过this语句调用该类的另一构造方法。 ②在一个实例方法内,若局部变量或参数和实例变量同名,则实例变量被屏蔽,此时可以采用“this.实例变量”的方式来指代实例变量。 ③在一个实例方法内,可通过this访问当前实例引用。 使用this的约束:this只能使用在构造方法或实例方法内,而不能在静态方法和静态代码块内使用。
  • 67. 5.16.2 super关键字super和this都可以使被屏蔽的成员方法或成员变量变为可见。成员方法与成员变量被屏蔽的情形有: ①按照变量的作用域规则,若在一个方法中的局部变量和与其所在类或父类的成员变量同名,则在成员方法内只有局部变量可见。 ②若子类的某个成员方法覆盖了父类的一个成员方法,则在子类的范围内父类的成员方法不可见。 ③若子类中定义了和父类同名的成员变量,则在子类的范围内父类的成员变量不可见。 在程序中使用super关键字的情形有以下几种: ①在类的构造方法中,可通过super语句调用该类的父类的构造方法 ②在子类中可以采用“super.<方法名>([实参表])”和“super.<父类变量名>”的方式分别访问父类的被屏蔽的成员方法和成员变量。 使用super的约束:super只能使用在构造方法或实例方法内,而不能在静态方法和静态代码块内使用。
  • 68. 5.11.3 Java的修饰符在Java语言中提供用来修饰类、变量和方法的修饰符。正确使用这些修饰符,有助于提高软件的可重用性、可维护性、可扩展性及系统的运行性能。下页表给出了类、成员方法、构造方法、成员变量和局部变量可用的各种修饰符。其中“○”表示可以修饰。
  • 69. 5.16.3 Java的修饰符(续一)修 饰 符顶层类成员方法成员类构造方法成员变量局部变量局部类abstract (抽象的)○○○ *static (静态的)○○○public (公有的)○○○○○protected (保护的)○○○○default (指无修饰符)○○○○○○ ○private (私有的)○○○○synchronized (同步的)○native (本地的)○transient (暂时的)○volatile (易失的)○final (不可改变的)○○ *○○ *
  • 70. 1.final修饰符final具有“不可改变的”含义,它可以修饰非抽象类、非抽象成员方法和成员变量。 1)用final修饰的类不能被继承,没有子类。 2)用final修饰的成员方法不能被子类的方法覆盖。 3)用final修饰的成员变量表示常量,必须初始化,且只能被赋一次值 4)final不能用来修饰构造方法。 (1)final类 继承关系的副作用是打破封装,子类能够访问父类的实现细节,而且能以方法覆盖的方式修改实现细节。可以考虑把类定义为final类型的情形有以下几点: 1)不是专门为继承而设计的类,类本身的方法之间有复杂的调用关系 2)出于安全的原因,类的实现细节不允许有任何改动。 3)在创建模型时,确信这个类不会再被扩展 .
  • 71. 1.final修饰符(续一)(2)final方法 在某些情况下,出于安全的原因,父类不允许子类覆盖某个方法,此时可以把这个方法声明为final类型。如所有Object类的子类都可以覆盖equals()方法,但不能覆盖getClass()方法。 (3)final变量 用final修饰的变量表示取值不会改变的常量。final变量的特征: ①final修饰符可以修饰静态变量、实例变量和局部变量,分别表示静态常量、实例常量和局部常量。 ②final变量必须显式初始化,否则编译错误。但非final类型的成员变量不必显式初始化。对于final类型的实例变量,可以在定义变量时或者在构造方法中进行初始化;对于final类型的静态变量,只能在定义变量时进行初始化。 ③final变量只能赋一次值。 ④若将引用类型的变量用final修饰,那么该变量只能始终引用一个对象,但可以改变对象的内容。
  • 72. 2.abstract修饰符abstract 修饰符可用来修饰类和成员方法。 ①用abstract修饰的类为抽象类,抽象类位于继承树的抽象层,抽象类不能被实例化,即不允许创建抽象类本身的实例。没有用abstract修饰的类为具体类,具体类可以被实例化。 ● 抽象类中可以没有抽象方法,但包含了抽象方法的类必须被定义为抽象类。 ● 如果子类没有实现父类中所有的抽象方法,子类也必须定义为抽象类。 ● 抽象类不能被定义为private、final和static类型。 ②用abstract修饰的方法为抽象方法,抽象方法没有方法体。抽象方法用来描述系统具有什么功能,但不提供具体的实现。没有用abstract修饰的方法称为具体方法,具体方法具有方法体,没有抽象构造方法。
  • 73. 3.static修饰符static修饰符可以用来修饰类的成员变量、成员方法和代码块。 ①用static修饰的成员方法和成员变量分别表示静态方法和静态变量,可以直接通过类名来访问。被static所修饰的成员变量和成员方法表明归某个类所有,它不依赖于类的特定实例,被类的所有实例共享。只要这个类被加载,Java虚拟机就能根据类名在运行数据区的方法区内定位到它们。 ②用static修饰的程序代码块表示静态代码块,当Java虚拟机加载时,应会执行该代码块。
  • 74. 谢谢!
  • 75. 2.内部类的类型与引用方式(续一)外部类与内部类的基本访问原则是: 在外部类中,通过一个内部类的对象来引用内部类中的成员;反之,在内部类中则可以直接引用它所在外部类的成员,包括静态成员、实例成员及私有成员。实际中,根据内部类在外部类中的位置不同,对内部类的引用也有所不同.
  • 76. (1)成员类(Member Class)指定义在方法体外的内部类,它进一步分为以下两种: ①静态成员类(Static Member Class) 静态成员类是指类声明中包含“static”关键字的内部成员类。 静态成员类的特性: a. 静态成员类可访问外部类的任一静态成员变量或静态成员方法; b. 像静态方法或静态变量一样,静态成员类有public、protected、private、default访问控制修饰符。 静态成员类的约束: a. 静态成员类不能与外部类重名; b. 像外部类的静态方法一样,不能直接访问外部类的实例变量和实例方法; c. 静态成员类只能定义于外部类的顶层代码或外部类其他静态成员类的顶层代码中(嵌套定义),而不能定义于外部类的某个成员方法中。
  • 77. ①静态成员类(Static Member Class)静态成员类的引用: 静态内部类是附属于外部类的,而不是附属于外部类的实例,对于静态内部类的引用不需要通过外部类的实例来创建内部类对象,可直接通过外部类名就可以实现,语法格式是:new 外部类名.内部类构造方法( ); 静态成员类的使用: 若B为A的辅助类,且只为A所用时,则可将B定义为A的静态成员类。例如JDK中的LinkedList类就有Entry静态成员类。
  • 78. ①静态成员类(Static Member Class) (续一)import java.util.*; public class LinkedList extends AbstractSequentialList { // …; private static class Entry { E element; Entry next; Entry previous; Entry(E element, Entry next, Entry previous) { this.element = element; this.next = next; this.previous = previous; } } // …; } 显然,Entry用来表示LinkedList中的一个节点,只被LinkedList自身使用。上述代码中的“”是一种泛型表示,泛型是JDK5.0的新增特性。
  • 79. ①静态成员类(Static Member Class) (续二)编译器编译静态成员类时,会创建一个名称为: “OuterClassName$StaticMemberClassName.class”的文件,其中OuterClassName是外部类的类名,StaticMemberClassName是静态成员类的类名.
  • 80. ②实例成员类(Instance Member Class)实例成员类是指类声明中不包含“static”关键字的内部成员类。 实例成员类的特性: a. 类似于外部类的实例方法,实例成员类有public、protected、private、default访问控制修饰符; b. 一个实例成员类的实例必然属于一个外部类的实例,实例成员类可访问外部类的任一个实例变量和实例方法。 实例成员类的约束: a. 实例成员类不能与外部类重名。 b. 不能在实例成员类中定义static变量、方法和类(static final形式的常量定义除外)。因为一个实例成员类的实例必然与一个外部类的实例关联,而这个static定义完全可以移到其外部类中去。 c. 实例成员类不能是接口(Interface)。因为实例成员类必须能被某个外部类实例化,而接口是不能实例化的。事实上,若以成员类的形式定义一个接口,则该接口实际上是一个静态成员类,static关键字对内部接口是内含(Implicit)的。
  • 81. ②实例成员类(Instance Member Class) (续一)实例成员类的引用:定义在方法体外的实例成员类如同成员变量和成员方法,也是附属于类的。其引用方法分两种情况: a. 当在外部类的内部对实例成员类实例化时,可直接调用实例成员类的构造方法来创建其实例,其语法格式是:new 内部类构造方法( ) ; 例:设类InnerClass是类OuterClass的一个实例成员类,则在类OuterClass的内部实例化InnerClass实例成员类的语句是:InnerClass in = new InnerClass ( ); b. 当在外部类的外部对实例成员类实例化时,需通过外部类的实例来引用,即先创建一个外部类的实例,再通过该外部类的实例来创建内部类的实例,其语法格式分为两种:
  • 82. ②实例成员类(Instance Member Class) (续二)格式一:外部类实例.new 内部类构造方法( ) 格式二:new 外部类构造方法( ). new 内部类构造方法( ) 例:设类InnerClass是类OuterClass的一个实例成员类,则在类OuterClass的外部实例化InnerClass实例成员类的两种方法是: 方法一:OuterClass out = new OuterClass ( ); OuterClass. InnerClass in = out.new InnerClass ( ); 方法二: OuterClass. InnerClass in = new OuterClass ( ).new InnerClass ( );
  • 83. ②实例成员类(Instance Member Class) (续三)实例成员类的引用:一个实例成员类的实例必然属于其外部类的一个实例,在实例成员内部类代码中可用this关键字来引用实例成员类的实例,若需要获得其所属外部类实例,则需要在this关键字前加上外部类的类名,如“OuterClass.this”的形式。 实例成员类的使用:实例成员类的显著特性就是实例成员类能访问它的外部类实例的任意变量与方法。方便一个类对外提供一个公共接口的实现是实例成员类的典型应用。
  • 84. ②实例成员类(Instance Member Class) (续四)以JDK中集合Collection类库为例,每种Collection类必须提供一个与其对应的迭代器Iterator实现以便客户端能以统一的方式遍历任一Collection 实例。每种Collection类的Iterator实现就被定义为该Collection类的实例成员类。例如JDK中AbstractList类的代码片断: public abstract class AbstractList extends AbstractCollection implements List { private class Itr implements Iterator { // ………; } public Iterator iterator() { return new Itr(); } } 因为定义在AbstractList中的实例成员类Itr可访问AbstractList中的任意成员变量和方法,所以很方便实现Iterator,无需AbstractList对外暴露更多的接口。 若没有成员类机制,则只有在AbastractList源码之外定义一个实现Iterator的类Itr,该类有一个AbstractList实例成员list,为了Itr能获取list的内部信息以便实现遍历,AbstractList必然要向Itr开放额外的访问接口。
  • 85. (2)局部类(Local Class)指定义在方法体内或代码块中的内部类。由于局部类附属方法体,因此只能在方法体中创建对象实例,并且创建的实例也只能在方法体中被访问。所创建的对象实例的生命周期与方法相同,当方法结束后,对象也就随之消失。 ①静态局部类 对一个静态成员类,去掉static关键词,将其定义移入其外部类的静态方法或静态初始化代码段中就成为了静态局部类。静态局部类与静态成员类的基本特性相同。例如,都只能访问外部类的静态字段或方法,不能访问外部类的实例变量和实例方法等。
  • 86. (2)局部类(Local Class) (续一)②实例局部类 对一个实例成员类,将其定义移入其外部类的实例方法或实例初始化代码中就成为了实例局部类。实例局部类与实例成员类的基本特性相同。
  • 87. (2)局部类(Local Class) (续二)局部类的约束: a. 局部类只在定义它的代码段中可见,不能在它所属代码段之外的代码中使用; b. 局部类没有public、protected、private; c. 不能以局部类形式定义一个接口,因为局部类只在其所属代码段中可见,定义这样的接口无意义。 d. 局部类类名不能与其外部类类名重复。 局部类的使用:局部类大部分以匿名类的形式使用。
  • 88. (本页无文本内容)
  • 89. 1.匿名类的定义匿名类是指没有名字的局部类,匿名类可以继承另一类或实现一个接口。定义在方法中的局部匿名类时,可在new后直接跟父类名或接口名,而不需要使用implements或extends关键字。匿名类不能在其他地方对该类实例化,除非重现匿名类的代码。定义匿名类的一般格式有二: ①继承基类的匿名类: new 父类构造方法 ( [参数列表] ) { <匿名类体> } 创建匿名类实例时,“参数列表”将被传入其父类对应的构造方法。
  • 90. 1.匿名类的定义(续一)②实现接口的匿名类: new 接口名( ) { <匿名类体> } 其中,new语句实际是声明并调用一个新的匿名类,它能继承一个给定的父类或者实现一个给定的接口,它能创建该匿名类的一个新实例,并把它作为其所在语句的结果而返回。被继承的类和要实现的接口是new语句的操作数,后跟匿名类的主体。
  • 91. 2.匿名类的特性与约束 匿名类作为一种特殊的局部类,局部类的特性与约束都适用于它。此外,匿名类在语法上不能同时继承类和实现接口,匿名类中不能显示定义构造方法。 3.匿名类的形式 实际应用中,匿名类的应用形式一般分为语句匿名类和参数匿名类两种。
  • 92. 3.匿名类的形式(1)语句匿名类 语句匿名类是指作为语句一部分的局部匿名类,语句匿名类返回该类对象的引用,可以应用在对象引用的任何地方,其一般应用格式有以下两种形式: ①格式一:保存匿名类的实例引用值 <类名/接口名> 匿名类的实例名 = new <类名/接口名> ([参数列表] ) { <匿名类体> }; //作为语句,此处的分号不能少 匿名类的实例名.匿名类实例方法( );
  • 93. 3.匿名类的形式(续一)②格式二:不保存匿名类的实例引用值 new <类名/接口名> ([参数列表] ) { <匿名类体> }.匿名类实例方法( ); //作为语句,此处的分号不能少
  • 94. (2)参数匿名类参数匿名类是指作为方法形式参数的局部匿名类,格式: 方法名( new <类名/接口名> ([参数列表] ) { <匿名类体> } ) ; //此处分号不能少 参数匿名类返回该类对象的引用,可以应用在方法形参为对象引用的任何地方,参数匿名类的定义、构造和使用发生在同一地方同时实现。
  • 95. 4.匿名类的使用 ①类的定义代码必须较少,一般不超过10行; ②只需要创建该类的一个实例; ③类的定义代码与类的使用代码紧邻; ④使用匿名不影响代码的易读性;