java知识五(继承、多态)
第一章 继承
Java中提供了一个关键字extends,用这个关键字,可以让一个类和另一个类建立起父子关系。
public class B extends A {
}
A类称为父类(基类或超类)。
B类称为子类(派生类)。
1.1 子类继承
子类能继承父类的非私有成员(成员变量、成员方法)
1.2 继承后对象的创建
子类的对象是由子类、父类共同完成的。
继承的好处:1.代码复用 2.减少了重复代码
1.2.1 People父类创建
package com.kgl.extendsdemo;
public class People {
private String name;
private int age;
private char sex;
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 char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public People() {
}
public People(String name, int age, char sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
}
1.2.2 Teacher类
package com.kgl.extendsdemo;
public class Teacher extends People{
private String skill;
public String getSkill() {
return skill;
}
public void setSkill(String skill) {
this.skill = skill;
}
public Teacher(String name, int age, char sex, String skill) {
super(name, age, sex);
this.skill = skill;
}
public Teacher() {
}
}
1.2.3 Consultant类
package com.kgl.extendsdemo;
public class Consultant extends People{
private int num;
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
}
1.2.4 使用
package com.kgl.extendsdemo;
public class Test {
public static void main(String[] args) {
Teacher t = new Teacher("张三",18,'男',"java");
System.out.println(t.getName());//张三
System.out.println(t.getAge());//18
System.out.println(t.getSex());//男
System.out.println(t.getSkill());//java
Consultant c = new Consultant();
c.setNum(10);
System.out.println(c.getNum());//10
}
}
1.3 权限修饰符
定义:就是用来限制类中的成员(成员变量、成员方法、构造器)能够被访问的范围。
四种权限修饰符及范围:
private:只能本类。
缺省(即默认,不写修饰符):本类、同一个包中的类。
protected:本类,同一个包中的类、子孙类中。
public:任意位置。
权限大小排序:private < 缺省 < protected < public
权限修饰符访问范围表
| 修饰符 | 本类里 | 同一个包中的类 | 子孙类 | 任意类 |
| private | √ | | | |
| 缺省 | √ | √ | | |
| protected| √ | √ | √ | |
| public | √ | √ | √ | √ |
package com.kgl.extends2modifier;
public class Fu {
private void privatemethod() {
System.out.println("privatemethod");
}
void method() {
System.out.println("method");
}
protected void protectedmethod() {
System.out.println("protectedmethod");
}
public void publicmethod() {
System.out.println("public");
}
public static void main(String[] args) {
Fu fu = new Fu();
fu.privatemethod(); //privatemethod
fu.method(); //method
fu.protectedmethod(); //protectedmethod
fu.publicmethod(); //public
}
}
package com.kgl.extends2modifier;
public class Test {
public static void main(String[] args) {
Fu fu = new Fu();
// fu.privatemethod();
fu.method(); //method
fu.protectedmethod(); //protectedmethod
fu.publicmethod(); //public
}
}
package com.kgl.extends3modifier;
import com.kgl.extends2modifier.Fu;
public class Demo {
public static void main(String[] args) {
Fu fu = new Fu();
// fu.privatemethod();
// fu.method();
// fu.protectedmethod(); //protectedmethod
fu.publicmethod(); //public
}
}
package com.kgl.extends3modifier;
import com.kgl.extends2modifier.Fu;
public class Zi extends Fu {
public static void main(String[] args) {
Zi zi = new Zi();
// zi.privatemethod();
// zi.method();
zi.protectedmethod(); //protectedmethod
zi.publicmethod(); //public
zi.show(); //protectedmethod public
}
public void show() {
// privatemethod();
// method();
protectedmethod();
publicmethod();
}
}
1.4 继承的特点
单继承:Java是单继承模式:一个类只能继承一个直接父类。
多层继承:Java不支持多继承,但支持多层继承。
祖宗类:Java中所有的类都是Object类的子类。
就近原则:优先访问自己类中,自己类中的没有才会访问父类。
1.4.1 Java中的类为啥不支持多继承?
反证法示例:
假设类A和类B都有同名方法method()。
如果类C同时继承A和B (class C extends A, B)。
当调用c.method()时,编译器无法确定是调用A的方法还是B的方法,从而产生歧义。
结论:因此,Java中的类不支持多继承。
1.4.2 Java中的祖宗类:Object
Java中所有类,要么直接继承了Object,要么默认继承了Object,要么间接继承了Object。
因此,Object是所有类的祖宗类。
1.5 继承后子类访问成员的特点:就近原则
访问顺序:在子类方法中访问其他成员(成员变量、成员方法),是依照就近原则的。
先子类局部范围找,然后子类成员范围找,然后父类成员范围找,如果父类范围还没有找到则报错。
重名处理:如果子父类中,出现了重名的成员,会优先使用子类的,如果此时一定要在子类中使用父类可以通过super关键字,指定访问父类的成员:
语法:super.父类成员变量/父类成员方法
package com.kgl.extends4feature;
public class Test {
public static void main(String[] args) {
Zi zi = new Zi();
zi.show();
}
}
class Fu{
String name="父的name";
public void run(){
System.out.println("父类的run方法");
}
}
class Zi extends Fu {
String name="子的name";
public void show(){
String name ="show的name";
System.out.println(name); //show的name
System.out.println(this.name); //子的name
System.out.println(super.name); //父的name
run(); //子类的run方法
this.run(); //子类的run方法
super.run(); //父类的run方法
}
public void run(){
System.out.println("子类的run方法");
}
}
1.6 方法重写
当子类觉得父类中的某个方法不好用,或者无法满足自己的需求时,子类可以重写一个方法名称、参数列表一样的方法,去覆盖父类的这个方法,这就是方法重写。
重写小技巧:使用Override注解,他可以指定java编译器,检查我们方法重写的格式是否正确,代码可读性也会更好。
1.6.1 方法重写的其它注意事项
子类重写父类方法时,访问权限必须大于或者等于父类该方法的权限(public > protected > 缺省)。
重写的方法返回值类型,必须与被重写方法的返回值类型一样,或者范围更小。
私有方法、静态方法不能被重写,如果重写会报错的。
package com.kgl.extends5override;
public class Test {
public static void main(String[] args) {
Cat cat = new Cat();
cat.cry(1);//🐱是喵喵~
System.out.println(cat.cry(1)); //🐱是喵喵~ 1
}
}
class Cat extends Animal{
// 方法重写:方法名称,形参列表必须一样,这个方法就是方法重写。
@Override //方法重写的校验注解(标志):要求方法名称和形参列表必须与被重写方法一致,否则报错! 更安全,可读性好,更优雅!
public int cry(int a){
System.out.println("🐱是喵喵~");
return a;
}
}
class Animal{
public int cry(int a){
System.out.println("动物会叫");
return a;
}
}
1.6.2 方法重写用场景
当子类觉得父类的方法不好用,或者不满足自己的需求时,就可以用方法重写。
子类重写object类的toString()方法,以便返回对象的内容。
package com.kgl.extends5override;
public class Test2 {
public static void main(String[] args) {
// 子类重写Object的toString
Student student = new Student("张三",18,"女");
System.out.println("名字是"+student.getName()+"性别是"+student.getSex()+"年龄是"+student.getAge()); //名字是张三性别是女年龄是18
// System.out.println(student.toString()); // com.kgl.extends5override.Student@5f184fc6 所谓的地址
System.out.println(student); //Student{name='张三', age=18, sex='女'}
// 注意1:直接输出对象,默认会调用Object的toString方法(可以省略不写调用toString的代码),返回对象的地址信息
// 注意2:输出对象的地址实际上是没有什么意义的,开发中更希望输出对象时看对象的内容信息,所以子类需要重写Object的toString方法。
// 以便以后输出对象时就默认调用子类重写的toString方法返回对象的内容。
}
}
class Student{
private String name;
private int age;
private String sex;
public Student(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
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 getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
}
1.7 子类构造器的特点
子类的全部构造器,都会先调用父类的构造器,再执行自己。
子类构造器实现调用父类构造器:
默认情况下,子类全部构造器的第一行代码都是super()(写不写都有),它会调用父类的无参数构造器。
如果父类没有无参数构造器,则我们必须在子类构造器的第一行手写super(…),指定去调用父类的有参数构造器。
super(…)调用父类有参数构造器为对象中包含父类这部分的成员变量进行赋值。
1.7.1 子类构造器实现调用父类构造器
package com.kgl.extends6constructor;
public class Test {
public static void main(String[] args) {
Zi zi = new Zi(); //父类有参构造方法 子类无参构造方法
}
}
class Zi extends Fu{
public Zi(){
// super(); 默认存在,写不写都有
super(1); //指定调用父类有参构造方法
System.out.println("子类无参构造方法");
}
}
class Fu{
private Fu(){
System.out.println("父类无参构造方法");
}
public Fu(int a){
System.out.println("父类有参构造方法");
}
}
1.7.2 子类构造器调用父类构造器的场景
父类
package com.kgl.extends6constructor;
public class People {
private String name;
private int age;
private char sex;
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 char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public People() {
}
public People(String name, int age) {
this.name = name;
this.age = age;
}
}
子类
package com.kgl.extends6constructor;
public class Teacher extends People {
private String skill;
public String getSkill() {
return skill;
}
public void setSkill(String skill) {
this.skill = skill;
}
public Teacher(String name, int age, String skill) {
super(name, age);
this.skill = skill;
}
public Teacher() {
}
}
调用
package com.kgl.extends6constructor;
public class Test2 {
public static void main(String[] args) {
Teacher teacher = new Teacher("李四",36,"java");
System.out.println(teacher.getName()); //李四
System.out.println(teacher.getAge()); //36
System.out.println(teacher.getSkill()); //java
System.out.println(teacher.getSex()); //char类型默认为空
}
}

1.8 this(…)调用兄弟构造器
任意类的构造器中,是可以通过this(…)去调用该本类的其他构造器的。
public class Student {
private String schoolName;
private String name;
public Student(String name){
this(name, "程序员");
}
public student(String name , String schoolName ){
this.name = name;
this.schoolName = schoolName;
}
}
1.8.1 this(…)和super(…)使用时的注意事项:
this(…) 、super(…) 都只能放在构造器的第一行,因此,有了this(…)就不能写super(…)了,反之亦然。
package com.kgl.extends6constructor;
public class Student {
private String schoolName;
private String name;
public Student() {}
@Override
public String toString() {
return "Student{" +
"schoolName='" + schoolName + '\'' +
", name='" + name + '\'' +
'}';
}
public String getSchoolName() {
return schoolName;
}
public Student(String name) {
this("程序员",name);
}
public Student(String schoolName, String name) {
this.schoolName = schoolName;
this.name = name;
}
public void setSchoolName(String schoolName) {
this.schoolName = schoolName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package com.kgl.extends6constructor;
public class Test3 {
public static void main(String[] args) {
// 理解this(...)调用兄弟构造器
// 创建对象,存储一个学生的数据。
Student s1 = new Student();
s1.setName("张三");
System.out.println(s1); //Student{schoolName='null', name='张三'}
Student s2 = new Student("李四");
System.out.println(s2); //Student{schoolName='程序员', name='李四'}
Student s3 = new Student("程序员","李四");
System.out.println(s3); //Student{schoolName='程序员', name='李四'}
}
}
第二章 多态
多态是在继承/实现情况下的一种现象,表现为:对象多态、行为多态。
People p1 = new Student(); //对象
p1.run(); //行为
People p2 = new Teacher(); //对象
p2.run(); //行为
2.1 多态的前提
有继承/实现关系;存在父类引用子类对象;存在方法重写。
多态的一个注意事项
多态是对象、行为的多态,Java中的属性(成员变量)不谈多态。
2.2 多态的好处
在多态形式下,右边对象是解耦合的,更便于扩展和维护,使用父类类型的变量作为方法的形参时,可以接收一切子类对象。
定义方法时,使用父类类型的形参,可以接收一切子类对象,扩展性更强、更便利。
多态下不能使用子类的独有功能,不能直接调用子类的独有方法。
Animal父类
package com.kgl.polymorphsm;
public class Animal {
String name = "动物";
public void run(){
System.out.println("动物会跑");
}
}
Wolf类
package com.kgl.polymorphsm;
public class Wolf extends Animal{
String name = "狼";
@Override
public void run() {
System.out.println("🐺跑的贼快");
}
public void eat(){
System.out.println("狼吃羊");
}
}
Tortoise类
package com.kgl.polymorphsm;
public class Tortoise extends Animal{
String name = "乌龟";
@Override
public void run() {
System.out.println("🐢跑的贼慢");
}
public void shrinkHead() {
System.out.println("乌龟缩头了");
}
}
实现
package com.kgl.polymorphsm;
public class Test {
public static void main(String[] args) {
// 对象多态,行为多态。
Animal a1 = new Wolf();
a1.run(); //🐺跑的贼快 方法:编译看左边,运行看右边
System.out.println(a1.name); //动物 成员变量:编译看左边,运行也看左边
go(a1); //com.kgl.polymorphsm.Wolf@404b9385开跑.... 🐺跑的贼快
Animal a2 = new Tortoise();
a2.run(); //🐢跑的贼慢 方法:编译看左边,运行看右边
// a2.shrinkHead();//多态下不能使用子类的独有功能,不能直接调用子类的独有方法。
System.out.println(a2.name); //动物 成员变量:编译看左边,运行也看左边
go(a2); //com.kgl.polymorphsm.Tortoise@7cca494b开跑.... 🐢跑的贼慢
}
// 使用父类类型的形参,可以接收一切子类对象,扩展性更强、更便利。
public static void go(Animal a) {
System.out.println(a+"开跑....");
a.run();
}
}
2.3 多态下的类型转换
可以把对象转换成其真正的类型,从而解决了多态下不能调用子类独有方法的问题。
自动类型转换:父类 变量名 = new 子类();
例如:People p = new Teacher();
强制类型转换:子类 变量名 = (子类) 父类变量
例如:Teacher t = (Teacher) p;
2.3.1 强制类型转换的注意事项
存在继承/实现关系就可以在编译阶段进行强制类型转换,编译阶段不会报错。
运行时,如果发现对象的真实类型与强转后的类型不同,就会报类型转换异常(ClassCastException)的错误出来。
错误示例:
Animal a1 = new Tortoise();
Wolf t2 = (Wolf) a1; //java.lang.ClassCastException
强转前建议
使用instanceof关键字,判断当前对象的真实类型,再进行强转。
语法:p instanceof Student
package com.kgl.polymorphsm3;
public class Test2 {
public static void main(String[] args) {
Animal a1 = new Tortoise();
a1.run(); //🐢跑的贼慢
// a1.shrinkHead(); // 多态下的一个问题:多态下不能调用子类独有功能。
// 强制类型转换:
Tortoise t1 = (Tortoise) a1;
t1.shrinkHead(); //乌龟缩头了
// Wolf t2 = (Wolf) a1; //java.lang.ClassCastException
if (a1 instanceof Wolf){
Wolf w1 = (Wolf) a1;
w1.eat();
}else if (a1 instanceof Tortoise){
Tortoise t11 = (Tortoise) a1;
t11.shrinkHead(); //乌龟缩头了
}
System.out.println("=======================");
Wolf w = new Wolf();
go(w); //开跑.... 🐺跑的贼快 狼吃羊
Tortoise t = new Tortoise();
go(t); //开跑.... 🐢跑的贼慢 乌龟缩头了
}
public static void go(Animal a) {
System.out.println("开跑....");
a.run();
// java建议强制转换前,应该判断对象的真实类型,再进行强制类型转换。
if(a instanceof Wolf){
Wolf w1 = (Wolf) a;
w1.eat();
}else if(a instanceof Tortoise){
Tortoise t1 = (Tortoise) a;
t1.shrinkHead();
}
}
}
三、加油站支付小模块
某加油站为了吸引更多的车主,推出了如下活动,车主可以办理金卡和银卡。
卡片信息包括:车牌号码、车主姓名、电话号码、卡片余额。
金卡办理时入存金额必须>=5000元,银卡办理时预存金额必须>=2000元,金卡支付时享受8折优惠,银卡支付时享受9折优惠,金卡消费满200元可以提供打印免费洗车票的服务。
需求:请使用面向对象编程,完成该加油站支付机的存款和消费程序。
3.1 Card
package com.kgl.demo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
// lombok技术可以实现为类自动添加getter setter方法 无参数构造器,toString方法等。
@Data // @Data注解可以自动生成getter setter方法 无参构造器 toString方法等
@NoArgsConstructor
@AllArgsConstructor
public class Card {
private String carid;
private String name;
private String phone;
private double money;
public void deposit(double money){
this.money+=money;
}
public void consume(double money){
this.money+=money;
}
}
3.2 GoldCard
package com.kgl.demo;
public class GoldCard extends Card{
private String cardname="金卡会员";
public GoldCard(String carid, String name, String phone, double money) {
super(carid, name, phone, money);
}
@Override
public void consume(double money){
System.out.println("您是"+cardname+",当前卡余额"+getMoney());
System.out.println("您当前消费"+money);
System.out.println("优惠后价格"+money*0.9);
if(getMoney()<money*0.9){
System.out.println("余额不足,请充值");
return;
}
setMoney(getMoney()-money*0.9);
System.out.println("消费后余额"+getMoney());
if (money*0.9>=200){
PrintTicket();
}else {
System.out.println("消费不足200,没有免费洗车");
}
}
public void PrintTicket(){
System.out.println("请打印洗车票");
}
}
3.3 SliverCard
package com.kgl.demo;
public class SliverCard extends Card{
private String cardname="银卡会员";
public SliverCard(String carid, String name, String phone, double money) {
super(carid, name, phone, money);
}
@Override
public void consume(double money){
System.out.println("您是"+cardname+",当前卡余额"+getMoney());
System.out.println("您当前消费"+money);
System.out.println("优惠后价格"+money*0.9);
if(getMoney()<money*0.9){
System.out.println("余额不足,请充值");
return;
}
setMoney(getMoney()-money*0.9);
System.out.println("消费后余额"+getMoney());
}
}
3.4 Test
package com.kgl.demo;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
GoldCard gold = new GoldCard("沪A123456","delei","1321234",5000);
pay(gold);
SliverCard sliver = new SliverCard("沪A12346","de","132123",2000);
pay(sliver);
}
public static void pay(Card c){
System.out.println("请刷卡输入消费金额:");
Scanner sc = new Scanner(System.in);
double money=sc.nextDouble();
c.consume(money);
}
}
更多推荐


所有评论(0)