6. 抽象类、接口、内部类
6.1 抽象类
6.1.1 声明抽象类
public abstract class Test{
abstarct void run();//没有方法体
}
- 抽象类不能实例化,即不能new一个对象,但可以用来声明变量类型,如Test myTest。
- 抽象类可以有或没有抽象方法,但有抽象方法的类必须定义为抽象类。
- 抽象方法没有方法体,而不是方法体为空。
- 当一种方法声明为抽象方法时,意味着这种方法必须被子类的方法所覆盖,否则子类仍然是抽象的。
- abstract不能与private、static、final或native并列修饰同一种方法。
- 抽象类可以有构造函数。
6.1.2 继承抽象类
- 子类继承抽象类,其实现方法的权限必须大于等于父类方法的权限。如抽象方法权限为默认,则子类可为默认,protected,public。
- 子类如果是非抽象类,那么子类必须覆盖重写父类当中的所有方法。子类如果是抽象类,可以选择性覆盖。于6.1.1中4同义。
- 抽象类的子类可以是抽象类,也可以是非抽象类。
6.2 接口
6.2.1 声明接口
接口本身也具有数据成员、抽象方法、默认方法和静态方法,但它与抽象类有下列不同。
(1)接口的数据成员都是静态的且必须初始化,即数据成员必须是静态常量。 (2)接口中除了声明抽象方法外,还可以定义静态方法和默认方法。
接口的理解:
- 接口是抽象方法的集合,一个类通过继承接口的方式,从而来继承接口的抽象方法。
- 接口不能实例化对象,没有构造方法。
- 接口中的所有方法必须是抽象方法。
- 接口不是被类继承而是被类实现。
- 接口不能包含成员变量,除了 static 和 final 变量。
- 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错);接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
- 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
注:1. JDK 1.8 以后,接口里可以有静态方法和方法体了。 2.JDK 1.8 以后,接口允许包含具体实现的方法,该方法称为"默认方法",默认方法使用 default 关键字修饰。更多内容可参考 Java 8 默认方法。3.JDK 1.9 以后,允许将方法定义为 private,使得某些复用的代码不会把方法暴露出去。 - 接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键字。
接口中的方法都是公有的。 - 接口的继承和多继承
Hockey接口自己声明了四个方法,从Sports接口继承了两个方法,这样,实现Hockey接口的类需要实现六个方法。相似的,实现Football接口的类需要实现五个方法,其中两个来自于Sports接口。
- 接口的继承
// 文件名: Sports.java
public interface Sports
{
public void setHomeTeam(String name);
public void setVisitingTeam(String name);
}
// 文件名: Football.java
public interface Football extends Sports
{
public void homeTeamScored(int points);
public void visitingTeamScored(int points);
public void endOfQuarter(int quarter);
}
// 文件名: Hockey.java
public interface Hockey extends Sports
{
public void homeGoalScored();
public void visitingGoalScored();
public void endOfPeriod(int period);
public void overtimePeriod(int ot);
}
- 接口的多继承:public interface Hockey extends Sports, Event
参考:菜鸟教程接口,接口定义与实现
例题:下列说法错误的是(B)
A.类只能继承一个父类,但是可以实现多个接口
B.抽象类自身可以定义成员而接口不可以
C.抽象类和接口都不能被实例化(忽略匿名内部类)
D.一个类可以有一个父类和多个接口
解释:B选项中,java 接口中可以定义成员变量,且必须是 static final的。
6.3 内部类
在类或接口内声明的类。方法内也可以声明内部类,这是局部内部类。不过局部内部类访问局部变量时,该局部变量必须有final修饰,访问类变量、实例变量无此要求。
- 内部类一旦编译成功,就会成为完全不同的两个类,分别为outer.class和outer$inner.class类。所以内部类的成员变量/方法名可以和外部类的相同。
- 要访问内部类,可以通过创建外部类的对象,然后创建内部类的对象来实现。
- 成员内部类里面为什么不能有静态成员和方法?
非静态内部类不能有静态成员!
成员内部类必须先实例化外部类对象然后再实例化成员内部类; - 嵌套类有两种类型:非静态内部类 静态内部类
- 非静态内部类:
非静态内部类是一个类中嵌套着另外一个类。 它有访问外部类成员的权限, 通常被称为内部类。
class OuterClass {
int x = 10;
/***私有的内部类**
如果你不希望外部类被内部类访问,可以将内部类声明为private
private class InnerClass {
int y = 5;
}*/
class InnerClass {
int y = 5;
}
}
public class MyMainClass {
public static void main(String[] args) {
OuterClass myOuter = new OuterClass();
OuterClass.InnerClass myInner = myOuter.new InnerClass();
System.out.println(myInner.y + myOuter.x);
}
}
输出结果:15
- 静态内部类:
注:静态内部类无法访问外部类的成员。
class OuterClass {
int x = 10;
static class InnerClass {
int y = 5;
}
}
public class MyMainClass {
public static void main(String[] args) {
OuterClass.InnerClass myInner = new OuterClass.InnerClass();
System.out.println(myInner.y);
}
}
输出结果为:5
另外还有:
- 局部内部类
指内部类定义在方法体内,只能在该方法或条件的作用域内才能使用,退出这写作用域就无法引用。 - 匿名内部类:
为了免去给内部类命名,或者只想使用一次,就可以选择使用匿名内部类。
习题:
- 内部类可以直接访问(定义它的)外部类的所有成员变量和方法。√
- 内部类可以访问外部类的私有成员和方法。√
- 外部类可以直接访问内部类的成员。×
- 匿名类一定是内部类。√
- 匿名类一定是final类。√
6.4 匿名类
- 匿名类的意义是解决没有显式声明的类想创建对象这一问题。
- 匿名类是不能有名字的类,它们不能被引用,只能在创建时用 new 语句来声明它们。
- 匿名类一定是内部类,不可以声明类变量和类方法。(实例变量和实例方法可以声明)?
- 匿名类不可以是抽象类,抽象类不可以创建对象。
- 和类有关的匿名类不可以额外实现某个指定的接口。
- 和接⼝有关的匿名类不可以是抽象类。
- 匿名类类不可以有static成员变量,但可以使⽤接⼝中的静态常量
参考:链接1
匿名类继承一个父类:
//以下实例创建的匿名类继承了 Polygon 父类:
class Polygon {
public void display() {
System.out.println("在 Polygon 类内部");
}
}
class AnonymousDemo {
public void createClass() {
// 创建的匿名类继承了 Polygon 类
Polygon p1 = new Polygon() {
public void display() {
System.out.println("在匿名类内部。");
}
};
p1.display();
}
}
class Main {
public static void main(String[] args) {
AnonymousDemo an = new AnonymousDemo();
an.createClass();
}
}
实现结果:在匿名类内部。
匿名类实现一个接口:
//以下实例创建的匿名类实现了 Polygon 接口:
interface Polygon {
public void display();
}
class AnonymousDemo {
public void createClass() {
// 匿名类实现一个接口
Polygon p1 = new Polygon() {
public void display() {
System.out.println("在匿名类内部。");
}
};
p1.display();
}
}
class Main {
public static void main(String[] args) {
AnonymousDemo an = new AnonymousDemo();
an.createClass();
}
}