Educoder —Java面向对象 - 封装、继承和多态的综合练习(2)

通关任务2:

任务描述
本关任务:按要求编写一个Java应用程序,巩固Java封装、继承和多态的知识。
编程要求
按照要求编写一个Java应用程序:

定义一个抽象类Person,包含抽象方法eat(),封装属性name、sex、age,声明包含三个参数的构造方法;

定义一个Chinese类,继承自Person类,重写父类的eat()方法,并定义一个自己特有的方法shadowBoxing();

定义一个English类,继承自Person类,重写父类的eat()方法,并定义一个自己特有的方法horseRiding();

编写测试类,定义一个showEat()方法,使用父类作为方法的形参,实现多态,分别调用showEat()方法,通过强制类型转换调用各自类特有的方法;

具体输出要求请看测试说明。

测试说明
测试输入:
张三

20
史蒂文

22
预期输出:
姓名:张三,性别:男,年龄:20,我是中国人,我喜欢吃饭!
姓名:史蒂文,性别:男,年龄:22,我是英国人,我喜欢吃三明治!
张三在练习太极拳!
史蒂文在练习骑马!

开始你的任务吧,祝你成功!

思路以及学要注意的地方(以供借鉴):

1、
//创建Person类对象person1,引用指向中国人,通过有参构造函数实例化中国人类对象
// 通过showEat()方法调用Chinese的eat()方法
上面是题目中的部分要求,正常写法是

Person person1=new Chinese(cName,cSex,cAge);
        showEat (person1);

但是会发现会出现以下报错在这里插入图片描述
此方法我的解决办法是:将创建的showEat方法设置为static静态方法,才能接受所传入的对象,解释如下:
在《Java编程思想》P86页有这样一段话:

static方法就是没有this的方法。在static方法内部不能调用非静态方法,反过来是可以的。而且可以在没有创建任何对象的前提下,仅仅通过类本身来调用static方法。这实际上正是static方法的主要用途。

这段话虽然只是说明了static方法的特殊之处,但是可以看出static关键字的基本作用,简而言之,一句话来描述就是:

方便在没有创建对象的情况下来进行调用(方法/变量)。

很显然,被static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问。

static可以用来修饰类的成员方法、类的成员变量,另外可以编写static代码块来优化程序性能。
  
2.

public void eat(){     //实现抽象方法eat()  编写方法体
        System.out.println("姓名:"+super.getname()+",性别:"+super.getsex()+",年龄:"+super.getage()+",我是中国人,我喜欢吃饭!");
    }
    public void shadowBoxing(){
        System.out.println(super.getname()+"在练习太极拳!");
    }

对于上述的代码输出中不能直接使用super.name直接进行输出
如果使用会出现以下错误:在这里插入图片描述

在这里插入图片描述
super可以用来调用父类的非私有成员变量和非私有方法,对于私有变量(已封装成员变量)的调用要使用super.getxxx()方式调用

代码如下:


```java
package case2;

import java.util.Scanner;

public class Task2 {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		String cName = sc.next();
		String cSex = sc.next();
		int cAge = sc.nextInt();
		String eName = sc.next();
		String eSex = sc.next();
		int eAge = sc.nextInt();
		// 创建测试类对象test
		// 创建Person类对象person1,引用指向中国人,通过有参构造函数实例化中国人类对象
		// 通过showEat()方法调用Chinese的eat()方法
		// 创建Person类对象person2,引用指向英国人,通过有参构造函数实例化英国人类对象
		// 通过showEat()方法调用English的eat()方法
		/********* begin *********/
        Person person1=new Chinese(cName,cSex,cAge);
        showEat (person1);
        Person person2=new English(eName,eSex,eAge);
        showEat (person2);
		/********* end *********/
		// 强制类型转换(向下转型) 调用Chinese类特有的方法shadowBoxing()
		// 强制类型转换(向下转型) 调用English类特有的方法horseRiding()
		/********* begin *********/
        Chinese chinese=(Chinese) person1;
        chinese.shadowBoxing();
        English english=(English) person2;
        english.horseRiding();
		/********* end *********/
	}

	// 定义showEat方法,使用父类作为方法的形参,实现多态,传入的是哪个具体对象就调用哪个对象的eat()方法
	/********* begin *********/
    //题目中没有static 这里应该定义一个静态方法,如果不定义静态方法的话,上面showeat(对象)无法正常调用
    public static void showEat(Person person){
        person.eat();
        
    }
	/********* end *********/
}

// 抽象类Person 封装属性name、sex和age
// 构造函数初始化name、sex和age
// 声明抽象方法eat()
abstract class Person {
	/********* begin *********/
    private String name;
    private String sex;
    private int age;
    public String getname(){
        return name;
    }
    public void setname(String name){
        this.name=name;
    }
    public String getsex(){
        return sex;
    }
    public void setsex(String sex){
        this.sex=sex;
    }
    public int getage(){
        return age;
    }
    public void setage(int age){
        this.age=age;
    }
    public Person (String name,String sex,int age){
        this.name=name;
        this.sex=sex;
        this.age=age;
    }
    public abstract void eat();//定义抽象方法  让子类实现
	/********* end *********/
}

// Chinese类继承自Person类
// 构造函数初始化name、sex和age
// 重写父类方法eat() 输出'姓名:name,性别:sex,年龄:age,我是中国人,我喜欢吃饭!'
// 定义子类特有方法shadowBoxing(),当父类引用指向子类对象时无法调用该方法 输出'name在练习太极拳!'
class Chinese extends Person {
	/********* begin *********/
    public Chinese(String name,String sex,int age){
        super(name,sex,age);
        sex=super.getname();
        name=super.getname();
        age=super.getage();
    }
    public void eat(){     //实现抽象方法eat()  编写方法体
        System.out.println("姓名:"+super.getname()+",性别:"+super.getsex()+",年龄:"+super.getage()+",我是中国人,我喜欢吃饭!");
    }
    public void shadowBoxing(){
        System.out.println(super.getname()+"在练习太极拳!");
    }
    

	/********* end *********/
}

// English类继承自Person类
// 构造函数初始化name、sex和age
// 重写父类方法eat() 输出'姓名:name,性别:sex,年龄:age,我是英国人,我喜欢吃三明治!'
// 定义子类特有方法horseRiding(),当父类引用指向子类对象时无法调用该方法 输出'name在练习骑马!'
class English extends Person {
	/********* begin *********/
    public English(String name,String sex,int age){
        super(name,sex,age);
        sex=super.getname();
        name=super.getname();
        age=super.getage();
    }
    public void eat(){     //实现抽象方法eat()  编写方法体
        System.out.println("姓名:"+super.getname()+",性别:"+super.getsex()+",年龄:"+super.getage()+",我是英国人,我喜欢吃三明治!");
    }
    public void horseRiding(){
        System.out.println(super.getname()+"在练习骑马!");
    }
	/********* end *********/
}

相关知识:
为了完成本关任务,你需要掌握:1.重写和重载;2.abstract(抽象类)和interface(接口);3.final关键字;4.static关键字;5.多态。

重写和重载
方法重载(overload):

必须是同一个类;

方法名(也可以叫函数)一样;

参数类型不一样或参数数量或顺序不一样;

不能通过返回值来判断重载。

方法的重写(override)子类重写了父类的同名方法,两同两小一大原则:

方法名相同,参数类型相同;

子类返回类型是父类返回类型的子类;

子类抛出异常小于等于父类方法抛出异常;

子类访问权限大于等于父类方法访问权限。

在重写中,运用的是动态单分配,根据new的类型确定对象,从而确定调用的方法;

在重载中,运用的是静态多分配,根据静态类型确定对象,不能根据new的类型确定调用方法;

多态中,Father f = new Son()。

成员变量:编译运行参考左边;
成员函数:编译看左边,运行看右边;
静态函数:编译运行看左边。

abstract(抽象类)和interface(接口)
抽象类

用abstract修饰的类表示抽象类,抽象类位于继承树的抽象层,抽象类不能被实例化。

用abstract修饰的方法表示抽象方法,抽象方法没有方法体。抽象方法用来描述系统具有什么功能,但不提供具体的实现,把具体实现留给继承该类的子类。

抽象类特点:

含有抽象方法的类必须声明为抽象类(不管其中是否有其他方法);

抽象类可以没有抽象方法,可以有普通方法;

抽象类必须被继承,抽象方法必须被重写(若子类还是抽象类,不需要重写);

抽象类不能被实例化(不能直接构造一个该类的对象)。

抽象方法特点:

在类中没有方法体(抽象方法只需声明,而不需实现某些功能);

抽象类中的抽象方法必须被实现;

如果一个子类没有实现父类中的抽象方法,则子类也变成了一个抽象类。

接口
interface 中的方法默认为public abstract (public、abstract可以省略),变量默认为public static final;类中的方法全部都是抽象方法。只有声明没有实现,在不同类中有不同的方法实现。

不同点:

接口中只能包含抽象方法和默认方法,不能为普通方法提供方法实现;抽象类中可以包含普通方法;

接口里不能定义静态方法(jdk1.8下可以定义static方法),抽象类可以定义静态方法;

接口中只能定义静态常量,不能定义普通成员变量;抽象类即可以定义变量又可以定义静态常量;

接口中不包含构造器,抽象类里可以包含构造器,抽象类中的构造器并不是用于创建对象,而是让其他子类调用这些构造器来完成抽象类的初始化操作;

接口里不能包含初始化块,但抽象类可以包含;

一个类最多只能有一个父类,包括抽象类;但一个类可以直接实现多个接口,通过实现多个接口可以弥补Java单继承的不足。

共同点:

接口和抽象类都不能被实例化,都位于继承树的顶端,用于被其他类实现的继承;

接口和抽象类都可以包含抽象方法,实现接口和继承抽象类的普通子类都必须实现这些方法。

final关键字
final修饰的类,就是最终类,不能被继承。

final修饰的方法,就是最终方法,最终方法不能被重写。

final修饰一个引用变量时,是指引用变量不能变,引用变量所指向的对象中的内容还是可以改变的。修饰基本数据类型变量时,内容不能变。

final成员变量必须在初始化代码块或在构造器中初始化。

作用:

final类:如果一个类不需要有子类,类的实现细节不允许改变,并且确信这个类不会再被扩展,那么就设计成final类。

final方法:①把方法锁定,防止任何继承类修改它的意义和实现。②高效,编译器在遇到调用final方法时候会转入内嵌机制,大大提升执行效率。

static关键字
static修饰的变量称为静态变量,静态变量属于整个类,而局部变量属于方法,只在该方法内有效。static不能修饰局部变量。static方法内部不能调用非静态方法。

静态变量只能在类主体中定义,不能在方法中定义;

static变量只会创建一份,不管创建几个对象,都共用一个变量。

类方法指被static修饰的方法,无this指针。其他的就是实例方法。类方法可以调用其他类的static方法。
类方法和对象方法的区别:

1、 类方法是属于整个类的,而实例方法是属于类的某个对象的。
由于类方法是属于整个类的,并不属于类的哪个对象,所以类方法的方法体中不能有与类的对象有关的内容。即类方法体有如下限制:

类方法中不能引用对象变量;

类方法中不能调用类的对象方法;

在类方法中不能使用super、this关键字。(this表示当前类的对象,由static修饰的方法是类直接调用,不需要创建对象,所以不能用this);

类方法不能被覆盖。

2、与类方法相比,对象方法几乎没有什么限制:

对象方法中可以引用对象变量,也可以引用类变量;

对象方法中可以调用类方法;

对象方法中可以使用super、this关键字。

static关键字的作用

为某特定数据类型或对象分配单一的存储空间,而与创建对象的个数无关;实现某个方法或属性与类而不是对象关联在一起;

静态变量属于类,在内存中只有一个复制,只要静态变量所在的类被加载,这个静态变量就会被分配空间。

多态
定义:不同类的对象对同一消息做出响应。同一消息可以根据发送对象的不同而采用多种不同的行为方式;

多态存在的三个必要条件:继承、重写、父类引用指向子类对象;

Java中多态的实现方式:接口实现,继承父类进行方法重写,同一个类中进行方法重载;

父类引用指向子类对象,该引用不能再访问子类新增的成员。Animal cat = new Cat()与直接new一个父类实例(Animal a = new Animal())的区别?
答:当父类是接口和抽象类时,不能实例化,只能运用多态,向上转型。普通类中,可以在子类中重写父类中的方法,这样就可以访问子类中的重写方法。

  • 16
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈陈不会敲代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值