Think in java 读书笔记
pikzas
2019.07.31
第九章 接口
知识点
1.抽象类和抽象方法
1.1.抽象类的设计目的
抽象类的主要功能是为了在实现多态过程中,避免错误的创建了父类实例对象而提供的语法支持。
在标准的多态实践中,父类仅仅是为子类提供了一个模板,子类提供具体实现。父类也是一个普通类的情形下,创建出一个父类的对象是没有意义的。他没有具体的方法实现。
1.2.什么是抽象方法
abstract void f(); 仅有声明而没有方法体的方法。
1.3.什么是抽象类
class 前有abstract修饰的类为抽象类
1.4.抽象类的特点
- 抽象类不能直接实例化,必须通过子类向上转型得到
- 如果一个类中含有一个或多个抽象方法,那么该类必须为抽象类
- 如果一个类是抽象类,其中也可以一个抽象方法也没有(可以有任意多个普通的方法)
- 抽象方法的修饰符不能是private的,那样禁止override,和抽象类的设计初衷相违背
- 抽象类中也可定义属性,属性的用法和普通类一致。
- 继承抽象类的子类中,如果实现了父类所有的抽象方法,那么他可以申明为一个普通类(也可以依旧申明为一个抽象类,甚至还可添加新的抽象方法),也可以声明为抽象类,但是如果尚有抽象方法未完全实现,那么该类必须也是抽象类。
2.接口
接口是抽象方法的一种极端形式,要求所有的方法都是抽象方法
2.1.接口的设计目的
除了为了更方便的实现多态,更为了突破JAVA的单继承限制,实现一种隐式的多继承(内部类),因为接口可以多实现
2.2.什么是接口
用interface关键字而不是class关键字标明的类,其中可以有属性,只能有抽象方法(提供方法名,确定参数列表和返回值),却不给任何实现。
2.3.接口的特点
- 接口类上的修饰符可以是public和default,和普通类的修饰符一样效果。
- 接口中的属性默认是public static final的,所以可以直接通过接口点属性拿来用,也不能对属性作出修改。而且不能添加访问修饰符private protected或者是default,会提示编译错误。
- 接口中的方法默认也是public abstract的,不写修饰符也默认是public abstract的,不能添加private protected default这些修饰符。
3.JAVA中的多继承
JAVA允许一个类单继承自一个实体类或者是抽象类,但是可以多实现多个接口。
public class Actor {
public void fight(){
System.out.println("do nothing");
};
}
public interface CanFight {
void fight();
}
public interface CanFly {
void fly();
}
public interface CanSwim {
void swim();
}
public class ActActor extends Actor implements CanFight,CanFly,CanSwim {
@Override
public void fly() {
}
@Override
public void swim() {
}
}
public class Test {
public static void a(Actor actor) {
actor.fight();
}
public static void b(CanFight canFight) {
canFight.fight();
}
public static void c(CanFly canFly) {
canFly.fly();
}
public static void d(CanSwim canSwim ) {
canSwim.swim();
}
public static void main(String[] args) {
ActActor actActor = new ActActor();
a(actActor);
b(actActor);
c(actActor);
d(actActor);
}
}
上面的例子中注意fight()方法并没有在ActActor中做实现,这是因为Actor类中已有实现
如果将Actor类中的fight()方法改为非public的,那么ActActor就相当于未对CanFight中的fight()方法做实现,此时编译会出错。
如果Actor类中没有fight()方法,但是Actor继承自Human类,Human类中有fight()方法,那么此时也是可以的。
此处示例也说明了接口的两个用处
- 可以将子类向上转型为不同的基类,代码更灵活
- 接口也不能实例化,从编译器就可以防止误用。
4.接口与接口,接口与类之间的关系
类可以实现接口,接口可以继承接口并且可以多继承接口
public interface Monster {
void menace();
}
/**
* 接口之间用继承
*/
public interface DangerousMonster extends Monster {
void destory();
}
public interface Lethal {
void kill();
}
/**
* 接口可以多继承接口,只要用逗号隔开就行。
*/
public interface Vampire extends DangerousMonster,Lethal {
void drinkBlood();
}
public class VeryBadVampire implements Vampire {
@Override
public void drinkBlood() {
}
@Override
public void destory() {
}
@Override
public void kill() {
}
@Override
public void menace() {
}
}
5.组合接口时候可能发生的名字冲突
前面讲过如果某个类的父类和其实现的接口都有同一个方法的时候,这个类可以不用再对该方法做实现。
但是如果多个接口,其中都有同一个方法,导致重载与覆盖同时发生,这时候可能就会发生错误。
public interface InterfaceA {
void f();
}
public class Father {
public int f(){}
}
/**
* 如下写法的子类会提示编译错误,因为方法必须override,但是又会与父类的方法冲突(同名,同参数列表,就返回值不同,不是overload,所以会提示编译错误)
*/
public class Son extends Father implements InterfaceA {
}
6.接口中的属性都默认是public static final的(不需要特别写明,默认就是)
接口中的属性如果是基本数据类型,他们都要大写,都是编译器常量,可以拿来替代枚举使用
接口中定义的域是不能为空final的,但是可以被非常量表达式初始化
这些属性不是接口的一部分,他们的值会存储在接口的静态存储区域内
import java.util.Random;
public interface RandVals {
Random RAND = new Random(47);
int RANDOM_INT = RAND.nextInt(10);
long RANDOM_LONG = RAND.nextLong() * 10;
}
7.接口的嵌套
接口可以嵌套在类或者其他接口中
- 接口嵌套在类内部,除了public,包访问权限之外,还多了private访问权限
package com.pikzas.thinkinjava.chapter09.demo6;
public class OuterClass {
//1.类内部接口可以是包访问权限
interface B{
void f();
}
//1.1. 对应的实现类可以是public的
public class BImpl implements B{
public void f(){}
}
//1.2. 对应的实现类可以是private的
private class BImpl2 implements B{
public void f(){}
}
//1.3. 对应的实现类可以是默认访问权限的
class BImpl3 implements B{
public void f() {
}
}
//2. 类内部接口可以是包访问权限
public interface C{
void f();
}
//2.1. 对应的实现类可以是public的
public class CImpl implements C{
public void f(){}
}
//2.2. 对应的实现类可以是private的
private class CImpl2 implements C{
public void f(){}
}
//2.3. 对应的实现类可以是默认访问权限的
class CImpl3 implements C{
public void f() {}
}
//2. 类内部接口可以是private的
private interface D{
void f();
}
//2.1. 对应的实现类可以是public的
public class DImpl implements D{
public void f(){}
}
//2.2. 对应的实现类可以是private的
private class DImpl2 implements D{
public void f(){}
}
//2.3. 对应的实现类可以是默认访问权限的
class DImpl3 implements D{
public void f() {}
}
}
package com.pikzas.thinkinjava.chapter09.demo6;
public class TestClass {
public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
OuterClass.B b1 = outerClass.new BImpl();
// OuterClass.B b2 = outerClass.new BImpl2(); BImpl2是private的,访问不到,编译报错
OuterClass.B b3 = outerClass.new BImpl3(); //BImpl3是包访问权限,可以访问到
OuterClass.C c1 = outerClass.new CImpl();
// OuterClass.C c2 = outerClass.new CImpl2(); CImpl2是private的,访问不到,编译报错
OuterClass.C c3 = outerClass.new CImpl3();
// OuterClass.D D接口是private的 访问不到
}
}
package com.pikzas.thinkinjava.chapter09;
import com.pikzas.thinkinjava.chapter09.demo6.OuterClass;
public class TestOuter {
public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
// OuterClass.B b3 = outerClass.new BImpl3(); 访问不到B接口,因为不和他在同一个包路径下
OuterClass.C c1 = outerClass.new CImpl();
// OuterClass.C c2 = outerClass.new CImpl2(); // 访问不到CImpl2实现,因为是private的
// OuterClass.C c3 = outerClass.new CImpl3(); // 访问不到CImpl3实现,因为不和他在同一个包路径下
// OuterClass.C c3 = outerClass.new CImpl3(); // 访问不到CImpl3实现,因为不和他在同一个包路径下
// OuterClass.D d = outerClass.new DImpl(); // 访问不到D接口,因为D接口是private的
}
}
- 接口嵌套在接口内部,内部的接口默认就是public的,如要标明访问权限,也只能写public
public class TestInterface {
}
class EImp implements E{
public void g(){}
}
class EGImp implements E.G{
public void f(){}
}
class EImp2 implements E{
@Override
public void g() {}
class EG implements E.G{
@Override
public void f() {}
}
}
8.接口在工厂方法的应用
public interface Game {
int play();
}
public class Dice implements Game {
Random random = new Random(47);
@Override
public int play() {
return (random.nextInt(6)+1);
}
}
public class CoinFlip implements Game {
Random random = new Random(47);
@Override
public int play() {
int rand = random.nextInt(10);
return rand > 5? 1:-1;
}
}
public interface GameFactory {
Game getGame();
}
public class DiceFactory implements GameFactory {
@Override
public Game getGame() {
return new Dice();
}
}
public class CoinFlipFactory implements GameFactory {
@Override
public Game getGame() {
return new CoinFlip();
}
}
public class StartGame {
public static void play(GameFactory gameFactory) {
Game game = gameFactory.getGame();
System.out.println(game.getClass().getSimpleName() + "---"+game.play());
}
public static void main(String[] args) {
for(int i = 0 ; i < 5 ; i++){
play(new DiceFactory());
play(new CoinFlipFactory());
System.out.println("-----");
}
}
}