ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Java学习之路之week4day1

2022-01-25 21:01:26  阅读:131  来源: 互联网

标签:Java String void 之路 public println week4day1 抽象类 out


向Java程序员的目标前进!

day15

面向对象—续5

学习内容

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

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

故事背景:孔子是一名专门讲解论语的老师。而孔子的父亲是刚刚下岗的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,String,void,之路,public,println,week4day1,抽象类,out
来源: https://blog.csdn.net/weixin_43527493/article/details/122691881

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有