java

继承机制下,用户可以复用一些定义好的类,减少重复代码的编写。

多态机制下,用户可以动态调整对象的调用,降低对象之间的依存关系。

为了优化继承与多态,一些类除了可继承父类外,还需要使用接口的形式。Java 中的类可以同时实现多个接口,接口被用来建立类与类之间关联的标准。

继承

重写

重写(还可以称为覆盖)就是在子类中将父类的成员方法的名称保留,重写成员方法的实现内容,更改成员方法的存储权限,或是修改成员方法的返回值类型(重写父类成员方法的返回值类型是基于 J2SE 5.0 版本以上编译器提供的新功能)。

Object类

创建一个类时,除非已经指定要从其他类继承,否则它就是从java.lang.Object类继承而来的。Java 中的每个类都源于java.lang.Object类,如 String、Integer 等类都是继承于 Object 类;

除此之外,自定义的类也都继承于 Object 类。由于所有类都是 Object 子类,所以在定义类时可省略extends Object关键字。

在 Object 类中主要包括clone()finalize()equals()toString()等方

法,其中常用的两个方法为equals()toString()方法。

由于所有的类都是 Object 类的子类,所以任何类都可以重写 Object 类中的方法。

注意:Object 类中的getClass()notify()notifyAll()wait()等方法不能被重写,因为这些方法被定义为 final 类型。

getClass() 方法

语法格式:

getClass().getname();

可以将 getClass() 方法与 toString() 方法联合使用。

toString() 方法

在实际的应用中通常重写toString()方法,为对象提供一个特定的输出模式。当这个类转换为字符串或与字符串连接时,将自动调用重写的toString()方法。

equals() 方法

equals() 方法的默认实现是使用"==运算符比较两个对象的引用地址,而不是比较对象的内容,所以要想真正做到比较两个对象的内容,需要在自定义类中重写 equals() 方法。

对象的类型转换

向上转型

将子类对象视为父类对象的做法称为"向上转型"。

向下转型

将父类对象强制转换为某个子类对象,这种方式称为显式类型转换。

关键字

instanceof 关键字判断对象类型

这个判断通常使用 instanceof 关键字来完成。可以使用 instanceof 关键字判断是否一个类实现了某个接口,也可以用它来判断一个实例对象是否属于一个类。

语法格式:

myobject instanceof ExampleClass

myobject:某类的对象引用。

ExampleClass:某个类。

使用 instanceof 关键字的表达式返回值为布尔值。如果返回值为 true,说明 myobject 对象为 ExampleClass 的实例对象;如果返回值为 false,说明 myobject 对象不是 ExampleClass 的实例对象。

instanceof 是 Java 语言的关键字,在 Java 语言中的关键字都为小写。

final 关键字

学习了继承后,我们知道,子类可以在父类的基础上改写父类内容,比如,方法重写。 那如果有一个方法我不想别人去改写里面内容,该怎么办呢? Java 提供了 <font style="color:rgb(38, 38, 38);">final</font> 关键字,意为"最终的",表示被 final 关键字修饰的内容都是不可变的。

final:可以用于修饰类、方法和变量。

  • 修饰类:final 修饰的类,不能被继承。
  • 修饰方法:final 修饰的方法,不能被重写。
  • 修饰变量:final 修饰的变量,有且仅能被赋值一次。

修饰类

如果希望一个类不被任何类继承,并且不允许其他人对这个类进行任何改动,可以将这个类设置为 final 形式。

final 类的语法格式如下:

final 类名{}
查询 API 文档发现像 <font style="color:rgb(38, 38, 38);">public final class String</font><font style="color:rgb(38, 38, 38);">public final class Math</font><font style="color:rgb(38, 38, 38);">public final class Scanner</font> 等,很多我们学习过的类,都是被 final 修饰的,目的就是供我们使用,而不让我们所以改变其内容。

如果将某个类设置为 final 形式,则类中的所有方法都被隐式设置为 final 形式,但是 final 类中的成员变量可以被定义为 final 或非 final 形式。

修饰方法

final 方法的语法格式如下:

修饰符 final 返回值类型 方法名(参数列表){
    //方法体
}

将方法定义为 final 类型,可以防止子类修改该类的定义与实现方式,同时定义为 final 的方法的执行效率要高于非 final 方法。在修饰权限中曾经提到过 private 修饰符,如果一个父类的某个方法被设置为 private 修饰符,子类将无法访问该方法,自然无法覆盖该方法。也就是说,一个定义为 private 的方法隐式被指定为 final 类型,因此无须将一个定义为 private 的方法再定义为 final 类型。

修饰变量

final 关键字定义的变量必须在声明时对其进行赋值操作。

final 除了可以修饰基本数据类型的常量,还可以修饰对象引用。由于数组也可以被看作一个对象来引用,所以 final 可以修饰数组。一旦一个对象引用被修饰为 final 后,它只能恒定指向一个对象,无法将其改变以指向另一个对象。一个既是 static 又是 final 的字段只占据一段不能改变的存储空间。

一个被定义为 final 的对象引用只能指向唯一一个对象,不可以将它再指向其他对象。但由于一个对象本身的值是可以改变的,因此为了使一个常量真正做到不可更改,可以将常量声明为 static final。

在 Java 中定义全局常量,通常使用 public static final 修饰,这样的常量只能在定义时被赋值。

修饰变量 - 局部变量

基本类型的局部变量,被 final 修饰后,只能赋值一次,不能再更改。 示例代码如下:
public class FinalDemo1 {
    public static void main(String[] args) {
        // 声明变量,使用final修饰
        final int a;
        // 第一次赋值
        a = 10;
        // 第二次赋值
        a = 20; // 报错,不可重新赋值

        // 声明变量,直接赋值,使用final修饰
        final int b = 10;
        // 第二次赋值
        b = 20; // 报错,不可重新赋值
    }
}
思考,下面两种写法,哪种可以通过编译? 写法 1:
final int c = 0;
for (int i = 0; i < 10; i++) {
    c = i;
    System.out.println(c);
}
写法 2:
for (int i = 0; i < 10; i++) {
    final int c = i;
    System.out.println(c);
}
根据 final 的定义,写法 1 报错!写法 2 可以运行,为什么通过编译呢?因为每次循环,都是一次新的变量 c。这也是大家需要注意的地方。

修饰变量 - 成员变量

成员变量涉及到初始化的问题,初始化方式有显示初始化和构造方法初始化,只能选择其中一个: 1)显示初始化(在定义成员变量的时候立马赋值)(常用);
public class Student {
    final int num = 10;
}
2) 构造方法初始化(在构造方法中赋值一次)(不常用,了解即可)。

注意:每个构造方法中都要赋值一次!

public class Student {
    final int num = 10;
    final int num2;

    public Student() {
        this.num2 = 20;
//     this.num2 = 20;
    }

     public Student(String name) {
        this.num2 = 20;
//     this.num2 = 20;
    }
}

总结

fianl 关键字的作用:final 代表最终的意思,可以修饰成员方法,成员变量,类,凡是被 final 修饰过的内容都是不可变的。 final 修饰类、方法、变量的效果
  • fianl 修饰类:该类不能被继承(不能有子类,但是可以有父类)
  • final 修饰方法:该方法不能被重写
  • final 修饰变量:表明该变量是一个常量,不能再次赋值
    • 变量是基本类型,不能改变的是值
    • 变量是引用类型,不能改变的是地址值,但地址里面的内容是可以改变的
    • 代码示例:
public static void main(String[] args){
    final Student s = new Student(23);
      s = new Student(24);  // 错误
     s.setAge(24);  // 正确
}

方法的重载

由于这些构造方法都需要根据类名进行命名,为了让方法名相同而形参不同的构造方法同时存在,必须用到方法重载

为什么要用到 方法重载?

方法的重载使得方法以统一的名称被管理,使程序代码有条理。

方法的重载就是在同一个类中允许存在一个以上的同名方法,只要这些方法的参数个数或类型不同即可。

构成方法重载的条件:

  • 方法的参数类型不同
  • 方法的返回值类型也不同
  • 参数个数不同
  • 参数顺序不同

抽象类

抽象类的概述

在 Java 中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类!

在解决实际问题时,一般将父类定义为抽象类,需要使用这个父类进行继承与多态处理。回想继承和多态原理,继承树中越是在上方的类越抽象,如鸽子类继承鸟类、鸟类继承动物类等。在多态机制中,并不需要将父类初始化对象,我们需要的只是子类对象,所以在 Java 语言中设置抽象类不可以实例化对象,因为图形类不能抽象出任何一种具体图形,但它的子类却可以。

抽象类的定义(abstract)

abstract 修饰类,这个类就是抽象类;修饰方法,这个方法就是抽象方法。

// 1.抽象类的定义

修饰符 abstract class 类名{
    修饰符 abstract 返回值类型 方法名称(形参列表)}

public abstract class 类名 {}


//示例:定义一个抽象类 Animal
public abstract class Animal{
    public abstract void run();
}


// 2.抽象方法的定义
public abstract void eat();

注意事项:
1. 抽象方法只有方法签名,不能声明方法体。
2. 一个类中如果定义了抽象方法,这个类必须声明成抽象类,否则报错。

使用 abstract 关键字定义的类称为抽象类,而使用这个关键字定义的方法称为抽象方法。抽象方法没有方法体,这个方法本身没有任何意义,除非它被重写,而承载这个抽象方法的抽象类必须被继承,实际上抽象类除了被继承之外没有任何意义。

抽象方法所在的类一定是抽象类,而抽象类中的方法不一定是抽象方法

抽象类的作用:

  • 抽象类的作用类似于"模板",其目的是方便开发人员根据抽象类的格式来修改和创建新类
  • 抽象类主要用于继承,有利于程序的扩展

抽象类的特点和注意事项:

抽象类不能创建对象,如果创建,编译无法通过而报错,只能创建其非抽象子类的对象。

那么抽象类如何实例化呢?参照多态的方式,通过子类对象实例化,这叫抽象类多态

  • 抽象类的子类

抽象类的子类要么(必须)重写抽象类中的所有抽象方法,要么子类也必须定义成抽象类,不然编译无法通过而报错

  1. 注意事项
  • 类有的成员(成员变量、方法、构造器)抽象类也都具备。
  • 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类。
  • 一个类继承了抽象类必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
  • 不能用 abstract 修饰变量、代码块、构造器。
  • 最重要的特征: 得到了抽象方法,失去了创建对象的能力(有得有失)
  1. final 和 abstract 是什么关系?
  • 互斥关系。
  • abstract 定义的抽象类作为模板让子类继承,final 定义的类不能被继承。
  • 抽象方法定义通用功能让子类重写,final 定义的方法子类不能重写。

抽象类的成员特点

  • 成员变量
    • 既可以是变量
    • 也可以是常量
  • 构造方法
    • 空参构造
    • 有参构造
  • 成员方法
    • 抽象方法
    • 普通方法

代码演示:

public abstract class Animal {
    private int age = 20;
    private final String city = "北京";
    public Animal() {}
    public Animal(int age) {
        this.age = age;
    }
    public void show() {
        age = 40;
        System.out.println(age);
//        city = "上海";
        System.out.println(city);
    }
    public abstract void eat();
}
public class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
}
public class AnimalDemo {
    public static void main(String[] args) {
        Animal a = new Cat();
        a.eat();
        a.show();
    }
}

抽象类的使用场景

使用场景说明:当系统中出现同一个功能多处在开发,而该功能中大部分代码是一样的,只有其中部分可能不同的时候。

- 模板方法模式实现步骤:
    1. 定义一个抽象类。
    2. 定义2个方法,一个是模板方法:把相同代码放里面去,不同代码定义成抽象方法。
    3. 子类继承抽象类,重写抽象方法。



- 模板方法我们是建议使用final修饰的,这样会更专业,那么为什么呢?

答:模板方法是给子类直接使用的,不是让子类重写的,一旦子类重写了模板方法,则模板方法就失效了,因此,加上 final 后可以防止子类重写了模板方法,这样更安全、专业。

- 模板方法的作用,模式解决了什么问题?
    * 提高了代码的复用性。
    * 模板方法已经定义了通用结构,模板方法不能确定的部分定义成抽象方法,交给子类实现,因此,使用者只需要关心自己需要实现的功能即可。

总结:

1、抽象类、抽象方法是什么样的?

  • 都是用 abstract 修饰的;抽象方法只有方法签名,不能写方法体。一个类中定义了抽象方法,这个类必须声明成抽象类。

2、抽象类基本作用是啥?

  • 作为父类,用来被继承的。

3、继承抽象类有哪些要注意?

  • 一个类如果继承了抽象类,那么这个类必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。

应用:抽象类的案例

请采用抽象类的思想实现猫和狗的案例,并在测试类中进行测试

  • 代码实现:
/*
    动物类
*/
public abstract class Animal {
    private String name;
    private int age;
    public Animal() {
    }
    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public abstract void eat();
}

/*
    猫类
*/
public class Cat extends Animal {
    public Cat() {
    }
    public Cat(String name, int age) {
        super(name, age);
    }
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
}

/*
    狗类
*/
public class Dog extends Animal {
    public Dog() {
    }
    public Dog(String name, int age) {
        super(name, age);
    }
    @Override
    public void eat() {
        System.out.println("狗吃骨头");
    }
}

/*
    测试类
*/
public class AnimalDemo {
    public static void main(String[] args) {
        //创建对象,按照多态的方式
        Animal a = new Cat();
        a.setName("加菲");
        a.setAge(5);
        System.out.println(a.getName()+","+a.getAge());
        a.eat();
        System.out.println("­­­­­­­­");
        a = new Cat("加菲",5);
        System.out.println(a.getName()+","+a.getAge());
        a.eat();
    }
}

接口

接口的概述

接口是抽象类的延伸,可以将它看作是纯粹的抽象类,接口中的所有方法都没有方法体。

接口的定义和使用(interface)

public interface 接口名 {}

  • 接口的实现类用 implements 关键字表示

public class 类名 implements 接口名 {}

  • 接口不能实例化

接口如何实例化呢?参照多态的方式,通过实现类对象实例化,这叫接口多态。

多态的形式:具体类多态,抽象类多态,接口多态。

  • 接口的子类

要么重写接口中的所有抽象方法,要么子类也是抽象类

注意:

1、在接口中,方法必须被定义为 public 或 abstract 形式,其他修饰权限不被 Java 编译器认可。或者说,即使不将该方法声明为 public 形式,它也是 public。

2、在接口中定义的任何字段都自动是 static 和 final 的。

  1. 接口的使用
  • 接口是用来被类实现(implements)的,实现接口的类称为实现类。实现类可以理解成所谓的子类。
修饰符 class 实现类 implements 接口1, 接口2, 接口3 , ... {

}

实现的关键字:implements
  • 从上面可以看出,接口可以被类单实现,也可以被类多实现。
  1. 接口实现的注意事项:
  • 一个类实现接口,必须重写完全部接口的全部抽象方法,否则这个类需要定义成抽象类。

接口的特点

  1. JDK 1.8 之前接口的特性:
  • 接口允许多继承
  • 接口没有构造方法,因为接口主要是扩展功能的,而没有具体存在
  • 接口中的成员变量只能是常量(属性),默认是 public static final 修饰的
  • 接口中的成员方法只能是抽象方法,默认是用 public abstract 修饰的
  • 接口继承接口用 extends,不能用 implement
  1. JDK 1.8 之后接口的特性:
  • 在接口内部可以定义多个常量和抽象方法,定义常量是必须进行初始化赋值,定义默认方法和静态方法时,可以有方法体,并且静态方法可以直接通过"接口.方法名"来调用
  • 在接口中定义常量时,可以省略"public static final"修饰符,接口会默认为常量添加"public static final"修饰符。
  • 与此类似的是,在接口中定义抽象方法时,也可以省略"public abstract"修饰符,定义 default 默认方法和 static 静态方法时,可以省略"public"修饰符,这些修饰符系统都会默认进行添加。
  1. JDK 1.8 之后接口的语法:

  • 接口代码演示:
public interface Inter {
    public int num = 10;
    public final int num2 = 20;
//    public static final int num3 = 30;
    int num3 = 30;
//    public Inter() {}
//    public void show() {}
    public abstract void method();
    void show();
}
public class InterImpl extends Object implements Inter {
    public InterImpl() {
        super();
    }
    @Override
    public void method() {
        System.out.println("method");
    }
    @Override
    public void show() {
        System.out.println("show");
    }
}
public class InterfaceDemo {
    public static void main(String[] args) {
        Inter i = new InterImpl();
//        i.num = 20;
        System.out.println(i.num);
//        i.num2 = 40;
        System.out.println(i.num2);
        System.out.println(Inter.num);
    }
}

应用:接口的案例

对猫和狗进行训练,他们就可以跳高了,这里加入跳高功能。

请采用抽象类和接口来实现猫狗案例,并在测试类中进行测试。

/*
    动物类
*/
public abstract class Animal {
    private String name;
    private int age;
    public Animal() {
    }
    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public abstract void eat();
}

/*
    跳高接口
*/
public interface Jumpping {
    public abstract void jump();
}

/*
    猫类
*/
public class Cat extends Animal implements Jumpping {
    public Cat() {
    }
    public Cat(String name, int age) {
        super(name, age);
    }
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
    @Override
    public void jump() {
        System.out.println("猫可以跳高了");
    }
}

/*
    测试类
*/
public class AnimalDemo {
    public static void main(String[] args) {
        //创建对象,调用方法
        Jumpping j = new Cat();
        j.jump();
        System.out.println("­­­­­­­­");
        Animal a = new Cat();
        a.setName("加菲");
        a.setAge(5);
        System.out.println(a.getName()+","+a.getAge());
        a.eat();
//        a.jump();
        a = new Cat("加菲",5);
        System.out.println(a.getName()+","+a.getAge());
        a.eat();
        System.out.println("­­­­­­­­");
        Cat c = new Cat();
        c.setName("加菲");
        c.setAge(5);
        System.out.println(c.getName()+","+c.getAge());
        c.eat();
        c.jump();
    }
}

类和接口的关系

继承关系,只能单继承,不能多继承,但是可以多层继承

  • 类与接口的关系

实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口

  • 接口与接口的关系

继承关系,可以单继承,也可以多继承

接口和抽象类的区别(面试题)

接口与继承

但这可能会在一个类中产生庞大的代码量,因为继承一个接口时需要实现接口中所有的方法。

多重继承的语法格式如下:

class 类名 implements 接口1, 接口2,, 接口 n

多态

多态的概述

同类型的对象,执行同一个行为,在不同时刻会表现出不同的行为特征

  1. 多态的前提
    • 有继承或实现关系
    • 有父类引用指向子类对象
    • 有方法重写
  2. 多态的常见形式

父类类型 对象名称 = new 子类构造器;

接口 对象名称 = new 实现类构造器;

父类类型 对象名称 = new 子类构造器;

接口 对象名称 = new 实现类构造器;

/*
    父类Animal
*/
public abstract class Animal {
    public String name = "父类动物类";
    public abstract void eat();
}

/*
    子类Dog
*/
public class Dog extends Animal {
    public String name = "子类狗类";
    @Override
    public void eat() {
        System.out.println("狗吃骨头");
    }
}

/*
    子类Cat
*/
public class Cat extends Animal {
    public String name = "子类猫类";
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
}

/*
    测试类Test
*/
public class Test {
    public static void main(String[] args) {
        // 多态的形式:父类类型 对象名称 = new 子类构造器;
        Animal a = new Dog();
        a.eat(); // 方法:编译看左边,运行看右边
        System.out.println(a.name); // 对于变量的调用:编译看左边,运行也看左边

        // 运行结果
        // 狗吃骨头
        // 父类动物类

        System.out.println("===========");

        Animal a2 = new Cat();
        a2.eat(); // 方法:编译看左边,运行看右边
        System.out.println(a.name);

        // 运行结果
        // 猫吃鱼
        // 父类动物类
    }
}

多态中的成员访问特点

- (成员)方法的调用:编译看左边,运行看右边
- (成员)变量的调用:编译看左边,运行也看左边(多态侧重行为多态)

代码演示:

public class Animal {
    public int age = 40;
    public void eat() {
        System.out.println("动物吃东西");
    }
}
public class Cat extends Animal {
    public int age = 20;
    public int weight = 10;
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
    public void playGame() {
        System.out.println("猫捉迷藏");
    }
}
public class AnimalDemo {
    public static void main(String[] args) {
        //有父类引用指向子类对象
        Animal a = new Cat();
        System.out.println(a.age);
//        System.out.println(a.weight);
        a.eat();
//        a.playGame();
    }
}

多态的优势和弊端

在多态形势下,右边对象可以实现解耦合,便于扩展和维护

提高程序的扩展性。定义方法时候,使用父类类型作为参数,在使用的时候,使用具体的子类类型参与操作

Animal a = new Dog();
a.eat(); // 后续业务行为随着对象而改变,后续代码无需修改
  1. 弊端

多态下会产生的一个问题:多态下不能使用子类的独有功能

多态下的类型转换

  1. 向上转型

父类引用指向子类对象就是向上转型,即自动类型转换(从子到父):是子类对象赋值给父类类型的变量指向

  1. 向下转型

**即强制类型转换(从父到子):**此时必须进行强制类型转换,否则报错

  • 格式:子类 对象变量 = (子类)父类类型的变量
  • 作用:可以解决多态下的劣势,可以实现调用子类独有的功能。
  • 注意事项:
    • 强制类型转换,编译阶段不会报错(注意:有继承关系或实现关系在编译阶段可以强制转换,不会报错),但是运行时可能会出错,出现类型转换异常
    • 即 -> 有继承或实现关系的类就可以在编译阶段进行强制类型转换;但是,如果转型后的类型和对象真实对象的类型不是同一种类型,那么在运行代码时,就会出现 ClassCastException
Animal c = new Cat();
Dog d = (Dog)c; // 出现类型转换异常 ClassCastException
  • 代码演示:
/**
    动物类
*/

public class Animal {
    public void eat() {
        System.out.println("动物吃东西");
    }
}

/**
    猫类
*/
public class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
    public void playGame() {
        System.out.println("猫捉迷藏");
    }
}

/**
    测试类
*/
public class AnimalDemo {
    public static void main(String[] args) {
        //多态
        //向上转型
        Animal a = new Cat();
        a.eat();
//      a.playGame();
        //向下转型
        Cat c = (Cat)        c.eat();
        c.playGame();
    }
}
  1. instanceof 关键字

Java 建议强转转换前使用 instanceof 判断当前对象的真实类型,再进行强制转换

变量名 instanceof 真实类型

判断关键字左边的变量指向的对象的真实类型,是否是右边的类型或者是其子类类型,
是则返回true,反之为false。

类型转换总结

  1. 引用数据类型的类型转换,有几种方式?
    • 自动类型转换、强制类型转换。
  2. 强制类型转换能解决什么问题?
    • 可以转换成真正的子类类型,从而调用子类独有功能。
  3. 强制类型转换需要注意什么?
    • 有继承关系/实现的 2 个类型就可以进行强制转换,编译无问题。
    • 运行时,如果发现强制转换后的类型不是对象真实类型则报错(ClassCastException)
  4. 强制类型转换前最好做什么事情,如何进行?
    • 使用 instanceof 判断当前对象的真实类型,再进行强制转换
    • 对象变量名 instanceof 真实类型

应用:多态的案例

  • 案例需求

请采用多态的思想实现猫和狗的案例,并在测试类中进行测试

  • 代码实现
public class Animal {
    private String name;
    private int age;

    public Animal() {
    }
    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }

    public void eat() {
        System.out.println("动物吃东西");
    }
}
public class Cat extends Animal {
    public Cat() {
    }
    public Cat(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
}
public class Dog extends Animal {
    public Dog() {
    }
    public Dog(String name, int age) {
        super(name, age);
    }
    @Override
    public void eat() {
        System.out.println("狗吃骨头");
    }
}
public class AnimalDemo {
    public static void main(String[] args) {
        //创建猫类对象进行测试
        Animal a = new Cat();

        a.setName("加菲");
        a.setAge(5);
        System.out.println(a.getName() + "," + a.getAge());
        a.eat();
        a = new Cat("加菲", 5);
        System.out.println(a.getName() + "," + a.getAge());
        a.eat();
    }
}

案例:模拟开发一款动物表演类的游戏

需求:

分析:

定义一个 USB 的接口(申明 USB 设备的规范必须是:可以接入和拔出)。

提供 2 个 USB 实现类代表鼠标和键盘,让其实现 USB 接口,并分别定义独有功能。

创建电脑对象,创建 2 个 USB 实现类对象,分别安装到电脑中并触发功能的执行。