Java基础之接口与抽象类及多态、内部类

final关键字

  • 被其修饰的类,不能被继承。
  • 被其修饰的方法,不能被覆盖。
  • 被其修饰的变量,是一个常量,不能被修改,所以定义时必须初始化(和C++的const类似)。

一般有final,会搭配static使用。如final static double PI = 3.14;

  • 常量的命名规则 --> 所有字母大写,多个单词,中间用下划线连接。

抽象类

猫和狗有共性,将共性抽取出来,放入Animal中,Animal是抽象的(想象不出实体是什么)。

public abstract class Animal {
  // 抽象类也有构造函数,给子类初始化用
    public Animal() {
        System.out.println("我们都是动物");
    }
    public abstract void eat();
    }
// 也可以存在非抽象函数
public void sleep() {
        System.out.println("睡觉了zzz~");
    }
}

class Cat extends Animal {
    Cat() {
        System.out.println("我是一只小猫咪");
    }
    @Override
    public void eat() {
        System.out.println("小猫吃鱼");
    }
}

class Dog extends Animal {
    Dog() {
        System.out.println("我是一条小狗狗");
    }

    @Override
    public void eat() {
        System.out.println("狗狗啃骨头");
    }
}

public class Demo {
  public static void main(String args[]) {
     public static void main(String[] args) {
        Animal a = new Cat();
        a.eat();
        a.sleep();
       // 打印如下内容
       // 我们都是动物
       // 我是一只小猫咪
       // 小猫吃鱼
       // 睡觉了zzz~
  }
}

因为类Cat、类Dog或者其他类要继承类Animal,且类Animal的show()是抽象的未被定义的,子类必须覆盖父类的方法,定义自己特有的eat()方法(猫和狗吃的东西不全一样),故在抽象类中的方法只是声明

抽象类的特点

  1. 方法只有声明没有实现,为抽象方法,修饰符abstract,抽象方法必须在抽象类中。
  2. 抽象类不可以实例化,即不能new。因为调用抽象方法没有意义。
  3. 子类必须覆盖抽象类的所有抽象方法后,该子类才能实例化,否则还是抽象类。

Q:抽象类有没有构造函数?

A:有,用来给子类初始化,如上例,最先打印我们都是动物

Q:可以没有抽象方法吗,或者说,可以不全是抽象方法吗?

A;可以。但是接口interface必须全是抽象方法

abstract关键字不能和哪些关键字共存

  • private:因为子类要覆盖抽象类的抽象方法,私有后不可访问,怎么覆盖?
  • static:抽象类中只是声明了,子类需要重写抽象类的方法。而静态方法不能被重写,所有矛盾了。
  • final:final修饰的方法不能被覆盖,子类要覆盖啊,所以不能加。

接口-interface

当一个抽象类中全部是抽象方法时,可将其定义为interface。

  • 变量规定为:public static final,即使写成int a,也被认为是public static final int a;
  • 方法规定为:public abstract
  • 接口中的成员,都是public的

接口和抽象类一样,不可实例化,用来给其他类扩展功能,接口与接口之间为继承关系,接口可以多继承。Java不支持直接多继承,改成了多实现,即一个子类多个接口,解决了单继承的局限性。

抽象类和接口

  • 抽象类只能被单继承,接口可以背多实现。

  • 抽象类中也可以定义非抽象方法,子类可以直接用非抽象方法。接口中只能定义抽象方法。共同点是,其抽象方法都必须被重写。

  • 抽象类定义基本的共性内容,接口是定义额外的功能。

多态

对于Animal a = new Cat(),new出子类,却让父类指向子类,就叫多态。

public void method(Animal a) {
  a.eat(); // 运行时根据具体传入的实参,来调用对应的方法
  // 若Dog和Cat都继承了Animal,参数传入Dog就执行dog.eat(),传入Cat就执行cat.eat()
  // 这有点像C++中的动态绑定
}

// 和以下函数重载比较起来,是不是方便了
public void method(Dog a) {
  a.eat()
}
public void method(Cat a) {
  a.eat();
}

多态好处

从上例可以看出,多态的好处:前期定义的代码可以用于后期,比如再来一个Wolf类继承Animal,就可以传入wolf调用以上函数,传入的就是wolf了。

多态缺点

前期定义的内容,不可调用后期子类的特有内容。举个例子

public void method(Animal a) {
  a.eat(); // 父类定义了该方法
  a.catchMouse(); // Animal没有定义“抓老鼠”的方法,若是传入的参数不是Cat,则报错
}

// 怎么解决?强转回来!如下
public void method(Animal a) {
  a.eat();
  // 先判断传入的实参,若是Cat,进行强转回Cat后,再执行“抓老鼠”
  if (a instanceof Cat){
    Cat c = (Cat)a;,
    c.catchMouse();
  }
  
  // 若传入的是狗,就执行“叫”
  if (a instanceof Dog) {
    Dog d = (Dog)a;
    d.bark();
  }
}

Animal a = new Cat();

  • 若子类没有覆盖父类的方法,就调用父类自己的方法。
  • 若子类调用了自己特有的方法,父类并没有定义该方法,则编译不通过。Cat类型提升为Animal后,Cat特有的方法会被舍弃,与Animal同名的函数被覆盖

换个说法,a是Animal,它只能用Animal定义过的方法和变量,但执行函数时实际执行的是Cat的重写方法。总的来说,如下

  • 对于成员变量,和静态函数:编译和运行都是看父类是否具有该变量和静态,若a调用了Cat独有的变量,则报错。
  • 对于成员函数,编译时候检查Animal是否定义该方法,运行时候执行子类重写的方法,若子类没有重写,自然执行父类的。

内部类-嵌套类

public class Out {
  private String out;
  
  private class In {
    private String in;
  }
  
}
  • 内部类可以直接访问外部类的成员(private的也行)
  • 但是外部类要访问内部类,必须建立内部类的对象。
  • 内部类也可以是static的,这是,内部类随着外部类的加载而加载,相当于是外部类。
Out.In in = new Out().new In(); // 非static内部类的实例化
Out.In in = new Out.In(); // static内部类可以这样实例化
In in = new In(); // static内部类也可以这样实例化,相当于外部类了,仅在本例有效

如果内部类用到了静态成员,则内部类必须是static的。

同名变量的访问。

public class Out {
  private int num = 1;
  
  private class In {
    int num= 2;
    public void show() {
      int num = 3;
      System.out.println(num); // 打印3
      System.out.println(this.num); // 打印2
      System.out.println(Out.this.num); // 打印1 
    }
    
  }
}

方法内部的内部类

public class Out {
    public void func() {
      // num是func方法中的局部变量,func中又定义了内部类,用到这个num,该num必须是final的
        final int num = 1;
        // java8中,会默认加上final,所以可以直接int num = 1;
        class In {
            public void show() {
                System.out.println(num);
            }
        }

    }
}

方法里的内部类不能访问外部类方法中的局部变量,除非变量被声明为final类型,因为内部类对象的生命周期超过局部变量的生命周期。有可能出现成员方法已调用结束,局部变量已死亡,但匿名内部类的对象仍然活着。

Java 8中:如果局部变量被匿名内部类访问,那么该局部变量相当于自动使用了final修饰。

匿名内部类

若一个程序的某个类只使用了一次,则可定义为匿名内部类,用完就消亡。条件是必须继承或者实现一个外部类或者接口。

abstract class Person {
    public abstract void eat();
}
// 不用内部类的情况,需要再写一个类继承Person覆盖方法
class Child extends Person {
  @Override
  public abstract void eat() {
    System.out.println("Child eat");
  }
}

public class Out {
    public static void main(String[] args) {
      // 匿名内部类,这里相当于继承了抽象类Person,新写了一个无名的子类,并覆盖了eat方法
        new Person() {
            public void eat() {
                System.out.println("eat something");
            }
        }.eat();
    }
}

下面内容转自Nerxious-博客园,总得得不错,特地搬过来。

// 不使用匿名内部类
abstract class Person {
    public abstract void eat();
}
 
class Child extends Person {
    public void eat() {
        System.out.println("eat something");
    }
}
 
public class Demo {
    public static void main(String[] args) {
        Person p = new Child();
        p.eat();
    }
}
//运行结果:eat something

// 可以看到,我们用Child继承了Person类,然后实现了Child的一个实例,将其向上转型为Person类的引用但是,如果此处的Child类只使用一次,那么将其编写为独立的一个类岂不是很麻烦?这个时候就引入了匿名内部类

 

// 匿名内部类的基本实现
abstract class Person {
    public abstract void eat();
}
 
public class Demo {
    public static void main(String[] args) {
        Person p = new Person() {
            public void eat() {
                System.out.println("eat something");
            }
        };
        p.eat();
    }
}
//运行结果:eat something

// 可以看到,我们直接将抽象类Person中的方法在大括号中实现了这样便可以省略一个类的书写并且,匿名内部类还能用于接口上

 
// 在接口上使用匿名内部类
interface Person {
    public void eat();
}
 
public class Demo {
    public static void main(String[] args) {
        Person p = new Person() {
            public void eat() {
                System.out.println("eat something");
            }
        };
        p.eat();
    }
}
//运行结果:eat something

 

// 由上面的例子可以看出,只要一个类是抽象的或是一个接口,那么其子类中的方法都可以使用匿名内部类来实现。最常用的情况就是在多线程的实现上,因为要实现多线程必须继承Thread类或是继承Runnable接口

// Thread类的匿名内部类实现
public class Demo {
    public static void main(String[] args) {
        Thread t = new Thread() {
            public void run() {
                for (int i = 1; i <= 5; i++) {
                    System.out.print(i + " ");
                }
            }
        };
        t.start();
    }
}
// 运行结果:1 2 3 4 5

// Runnable接口的匿名内部类实现
public class Demo {
    public static void main(String[] args) {
        Runnable r = new Runnable() {
            public void run() {
                for (int i = 1; i <= 5; i++) {
                    System.out.print(i + " ");
                }
            }
        };
        Thread t = new Thread(r);
        t.start();
    }
}
// 运行结果:1 2 3 4 5

by @sunhiayu

2016.12.11

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,898评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,401评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,058评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,539评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,382评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,319评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,706评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,370评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,664评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,715评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,476评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,326评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,730评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,003评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,275评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,683评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,877评论 2 335

推荐阅读更多精彩内容

  • 一:java概述:1,JDK:Java Development Kit,java的开发和运行环境,java的开发工...
    ZaneInTheSun阅读 2,612评论 0 11
  • 一、继承 当两个事物之间存在一定的所属关系,即就像孩子从父母那里得到遗传基因一样,当然,java要遗传的更完美,这...
    玉圣阅读 1,043评论 0 2
  • 你很清楚的知道什么时候用抽象类,什么时候用接口么?p.s. 多文字预警! 1 抽象类和接口简介 1.1 抽象类 ...
    Sharember阅读 2,325评论 9 55
  • 1.import static是Java 5增加的功能,就是将Import类中的静态方法,可以作为本类的静态方法来...
    XLsn0w阅读 1,200评论 0 2
  • 面向对象主要针对面向过程。 面向过程的基本单元是函数。 什么是对象:EVERYTHING IS OBJECT(万物...
    sinpi阅读 1,036评论 0 4