本文是Java基础课程的第九课。也是Java面向对象编程中非常重要的部分,主要介绍Java中的抽象类、接口及内部类等内容
文章目录
一、抽象类
1、抽象类是什么
在使用面向对象思想描绘现实世界的过程中,有时会遇到这样的情况:设想中的一个类没有足够的信息来描绘一个具体对象的行为。比如有一个类用来描绘所有的动物,该类中有一个方法用来描绘动物发出叫声的行为,当动物类作为基类,派生出狗类及猫类时,狗类和猫类中可以分别具体描绘狗和猫是怎样发出叫声的,而单就动物类而言,由于动物的范围很笼统,便没有办法描绘动物具体怎样发出叫声。
在Java中,可以创建这样的类,该类中可以没有足够的信息来描绘一个具体对象的行为,这样的类便是抽象类。
2、声明和使用抽象类
2.1、声明抽象类
在Java中,声明抽象类的方法非常简单,只需使用abstract
关键字即可。
声明抽象类的语法如下:
[权限修饰符] abstract class 类名 {
// 类体
}
下面是一个示例:
Animal
类的源码:
package com.codeke.java.test;
/**
* 动物类
*/
public abstract class Animal {
// 属性
String type; // 类型
String breed; // 品种
String name; // 名称
/**
* 构造函数
*/
public Animal() {
super();
}
/**
* 构造函数
* @param type 类型
* @param breed 品种
* @param name 名称
*/
public Animal(String type, String breed, String name) {
this.type = type;
this.breed = breed;
this.name = name;
}
}
Test
类的源码:
package com.codeke.java.test;
/**
* 测试类
*/
public class Test {
public static void main(String[] args) {
// Animal animal = new Animal() ; // 这句代码报错,抽象类不能被直接实例化
}
}
说明:
- 当一个类是抽象类时,它便不能再被用来实例化对象。本例中,
Animal
类是一个抽象类,使用语句new Animal()
试图实例化Animal
类的对象时会报错。 - 抽象类除了不能被用来实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
2.2、声明抽象方法
在抽象类的基础上,需要使用抽象方法来描述缺少的行为信息。抽象方法就是只有方法头(方法声明),缺少方法体的方法,声明抽象方法仍然使用abstract
关键字。
声明抽象方法的语法如下:
[权限修饰符] abstract 返回值类型 方法名称([参数列表]);
下面是一个示例:
修改后Animal
类的源码:
package com.codeke.java.test;
/**
* 动物类
*/
public abstract class Animal {
// 属性
String type; // 类型
String breed; // 品种
String name; // 名称
/**
* 构造函数
* @param type 类型
* @param breed 品种
* @param name 名称
*/
public Animal(String type, String breed, String name) {
this.type = type;
this.breed = breed;
this.name = name;
}
/**
* 构造函数
*/
public Animal() {
super();
}
/**
* 抽象方法,发出声音
*/
public abstract void makeSound();
}
说明:
- 本例中,
Animal
类中的makeSound()
方法是一个抽象方法,它没有方法体。 - 抽象类可以包含普通的方法,也可以不包含普通的方法。
- 抽象类可以包含抽象方法,也可以不包含抽象方法。
- 如果一个类中包含抽象方法,那么这个类必须是抽象类。
- 构造方法,类成员方法(用
static
修饰的方法)不能声明为抽象方法。
2.3、继承抽象类
由于抽象类不能被用来实例化对象,所以抽象类必须作为基类,被派生类继承后,才能被使用。正因如此,开发人员通常需要在设计阶段慎重考虑哪些类要设计为抽象类。
下面下面是一个示例:
Animal
类的源码同上例,不再赘述。
Dog
类的源码:
package com.codeke.java.test;
/**
* 狗类
*/
public class Dog extends Animal {
/**
* 派生类构造函数
* @param breed 品种
* @param name 名称
*/
public Dog(String breed, String name) {
super("狗", breed, name);
}
/**
* 重写基类的makeSound()方法
*/
@Override
public void makeSound() {
System.out.printf("%s发出叫声,汪汪汪。\n", name);
}
}
Cat
类的源码:
package com.codeke.java.test;
/**
* 猫类
*/
public class Cat extends Animal {
/**
* 派生类构造函数
* @param breed 品种
* @param name 名称
*/
public Cat(String breed, String name) {
super("猫", breed, name);
}
/**
* 重写父类的makeSound()方法
*/
@Override
public void makeSound() {
System.out.printf("%s发出叫声,喵喵喵。\n", name);
}
}
测试类PetShop
类的源码:
package com.codeke.java.test;
/**
* 宠物店(测试类)
*/
public class PetShop {
/**
* 用来测试的main方法
*/
public static void main(String[] args) {
// 实例化Cat类和Dog类的对象,并将它们赋值给基类Animal类的变量
// Cat类和Dog类各自完整的实现了Animal类中的抽象方法
Animal animal1 = new Cat("波斯猫", "大花");
Animal animal2 = new Dog("牧羊犬", "大黑");
// 使用Animal类的变量调用Animal类中被派生类重写过的方法
animal1.makeSound();
animal2.makeSound();
}
}
说明:
- 本例中,
Cat
类和Dog
类分别完整的实现(方法实现:给出每个抽象方法的方法体)了Animal
类中的抽象方法,因此,Cat
类和Dog
类可以被用来实例化对象。 - 抽象类的派生类只有实现了基类中所有的抽象方法后才能被用来实例化派生类对象。
- 如果有任何一个抽象方法未在派生类中被实现,该派生类也必须是抽象类。
3、抽象类和抽象方法总结
Java中抽象类的要点总结如下:
- 抽象类不能被实例化(编译无法通过)。只有抽象类的非抽象派生类可以被用来实例化对象。
- 抽象类中不一定包含抽象方法,但是包含抽象方法的类必定是抽象类。
- 抽象类中的抽象方法只是声明,不包含方法体(就是不给出方法的具体实现)。
- 构造方法,类成员方法(用
static
修饰的方法)不能声明为抽象方法。 - 抽象类的派生类必须给出抽象类中的抽象方法的具体实现,除非该派生类也是抽象类。
- 抽象类和抽象方法进一步诠释和巩固了Java面向对象编程核心特征中的多态性,使开发人员可以在设计、编码过程中更好的运用多态性。
二、接口(interface)
1、Java中的接口(interface)是什么
在前面的章节中,已经介绍过API(应用程序编程接口)的概念,API即是在编程过程中,应用之间进行信息交换的媒介(或者理解为方式、渠道、手段等等)。另外也提到,当声明一个方法时,方法头(方法声明)对于开发人员来说即是一个API。更进一步说, 前文中提到的抽象方法对开发人员来说,即是API。
在Java中,接口(interface)是和类(class)并列的概念。类用来描述对象的属性和行为;而接口(interface)用来描述一组抽象的行为,或者说一组API。具体来说,Java中的接口(interface)即是一组抽象方法的集合,它被用来声明一组抽象方法,向外界提供一组统一的、规范的、可供使用的API。
在Java中,一个接口(interface)一经声明,也就成为了一种新的引用数据类型。这意味着,开发人员可以声明接口类型的变量,但是,与抽象类类似,开发人员无法示例化一个接口类型的对象。另外,接口(interface)中的抽象方法也需要由具体的类去重写并提供具体的方法实现,这一过程被称为 接口 的 实现,这样的类被称为该接口的 实现类 。实现了同一个接口的多个实现类同时具有了相同的行为,每个实现类又可以根据自身需求给出这些行为的具体实现,当使用接口类型的变量引用实现类的对象时,接口中的方法便表现出多态性。
接口(interface)声明抽象方法,不给出方法体,由实现类去实现所声明的抽象方法。这提供了一种方法声明与方法实现的分离机制。在设计层面,这种机制可以避免开发人员被一些具体的程序细节一叶障目,从而让开发人员从更加宏观的角度对应用做更好的审视;在代码层面,接口可以降低程序的耦合度,使程序更容易维护和扩展。
2、声明和使用接口(interface)
2.1、声明接口(interface)
在Java中,声明接口(interface)需要使用interface
关键字。
声明接口(interface)的语法如下:
[权限修饰符] interface 接口名 {
// 常量成员列表
// 抽象方法成员列表
}
说明:
- 接口(interface)也声明在以
.java
结尾的文件中,编译后也保存在以.class
结尾的文件中。 - 接口(interface)的访问权限是public或默认权限,与类的访问权限类似。
- 接口(interface)中最主要的部分便是抽象方法成员列表,在接口(interface)中声明抽象方法的语法如下:
接口(interface)中的方法通常且默认是公开和抽象的,即接口(interface)中的方法通常会被隐式的指定为返回值类型 方法名称([参数列表]);
public abstract
(如果要显式声明,通常也只能是public abstract
)。 - 接口(interface)中还可以声明常量,接口(interface)中声明常量的语法如下:
接口(interface)中的常量必须且默认被指定为数据类型 常量名称 = 字面量值;
public static final
(如果要显式声明,也只能是public static final
)。 - 注意,接口(interface)中没有构造方法。
- 还需注意,接口(interface)并不继承
java.lang.Object
类,但是接口(interface)隐式的声明了一组与java.lang.Object
类中公开(由public
修饰)的方法相一致的方法。
下面是一个示例:
Flyable
接口的源码:
package com.codeke.java.test;
/**
* 飞行接口
*/
public interface Flyable {
void fly(); // 飞行方法
}
Swimmable
接口的源码:
package com.codeke.java.test;
/**
* 游泳接口
*/
public interface Swimmable {
void swim(); // 游泳方法
}
说明:
- 本例中,声明了两个接口,为
Flyable
接口和Swimmable
接口,表示可以飞行的和可以游泳的,Flyable
接口中有方法fly()
表示飞行的行为,Swimmable
接口中有方法swim()
表示游泳的行为。接口(interface)的名称一般使用名词或者描述某种能力的形容词,如Appendable
(可追加的)、Iterable
(可迭代的)、Comparable
(可比较的)。
2.2、实现接口(interface)
接口(interface)描述、规范了一组抽象的行为,需要具体的实现类来实现。声明实现类需要使用implements
关键字,其语法为:
[修饰符] class 实现类名 implements 接口名1[, 接口名2, ...] {
// 重写的接口中的方法
// 其他类成员
}
说明:
- Java中的类可以同时实现多个接口。
- 实现类应该重写其实现的所有接口中·的所有抽象方法,如果有抽象方法没有被重写,则该实现类应该是抽象的。
下面是一个示例:
Flyable
接口和Swimmable
接口的源码同上例;
Duck
类的源码:
package com.codeke.java.test;
/**
* 鸭子类
*/
public class Duck implements Flyable, Swimmable{
// 属性
String type; // 类型
String breed; // 品种
String name; // 名称
// 重写的飞行的方法
@Override
public void fly() {
System.out.printf("%s在天上飞!\n", this.name);
}
// 重写的游泳的方法
@Override
public void swim() {
System.out.printf("%s在水里游!\n", this.name);
}
// 构造方法
public Duck(String type, String breed, String name) {
this.type = type;
this.breed = breed;
this.name = name;
}
}
测试类PetShop
类的源码:
package com.codeke.java.test;
/**
* 宠物店(测试类)
*/
public class PetShop {
public static void main(String[] args) {
// 实例化实现类(鸭子类)的对象
Duck duck = new Duck("鸭子", "野鸭", "大鸭");
// 声明了一个接口(Flyable)类型的变量,引用了鸭子对象
Flyable flyable = duck;
// 声明了一个接口(Swimmable)类型的变量,也引用了鸭子对象
Swimmable swimmable = duck;
// 使用接口(Flyable)类型的变量调用fly()方法,实际执行的是实现类中重写过的方法
flyable.fly();
// 使用接口(Swimmable)类型的变量调用swim()方法,实际执行的是实现类中重写过的方法
swimmable.swim();
}
}
说明:
- 本例中,
Duck
类同时实现了Flyable
接口和Swimmable
接口,并提供了fly()
方法和swim()
方法的实现。 - 在本例的测试类
main
方法中,使用接口类型声明了变量,并引用了实现类的对象,在使用接口类型的变量调用方法时,实际执行的是实现类中的提供的方法实现,这体现了多态性。
在Java中,一个类可以作为派生类,继承一个基类,同时,也可以作为实现类,实现一个或多个接口(interface),其语法为:
[修饰符] class 类名 extends 基类名 implements 接口名1[, 接口名2, ...] {
// 重写的基类中的和接口中的方法
// 其他类成员
}
下面是另一个示例:
Flyable
接口和Swimmable
接口的源码同上例;
Animal
类的源码:
package com.codeke.java.test;
/**
* 动物类
*/
public abstract class Animal {
// 属性
String type; // 类型
String breed; // 品种
String name; // 名称
/**
* 构造函数
* @param type 类型
* @param breed 品种
* @param name 名称
*/
public Animal(String type, String breed, String name) {
this.type = type;
this.breed = breed;
this.name = name;
}
/**
* 构造函数
*/
public Animal() {
super();
}
/**
* 抽象方法,发出声音
*/
public abstract void makeSound();
}
Duck
类的源码:
package com.codeke.java.test;
/**
* 鸭子类
*/
public class Duck extends Animal implements Flyable, Swimmable{
// 属性
String type; // 类型
String breed; // 品种
String name; // 名称
// 重写发出声音的方法
@Override
public void makeSound() {
System.out.printf("%s发出叫声,嘎嘎嘎。\n", this.name);
}
// 重写的飞行的方法
@Override
public void fly() {
System.out.printf("%s在天上飞!\n", this.name);
}
// 重写的游泳的方法
@Override
public void swim() {
System.out.printf("%s在水里游!\n", this.name);
}
// 构造方法
public Duck(String type, String breed, String name) {
this.type = type;
this.breed = breed;
this.name = name;
}
}
测试类PetShop
类的源码:
package com.codeke.java.test;
/**
* 宠物店(测试类)
*/
public class PetShop {
public static void main(String[] args) {
// 实例化实现类(鸭子类)的对象
Duck duck = new Duck("鸭子", "野鸭", "大鸭");
// 声明了一个接口(Flyable)类型的变量,引用了鸭子对象
Flyable flyable = duck;
// 声明了一个接口(Swimmable)类型的变量,也引用了鸭子对象
Swimmable swimmable = duck;
// 声明了一个Animal类型的变量,也引用了鸭子对象
Animal animal = duck;
// 使用接口(Flyable)类型的变量调用fly()方法,实际执行的是实现类中重写过的方法
flyable.fly();
// 使用接口(Swimmable)类型的变量调用swim()方法,实际执行的是实现类中重写过的方法
swimmable.swim();
// 使用Animal类型的变量调用makeSound()方法,实际执行的是派生类中重写过的方法
animal.makeSound();
}
}
说明:
- 本例中,
Duck
类继承了Animal
类,提供了makeSound()
方法的实现,Duck
又同时实现了Flyable
接口和Swimmable
接口,并提供了fly()
方法和swim()
方法的实现。 - 通过本例的测试类
main
方法,可以看到Java中类的继承机制及接口的实现机制,都体现了多态性。 - 派生类和基类之间的关系可以用 is a 来形容,与此对应的,实现类和接口(interface)之间的关系通常可以用 like a 来形容。
2.3、接口(interface)的继承
在Java中,接口(interface)之间也可以存在继承关系,接口之间的继承也使用extends
关键字来体现,语法如下:
[修饰符] interface 派生接口名 extends 基接口名1 [, 基接口名2, ...] {
// 常量成员列表
// 抽象方法成员列表
}
前面的章节中提到,Java中的类不支持多继承。与此不同的,Java中的接口(interface)支持多继承,以下的代码是合法的:
interface A { }
interface B { }
interface C extends A, B { }
2.4、接口(interface)中的默认方法
在JDK1.8以前,开发人员在使用接口(interface)时有一点非常不方便,即当需要修改接口时,需要修改全部该接口的实现类。从JDK1.8开始,为了解决这一问题,接口(interface)中新增了默认方法。接口(interface)中的默认方法,即接口中已经存在默认实现的方法,实现类可以选择不去实现该方法。
在接口(interface)中声明默认方法需要使用default
关键字,语法如下:
default 返回值类型 方法名称([参数列表]) {
// 方法体
}
说明:
- 接口(interface)中的默认方法必须是公开的,即接口(interface)中的默认方法会被隐式的指定为
public
(如果要显式声明,也只能是public
)。
下面是一个示例:
TestInterface
接口的源码:
package com.codeke.java.test;
public interface TestInterface {
default void print(){
System.out.println("TestInterface中的print()方法执行了");
}
}
TestClass
类的源码:
package com.codeke.java.test;
public class TestClass implements TestInterface { }
测试类Test
类的源码:
package com.codeke.java.test;
public class Test {
public static void main(String[] args) {
TestClass testClass = new TestClass();
testClass.print();
}
}
执行输出结果:
TestInterface中的print()方法执行了
说明:
- 本例中,实现类
TestClass
并没有重写其实现的TestInterface
接口中的默认方法print()
,在Test
类的main
方法中实例化TestClass
类的对象,并调用print()
方法,执行的是TestInterface
接口中的默认方法print()
的方法体。
下面是另一个示例:
TestInterface
接口的源码同上例;
TestSupClass
类的源码:
package com.codeke.java.test;
public abstract class TestSupClass implements TestInterface{
@Override
public void print(){
System.out.println("TestSupClass中的print()方法执行了");
}
}
TestClass
类的源码:
package com.codeke.java.test;
public class TestClass extends TestSupClass { }
测试类Test
类的源码同上例;
执行输出结果:
TestSupClass中的print()方法执行了
说明:
- 默认方法遵循“类优先”原则 ,即若一个接口(interface)中定义了一个默认方法,而实现类(或实现类所继承的基类)中又定义了一个具有相同名称和参数列表的方法时,位于继承关系下游的类中的方法优先被调用。
再来看另一个示例:
TestInterfaceA
接口的源码:
package com.codeke.java.test;
public interface TestInterfaceA {
default void print(){
System.out.println("TestInterfaceA中的print()方法执行了");
}
}
TestInterfaceB
接口的源码:
package com.codeke.java.test;
public interface TestInterfaceB {
default void print(){
System.out.println("TestInterfaceB中的print()方法执行了");
}
}
TestClass
类的源码:
package com.codeke.java.test;
public class TestClass implements TestInterfaceA, TestInterfaceB {
@Override
public void print() {
TestInterfaceA.super.print();
// TestInterfaceB.super.print();
}
}
测试类Test类的源码同上例;
执行输出结果:
TestInterfaceA中的print()方法执行了
说明:
- 本例中,
TestInterfaceA
接口和TestInterfaceB
接口中都有默认方法print()
,TestClass
类同时实现TestInterfaceA
接口和TestInterfaceB
接口,此时,需要在TestClass
类中显式的覆盖print()
方法来解决冲突。如果一个类实现多个接口,并且这些接口提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须覆盖该方法来解决冲突。 - 本例中,在实现类
TestClass
重写的print()
方法中,语句TestInterfaceA.super.print()
可以调用接口TestInterfaceA
中的默认方法print()
。
2.5、接口(interface)中的静态方法
在JDK1.8以前,接口(interface)中不允许出现静态成员方法;而从JDK1.8开始,为了更灵活起见,接口(interface)中可以声明静态成员方法。
下面是一个示例:
TestInterface
接口的源码:
package com.codeke.java.test;
public interface TestInterface {
// 接口中的静态方法
static void print(){
System.out.println("TestInterface接口中的print()方法执行了");
}
}
测试类Test
类的源码:
package com.codeke.java.test;
public class Test {
public static void main(String[] args) {
TestInterface.print();
}
}
执行输出结果:
TestInterface接口中的print()方法执行了
说明:
- 接口(interface)中的静态成员方法,只能使用该接口名直接调用。
3、接口(interface)和抽象类的异同
接口(interface)和抽象类的相同点:
接口(interface)、抽象类 | |
---|---|
相同点 | 都是引用类型; 都不能实例化; 都可以包含抽象方法。 |
接口(interface)和抽象类的不同点:
接口(interface) | 抽象类 |
---|---|
实现类可以实现多个接口(interface) | 派生类只能继承一个抽象基类 |
接口(interface)可以继承多个其他接口(interface) | 类只能继承一个抽象基类 |
实现类和接口(interface)的关系是 like a | 派生类和抽象基类的关系是is a |
成员包括常量、抽象方法、 默认方法、静态方法、 内部类 | 成员包括实例成员(实例变量、实例方法)、 静态成员(静态变量、静态方法)、 抽象方法、内部类 |
实现类在实现抽象方法时必须指定public 权限 | 派生类在重写抽象方法时不能缩小访问权限 |
接口(interface)没有构造方法 | 抽象类有构造方法 |
不继承自java.lang.Object | 继承自java.lang.Object |
三、内部类
1、内部类是什么
在Java 中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。广泛意义上的内部类一般来说包括:成员内部类、局部内部类、匿名内部类。
2、常见的内部类
2.1、成员内部类
成员内部类是最普通的内部类,它的定义位于另一个类的内部,和成员变量、成员方法同级。这里提到的另一个类也被称为外部类。成员内部类看起来就像是外部类的一个成员。
成员内部类也可以按照是否使用static
修饰而分为实例成员内部类和静态成员内部类。
下面是一个示例:
TestOutterClass
类的源码:
package com.codeke.java.test;
// 外部类TestOutterClass
public class TestOutterClass {
private String str = "我是TestOutterClass类中的成员变量str";
public void print() {
System.out.println("TestOutterClass类中的print()方法执行了");
System.out.println("TestOutterClass类中的print()方法打印str:" + str);
}
// 内部类TestInnerClass
class TestInnerClass {
private String str = "我是TestInnerClass类中的成员变量str";
public void print() {
// print(); 内部类和外部类有相同名称和参数列表的方法,默认调用内部类的方法,这里会死循环,
// 需要通过下面的语句调用外部类的print()方法
TestOutterClass.this.print();
System.out.println("TestInnerClass类中的print()方法执行了");
System.out.println("TestInnerClass类中的print()方法打印str:" + str);
}
}
}
测试类Test
类的源码:
package com.codeke.java.test;
public class Test {
public static void main(String[] args) {
TestOutterClass testOutterClass = new TestOutterClass();
TestOutterClass.TestInnerClass testInnerClass = testOutterClass.new TestInnerClass();
testInnerClass.print();
}
}
执行输出结果:
TestOutterClass类中的print()方法执行了
TestOutterClass类中的print()方法打印str:我是TestOutterClass类中的成员变量str
TestInnerClass类中的print()方法执行了
TestInnerClass类中的print()方法打印str:我是TestInnerClass类中的成员变量str
说明:
- 没有被
static
修饰的成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private
成员和静态成员);而被static
修饰的成员内部类只能访问外部类中同样被static
修饰的成员。 - 没有被
static
修饰的成员内部类是依附外部类对象而存在的,也就是说,如果要创建该类型的成员内部类的对象,前提是必须存在一个外部类的对象。如同本例Test
类main
方法中那样;被static
修饰的成员内部类并不依附外部类对象,创建该类型的内部类对象使用new 外部类名.内部类名()
即可。 - 在成员内部类中,当成员内部类拥有和外部类同名的、非静态的成员变量或者方法(方法需要参数列表也相同)时,默认情况下访问的是内部类的成员。如果要访问外部类的同名成员,需要使用
外部类名.this.成员名
,如本例中的TestOutterClass.this.print()
。 - 没有被
static
修饰的成员内部类中不能存在静态成员,外部类中如果要访问该类型成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问;对于被static
修饰的成员内部类,外部类中如果要访问该类型成员内部类的静态成员,可以不需要内部类对象,而访问非静态成员时,仍然需要内部类对象。 - 成员内部类作为外部类的成员,可以使用所有的访问权限修饰符,即
public
、protected
、缺省、private
。
2.2、局部内部类
局部内部类是定义在一个方法或者一个语句块里面的类,它的访问仅限于方法内或者语句块内。
下面是一个示例:
TestOutterClass
类的源码:
package com.codeke.java.test;
public class TestOutterClass {
public void print(){
// 局部内部类
class TestInnerClass {
void print(){
System.out.println("TestInnerClass类中的print()方法执行了");
}
}
new TestInnerClass().print();
}
}
测试类Test
类的源码:
package com.codeke.java.test;
public class Test {
public static void main(String[] args) {
new TestOutterClass().print();
}
}
执行输出结果:
TestInnerClass类中的print()方法执行了
说明:
- 局部内部类就像是方法里面的一个局部变量一样,是不能用
public
、protected
、private
以及static
修饰符修饰的。
2.3、匿名内部类
在开发过程中,如果接口或者抽象类中需要实现的方法很少,临时使用接口或者抽象类时,重新定义一个类单独实现方法又有些麻烦,可以直接使用匿名内部类对接口或者抽象类进行实现。
下面是一个示例:
Printable
接口的源码:
package com.codeke.java.test;
public interface Printable {
void print();
}
测试类Test
类的源码:
package com.codeke.java.test;
public class Test {
public static void main(String[] args) {
Printable printable = new Printable() {
@Override
public void print() {
System.out.println("匿名内部类中的print()方法执行了");
}
};
printable.print();
}
}
执行输出结果:
匿名内部类中的print()方法执行了
说明
- 本例中,自始至终并没有见到实现了
Printable
接口的类的类名,这也是该类被称为匿名内部类的原因。
需要注意,内部类丰富了开发人员继承类、实现接口的手段,并且利于数据安全和代码封装、隐藏,但内部类不利于代码复用,一定程度上增加了代码阅读难度,故在开发过程中,需要根据具体情况选择使用。