
在前面的例子中,我们编写的每一个类都定义了相关的方法,这些方法都有具体的方法体来描述这个类的某种行为。但是在某些特殊情况下,某个父类只是确定子类应该包含哪些方法,却不知道子类的具体实现,就引入的抽象类……
7、抽象类、接口和内部类
在前面的例子中,我们编写的每一个类都定义了相关的方法,这些方法都有具体的方法体来描述这个类的某种行为。但是在某些特殊情况下,某个父类只是确定子类应该包含哪些方法,却不知道子类的具体实现,就引入的抽象类。举例来说,编写一个Shape类表示形状,这个类提供了计算面积和周长的两个方法,但是不同的子类例如圆、三角形、长方形的周长和面积的计算方式都不一样,要满足这样一个场景,就可以提供抽象方法,只有方法名,没有具体实现的方法。
抽象类
抽象类就是用abstract关键字修饰的类,它是一种特殊的类,抽象方法同样也是用abstract修饰,看下实例:
1 |
public abstract class { |
我们定义了一个Shape类,用abstract修饰,提供了构造器,并且提供了计算面积和周长的两个抽象方法,还有一个打印颜色的普通方法。抽象方法的定义中是不能包含{}的,就算没有方法体,下面定义一个类Circle继承Shape看一下用法:
1 |
public class Circle extends { |
运行main方法后输出结果:
1 |
构造方法 |
Circle类很简单,定义了属性半径,并具体实现了Shape类中两个抽象方法,抽象类有自己的构造器但是不能用new去实例出一个对象,构造器主要用来被子类调用。一个抽象类有这样一些规则:
- 抽象类中可以拥有抽象方法和普通方法;
- 有抽象方法的类一定是抽象类;
- 抽象类不能像普通类一样用new关键字去创建实例,只能当做父类被继承。
- 多态很灵活很好用。
抽象类的用法总体来说是比较简单的,但是其中的思想可能会要在实践中才能体会,作为一个抽象类,在实际的项目中,应该是作为构架一级的存在,制定某些规则由子类去实现,就像一个公司的构架中,有老板,经理和员工,员工来执行事务,经理可以对事务进行管理,但是没必要具体去做,当然经理也有老板交代的事只能自己去完成,这里制定和管理事务就类似抽象方法、具体要做的事就是抽象类中的普通方法了,那么老板呢?假设一个老板主要是制定规则,所有的事都是手底下的人去完成,他不需要自己去做,那么,老板就是我们即将要拿出来的概念——接口。
接口
不同于类定义,接口使用class,而是用interface关键字定义接口,但是接口的访问修饰符只有两种:public或者default(默认),接口名的定义遵循类名定义规则,接口名首字母大写,多个单词首字母都大写的驼峰式命名。从上面对抽象类的说明,我们知道接口也是一种规范,所以,接口中是不包含构造器和初始化块,一个接口中可以包含成员变量(只能是静态常量)、方法(抽象方法、类方法或默认方法)、内部类(内部接口、枚举)定义。
我们知道接口中定义的是多个类共同的行为规范,因此,接口中所有成员都应该是public访问权限。定义接口中的成员可以省略public,如果要指定,就只能是public访问控制修饰符。
1 |
public interface Boss { |
上面实例的接口中,定义了成员变量、普通方法和类方法,成员变量的定义必须要赋予初始值,否则编译错误,我们知道final的变量都是要赋予初始值的,在接口中定义成员变量,默认使用public static final 修饰的,不写的话系统默认提供。下面一个普通方法,本质就是abstract修饰的一个抽象方法,所以没有方法体。此外还有一个static修饰的类方法,此方法可用使用接口名直接调用。事实上,在实际的项目中,接口中我们一般会定义一些相关类共同使用的常量由接口直接调用,和一些普通方法供实现类重写,很少在接口中写一个具体的方法,这与我们的初衷有悖。
说到接口是用来定义规则的,那么定义了规则就要有具体的类去实现,接口不同于类的继承,接口的复用我们称为实现,使用implements关键字,而且接口是可以多实现的,也就是说一个普通的类可以同时实现多个接口,而类继承只能是单继承,一个类同一时间只能继承一个父类。
1 |
public interface Product extends Boss{ |
1 |
public class TestImpl implements Boss, Product { |
总得来说要注意的点是:
- 类实现接口使用implements关键字,普通类继承父类、接口继承接口使用extends关键字;
- implements后面可以跟多个接口用“,”分割,extends后面只能继承一个类;
- 接口没有构造方法,不能被实例化,主要用来定义通用规则,要灵活使用多态创建对象。
- 接口中定义的方法默认使用public修饰,成员变量默认使用public static final修饰。
从这里我们也可以看出接口和抽象类最大的区别,当然接口的实际应用是很广的,后面还会大量用到。
内部类
内部类,顾名思义就是定义在一个类内部的类,为什么要使用内部类呢?
- 内部类在程序中实现了更深一层的封装,内部类可以访问外部类的私有数据,但是外部类不能访问内部类的实现细节;
- 内部类离开了当前外部类是没有任何意义的,只作用于当前类;
看一下内部类的主要分类,从其中体会作用:
非静态内部类:
前面我们说过静态和非静态,也就是static修饰符的作用,非静态内部类就是在类内部定义的普通类,如下:
1 |
public class InnerClass { |
可以看到,内部类的使用方法和普通类其实是一样的,需要注意的是,我们有时候会见到一个java文件中有多个类,但是它们都是独立存在的,这中写法不能叫做内部类,只有定义在一个类的范围之内才是。
上面的实例中,我们在一个public类中定义了一个private修饰的类,内部类作为一个成员,自然可以使用public、private、protected和static修饰,外部类是不可以的。此外需要注意的一个细节是:一个普通的java文件中,有且只有一个public修饰的类,且这个类名要作为文件名存在,这个规则是正确的,但要注意和内部类的概念区分开。
静态内部类
所谓静态,自然就是static修饰,这样的类叫做静态内部类。既然是static修饰的,它就属于当前类本身,而不是当前外部类的某一个对象。不同于普通的非静态内部类,静态内部类中是可以包含静态成员也可以包含非静态成员的。我们知道静态成员是不能访问非静态成员的,所以静态内部类不能访问外部类的实例成员,只能访问外部类的静态成员。至于说到类的访问修饰符,参照前面封装中说到的细节,大同小异。
1 |
static class ClassB{ |
此外,接口中也可以定义内部类,且默认使用public static修饰,也就是说接口中定义的内部类只能是静态内部类。
局部内部类
这个局部内部类不同于前面说的两种类型,它是在方法中定义的内部类,因为这种内部类只作用于当前方法,所以局部内部类不能使用访问控制符和static修饰,实际开发中真没见过这种写法,此处略过。
匿名内部类
我们先以一个实例演示:
1 |
public class NoneNameInnerClass { |
匿名内部类的使用有规则制约;
- 创建匿名内部类时会立即创建对象,所以匿名内部类不能使抽象类;
- 匿名内部类必须继承一个父类或者实现一个接口,但最多也只能实现一个接口;
- 匿名内部类不能定义构造器,但是可以定义初始化块。
在实际开发中,内部类这一部分相对较少,其使用也是扩展自原有知识,写的不够清晰,后面用到再补回来。




近期评论