抽像类和接口

目录

抽象类

一、抽象类 (abstract)

二、抽象方法

三、抽象类和抽象方法的应用

四、final用法

 接口

 五、什么是接口

 六、接口进阶

七、面向对象设计原则

八、本章总结 


抽象类

一、抽象类 (abstract)

语法:

[访问修饰符]  abstract class <类名> {
     //省略代码……
}

示例:

public abstract class Crop {
     //省略代码……
}

测试:

 结论:
   抽象类不能被实例化(Crop is abstract; cannot be instantiated)

二、抽象方法

问题: 下面代码图的执行结果,有什么问题?

 分析:
​     从图中不难发现 Crop中print()方法体中输出的作物特性信息是无实际意义的,简单来说就是无用代码。
方案: 
​     抽象类中使用抽象方法优化代码
什么是抽象方法?
​     使用**abstract**关键字修饰的方法称之为抽象方法
​            抽象方法没有方法体
​            抽象方法必须在抽象类里
​            抽象方法必须在子类中被实现,除非子类是抽象类
语法:

[访问修饰符]  abstract  class 类名 {
    [访问修饰符] abstract <返回类型> <方法名>([参数列表]);
}

示例:

//抽象类
public abstract class Crop { 
    
    //抽象方法,只有方法的声明,没有方法体中具体的实现
    public abstract void print();
    
    //普通方法
    public void test(){   
    }
}

结论
​     有抽象方法的类必然是抽象类。
​     但抽象类中的方法并不一定都是抽象方法。
​     抽象方法只有方法声明,无具体实现。

三、抽象类和抽象方法的应用

需求: 为农场增加除虫功能,具体要求如下
在“星沐生态农场”补充为作物除虫的相关功能,每种作物输出不同的除虫信息
苹果树:萌芽前和开花时打药3次,果实生长期打药2次,采摘期打药1次
玉米:种子喷洒农药后再播种,防止地下害虫咬食,果实生长期打药1次
分析:

每种作物有各自的除虫方案
在父类Crop中,无法抽取出其所有子类共同的除虫信息
在父类中定义抽象方法disinfestation()
在每个子类中重写该方法,实现该类对象的除虫操作
示例:

Crop类

public abstract class Crop{

 //省略属性和方法...

 //定义除虫的抽象方法
 public abstract void disinfestation();
}

AppleTree类

public class AppleTree extends Crop {
 // 没有重写父类的抽象方法disinfestation()
}

如图所示,当子类继承父类,父类是一个抽象类的时候,会出现红色的警告线,告诉我们编译错误,这也恰好论证了我们前面提出的观点:
​    抽象方法必须在子类中被实现,除非子类是抽象类

语法:

 方案:
在子类中重写父类定义的抽象方法disinfestation()
AppleTree类

@Override 
 public void disinfestation() {
      System.out.println(super.getName()+"萌芽前和开花时打药3次," 

   + "果实生长期打药2次,采摘期打药1次。");
} 

Corn类

@Override
 public void disinfestation() {
      System.out.println(super.getName()+"种子喷洒农药后再播种,"
             +"防止地下害虫咬食,果实生长期打药1次。");
 }

测试类关键代码

 public static void main(String[] args) {    
      Crop crop = new AppleTree("富士");//苹果树
      crop.disinfestation();
      crop = new Corn(50);//玉米
      crop.disinfestation();
 }

运行结果:

 抽象类的优势
​    提高可重用性
​      抽象类可以看作是类的一个模版,定义了子类的行为,可以为子类提供默认实现,无需子类中重复实现这些方法

代码松耦合,更易于维护
子类可以分别实现抽象父类中定义的抽象方法,将方法定义和方法实现的分离

方便实现多态
   抽象类作为继承关系下的抽象层,不能被实例化,通常定义抽象类类型变量,其具体引用是实现抽象类的子类对象
抽象类的应用场合
   抽象类用来列举一个类所需要的共性行为
   抽象类不明确提供具体实现方法
   抽象类必须由其子类实现它的抽象方法(除非子类也具有抽象性)

 抽象类与抽象方法使用总结
​     抽象方法只有方法声明,没有方法实现
​     有抽象方法的类必须声明为抽象类
​     子类必须重写所有的抽象方法才能实例化;否则,子类也必须声明成抽象类
​     抽象类中可以没有、有一个或多个抽象方法,甚至可以定义全部方法都是抽象方法
    抽象类可以有构造方法,其构造方法可以被本类的其他构造方法调用
​         不是由private修饰构造方法,可以被其子类的构造方法调用
    abstract可以用来修饰类和方法,不能用来修饰属性和构造方法

四、final用法

问题:
Dog类不希望再被其他类继承,应该怎么做?
  使用final类

public final class Puppy extends Dog { 
    //…
}

方法不希望被重写?
  使用final方法

public class Student {
    public final void print() {
        System.out.println("我是一个学生!");
    }
}

属性值不希望被修改?
使用常量

public class Dog {
    final String  name ="帅帅";
    public void setName(String name) {
        this.name=name; //编译错误,不可再赋值
    }
}

常见错误:
1.使用final修饰引用型变量,变量的值是固定不变的,而变量所指向的对象的属性值是可变的

public class Student {
    String name;
    public Student(String name) {
        this.name = name;
    }

    public static void main(String[] args) {
        final Student stu = new Student("李明");
        stu.name = "李明航"; 
        stu = new Student("王亮");//编译错误,使用final修饰的引用型变量不可以再指向其他对象
    }
}

2.使用final修饰的方法参数,这些参数的值是固定不变的

class Value {
    int v;
}

public class Test {
    public void changeValue(final int i, final Value value) { 
        i = 8; //编译错误:使用final修饰的方法参数,在整个方法中不能改变参数值
        value.v = 8; 
    }
}

3.常见错误示例:
抽象方法只有声明无具体实现,static方法可通过类名直接访问,但无法修饰一个没有实现的方法

 抽象方法需在子类中重写,但private方法不能被子类继承,自然无法进行重写

抽象方法需要在子类中重写,但final修饰的方法表示该方法不能被子类重写,前后是相互矛盾的

 

 总结:
 final可以用来修饰类、方法和属性,不能修饰构造方法
 Java提供的很多类都是final类,不能重写
  如:String类、Math类
Object类有一些final方法,只能被子类继承而不能被重写
  如:getClass( )、notify( )、wait( )
Object类的hashCode( )、toString( )、equals(Object obj)方法不是final方法,可以被重写

经验:
 abstract和static不能结合使用
 abstract和private不能结合使用
 abstract和final不能结合使用

 接口

 类只能单继承,接口才可以改变这种囧境。

 五、什么是接口

生活中的接口就是一套规范
​    如:主板上的PCI插槽有统一的标准,规定了尺寸、排线
​    满足规范标准的设备可以组装在一起

 Java 中,接口
 是一种规范和标准
​    可以约束类的行为,使得实现接口的类(或结构)在形式上保持一致
是一些方法特征的集合
​     可看作是一种特殊的“抽象类”
​    但采用与抽象类完全不同的语法
抽象类利于代码复用,接口利于代码的扩展和维护
 是一个不能实例化的类型
 定义接口

//如果是public,则在整个项目中可见,如果省略,则只在该包中可见
[访问修饰符]  interface 接口名 {
// 接口成员
}

接口中的变量都是全局静态常量
  自动使用public static final修饰
  必须在定义时指定初始值

public interface Cirlcle {
      int P=5; 
      public static final int P = 5;
      int P;
  } 
public class 类名 implements 接口名 {
 //类成员
}

  实现类必须实现接口的所有方法
  实现类可以实现多个接口
注意:JDK1.8版本之前,接口中只能定义抽象方法,自JDK1.8版本开始,接口还允许定义静态方法和默认方法
  向后兼容
允许开发者在已有接口里添加新的方法时不需改动已经实施该接口的所有实现类
接口表示一种能力
 “做这项工作需要一个钳工(木匠/程序员)”
 钳工是一种“能力”,不关心具体是谁
 接口是一种能力
 体现在接口的方法上
 面向接口编程

 

 六、接口进阶

定义复杂的接口
接口的多继承语法:

//通过extends实现接口的继承关系
[ 访问修饰符 ] interface 接口名 extends 父接口 1, 父接口 2,……{
    //常量定义
    //方法定义
}

注意事项1: 一个接口可以继承多个接口,但接口不能继承类
类实现多个接口:

//通过implements实现多个接口
[ 访问修饰符 ] class 类名 extends 父类名 implements 接口 1, 接口 2,……{
    //类的成员
}

注意事项2:
   一个普通类只能继承一个父类,但能同时实现多个接口
   extends 关键字必须位于 implements关键字之前
   类必须实现所有接口(接口1、接口2…)的全部抽象方法,否则必须定义为抽象类
注意事项3:
  如果一个类实现了多个接口,且这些接口具有相同的默认方法,应该如何处理?
  在实现类中必须提供自己的默认方法,来覆盖接口中的默认方法。例如:接口A中定义了默认方法print(),接口B中也定义了不同实现的默认方法print()。如果类C实现了接口A和接口B,则类C中必须定义自己的print()方法;否则,在调用C对象的print()方法时无法确定是访问接口A的print()方法,还是访问接口B的print()方法

interface A {
     default void print () {
        System.out.println("这是A接口的一个默认方法");
    }
}
interface B {
     default void print () {
        System.out.println(“这是B接口的一个默认方法");
    }
}
class C implements A,B {
    //C类中必须定义自己的print()方法
    void print () {
        System.out.println(“这是C类的print方法");
    }
}
public static void main(String[] args) {
     C cObject = new C();
    //无法确定是访问接口A或接口B的print()方法
     cObject.print();//调用C类print()方法
}

七、面向对象设计原则

为了让代码更具灵活性,更能适应变化,需遵循的原则
​     摘取代码中变化的部分,形成接口
​    多用组合,少用继承
​    面向接口编程,不依赖于具体实现
​    针对扩展开放,针对改变关闭
​         ——开闭原则
经验 面向接口编程可以实现接口和实现的分离,能够在客户端未知的情况下修改实现代码
如何理解接口是一种能力?
接口有比抽象类更好的特性
   可以被多继承
   设计和实现完全分离
   更自然的使用多态
   更容易搭建程序框架
   更容易更换实现
   ……

八、本章总结 

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值