接口与继承

八.接口与继承

1.接口的定义为的关键字为interface
2.接口中只能有方法和变量,变量为public static final类型,方法只能被可以为public(默认)和protected修饰,方法不能有具体的实现
3.一个类可以实现多个接口
4.一个类实现接口,必须要实现接口的所有的方法

1.接口

接口就像是一种约定,我们约定某些英雄是物理系英雄,那么他们就一定能够进行物理攻击。 
实现某个接口,就相当于承诺了某种约定
 什么样的情况下该使用接口? 
 如上的例子,似乎要接口,不要接口,都一样的,那么接口的意义是什么呢

学习一个知识点,是由浅入深得进行的。 这里呢,只是引入了接口的概念,要真正理解接口的好处,需要更多的实践,以及在较为复杂的系统中进行大量运用之后,才能够真正理解,比如在学习了多态之后就能进一步加深理解
代码示例:
// 接口
public interface AP { 
    public void magicAttack();
}
// 实现
public class APHero extends Hero implements AP{ 
    @Override
    public void magicAttack() {
        System.out.println("进行魔法攻击");
    } 
}

2.对象转型

1.明确引用类与对象类型的概念

首先,明确引用类型与对象类型的概念
在这个例子里,有一个对象 new ADHero(), 同时也有一个引用ad
对象是有类型的, 是ADHero
引用也是有类型的,是ADHero
通常情况下,引用类型和对象类型是一样的
接下来要讨论的类型转换的问题,指的是引用类型和对象类型不一致的情况下的转换问题 

[图片上传失败...(image-6ce689-1585128357168)]

public class Hero {
       public String name;
       protected float hp;
       public static void main(String[] args) {
           ADHero ad = new ADHero();
       }
    }
    ```
    
#### 2.子类转父类(向上转型)

所谓的转型,是指当引用类型和对象类型不一致的时候,才需要进行类型转换
类型转换有时候会成功,有时候会失败(参考基本类型的类型转换)
到底能否转换成功? 教大家一个很简单的判别办法
把右边的当做左边来用,看说得通不
右边ad引用所指向的对象的类型是 物理攻击英雄
左边h引用的类型是 普通英雄
把物理攻击英雄 当做 普通英雄,说不说得通? 说得通,就可以转
所有的子类转换为父类,都是说得通的。比如你身边的例子
苹果手机 继承了 手机,把苹果手机当做普通手机使用
怡宝纯净水 继承了 饮品, 把怡宝纯净水 当做饮品来使用


[图片上传失败...(image-d96d98-1585128357168)] 

```java
public class Hero {
   public String name;
   protected float hp;
   public static void main(String[] args) {
              Hero h = new Hero();
             ADHero ad = new ADHero();
          //类型转换指的是把一个引用所指向的对象的类型,转换为另一个引用的类型
   
              //把ad引用所指向的对象的类型是ADHero
               //h引用的类型是Hero
               //把ADHero当做Hero使用,一定可以
               h = ad;
            }
    }
    ```
    
    #### 3.父类转子类(向下转型)
    
    父类转子类,有的时候行,有的时候不行,所以必须进行强制转换。
    强制转换的意思就是 转换有风险,风险自担。 
    
    什么时候行?
    
    ```java
    1.        Hero h =new Hero();
   2.        ADHero ad = new ADHero();
3.        h = ad;
4.        ad = (ADHero) h;

第3行,是子类转父类,一定可以的
第4行,就是父类转子类,所以要进行强转。
h这个引用,所指向的对象是ADHero, 所以第4行,就会把ADHero转换为ADHero,就能转换成功。

什么时候不行?

1.        Hero h =new Hero();
2.        ADHero ad = new ADHero();
3.        Support s =new Support();
4.        h = s;
5.        ad = (ADHero)h;

第4行,是子类转父类,是可以转换成功的
第5行,是把h引用所指向的对象 Support,转换为ad引用的类型ADHero。 从语义上讲,把物理攻击英雄,当成辅助英雄来用,说不通,所以会强制转换失败,并且抛出异常

以下是对完整的代码的关键行分析
14行: 把ad当做Hero使用,一定可以
转换之后,h引用指向一个ad对象
15行: h引用有可能指向一个ad对象,也有可能指向一个support对象
所以把h引用转换成AD类型的时候,就有可能成功,有可能失败
因此要进行强制转换,换句话说转换后果自负
到底能不能转换成功,要看引用h到底指向的是哪种对象
在这个例子里,h指向的是一个ad对象,所以转换成ADHero类型,是可以的
16行:把一个support对象当做Hero使用,一定可以
转换之后,h引用指向一个support对象
17行:这个时候,h指向的是一个support对象,所以转换成ADHero类型,会失败。
失败的表现形式是抛出异常 ClassCastException 类型转换异常

[图片上传失败...(image-d4f480-1585128357168)]

   package charactor;

   import charactor1.Support;

   public class Hero {
       public String name;
       protected float hp;
       public static void main(String[] args) {
           Hero h =new Hero();
           ADHero ad = new ADHero();
            Support s =new Support();

           h = ad;
           ad = (ADHero) h;
           h = s;
           ad = (ADHero)h;
       }

   }

4.没有继承关系的两个类,互相转换

没有继承关系的两个类,互相转换,一定会失败
虽然ADHero和APHero都继承了Hero,但是彼此没有互相继承关系
"把魔法英雄当做物理英雄来用",在语义上也是说不通的

[图片上传失败...(image-1b916e-1585128357168)]

public class Hero {
public String name;
protected float hp;

public static void main(String[] args) {
     ADHero ad = new ADHero();

   APHero ap = new APHero();

      // 没有继承关系的类型进行互相转换一定会失败,所以会出现编译错误
  ad = (ADHero) ap;
        }
   }
#### 5.实现类转换成接口(向上转型)

引用ad指向的对象是ADHero类型,这个类型实现了AD接口

10行: 把一个ADHero类型转换为AD接口
从语义上来讲,把一个ADHero当做AD来使用,而AD接口只有一个physicAttack方法,这就意味着转换后就有可能要调用physicAttack方法,而ADHero一定是有physicAttack方法的,所以转换是能成功的。

[图片上传失败...(image-422689-1585128357168)] 

```java
public class Hero {

public String name;
protected float hp;

public static void main(String[] args) {
ADHero ad = new ADHero();
AD adi = ad;
}
}


    #### 6.接口转换成实现类(向下转型)
   
10行: ad引用指向ADHero, 而adi引用是接口类型:AD,实现类转换为接口,是向上转型,所以无需强制转换,并且一定能成功
   12行: adi实际上是指向一个ADHero的,所以能够转换成功
    14行: adi引用所指向的对象是一个ADHero,要转换为ADAPHero就会失败。 
    
    假设能够转换成功,那么就可以使用magicAttack方法,而adi引用所指向的对象ADHero是没有magicAttack方法的。 
   
    [图片上传失败...(image-cd4e55-1585128357168)] 
    
   ```java
public class Hero {
  public String name;
  protected float hp;

   public static void main(String[] args) {
           ADHero ad = new ADHero();            
     AD adi = ad;   
         ADHero adHero = (ADHero) adi;           
         ADAPHero adapHero = (ADAPHero) adi;
          adapHero.magicAttack();
      }        
}

#### 7.instanceof

instanceof Hero 判断一个引用所指向的对象,是否是Hero类型,或者Hero的子类               

```java
public class Hero {
public String name;
protected float hp;

public static void main(String[] args) {
  ADHero ad = new ADHero();
 APHero ap = new APHero();

 Hero h1= ad;
  Hero h2= ap;       
 //判断引用h1指向的对象,是否是ADHero类型
      System.out.println(h1 instanceof ADHero);       
      //判断引用h2指向的对象,是否是APHero类型
   System.out.println(h2 instanceof APHero);        
      //判断引用h1指向的对象,是否是Hero的子类型
       System.out.println(h1 instanceof Hero);
    }

}
```

3.重写

子类可以继承父类的对象方法

在继承后,重复提供该方法,就叫做方法的重写

又叫覆盖 override

子类继承了父类,即子类也继承了父类的方法,子类可以重写父类的方法,来达到自己的目的, 如果没有重写这样的机制,也就是说LifePotion这个类,一旦继承了Item,所有方法都不能修改了。 ,这样增加了开发时间和维护成本

// 父类
public class Item {
       String name;
       int price;
       public void buy(){
           System.out.println("购买");
       }
       public void effect() {
           System.out.println("物品使用后,可以有效果");
       }
}
// 子类
public class LifePotion extends Item{
// 重写父类的方法
       public void effect(){
           System.out.println("血瓶使用后,可以回血");
       }
}

4.多态

操作符的多态:

​ +可以作为算数运算,也可以作为字符串连接

类的多态:

​ 父类引用指向子类对象(核心)

示例1:操作符的多态

同一个操作符在不同情境下,具备不同的作用
如果+号两侧都是整型,那么+代表 数字相加
如果+号两侧,任意一个是字符串,那么+代表字符串连接
public class Hero {
    public String name;
    protected float hp; 
    public static void main(String[] args) {        
        int i = 5;
        int j = 6;
        int k = i+j; //如果+号两侧都是整型,那么+代表 数字相加      
        System.out.println(k);         
        int a = 5;
        String b = "5";         
        String c = a+b; //如果+号两侧,任意一个是字符串,那么+代表字符串连接
        System.out.println(c);         
    }       
}

示例2:观察类的多态现象

观察类的多态现象:
1. i1和i2都是Item类型
2. 都调用effect方法
3. 输出不同的结果
多态: 都是同一个类型,调用同一个方法,却能呈现不同的状态 
//------------------父类-------------
public class Item {
    String name;
    int price; 
    public void buy(){
        System.out.println("购买");
    }
    public void effect() {
        System.out.println("物品使用后,可以有效果 ");
    }     
    public static void main(String[] args) {
        Item i1= new LifePotion();
        Item i2 = new MagicPotion();
        System.out.print("i1  是Item类型,执行effect打印:");
        i1.effect();
        System.out.print("i2也是Item类型,执行effect打印:");
        i2.effect();
    } 
}
// ---------------------血瓶---------
public class LifePotion extends Item {
    public void effect(){
        System.out.println("血瓶使用后,可以回血");
    }
}
// ----------------------蓝瓶---------
public class MagicPotion extends Item{ 
    public void effect(){
        System.out.println("蓝瓶使用后,可以回魔法");
    }
}

示例3:类的多态条件

要实现类的多态,需要如下条件
1. 父类(接口)引用指向子类对象
2. 调用的方法有重写

示例4:类的多态比较-不使用多态

如果不使用多态,
假设英雄要使用血瓶和魔瓶,就需要为Hero设计两个方法
useLifePotion
useMagicPotion
除了血瓶和魔瓶还有很多种物品,那么就需要设计很多很多个方法,比如
usePurityPotion 净化药水
useGuard 守卫
useInvisiblePotion 使用隐形药水
等等等等 
import property.LifePotion;
import property.MagicPotion;   
public class Hero {
    public String name;
    protected float hp; 
    public void useLifePotion(LifePotion lp){
        lp.effect();
    }
    public void useMagicPotion(MagicPotion mp){
        mp.effect();
    } 
    public static void main(String[] args) {         
        Hero garen =  new Hero();
        garen.name = "盖伦";     
        LifePotion lp =new LifePotion();
        MagicPotion mp =new MagicPotion();         
        garen.useLifePotion(lp);
        garen.useMagicPotion(mp);         
    }       
}

示例5:类的多态比较-使用多态

如果物品的种类特别多,那么就需要设计很多的方法
比如useArmor,useWeapon等等
这个时候采用多态来解决这个问题
设计一个方法叫做useItem,其参数类型是Item
如果是使用血瓶,调用该方法
如果是使用魔瓶,还是调用该方法
无论英雄要使用什么样的物品,只需要一个方法即可 
import property.Item;
import property.LifePotion;
import property.MagicPotion;   
public class Hero {
    public String name;
    protected float hp; 
    public void useItem(Item i){
        i.effect();
    }
    public static void main(String[] args) {         
        Hero garen =  new Hero();
        garen.name = "盖伦";     
        LifePotion lp =new LifePotion();
        MagicPotion mp =new MagicPotion();         
        garen.useItem(lp);
        garen.useItem(mp);              
    }       
}

5.隐藏

与重写类似,方法的重写是子类覆盖父类的对象方法

隐藏,就是子类覆盖父类的类方法

// 父类有一个类方法 :battleWin 
public class Hero {
    public String name;
    protected float hp;  
    //类方法,静态方法
    //通过类就可以直接调用
    public static void battleWin(){
        System.out.println("hero battle win");
    }      
}
// 子类隐藏父类的类方法
public class ADHero extends Hero implements AD{  
    @Override
    public void physicAttack() {
        System.out.println("进行物理攻击");
    }    
    //隐藏父类的battleWin方法
    public static void battleWin(){
        System.out.println("ad hero battle win");
    }        
    public static void main(String[] args) {
        Hero.battleWin();
        ADHero.battleWin();
    }  
}

6.super

super关键字调用父类的构造方法,如果super()里面什么都没有则调用父类的无参构造方法,如果其中有参数,则调用父类对应的构造方法,

当一个类继承一个父类的时候,那这个子类实例化时会默认调用父类的无参构造方法,若有参数,则调用自己本身的对应构造方法和父类的无参构造方法,若想调用父类的有参构造方法时,则需要在自身有参构造方法中加入super("参数"),即可完成调用

super.属性:调用父类属性

super():调用父类的构造方法

super.方法名;调用父类的方法

this.属性:调用自己的属性

this() :调用自身的构造方法

this.方法名:调用自身的方法

public class Hero {
  // 无参构造方法
  public Hero() {
      System.out.println("这是父类无参构造方法");
  }
  // 含参构造方法
  public Hero(String name) {
      System.out.println("这是父类含参构造方法!"+name); 
  }
  public void attack() {  
  }
}
public class Keyword_Super extends Hero{
  public Keyword_Super() {
      System.out.println("子类无参构造方法");
  }
  public Keyword_Super(String name) {
      super(name);
//        this(); // 调用自身的无参构造方法
      System.out.println("子类含参构造方法");
  }
  public static void main(String args[]) {
      new Keyword_Super();
      new Keyword_Super("mhq");
  }
}

7.Object()

// 步骤1:Object是所有类的父类
  声明一个类的时候,默认是继承Object
  eg:public class Hero extends Object
// 步骤2:toString()
  Object类提供一个toString方法,所以所有的类都有toString方法
  toString()的意思是返回当前对象的字符串表达
  通过 System.out.println 打印对象就是打印该对象的toString()返回值 
// 步骤3:finalize()
  当一个对象没有任何引用指向的时候,它就满足垃圾回收的条件
  当它被垃圾回收的时候,它的finalize() 方法就会被调用。
  finalize() 不是开发人员主动调用的方法,而是由虚拟机JVM调用的。
      注意:这个一定不是说,有垃圾就会回收,而是多到一定的量的时候才会回收
代码实现:
public class Hero {
    public String name;
    protected float hp;     
    public String toString(){
        return name;
    }     
    public void finalize(){
        System.out.println("这个英雄正在被回收");
    }      
    public static void main(String[] args) {
        //只有一引用
        Hero h;
        for (int i = 0; i < 100000; i++) {
            //不断生成新的对象
            //每创建一个对象,前一个对象,就没有引用指向了
            //那些对象,就满足垃圾回收的条件
            //当,垃圾堆积的比较多的时候,就会触发垃圾回收
            //一旦这个对象被回收,它的finalize()方法就会被调用
            h = new Hero();
        } 
    }
}
// 步骤4:equals()
   equals() 用于判断两个对象的内容是否相同,比如两个人的钱一样多,那就可以相等
// 步骤5:==
   这不是Object的方法,但是用于判断两个对象是否相同
更准确的讲,用于判断两个引用,是否指向了同一个对象
代码实现:
public class Test {
    public String name;
    protected float hp;      
    public boolean equals(Object o){
        if(o instanceof Test){
            Test h = (Test) o;
            return this.hp == h.hp;
        }
        return false;
    }      
    public static void main(String[] args) {
        Test h1= new Test();
        h1.hp = 300;
        Test h2= new Test();
        h2.hp = 400;
        Test h3= new Test();
        h3.hp = 300;        
        System.out.println(h1.equals(h2));
        System.out.println(h1.equals(h3));
        System.out.println("------------------");
        System.out.println(h1==h2);
        System.out.println(h1==h3);
    }
}
// 步骤6:hashCode()
   hashCode方法返回一个对象的哈希值
// 步骤7:线程同步相关方法
   Object还提供线程同步相关方法
  wait()
  notify()
  notifyAll()
// 步骤8:getClass()
   getClass()会返回一个对象的类对象,属于高级内容,不适合初学者过早接触,关于   类对象的详细内容请参考反射机制

8.final

示例1:final修饰类

当Hero被修饰成final的时候,表示Hero不能够被继承

示例2:final修饰方法

方法被修饰成final,那么该方法在其子类中,不能够被修改重写(不能体现多态性) 

示例3:final修饰基本类型变量

final修饰基本类型变量,表示该变量只有一次赋值机会 

示例4:final修饰引用

final修饰引用
h引用被修饰成final,表示该引用只有1次指向对象的机会
public class Hero extends Object {        
    String name; //姓名        
    float hp; //血量        
    float armor; //护甲        
    int moveSpeed; //移动速度     
    public static void main(String[] args) { 
        final Hero h;
        h  =new Hero(); // 可以被引用
        h  =new Hero(); // 被final修饰不能有第二次引用       
        h.hp = 5;         
    }      
}

示例5:常量

常量指的是可以公开,直接访问,不会变化的值
比如 itemTotalNumber 物品栏的数量是6个 public static final int itemTotalNumber = 6;
public class Hero extends Object {     
    public static final int itemTotalNumber = 6;//物品栏的数量        
    String name; //姓名       
    float hp; //血量        
    float armor; //护甲        
    int moveSpeed; //移动速度     
    public static void main(String[] args) { 
        final Hero h;
        h  =new Hero();         
        h.hp = 5;         
    }      
}

9.抽象类

步骤1:抽象类

/*
当一个类中有抽象方法,那么这个类就一定要变成抽象类即(abstract).
抽象方法和接口有点类似也可以没有实现体,但是继承这个抽象类的子类,必须去重写,实现父类的抽象方法
*/
代码示例:
// 父类:
public abstract class Hero {
    String name; 
    float hp; 
    float armor; 
    int moveSpeed; 
    public static void main(String[] args) { 
    } 
    // 抽象方法attack
    // Hero的子类会被要求实现attack方法
    public abstract void attack(); 
}
// 子类:
public class ADHero extends Hero implements AD { 
    public void physicAttack() {
        System.out.println("进行物理攻击");
    } 
    @Override
    public void attack() {
        physicAttack();
    } 
}

步骤2:抽象类可以没有抽象方法

类可以在不提供抽象方法的前提下,声明为抽象类
一旦一个类被声明为抽象类,就不能够被直接实例化
public abstract class Hero {
    String name;          
    float hp;          
    float armor;          
    int moveSpeed;      
    public static void main(String[] args) {
        //虽然没有抽象方法,但是一旦被声明为了抽象类,就不能够直接被实例化
        Hero h= new Hero();
    }          
}

步骤3:抽象类和接口的区别

// 区别1:
子类只能继承一个抽象类,不能继承多个
子类可以实现多个接口
// 区别2:
抽象类可以定义
public,protected,package,private
静态和非静态属性
final和非final属性
但是接口中声明的属性,只能是
public
静态
final的
即便没有显式的声明
注: 抽象类和接口都可以有实体方法。 接口中的实体方法,叫做默认方法
public interface AP {
    public static final int resistPhysic = 100;     
    //resistMagic即便没有显式的声明为 public static final
    //但依然默认为public static final
    int resistMagic = 0;     
    public void magicAttack();
    // 以下便是默认方法的格式
    public default void apattack(){
    }
}

步骤4:

目的:
有的物品使用之后就消失了,比如血瓶
有的物品使用了之后还会继续存在,比如武器
为Item类设计一个抽象方法
 
public abstract boolean disposable()

不同的子类,实现disposable后,会返回不同的值。
比如LifePotion就会返回true,因为是会消失了。
而Weapon,Armor 就会返回false,因为是不会消失了 
/** 答案:
 * 有的物品使用之后就消失了,比如血瓶
 
    有的物品使用了之后还会继续存在,比如武器
     
    为Item类设计一个抽象方法 
      
    public abstract boolean disposable()
      
    不同的子类,实现disposable后,会返回不同的值。
    比如LifePotion就会返回true,因为是会消失了。
    而Weapon,Armor 就会返回false,因为是不会消失了 
 *
 */
public abstract class Item {
    String name;
    int price;     
    public abstract boolean disposable();//是否是一次性的判断类型抽象方法     
    public static void main(String[] args) {
        LifePotion lp = new LifePotion();
        Weapon wp = new Weapon();
        Armor ar = new Armor();
         
        System.out.println(lp.disposable());
        System.out.println(wp.disposable());
        System.out.println(ar.disposable());
    }
}
//------------------血瓶子类-------------------- 
public class LifePotion extends Item{
 
    @Override
    public boolean disposable() {
        return true;
    }
}
//-------------------武器子类------------------- 
public class Weapon extends Item{ 
    @Override
    public boolean disposable() {
        return false;
    }
}
//-------------------护甲子类------------------- 
public class Armor extends Item { 
    @Override
    public boolean disposable() {
        return false;
    }
}

你可能感兴趣的:(接口与继承)