Java学习笔记:抽象类、内部类、默认方法及综合案例

Java学习笔记:抽象类、内部类、默认方法及综合案例

  • 学习参考网址https://how2j.cn/p/6235

抽象类

  • 在类中声明一个方法,这个方法没有实现体,是一个“”方法 
  • 这样的方法就叫抽象方法,使用修饰符“abstract
  • 当一个类有抽象方法的时候,该类必须被声明为抽象类

  • 抽象类
    • 为Hero增加一个抽象方法 attack,并且把Hero声明为abstract的。
    • APHero,ADHero,ADAPHero是Hero的子类,继承了Hero的属性和方法。
    • 但是各自的攻击手段是不一样的,所以继承Hero类后,这些子类就必须提供不一样的attack方法实现

  • Hero.java
package charactor;
 
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();
 
}
  • ADHero .java
package charactor;
 
public class ADHero extends Hero implements AD {
 
    public void physicAttack() {
        System.out.println("进行物理攻击");
    }
 
    @Override
    public void attack() {
        physicAttack();
    }
 
}
  • APHero .java
package charactor;
 
public class APHero extends Hero implements AP {
 
    @Override
    public void magicAttack() {
        System.out.println("进行魔法攻击");
    }
 
    @Override
    public void attack() {
        magicAttack();
    }
 
}
  • ADAPHero .java
package charactor;
 
public class ADAPHero extends Hero implements AD, AP {
 
    @Override
    public void attack() {
 
        System.out.println("既可以进行物理攻击,也可以进行魔法攻击");
    }
 
    public void magicAttack() {
        System.out.println("进行魔法攻击");
    }
 
    public void physicAttack() {
        System.out.println("进行物理攻击");
    }
 
}

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

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

  • 案例演示:
有的物品使用之后就消失了,比如血瓶

有的物品使用了之后还会继续存在,比如武器

为Item类设计一个抽象方法 
 
public abstract boolean disposable()
 
不同的子类,实现disposable后,会返回不同的值。
比如LifePotion就会返回true,因为是会消失了。
而Weapon,Armor 就会返回false,因为是不会消失了
  • Item .java
package property;
 
public abstract class Item {
    String name;
    int price;
 
    public abstract boolean disposable();  
     
}
  • Armor .java
package property;
 
public class Armor extends Item{
 
    int ac; //护甲等级
     
    @Override
    public boolean disposable() {
        return false;
    }
     
}
  • LifePotion .java
package property;
 
public class LifePotion extends Item {
    public void effect(){
        System.out.println("血瓶使用后,可以回血");
    }
 
    public boolean disposable() {
        // TODO Auto-generated method stub
        return true;
    }
}

 

内部类

  • 内部类分为四种: 
    • 非静态内部类 
    • 静态内部类 
    • 匿名类 
    • 本地类
  • 非静态内部类
    • 非静态内部类 BattleScore “战斗成绩”
    • 非静态内部类可以直接在一个类里面定义
    • 比如:
      • 战斗成绩只有在一个英雄对象存在的时候才有意义
      • 所以实例化BattleScore 的时候,必须建立在一个存在的英雄的基础上
    • 语法: new 外部类().new 内部类()
    • 作为Hero的非静态内部类,是可以直接访问外部类的private实例属性name的
package charactor;
 
public class Hero {
    private String name; // 姓名
 
    float hp; // 血量
 
    float armor; // 护甲
 
    int moveSpeed; // 移动速度
 
    // 非静态内部类,只有一个外部类对象存在的时候,才有意义
    // 战斗成绩只有在一个英雄对象存在的时候才有意义
    class BattleScore {
        int kill;
        int die;
        int assit;
 
        public void legendary() {
            if (kill >= 8)
                System.out.println(name + "超神!");
            else
                System.out.println(name + "尚未超神!");
        }
    }
 
    public static void main(String[] args) {
        Hero garen = new Hero();
        garen.name = "盖伦";
        // 实例化内部类
        // BattleScore对象只有在一个英雄对象存在的时候才有意义
        // 所以其实例化必须建立在一个外部类对象的基础之上
        BattleScore score = garen.new BattleScore();
        score.kill = 9;
        score.legendary();
    }
 
}

  • 静态内部类
    • 在一个类里面声明一个静态内部类
    • 比如敌方水晶,当敌方水晶没有血的时候,己方所有英雄都取得胜利,而不只是某一个具体的英雄取得胜利。
    • 与非静态内部类不同,静态内部类水晶类的实例化 不需要一个外部类的实例为基础,可以直接实例化
    • 语法:new 外部类.静态内部类();
    • 因为没有一个外部类的实例,所以在静态内部类里面不可以访问外部类的实例属性和方法
    • 除了可以访问外部类的私有静态成员外,静态内部类和普通类没什么大的区别
package charactor;
  
public class Hero {
    public String name;
    protected float hp;
  
    private static void battleWin(){
        System.out.println("battle win");
    }
     
    //敌方的水晶
    static class EnemyCrystal{
        int hp=5000;
         
        //如果水晶的血量为0,则宣布胜利
        public void checkIfVictory(){
            if(hp==0){
                Hero.battleWin();
                 
                //静态内部类不能直接访问外部类的对象属性
                System.out.println(name + " win this game");
            }
        }
    }
     
    public static void main(String[] args) {
        //实例化静态内部类
        Hero.EnemyCrystal crystal = new Hero.EnemyCrystal();
        crystal.checkIfVictory();
    }
  
}

  • 匿名类
    • 匿名类指的是在声明一个类的同时实例化它,使代码更加简洁精练
    • 通常情况下,要使用一个接口或者抽象类,都必须创建一个子类
    • 有的时候,为了快速使用,直接实例化一个抽象类,并“当场”实现其抽象方法。
    • 既然实现了抽象方法,那么就是一个新的类,只是这个类,没有命名。
    • 这样的类,叫做匿名类
package charactor;
   
public abstract class Hero {
    String name; //姓名
          
    float hp; //血量
          
    float armor; //护甲
          
    int moveSpeed; //移动速度
      
    public abstract void attack();
      
    public static void main(String[] args) {
          
        ADHero adh=new ADHero();
        //通过打印adh,可以看到adh这个对象属于ADHero类
        adh.attack();
        System.out.println(adh);
          
        Hero h = new Hero(){
            //当场实现attack方法
            public void attack() {
                System.out.println("新的进攻手段");
            }
        };
        h.attack();
        //通过打印h,可以看到h这个对象属于Hero$1这么一个系统自动分配的类名
          
        System.out.println(h);
    }
      
}

  • 本地类
    • 本地类可以理解为有名字的匿名类
    • 内部类与匿名类不一样的是,内部类必须声明在成员的位置,即与属性和方法平等的位置
    • 本地类和匿名类一样,直接声明在代码块里面,可以是主方法,for循环里等等地方
package charactor;
   
public abstract class Hero {
    String name; //姓名
          
    float hp; //血量
          
    float armor; //护甲
          
    int moveSpeed; //移动速度
      
    public abstract void attack();
      
    public static void main(String[] args) {
          
        //与匿名类的区别在于,本地类有了自定义的类名
        class SomeHero extends Hero{
            public void attack() {
                System.out.println( name+ " 新的进攻手段");
            }
        }
         
        SomeHero h  =new SomeHero();
        h.name ="地卜师";
        h.attack();
    }
      
}

  • 在匿名类中使用外部的局部变量
    • 在匿名类中使用外部的局部变量,外部的局部变量必须修饰为final
    • 注:在jdk8中,已经不需要强制修饰成final了,如果没有写final,不会报错,因为编译器偷偷的帮你加上了看不见的final
package charactor;
   
public abstract class Hero {
 
    public abstract void attack();
      
    public static void main(String[] args) {
 
        //在匿名类中使用外部的局部变量,外部的局部变量必须修饰为final
        final int damage = 5;
         
        Hero h = new Hero(){
            public void attack() {
                System.out.printf("新的进攻手段,造成%d点伤害",damage );
            }
        };
 
    }
      
}
package charactor;
   
public abstract class Hero {
 
    public abstract void attack();
      
    public static void main(String[] args) {
 
        //在匿名类中使用外部的局部变量damage 必须修饰为final
        int damage = 5;
         
        //这里使用本地类AnonymousHero来模拟匿名类的隐藏属性机制
         
        //事实上的匿名类,会在匿名类里声明一个damage属性,并且使用构造方法初始化该属性的值
        //在attack中使用的damage,真正使用的是这个内部damage,而非外部damage
         
        //假设外部属性不需要声明为final
        //那么在attack中修改damage的值,就会被暗示为修改了外部变量damage的值
         
        //但是他们俩是不同的变量,是不可能修改外部变量damage的
        //所以为了避免产生误导,外部的damage必须声明为final,"看上去"就不能修改了
        class AnonymousHero extends Hero{
            int damage;
            public AnonymousHero(int damage){
                this.damage = damage;
            }
            public void attack() {
                damage = 10;
                System.out.printf("新的进攻手段,造成%d点伤害",this.damage );
            }
        }
         
        Hero h = new AnonymousHero(damage);
         
    }
      
}

  • 案例演示:
    • 创建一个Item的匿名类
    • Item有抽象方法disposable()
package property;
 
public abstract class Item {
    String name;
    int price;
 
    public abstract boolean disposable();  
     
    public static void main(String[] args) {
        Item i = new Item(){
            @Override
            public boolean disposable() {
                return false;
            }
        };
        System.out.println(i.disposable());
    }
     
}

 

默认方法

  • 默认方法是JDK8新特性,指的是接口也可以提供具体方法了,而不像以前,只能提供抽象方法
  • Mortal 这个接口,增加了一个默认方法 revive,这个方法有实现体,并且被声明为了default
package charactor;
 
public interface Mortal {
    public void die();
 
    default public void revive() {
        System.out.println("本英雄复活了");
    }
}

  • 为什么会有默认方法
    • 假设没有默认方法这种机制,那么如果要为Mortal增加一个新的方法revive,那么所有实现了Mortal接口的类,都需要做改动。
    • 但是引入了默认方法后,原来的类,不需要做任何改动,并且还能得到这个默认方法
    • 通过这种手段,就能够很好的扩展新的类,并且做到不影响原来的类

  • 案例演示:
    • 为AD接口增加一个默认方法 attack()
    • 为AP接口也增加一个默认方法 attack() 
    • 问: ADAPHero同时实现了AD,AP接口,那么 ADAPHero 对象调用attack()的时候,是调用哪个接口的attack()?
      • 作为同时继承了AD和AP中的 默认方法attack,就必须在实现类中重写该方法
      • 从而免去到底调用哪个接口的attack方法这个模棱两可的问题
package charactor;
 
public class ADAPHero extends Hero implements AD,AP,Mortal{
 
    @Override
    public void magicAttack() {
        // TODO Auto-generated method stub
         
    }
 
    @Override
    public void physicAttack() {
        // TODO Auto-generated method stub
         
    }
 
    @Override
    public void die() {
        System.out.println(name+ " 这个混合英雄挂了");
    }
 
    //作为同时继承了AD和AP中的 默认方法attack,就必须在实现类中重写该方法
    //从而免去到底调用哪个接口的attack方法这个模棱两可的问题
    @Override
    public void attack() {
        //
        System.out.println("这个ADAPHero自己的attack方法");
    }
 
}

 

综合案例

  • UML 图 —— 类之间的关系
    • UML-Unified Module Language
    • 统一建模语言,可以很方便的用于描述类的属性,方法,以及类和类之间的关系

Java学习笔记:抽象类、内部类、默认方法及综合案例_第1张图片


  • 解释UML-类图

Java学习笔记:抽象类、内部类、默认方法及综合案例_第2张图片


  • 解释UML-接口图

Java学习笔记:抽象类、内部类、默认方法及综合案例_第3张图片


  • 解释UML-继承关系
    • 带箭头的实线,表示 Spider,Cat, Fish都继承于Animal这个父类.
    • 注: 模糊是表示,此时不需要关注模糊的那部分内容

Java学习笔记:抽象类、内部类、默认方法及综合案例_第4张图片


  • 解释UML-实现关系

Java学习笔记:抽象类、内部类、默认方法及综合案例_第5张图片


1. 创建Animal类,它是所有动物的抽象父类。
2. 声明一个受保护的整数类型属性legs,它记录动物的腿的数目。
3. 定义一个受保护的构造器,用来初始化legs属性。
4. 声明抽象方法eat。
5. 声明具体方法walk来打印动物是如何行走的(包括腿的数目)。
public abstract class Animal {
 
    protected int legs;
    protected Animal(int legs){
        this.legs = legs;
    }
    public abstract void eat();
    public void walk(){
        System.out.println("用 " + legs +  " 条腿走路");
    }
}

1. Spider继承Animal类。
2. 定义默认构造器,它调用父类构造器来指明所有蜘蛛都是8条腿。
3. 实现eat方法
public class Spider extends Animal{
 
    @Override
    public void eat() {
        System.out.println("spider eating");
    }
    public Spider(){
        super(8);
    }
     
}

根据UML类创建pet(宠物)接口
1. 提供getName() 返回该宠物的名字
2. 提供setName(String name) 为该宠物命名
3. 提供 play()方法
public interface Pet {
 
    public void setName(String name);
    public String getName();
    public void play();
}

1. 该类必须包含String属性来存宠物的名字。
2. 定义一个构造器,它使用String参数指定猫的名字;该构造器必须调用超类构造器来指明所有的猫都是四条腿。
3. 另定义一个无参的构造器。该构造器调用前一个构造器(用this关键字)并传递一个空字符串作为参数
4. 实现Pet接口方法。
5. 实现eat方法。
public class Cat extends Animal implements Pet{
 
    private String name;
 
    public Cat(String name){
        super(4);
        this.name = name;
    }
     
    public Cat(){
        this("");
    }
    @Override
    public void setName(String name) {
        this.name = name;
         
    }
 
    @Override
    public String getName() {
        // TODO Auto-generated method stub
        return name;
    }
 
    @Override
    public void play() {
        System.out.println("Cat is playing");
    }
 
    @Override
    public void eat() {
        System.out.println("eating");
         
    }
     
}

1. 创建Fish类,它继承Animal类
2. 重写Animal的walk方法以表明鱼不能走且没有腿。
3. 实现Pet接口
4. 无参构造方法
5. 提供一个private 的name属性
public class Fish extends Animal implements Pet{
 
    private String name;
    protected Fish() {
        super(0);
    }
 
    @Override
    public void eat() {
        System.out.println("Fish is eating");
    }
     
    public void walk(){
        System.out.println("swimming");
    }
 
    @Override
    public void setName(String name) {
        // TODO Auto-generated method stub
        this.name = name;
         
    }
 
    @Override
    public String getName() {
        // TODO Auto-generated method stub
        return this.name;
    }
 
    @Override
    public void play() {
        // TODO Auto-generated method stub
        System.out.println("playing");
    }
 
}

 

你可能感兴趣的:(Java学习之路,java)