zoukankan      html  css  js  c++  java
  • 疯狂java学习笔记之面向对象(九)

    一、抽象(abstract):

      1、抽象类:

       使用abstract修饰的类就是抽象类;

       相比于普通类抽象类增加了支持抽象方法的功能,但也丢失了创建实例的功能(抽象类中不能创建实例),其他普通类有的抽象类可以有。

       抽象类的三个注意点:

        ①、虽然抽象类不能创建实例,但它依然有构造器(主要给其子类的构造器调用-子类至少调用父类构造器一次);

        ②、抽象类中可以没有抽象方法(可有可无);

        ③、抽象类总是不能创建实例/对象的 - 即使它没有包含抽象方法。

     1 public abstract class TestAbstract{
     2     private int age;
     3 
     4     {
     5         System.out.println("-这是个对象初始化块-");
     6     }
     7 
     8     public TestAbstract(){}
     9 
    10     //虽然抽象类不能创建实例,但它依然有构造器(主要是供其子类构造器调用,至少调用一次)
    11     //子类不使用super()时则隐式调用父类无参构造器,有super()则根据参数来决定。
    12     public TestAbstract(int age){
    13         this.age = age;
    14     }
    15 
    16     public static void main(String[] args){
    17         System.out.println("Hello World!!");
    18         //抽象类中不能创建实例,编译时即报错
    19         //TestAbstract ta = new TestAbstract();
    20     }
    21 }

      2、抽象方法:

       使用abstract修饰且没有方法体的方法就是抽象方法。

       注意点:

        ①、抽象方法不能有方法体 - 花括号内容(出现花括号没任何代码都算有方法体);

        ②、abstract不能和final同时出现 - 因为abstract方法必须由子类去重写,而final修饰的方法又不允许重写,因此不能同时出现;

          对于abstract类来说,该类主要用于派生子类,final修饰的类同样不允许被集成,因此也不能同时出现 - abstract和final是永远互斥的;

        ③、abstract与static不能同时修饰方法   - static修饰的方法编译时已经确定下来,无法让子类@Override;

        ④、abstract与private不能同时修饰方法 - private修饰的方法不能被子类访问,而abstract又要求方法必须由子类重写,所以两者是互斥的。

     1 public abstract class TestAbstract2{
     2     //抽象类相对于普通类除了不能创建实例,其他功能都是有的,普通方法等能存在。
     3     public void info(){}
     4 
     5     //抽象方法中不能有方法体-即使方法体内容为空
     6     //public abstract void aa(){}
     7 
     8     public abstract void test();
     9 
    10     //abstract 与static不能同时修饰方法 - static修饰的方法编译时已经确定下来,无法让子类@Override
    11     //public abstract static void bb();
    12 
    13     //abstract方法必须由子类重写,而private又不能让子类访问所以出错
    14     private abstract void cc();
    15 
    16 }

      三、抽象的作用:

       抽象类的作用:主要是与"模版模式" 结合一起使用的。

       比如有如下场景需求:程序要实现A方法,但A方法又需要调用B方法且B方法在该类中暂时不知道如何实现(因为其子类实现B方法的方法可能都不一样,因此B方法可定义成抽象方法)。

       抽象类的子类一般要实现抽象父类提供的所有方法,否则该子类也只能是抽象类(这样就无实际意义~)  

     1 abstract class SpeedMeter{
     2     //定义一个转动速度。
     3     private double turnRate;
     4 
     5     //抽象类的构造器主要用于被子类的构造器调用
     6     public SpeedMeter(double turnRate){
     7         this.turnRate = turnRate;
     8     }
     9 
    10     //对于private修饰的Field一般推荐设置个set和get方法,典型的封装
    11     public void setTurnRate(double turnRate){
    12         this.turnRate = turnRate;
    13     }
    14 
    15     public double getTurnRate(){
    16         return this.turnRate;
    17     }
    18 
    19     //计算速度的方法
    20     /*
    21         这里就是模版模式,此处我们定义一个计算速度的模板:转速*周长
    22         但此处并不清楚计算周长方法,所以此处把周长getSpeed()定义成抽象方法,子类只需提供计算周长的方法即可
    23         计算周长的方法是有差异的,车轮:2*PI*半径等 
    24     */
    25     public abstract double getSpeed();
    26 
    27 }
    28 
    29 class CarSpeedMeter extends SpeedMeter{
    30     private double radius;
    31 
    32     public CarSpeedMeter(double radius,double turnRate){
    33         super(turnRate);
    34         this.radius = radius;
    35     }
    36 
    37     @Override
    38     public double getSpeed(){
    39         return 2 * radius * Math.PI;
    40     }
    41 }
    42 
    43 class TankSpeedMeter extends SpeedMeter{
    44     private double bianchang;
    45     public TankSpeedMeter(double bianchang,double turnRate){
    46         super(turnRate);
    47         this.bianchang = bianchang;
    48     }
    49 
    50     //假设坦克的周长计算方法为 8 * 边长
    51     @Override
    52     public double getSpeed(){
    53         return 8 * bianchang;
    54     }
    55 }
    56 
    57 public class TestSpeedMeter{
    58     public static void main(String[] args){
    59         SpeedMeter sm = new CarSpeedMeter(20,2000);
    60         System.out.println("汽车的速度为: " + sm.getSpeed());
    61         SpeedMeter tk = new TankSpeedMeter(8,1000);
    62         System.out.println("坦克的速度为: " + tk.getSpeed());
    63     }
    64 }

    二、接口(interface):

      接口的作用非常丰富,接口往往是和设计模式结合一起使用的。

      接口可以认为是一种"彻底"的抽象类,它是从多个相似的类中抽取出来的一种规范,接口体现的是规范

      接口体现的规范:如主板上各种不同类型的"插槽",无论接入的哪个厂商、型号的硬件都可以彼此进行通信(任何一套公开的标准/规范,需要通过接口来体现)。

      接口定义语法:

      [修饰符] interface 接口名{

        //0~N 个Field、特殊的Field

        //0~N 个抽象方法

        //0~N 个内部类、内部接口、内部枚举类定义

      }

      [修饰符]只能是public和默认省略,不能是protected因为接口在保外,而protected只能在包名进行访问

      接口名: 由多个单词连缀而成,每个单词的首字母要大写,推荐以i开头(c#定义已i开头且是个不错的习惯)。

      注意点:

       ①、接口不能有构造器,也不能有初始化块;

       ②、接口的Field默认有3个修饰符: public static final,无论你写或不写,反正都有这三个修饰符;

        接口的Field定义时必须指定初始值 -- 接口Field默认有static final修饰,final修饰的变量只能被赋值一次,你不指定系统给你赋默认值0、0.0、null等。

       ③、接口的方法默认有2个修饰符:public abstract,无论你写与不写,反正都有这二个修饰符;

        接口的方法不能有static,因为接口的方法默认有abstract修饰。

       ④、接口的内部类、内部枚举类、内部接口,默认有两个修饰符: public static

     1 public interface iWalk{
     2     int age = 33;
     3     //接口中的Field默认有public static final修饰,必须在声明时指定初始值
     4     //final修饰的变量必须在声明时/初始块中指定,但接口又不能有初始化块,那必须在声明时指定了
     5     //String name;
     6     
     7     //接口的所有成员都需要提供给外部类、包访问,接口公开的标准必须由public修饰
     8     //private double weight = 99;
     9 
    10     //接口中的方法默认有public abstract修饰
    11     void info();
    12 
    13     //抽象方法不能有方法体
    14     //void test(){}
    15 }

      接口的继承:

      一个接口可以有N个直接父接口;

      接口可用于定义变量不能直接创建实例提供其他类来实现自己

      implements:实现一个或N个接口,接口实现类要么为接口所有的抽象方法提供实现,否则你的实现类也只能是抽象类。接口主要是在"面向接口编程"时提供更好、更灵活的机制。

     1 interface IEatable{
     2     void eat();
     3 }
     4 
     5 interface IFlyable{
     6     void fly();
     7 }
     8 
     9 interface Iwalkable extends IEatable{
    10     int age = 20;
    11     void info();
    12     /*
    13         接口中的方法必须为抽象方法
    14     public static void main(String[] args){
    15         Iwalkable iw = new Sparrow(); 
    16     }
    17     */
    18 }
    19 
    20 public class Sparrow implements IFlyable,Iwalkable{
    21     //因为Sparrow实现了两个接口,所以它必须实现两个接口的所有方法
    22     @Override
    23     public void eat(){
    24         System.out.println("这只麻雀在一口一口吃麦子");
    25     }
    26 
    27     public void fly(){
    28         System.out.println("这麻雀在飞走了!");
    29     }
    30 
    31     public void info(){
    32         System.out.println("这是一只小小麻雀");
    33     }
    34 
    35     public static void main(String[] args){
    36         //向上转型,iw引用变量编译时只是Iwalkable类型,编译器只能允许它调用Iwalkable内的方法
    37         Iwalkable iw = new Sparrow();
    38         iw.info();
    39         iw.eat();
    40         //iw编译时为Iwalkable类型,直接调用fly方法会报错
    41         //iw.fly();
    42         
    43         //Sparrow sp = new Sparrow();
    44         //强制向下转型
    45         Sparrow sp = (Sparrow)iw;
    46         sp.fly();
    47 
    48         IFlyable ia = new Sparrow();
    49         ia.fly();
    50     }
    51 }

    接口与抽象类之间的相似之处:

      ①、都可以包含抽象方法;

      ②、都不能创建实例

      ③、子类抽象类或实现接口,都要求实现所有抽象方法,否则这个子类也只能是抽象类

    接口与抽象类之间的区别

      ①、接口里只能有抽象方法,而抽象类中可以没有抽象方法也可以包含普通方法;

      ②、接口中不能定义static方法,而抽象类中可以包含static方法;

      ③、接口的Field默认有public static final三个修饰符修饰,而抽象类的Field完全可以是普通Field;

      ④、接口不能包含构造器,而抽象类完全可以有构造器(供子类至少调用一次);

      ⑤、接口不能包含初始化块,而抽象类完全可以有初始化块;

      ⑥、接口可以有多个直接的父接口,而抽象类只能有一个直接的父类。

  • 相关阅读:
    Java面试基础 -- Git篇
    Java面试基础
    如何避免死锁?
    如何减少上下文切换?
    Java中的volatile变量有什么作用?
    Thread类中start()方法喝run()方法有什么不同?
    (一)java异常处理的几个问题
    SUSE CaaS Platform 4
    SUSE CaaS Platform 4
    SUSE Ceph 增加节点、减少节点、 删除OSD磁盘等操作
  • 原文地址:https://www.cnblogs.com/dtest/p/4177170.html
Copyright © 2011-2022 走看看