抽象方法和抽象类

抽象方法

使用abstract修饰的方法,没有方法体,只有声明。定义的是一种规范,就是告诉子类必须要给抽象方法提供具体的实现(子类也可以部分实现或完全不实现父类的所有抽象方法,但此时子类必须声明为抽象类)。

抽象类

抽象类的概念以及使用

  1. 定义了抽象方法的类必须被声明为抽象类,不过抽象类可以没有抽象方法。
  2. 抽象类可以包含属性、方法、构造方法。但是构造方法不能用来new实例,只能用来被子类调用。
  3. 抽象类只能用来被继承,不能使用new关键字创建对象。
  4. 子类必须要给抽象方法提供具体的实现,子类也可以部分实现或完全不实现父类的所有抽象方法,但此时子类必须声明为抽象类。

为什么需要抽象类

  1. 抽象方法和抽象类看起来像是多余的,对于抽象方法,不知道如何实现,定义一个空的方法体不就好了,抽象类不能创建对象,看起来只是添加一个限制条件。

  2. 在Java中引入抽象方法和抽象类,是一种规范体现,对于某些类和方法的使用,能引导使用者正确的使用它们。

    (1)使用抽象方法而不是使用非空方法体,子类就知道它必须实现该方法,而不能忽略,忽略的话编译器就会报错。

    (2)使用抽象类,类的使用者创建对象的时候,就知道要使用某个具体子类,而不可能使用不完整的抽象类。

用一个植物的例子更直观的明白抽象类以及抽象方法的使用:

public class AbstractTest {
    public static void main(String[] args) {
        Flower flower=new Flower();
        flower.plant();
        flower.growsIn();
        Aqua_plant aqua_plant=new Aqua_plant();
        aqua_plant.plant();
        aqua_plant.growsIn();
    }
}

abstract class Plant{
    //普通方法
    public void plant(){
        System.out.println("都是植物");
    }
    //抽象方法
    public abstract void growsIn();
}

class Flower extends Plant{
    @Override
    public void growsIn() {
        System.out.println("花生在土里。");
    }
}

class Aqua_plant extends Plant{
    @Override
    public void growsIn() {
        System.out.println("水草生在水里。");
    }
}

接口

接口的概念

接口就是比“抽象类”还“抽象”的“抽象类”,可以更加规范的对子类进行约束。全面地专业地实现了:规范和具体实现的分离。

接口的使用

声明格式:

[访问修饰符]  interface 接口名   [extends  父接口1,父接口2…]  {
    常量定义;
    方法定义;
    (静态方法定义;)
}
  1. 访问修饰符:只能是public或默认。
  2. 接口名:和类名采用相同命名机制。
  3. extends:接口可以多继承。
  4. 常量:接口中的属性只能是常量,总是:public static final 修饰。不写也是。
  5. 方法:接口中的方法只能是:public abstract。 省略的话,也是public abstract。
  6. JDK1.8之后,接口可以包含普通的静态方法

子类使用方法:

  1. 子类通过implements来实现接口中的规范,一个类可以实现多个接口。
  2. 接口不能创建实例,但是可用于声明引用变量类型。
  3. 一个类实现了接口,必须实现接口中所有的方法,并且这些方法只能是public的。
  4. JDK1.7之前,接口中只能包含静态常量、抽象方法,不能有普通属性、构造方法、普通方法,JDK1.8后,接口可以包含普通的静态方法。

接口与抽象类的区别

接口和抽象类中都包含了抽象类,但是接口和抽象类有以下几种区别:

  1. 接口是多继承的,而类是单继承。
  2. 一个类可以实现任意多个接口,但最多只能作为一个抽象类的子类。
  3. 一个抽象类可以有若干个抽象方法(但到少要有一个),而接口的所有方法都是抽象的,无论是否将它的方法显示地声明为抽象的。
  4. 一个抽象类可以声明实例变量,其子类可以继承这些实例变量。而一个接口不能声明实例变量,不过接口可以声明static final修饰域。
  5. 抽象类可以有构造方法,而接口不能,但是两者都不能直接实例化,都必须通过其他类的实现才能使用。
  6. 抽象类的可见性修饰符可以是public、protected、private或无修饰符(表示包内可见);而接口的可见性修饰符只能是 public,或无修饰符(包内可见)。
  7. 抽象类的方法的可见性修饰符可是以protected、private,或无(表示包内可见);而一个接口的方法的可见性修饰符只能是 public (JDK1.8之后,接口可以包含普通的静态方法)
  8. 抽象类是从object类派生而来,它继承了object的clone()和equals()方法。
  9. 设计模式讲,继承强调是:is-a;而接口强调的则是has-a。

使用一个人的例子更直观的了解下接口:

public class InterfaceTest {
    public static void main(String[] args) {
        People.say();  //调用接口中的静态方法需要通过接口来调用
        Teacher teacher=new Teacher();
        teacher.profession();
        teacher.work();
        Student student=new Student();
        student.profession();
        student.work();
    }
}
interface A{
}
interface B{
}
interface People extends A,B/*接口的多继承*/{
    //定义静态常量
    public static final int a=10;
    /*public static final */boolean flag=true;  //默认也是public static final修饰
    //定义抽象方法
    /*public abstract */void profession();  //默认也是public abstract修饰
    public abstract void work();
    //JDK1.8之后,接口可以包含普通的静态方法
    public static void say(){
        System.out.println("Hello!");
    }
}
//子类必须实现接口中所有的抽象方法(静态方法就不需要实现)
class Teacher implements People,A,B/*子类可以实现多个接口*/ {
    @Override
    public void profession() {
        System.out.print("教师");
    }
    @Override
    public void work() {
        System.out.println("教学");
    }
}
class Student implements People{
    @Override
    public void profession() {
        System.out.print("学生");
    }
    @Override
    public void work() {
        System.out.println("学习");
    }
}

内部类

内部类的概念

内部类我们从外面看是非常容易理解的,无非就是在一个类的内部在定义一个类。一般情况,我们把类定义成独立的单元,有些情况下,我们把一个类放在另一个类的内部定义,称为内部类。内部类可以使用public、default、protected 、private以及static修饰,而外部类(我们以前接触的类)只能使用public和default修饰。

注意事项:内部类只是一个编译时概念,一旦我们编译成功,就会成为完全不同的两个类。对于一个名为Outer的外部类和其内部定义的名为Inner的内部类。编译完成后会出现Outer.class和Outer$Inner.class两个类的字节码文件。所以内部类是相对独立的一种存在,其成员变量/方法名可以和外部类的相同。

内部类的作用

  1. 内部类提供了更好的封装。只能让外部类直接访问,不允许同一个包中的其他类直接访问。

  2. 内部类可以直接访问外部类的私有属性,内部类被当成其外部类的成员。 但外部类不能访问内部类的内部属性。

  3. 接口只是解决了多重继承的部分问题,而内部类使得多重继承的解决方案变得更加完整。

  4. 由于内部类提供了更好的封装特性,并且可以很方便的访问外部类的属性。所以,在只为外部类提供服务的情况下可以优先考虑使用内部类。

  5. 使用内部类间接实现多继承:每个内部类都能独立地继承一个类或者实现某些接口,所以无论外部类是否已经继承了某个类或者实现了某些接口,对于内部类没有任何影响。

内部类的分类

在Java中内部类主要分为成员内部类(非静态内部类、静态内部类)、匿名内部类、局部内部类。

成员内部类

非静态内部类
  1. 外部类里使用非静态内部类和平时使用其他类没什么不同。
  2. 非静态内部类必须寄存在一个外部类对象里。因此,如果有一个非静态内部类对象那么一定存在对应的外部类对象。非静态内部类对象单独属于外部类的某个对象。
  3. 非静态内部类可以直接访问外部类的成员,但是外部类不能直接访问非静态内部类成员。
  4. 非静态内部类不能有静态方法、静态属性和静态初始化块。
  5. 外部类的静态方法、静态代码块不能访问非静态内部类,包括不能使用非静态内部类定义变量、创建实例。
  6. 非静态内部类成员变量的访问方法:A.内部类里方法的局部变量:变量名。B.内部类属性:this.变量名。C.外部类属性:外部类名.this.变量名。
  7. 内部类的访问方法:A.外部类中定义内部类:new 内部类名();B.外部类以外的地方使用非静态内部类:外部类名.内部类名 变量名= new 外部类名().new 内部类名()。
静态内部类
  1. 当一个静态内部类对象存在,并不一定存在对应的外部类对象。 因此,静态内部类的实例方法不能直接访问外部类的实例方法。
  2. 静态内部类看做外部类的一个静态成员。 因此,外部类的方法中可以通过:静态内部类.名字的方式访问静态内部类的静态成员,通过new 静态内部类()访问静态内部类的实例。在外部类以外的地方使用可以通过外部类名.内部类名 变量名 =new 外部类名.内部类名();来访问。

匿名内部类

匿名内部类适用于仅需要使用一次的类。

  • 静态内部类的语法:
new 父类构造器(实参类表)或实现接口() {
    //匿名内部类类体
}
  • 注意事项:
  1. 匿名内部类没有访问修饰符。
  2. 匿名内部类没有构造方法。因为它连名字都没有那又何来构造方法呢。

局部内部类

局部内部类是定义在方法内部的,作用域只限于本方法。局部内部类的用法和成员内部类的用法一样,但是它只能在定义的方法体中使用,离开了方法体就失效。