本章教学内容:
5.1 抽象类
在面向对象的概念中,所有的对象都是通过类来表述,但并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一类具体的对象,这样的类就是抽象类。
抽象类的作用有点类似“模版”,往往用来表征对问题领域进行分析、设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象。其目的是要设计者依据它的格式来修改并创建新的类。但是并不能直接由抽象类创建对象,只能通过抽象类派生出新的类,再由它来创建对象。
5.1.1抽象类的定义
定义抽象类的一般格式为:
abstract class 类名称{
声明数据成员;
访问权限 返回值的数据类型 方法名称(参数列表){
方法体;
}// 定义一般方法
abstract 返回值的数据类型 方法名称(参数列表){
方法体;
}// 定义抽象方法
关于抽象类的几点说明:
1.抽象类必须用 abstract 关键字来修饰。
2.在抽象类的类体中,可以定义数据成员、一般的方法和抽象方法。其中一般方法和先前介绍过的方法没有什么两样;“抽象方法”是以 abstract 关键字为开头的方法,此方法只声明了返回值的数据类型、方法名称与所需的参数,但没有定义方法体。即抽象方法只需声明,不需实现。
3.含有抽象方法的类必须被声明为抽象类,抽象类的子类必须复写所有的抽象方法后才能被实例化,否则这个子类还是个抽象类。
4.抽象类不能被实例化,也就是不能用 new 关键字去产生对象。
5.1.2抽象类的使用
定义抽象类的目的是提供可由子类共享的一般形式,子类可以根据自身需要扩展抽象类。
注意:
1.抽象类不能实例化,即不能直接new一个抽象类,但可以指向一个实现它的子类对象;
2.abstract不能与final同时修饰一个类;
3.abstract不能和static、private、final或native并列修饰同一方法。
5.2 接口
Java是单继承的语言,单继承限制了类的多重体现。为了弥补这一缺陷,Java语言设计者提出了一种折中的得解决办法,即利用接口模拟多继承。
接口(interface)的结构和抽象类非常相似,也具有数据成员与抽象方法,但它与抽象类又有不同之处,如:
1.接口里的数据成员必须初始化,且数据成员均为常量。
2.接口里的方法必须全部声明为 abstract,也就是说,接口不能像抽象类一样保有一般的方法,而必须全部是“抽象方法”。
接口的用处体现在以下几个方面:
1.通过接口实现不相关类的相同行为,而无须考虑这些类之间的关系。
2.通过接口指明多个类需要实现的方法。
3.通过接口了解对象的交互界面,而无须了解对象所对应的类。
5.2.1 接口的定义
接口定义的一般格式如下:
<访问符> interface 接口名 {
[访问符] 数据类型 成员名称 = 常量 ;
[访问符] <返回类型> 方法名([参数列表]);
}
5.2.2 接口的实现
利用接口打造新类的过程,称之为接口的实现。每一个由接口实现的类必须在类内部实现接口中定义的所有方法,且可自由地使用接口中的常量。一个类可以实现多个接口。
接口实现的语法格式:
class 类名称 implements 接口 A,接口 B{
类体
}
说明:
1.使用 “implements”关键字可以实现多个接口,接口之间用逗号隔开;
2.一个类实现接口时,必须实现接口中定义的所有方法,除非将该类定义为抽象类。
5.2.3 接口的继承
接口与一般类一样,可通过继承的技术来派生出新的接口。原来的接口称为基本接口或父接口,派生出的接口称为派生接口或子接口。通过这种机制,派生接口不仅可以保留父接口的成员,同时也可加入新的成员以满足实际的需要。
同样的,接口的继承也是通过关键字extends来实现的。但是与类的继承不同的是,一个接口可以继承多个接口。
接口继承的一般格式:
interface 子接口名称 extends 父接口 1,父接口 2,… {
……
}
5.2.4 抽象类与接口的区别
1.接口中的数据必须是常量,默认是public static final 型,且必须给其初值,所以实现类中不能重新定义,也不能改变其值;抽象类可以有各种类型的数据,默认是缺省的friendly,在其实现类中可以重新定义,也可以修改。
2.接口中不能有已经实现的方法;抽象类中可以有已经实现的方法。
3.接口中的方法默认都是public abstract类型;抽象类中的方法默认是缺省的friendly。
4.Java对类的扩展只允许单重继承;对接口允许多重继承。
抽象类与接口都能用来产生通用功能。在什么情况下应该使用接口,什么情况下应该使用类呢?一般的,明显的“是(is-a)”关系应该用类模拟。例如,员工是一个人,所以员工和人之间的关系应该用类模拟。弱的“是(is-a)”关系可以用接口模拟。例如:所有的同类对象都可以比较,所以就可以定义一个用来比较的接口。
5.3 内部类
内部类是指在一个类的内部嵌套定义的类。如果在类Outer的内部再定义一个类Inner,此时类Inner就称为内部类,而类Outer则称为外部类。
引入内部类的主要原因是:
1.当一个类存在的价值仅仅是为某一个类服务时,应当使其成为内部类。
2.内部类能够隐藏起来,不为同一包的其他类访问。
3.内部类可以很方便的访问其所处外部类的所有属性。
4.在回调方法处理中,匿名内部类尤为便捷,特别是GUI中的事件处理。
Java内部类主要有成员内部类、局部内部类、静态内部类和匿名内部类四种。
5.3.1成员内部类
成员内部类的定义非常简单,就是在外部类的内部定义一个类,该类的存在形式与外部类的成员变量和方法并列。所以称之为成员内部类。
内部类可以直接访问外部类中的所有属性和方法,但是外部类访问内部类是必须通过创建内部类的对象才能访问。(InnerClass inner=new InnerClass();)
内部类是编译时的一个概念,一旦编译成功,就会成为完全不同的两个类。
注意:
成员内部类中不能定义静态变量,但可以访问外部类的所有成员。(内部类访问同名变量时,需要加this,比如:OutClass.this.变量名;不同命时直接访问)。
5.3.2 局部内部类
在外部类的成员方法中定义的内部类称为局部内部类。与局部变量类似,局部内部类不能用public或private访问修饰符进行声明。它的作用域被限定在声明该内部类的方法中。
局部内部类中不可定义静态变量(属于类变量,所有对象共享),可以访问其所在方法内的final变量。
5.3.3静态内部类
当内部类只是为了将其隐藏起来,不需要和外部类发生联系,可以将内部类声明为static。
用static可以声明属性和方法,用static也可以声明内部类,用static声明的内部类变成外部类,但是用static声明的内部类不能访问非static的外部类属性。由编译结果来看,由于内部类InnerClass声明为static类型,所以无法访问外部类中的费static类型成员。
注意:
静态内部类和成员内部类的区别:静态内部类的对象可以直接生成,而不需要通过生成外部类的对象来生成。这样实际上是静态内部类成为一个顶级类。
静态内部类不可用private来进行定义。
静态内部类可用public,protected,private修饰,不能访问外部类的非静态成员。
5.3.4 匿名内部类
匿名内部类就是没有名字的内部类,是一种特殊的局部内部类。
匿名内部类的特点:
1.一个类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是覆盖。
2.只是为了获得一个对象实例,不需要知道其实际类型。
3.类名没有意义,也就是不需要使用到。
将局部内部类特殊化— —如果只创建一个类的一个对象,可以考虑匿名内部类,在GUI事件处理中大量用到。
适合使用匿名内部类的情况:
1.只用到类的一个实例,后面不会再用到这个类了。
2.类在定义后马上用到;
3.类非常小。
使用匿名内部类需遵循的原则:
1.匿名内部类不能有构造方法;
2.匿名内部类不能定义任何静态成员;
3.只能创建匿名内部类的一个实例;
4.一个匿名内部类一定跟在new的后面,创建其实现的接口或父类的对象。