Java教程

Java学习之路之week4day1

本文主要是介绍Java学习之路之week4day1,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

向Java程序员的目标前进!

day15

面向对象—续5

学习内容

  • day15
  • 面向对象—续5
    • 多态的案例—“孔子装爹”
    • 多态的练习
      • 练习:台灯的多态版
      • 练习:动物类的多态版
    • 抽象类
      • 抽象方法
      • 抽象类的格式
      • 抽象类的本质
      • 抽象类的特点
      • 抽象类的注意事项
      • 抽象类的实例化
      • 抽象类的成员特点
      • 面试题:定义抽象类的意义
      • 练习:台灯的抽象类版
      • 面试题:abstract不能和哪些关键字冲突
      • 练习:动物类的抽象类版
      • 练习:猫狗案例的抽象类版
    • 接口
      • 接口的格式
      • 类实现接口的格式
      • 接口的特点
      • 接口的例子
      • 接口成员特点
      • 类与类,类与接口以及接口与接口的关系
      • 面试题:接口和抽象类有什么区别
      • 练习:运动员和教练

多态的案例—“孔子装爹”

为了加深多态的成员特点的理解,我们将以伪代码的形式介绍。

故事背景:孔子是一名专门讲解论语的老师。而孔子的父亲是刚刚下岗的Java程序员,由于当时JavaSE非常火,很多人都需要学习,孔子的父亲就开始当老师讲JavaSE。有一天,孔子的爹被请去讲课了,孔子一个人在家。这时,还有一些人慕名前来找孔子爹学习JavaSE,孔子开始装他爹的模样,带上装备,黏上胡子,样子很像!1

伪代码:

class 孔子爹{
    int age = 40;
    //teach功能
    public void teach(){
        System.out.println("讲解JavaSE");
    }
}

class 孔子 extends 孔子爹{
    int age = 20;
    @Override
    public void teach(){
        System.out.println("讲解论语");
    }
    //特有功能
    public void playGame(){
        System.out.println("会写书");
    }
}

对以上伪代码在测试类中进行测试:

public class Test {
    public static void main(String[] args) {
        孔子爹 k1 = new 孔子() ;//向上转型
        System.out.println(k1.age);//编译看左,运行看左。所以这里输出结果是40
        k1.teach();//编译看左,运行看右。输出结果是“讲解论语”,一讲课就暴露了。
        k1.playGame();//这句会报错,因为这是子类的特有功能,访问不了,为了节省内存空间,推荐使用向下转型。将父类的引用强制转换为子类的引用
        孔子 k2 = (孔子) k1;
        k2.playGame();//这样写不会报错
    }
}

多态的练习

练习:台灯的多态版

/**
 * 需求:设计一个台灯类(Lamp)其中台灯有灯泡类(Buble)这个属性,
 * 还有开灯(on)这个方法。设计一个灯泡类(Buble),
 * 灯泡类有发亮的方法,其中有红灯泡类(RedBuble)和绿灯泡类(GreenBuble)
 * 他们都继承灯泡类(Buble)一个发亮的方法,请设计出一段代码可以使台灯开启灯泡发亮
 * 红灯泡发红光,绿灯泡发绿光!(多态)
 *
 */
//灯泡类
class Buble {
    //定义一个发亮的方法
    public void shine(){
        System.out.println("灯泡可以发亮了...") ;
    }
}

//绿灯泡
class GreenBuble extends Buble {
    @Override
    public void shine() {
        System.out.println("灯泡可以发绿光了...");
    }
}

//台灯类
class Lamp {
    //台灯有灯泡类(Buble)这个属性
    private Buble buble ;//灯泡类
    //灯(on)这个方法,开灯---灯泡要"发亮"
    public void on(Buble buble){
        buble.shine() ;//调用灯泡的发亮的方法
    }
}

//红灯泡
class RedBuble extends Buble {
    @Override
    public void shine() {
        System.out.println("灯泡可以发红光了...");
    }
}

//测试类
public class Test1 {
    public static void main(String[] args) {
        //创建台灯类对象
        Lamp lamp = new Lamp() ;
        Buble buble = new RedBuble() ;
        //调用开灯方法
        lamp.on(buble) ; 
        
        System.out.println("--------------------------------");
        
        Buble buble2 = new GreenBuble() ;
        lamp.on(buble2);

        System.out.println("--------------------------------");
        //匿名对象:子类匿名对象
        lamp.on(new RedBuble());
        lamp.on(new GreenBuble());
    }
}

练习:动物类的多态版

/**
 * 需求:定义一个动物类,里面有一个方法voice()
 * 定义一个类Cat,实现voice方法
 * 然后增加一种新的动物类型:Pig(猪),实现voice()方法。
 * 定义一个Dog类,实现voice方法
 * 定义一个Store(宠物店)类的getInstance方法:
 * 如果传入的参数是字符串dog,则返回一个Dog对象;
 * 如果传入pig,则返回一个Pig对象;否则,返回一个Cat对象。
 * 
 */
//动物类
class Animal {
    public void voice(){
        System.out.println("动物都需要发出声音...");
    }
}

//猫类
class Cat extends Animal {
    @Override
    public void voice() {
        System.out.println("猫发出喵喵叫的声音...");
    }
}

//狗类
class Dog extends Animal {
    @Override
    public void voice() {
        System.out.println("狗发出汪汪叫的声音");
    }
}

//猪类
class Pig extends Animal {
    @Override
    public void voice() {
        System.out.println("猪发出哼哼叫的声音...");
    }
}

//宠物店类
class Store {
    private Store(){} //构造方法私有化,外界不能new,然后功能加入static
    public static Animal getInsance(String type){
        if(type.equals("dog")){
            return  new Dog() ;
        }else if(type.equals("pig")){
            return  new Pig() ;
        }else{
            return  new Cat() ;
        }
    }
}

//测试类
public class Test2 {
    public static void main(String[] args) {
        Animal a = Store.getInsance("pig");//new Pig() ;
        a.voice();
        a = Store.getInsance("cat") ; //new Cat() ;
        a.voice();
        a = Store.getInsance("dog") ; //new Dog() ;
        a.voice();
    }
}

抽象类

回顾之前的猫狗案例,它们都有父类动物类。动物类是一个很抽象的事物,在定义时,我们只知道动物都会吃和睡。但是我们说一个动物,说的都是最具体的动物。只有说到具体的动物,我们才知道它吃的东西和其它的生活习性。

我们在开发中,应该将这种具有概括性的事物抽象化。并且,它们的吃或者睡的行为也不应该再给出具体的体现,应该只是声明出来(定义为没有方法提的方法),让具体的事物(子类)进行重写!

抽象方法

和之前学习定义成员方法是一样的,只是没有方法体和{}而已

格式:

	权限修饰符 abstract 返回值类型 方法名(参数列表);

抽象类的格式

	abstract class 类名{}

抽象类的本质

一旦定义了抽象类和抽象方法,再定义子类时必须重写所有父类的抽象方法!

本质:强制子类必须做的事情!

抽象类的特点

抽象类不能实例化!(不能创建对象)

抽象类的注意事项

  • 如果一个类中有抽象方法,那么这个类必须定义为抽象类!
  • 抽象类中不一定有抽象方法,也可以是非抽象方法!
  • 抽象类不能实例化,需要用具体的子类实例化
  • 如果抽象类的子类是抽象类,就不能实例化。但是一定要存在一个最具体的子类,否则定义抽象类没有意义!
  • 子类重写父类方法的时候,定义子类的方式时访问权限必须足够大,或者跟父类保持一致(不能更低),否则会报错!

抽象类的实例化

通过抽象类多态,父类引用指向子类对象。前提是要有最具体的子类,比如:

	Fu fu = new Zi() ;

这里的Fu就是一个抽象类类型。

抽象类的成员特点

  1. 成员变量:既可以是变量,也可以是常量。被final修饰的变量不能再赋值。
  2. 成员方法:既可以是抽象方法,也可以是非抽象方法
  3. 构造方法:存在继承关系所以分层初始化。先让父类初始化,然后子类再进行初始化!

面试题:定义抽象类的意义

问题:如果一个类中没有抽象方法,那么把这个类定义为抽象类的意义何在?

解答:这是属于设计层面的问题。定义抽象类,目的是不想直接让这个类创建对象,因为最终还是有具体的子类存在的。

比如jdk提供的日历类Calendar,它是一个抽象类,不能创建对象。但是Calendar提供了静态方法,返回值就是它自己。所以调用其它成员方法的本质就是在创建具体的子类对象。

练习:台灯的抽象类版

/**
 * 需求:设计一个台灯类(Lamp)其中台灯有灯泡类(Buble)这个属性,
 * 还有开灯(on)这个方法。设计一个灯泡类(Buble),
 * 灯泡类有发亮的方法,其中有红灯泡类(RedBuble)和绿灯泡类(GreenBuble)
 * 他们都继承灯泡类(Buble)一个发亮的方法,请设计出一段代码可以使台灯开启灯泡发亮
 * 红灯泡发红光,绿灯泡发绿光!
 *
 */

//灯泡类
abstract class Buble {
   public abstract void shine() ;
}

//绿灯泡类
class GreenBuble extends Buble {
    @Override
    public void shine() {
        System.out.println("灯泡可以发绿光了");
    }
}

//台灯类
class Lamp {
    private Buble buble ;
    public void on(Buble buble){
        buble.shine() ;
    }
}

//红灯泡类
class RedBuble extends Buble {
    @Override
    public void shine() {
        System.out.println("灯泡可以发红光了");
    }
}

//测试类
public class Test {
    public static void main(String[] args) {
        //创建台灯类对象
        Lamp lamp = new Lamp() ;
        Buble buble = new RedBuble() ;//抽象类多态
        //调用开灯方法
        lamp.on(buble) ; 
        System.out.println("--------------------------------");
        
        Buble buble2 = new GreenBuble() ;
        lamp.on(buble2);

        System.out.println("--------------------------------");
        //匿名对象:子类匿名对象
        lamp.on(new RedBuble());
        lamp.on(new GreenBuble());
    }
}

面试题:abstract不能和哪些关键字冲突

abstract的应用场景:修饰类或成员方法

修饰成员方法时注意:

  • abstract不能和private关键字使用。因为被private修饰的成员只能在当前类访问,而加入abstract后需要让子类实现这个方法。这已经超出了当前访问的范围,所以这是冲突的。

  • abstract不能和final关键字使用。因为被final修饰的成员方法是不能被重写的,而abstract修饰的抽象方法需要被子类强制重写的,所以这是冲突的。

  • abstract不能和static关键字使用。因为被static修饰的方法是跟类相关的,修饰后会随着类的加载而加载。而抽象方法是需要被子类重写的,最终需要使用对象来实现抽象类多态。abstract和static一块使用抽象方法在父类中又没有方法体,加载进入内存就没有意义了。所以这是冲突的。

//定义一个抽象方法
    //private abstract  void show() ;//非法格式 需要子类实现show方法,而private修饰的方法只能在当前访问
    //public final abstract void show() ;//非法格式
    //public static abstract  void show() ; //非法格式

    //标准的格式
    public abstract 返回值类型 方法名(参数列表);//参数列表可能空参/有参(基本类型/引用类型)

练习:动物类的抽象类版

/**
 * 需求:定义一个动物类,里面有一个方法voice(),
 * 定义一个类Cat,实现voice方法
 * 然后增加一种新的动物类型:Pig(猪),实现voice()方法。
 * 定义一个Dog类,实现voice方法
 * 定义一个Store(宠物店)类的getInstance方法:
 * 如果传入的参数是字符串dog,则返回一个Dog对象;
 * 如果传入pig,则返回一个Pig对象;否则,返回一个Cat对象。
 */

//动物类
abstract class Animal { //这个动物类----抽象类
    //优化
    //动物都需要发声,将voice方法应该仅仅声明即可,需要让子类实现发声的方法
    public abstract  void voice() ;
}

//猫类
class Cat extends Animal {
    @Override
    public void voice() {
        System.out.println("猫发出喵喵叫的声音...");
    }
}

//狗类
class Dog extends Animal {
    @Override
    public void voice() {
        System.out.println("狗发出汪汪叫的声音");
    }
}

//猪类
class Pig extends Animal {
    @Override
    public void voice() {
        System.out.println("猪发出哼哼叫的声音...");
    }
}

//宠物店
class Store {
    private Store(){} //构造方法私有化,外界不能new,然后功能加入static
    public static Animal getInsance(String type){
        if(type.equals("dog")){
            return  new Dog() ;
        }else if(type.equals("pig")){
            return  new Pig() ;
        }else{
            return  new Cat() ;
        }
    }
}

//测试类
public class Test {
    public static void main(String[] args) {
        Animal a = Store.getInsance("pig");//new Pig() ;
        a.voice();
        a = Store.getInsance("cat") ; //new Cat() ;
        a.voice();
        a = Store.getInsance("dog") ; //new Dog() ;
        a.voice();
    }
}

练习:猫狗案例的抽象类版

/**
 * 需求:用抽象类实现“猫狗”案例
 * 定义一个动物类(Animal),属性有姓名、年龄、颜色,行为有吃和睡
 * 再定义猫类(Cat)和狗类(Dog),都继承自动物类
 * 猫和狗吃的不一样,重写它们的方法
 * 分别定义方法 猫有特有功能:玩毛线;狗有特有功能:看门 
 */

//动物类
abstract class Animal { //抽象类
    //姓名,年龄,颜色
    private String name ;
    private int age ;
    private String color ;

    //无参构造方法
    public Animal() {
    }
    //有参构造方法
    public Animal(String name, int age, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
    }
    //公共的访问方法
    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 String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }
    //动物的吃和睡觉,应该给出声明即可---抽象方法
    public abstract  String eat() ;
    public abstract  void sleep() ;
}

//猫类
class Cat extends Animal {
    //构造方法
    public Cat() {
    }
    public Cat(String name, int age, String color) {
        super(name, age, color);
    }
    @Override
    public String eat() {
        return "猫吃鱼...";
    }
    @Override
    public void sleep() {
        System.out.println("猫舔着爪子睡觉...");
    }
    //特有功能
    public void playGame() {
        System.out.println("猫会玩毛线...");
    }
}

//狗类
class Dog extends Animal {
    //构造方法
    public Dog() {
    }
    public Dog(String name, int age, String color) {
        super(name, age, color);
    }
    @Override
    public String eat() {
        return "狗吃骨头";
    }
    @Override
    public void sleep() {
        System.out.println("狗趴着睡觉...");
    }
    //特有功能
    public void catchRabit(){
        System.out.println(this.getName()+"会抓兔子");
    }
}

public class Test {
    public static void main(String[] args) {
        //测试类
        //方式1:无参构造+setXXX()/getXXX()
        //多态测试
        Animal a = new Dog() ;//抽象类多态
        String result = a.eat();
        a.setName("黑子") ;
        a.setAge(3) ;
        a.setColor("棕色") ;
        System.out.println(a.getName()+"---"+a.getAge()+"---"+a.getColor());
        System.out.println(result);
        a.sleep(); ;
        //特有功能
        //向下转型
        Dog d = (Dog) a;
        d.catchRabit() ;

        System.out.println("--------------------------------------") ;
        
        //方式2:有参构造方法+getXXX()
        //猫类测试
        Animal a3 = new Cat("橘猫", 3, "花色");
        System.out.println(a3.getName()+"---"+a3.getAge()+"---"
                +a3.getColor());
        String str2 = a3.eat();
        System.out.println(str2);
        a3.sleep();
        Cat cc = (Cat) a3;
        cc.playGame();
    }
}

接口

继续回到猫狗的例子,猫和狗属于动物的一种,但是它能够具有跳高、钻火圈、做数学题这些它们本身不具备的功能。这是经过后天学习和驯养员培养出来,才具备了这些额外的功能。通过这个例子我们可以总结出来,一个事物如果实现了额外的功能,那么它也就具备了这个功能。

回到Java中,接口的定义其实与上面的总结十分相似。接口是一种规范,如果类能够实现接口中额外的功能,那么就说明当前这个类具备这个功能。从宏观角度来说,接口能让事物实现额外的功能接口是比抽象类还抽象的一种类型。

接口的格式

	public interface 接口名{}

接口名和类名的命名规范是一致的,都遵循"大驼峰命名法"

类实现接口的格式

	class 类名 implements 接口名{}

接口的特点

  1. 接口中的方法只能是抽象方法,不能有方法体

  2. 接口不能实例化(不能创建对象)

    接口的实例化是通过接口多态实现的

接口的例子

/**
 * 在AnimalArain接口中定义两个功能:
 * jump():跳高
 * compute():计算
 * 最后编写测试类测试
 */

interface Jump{
//    public void jump(){ //接口的方法只能是抽象方法
//
//    }
   public abstract void jump() ;
}

interface  ComplateCal{//做计算的接口
    public abstract  void cal() ;
}
class Dog{
    public void lookDoor(){
        System.out.println("狗可以看门");
    }
}

//跳高高它是狗,然后局部额外的功能,跳高
class JumpDog extends Dog implements Jump,ComplateCal{ //继承一个类的同时,可以实现多个接口

    @Override
    public void jump() {
        System.out.println("狗可以跳高了...");
    }

    @Override
    public void cal() {
        System.out.println("狗可以做计算了...");
    }
}

//测试类
public class InterfaceDemo {
    public static void main(String[] args) {
        //创建接口对象
        // Jump jump = new Jump () ;//接口不能实例化,
        //如何实例化呢:接口多态---提供接口的子实现类
        Jump jump = new JumpDog() ;//接口类型---->子实现类对象 (接口类型)
        jump.jump() ;
        //向下转型
        JumpDog jumpDog  = (JumpDog) jump;
        jumpDog.lookDoor() ;
        jumpDog.cal();
        ComplateCal cc = new JumpDog() ;//接口多态
        cc.cal() ;
    }
}

接口成员特点

  • 成员变量只能是常量,默认修饰符是public static final,可以省略
  • 没有构造方法,只是提供一些抽象方法,让子实现类实现这些方法
  • 成员方法只能是抽象方法,默认修饰符是public abstract,可以省略

类与类,类与接口以及接口与接口的关系

  • 类与类:继承关系,只能单继承,不支持多继承,但是可以多层继承

  • 类与接口:实现关系,可以单(接口)实现,也可以多(接口)实现。还可以在继承一个类的同时实现多个接口

    	class 子实现类名 extends 父类名 implements 接口1,接口2,...{}
    
  • 接口与接口:继承关系,不仅可以单继承,还可以多继承,也可以多层继承

面试题:接口和抽象类有什么区别

  1. 成员的区别

    • 接口的成员:

      • 成员变量:只能是常量,存在默认的修饰符public static final

      实际开发中,如果要自定义一个常量,就先定义一个接口,里面再写常量即可!

      • 成员方法:一般说的是抽象方法,可以是default默认方法或者静态方法,必须有方法体
      • 构造方法:没有
    • 抽象类的成员

      • 成员变量:既可以是变量,也可以是常量。被final修饰的变量不能再赋值。
      • 成员方法:既可以是抽象方法,也可以是非抽象方法,如果是抽象方法,方法中的abstract不能省略。
      • 构造方法:存在有参构造/无参构造都可以。因为是继承关系,肯定是分层初始化的。
  2. 关系的区别

    • 类与类的关系:继承关系

      类可能是抽象类或具体类。继承关系只支持单继承,不支持多继承,可以多层继承

    • 类与接口的关系:实现关系

      一个类继承另一个类的同时可以实现多个接口

    • 接口与接口的关系:继承关系

      可以单继承,可以多继承,也可以多层继承

  3. 设计理念的区别

    • 抽象类最终肯定有具体的子类,是继承关系,体现的是一种“is a”的关系,可以用抽象类多态描述

    • 接口描述的是事物本身不具备的功能,是通过后台学习培养出来的额外的拓展功能。

      接口的核心思想体现的是“like a”的关系

练习:运动员和教练

分析:

在这里插入图片描述

实现:

//篮球教练类
class BasketballCoach extends Coach{
    public BasketballCoach() {
    }
    public BasketballCoach(String name, int age, String gender) {
        super(name, age, gender);
    }
    @Override
    public void teach() {
        System.out.println("篮球教练教运动员怎么运球");
    }
}

//篮球运动员类
class BasketBallPlayer extends Player {
    public BasketBallPlayer() {
    }
    public BasketBallPlayer(String name, int age, String gender) {
        super(name, age, gender);
    }
    @Override
    public void study() {
        System.out.println("篮球运动员学习如何运球和投篮");
    }
}

//教练类
abstract class Coach extends Person{
    public Coach() {
    }
    public Coach(String name, int age, String gender) {
        super(name, age, gender);
    }
    @Override
    public void eat() {
        System.out.println("教练吃的是快餐");
    }
    public abstract void teach();
}

//人类
public abstract class Person { //抽象类
    private String name ;    //姓名
    private int age ;    //年龄
    private String gender ;    //性别

    //无参
    public Person() {
    }
    //有参构造
    public Person(String name, int age, String gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }
    //公共访问方法
    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 String getGender() {
        return gender;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
    //吃的东西不一样,所以让子类实现,仅仅给出声明即可
    public abstract  void eat() ;
}

//乒乓球教练类
class PingPangCoach extends Coach implements SpeakEnglish{
    public PingPangCoach() {
    }
    public PingPangCoach(String name, int age, String gender) {
        super(name, age, gender);
    }
    @Override
    public void teach() {
        System.out.println("乒乓球教练教运动员怎么打球");
    }
    @Override
    public void speak() {
        System.out.println("乒乓球教练会说英语!");
    }
}

//乒乓球运动员类
class PingPangPlayer extends Player implements SpeakEnglish {
    public PingPangPlayer() {
    }
    public PingPangPlayer(String name, int age, String gender) {
        super(name, age, gender);
    }
    @Override
    public void study() {
        System.out.println("乒乓球运动员学习如何发球和接球");
    }
    @Override
    public void speak() {
        System.out.println("乒乓球运动员会说英语!");
    }
}

//运动员类
abstract class Player extends  Person { //抽象类
    //构造方法
    public Player() {
    }
    public Player(String name, int age, String gender) {
        super(name, age, gender);
    }
    @Override
    public void eat() {
        System.out.println("运动员吃的是营养餐");
    }
    //学习的的内容不一样,只有见到具体的运动员才能知道学的是什么
    //学习的功能---给出声明即可
    public abstract  void study() ;
}

//说英语的接口
interface SpeakEnglish {
    //说英语
    public abstract void speak() ;
}

//测试类
public class Test {
    public static void main(String[] args) {
        SpeakEnglish se = new PingPangPlayer() ;
        se.speak(); //会说英语
        PingPangPlayer pingPangPlayer = (PingPangPlayer)se;
        pingPangPlayer.setName("马龙") ;
        pingPangPlayer.setAge(30) ;
        pingPangPlayer.setGender("男");
        System.out.println(pingPangPlayer.getName()+"---"+pingPangPlayer.getAge()+"---"+pingPangPlayer.getGender()) ;
        pingPangPlayer.eat() ;
        pingPangPlayer.study();
        
        System.out.println("--------------------------------------") ;
        
        Coach pc = new PingPangCoach("刘国梁",40,"男");
        PingPangCoach pingPangCoach = (PingPangCoach) pc;
        pingPangCoach.speak();
        System.out.println(pc.getName()+"---"+pc.getAge()+"---"+pc.getGender());
        pc.eat();
        pc.teach();
    }
}

博客难免会产生一些错误。如果写的有什么问题,欢迎大家批评指正。


  1. 故事是完全虚构的。 ↩︎

这篇关于Java学习之路之week4day1的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!