面向对象(二)
继承
在前面的学习我们可以知道,类是对某一批对象的抽象,而继承的本质是对某一批类的抽象,类比于现实世界中对动植物进行界门纲目科属种的逐级分类。
子类(派生类)是父类(基类)的拓展,子类继承父类,我们用extends
这个关键词表示继承的关系。(extends v.拓展
)子类与父类之间具有一个is
的关系,比如学生(子类)是人(父类)。
子类可以继承父类所有public
的属性与方法,而父类private
的属性与方法,子类是无法继承的。当我们需要让子类继承父类的属性与方法,且又不被继承关系以外的访问到的时候,我们就需要用到protected
关键词,让子类能够继承父类使用protected
的属性与方法。
在Java里,所有的类,都默认直接或者间接继承了Object
类。
注意,在Java里,只有单继承,无多继承,一个子类只能有一个父类。
tips:在IDEA里,ctrl+h
快捷键可以打开继承树,可以很直观看到继承关系。
例:
main:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class test { public static void main(String[] args) { Student student1 = new Student(); student1.setName("XiaoMing"); student1.setAge(17); student1.setID(810); student1.setGrade(99); Student student2 = new Student("XiaoHong", 16, 114, 100); System.out.println(student1.getName()+" "+student1.getAge()+" "+student1.getID()+" "+student1.getGrade()); System.out.println(student2.getName()+" "+student2.getAge()+" "+student2.getID()+" "+student2.getGrade()); } }
|
Person:(父类)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class Person { protected String name; protected int age; public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public int getAge() { return age; } }
|
Student:(子类)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class Student extends Person{ private int grade; private int ID; public Student(){} public Student(String aName, int aAge, int aID, int aGrade){ name = aName; age = aAge; ID = aID; grade = aGrade; } public int getGrade() { return grade; } public void setGrade(int grade) { this.grade = grade; } public int getID() { return ID; } public void setID(int ID) { this.ID = ID; } }
|
super
super
关键词可以调用父类的属性与方法,用来引用当前对象的父类。
直接进入例子:
main:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class test { public static void main(String[] args) { Student student = new Student();
student.output();
} }
|
Person(父类):
1 2 3 4 5 6 7 8
| public class Person { public Person(){ System.out.println("父类无参构造被执行"); } public void print(){ System.out.println("Person"); } }
|
Student(子类):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Student extends Person{ public Student(){ System.out.println("子类无参构造被执行"); } public void print(){ System.out.println("Student"); } public void output(){ print(); this.print(); super.print(); } }
|
注意点:
super
只能出现在子类的方法或者构造器中
- 当父类没有无参构造时,子类就无法调用父类无参构造,也就是说必须要在子类构造器内写上父类的有参构造。
例:
1 2 3 4 5 6 7 8 9 10
| public class Person { public Person(String name){} }
public class Student extends Person{ public Student(){ super("name"); } }
|
方法重写
子类可以重写父类的方法。
方法重写的特点:
- 方法名必须相同
- 参数列表必须相同(与方法重载不同)
- 修饰符:范围可以扩大,不可以缩小。
tips:在IDEA里,ctrl+inside
快捷键可以快速调用方法重写功能。
不能重写的方法:
- static 方法;
- private 方法;
- final 常量;
main:
1 2 3 4 5 6
| public class test { public static void main(String[] args) { B b = new B(); b.output(); } }
|
A(父类):
1 2 3 4 5
| public class A { protected void output(){ System.out.println("A-out"); } }
|
B(子类):
1 2 3 4 5 6
| public class B extends A{ @Override public void output() { System.out.println("B-out"); } }
|
多态
多态的定义
同一个行为具有多个不同表现形式或形态的能力。
比如在现实世界中,猫和狗(子类)都是动物(父类),他们都会吃(父类继承的方法),但是猫吃鱼,狗吃骨头(方法重写)。
多态存在的三个必要条件
- 继承
- 重写
- 父类引用指向子类对象
Person s2 = new Student();
Demo1:
1 2 3 4 5 6 7 8
| public class Demo { public static void main(String[] args) { Pet pet1 = new Dog(); Pet pet2 = new Cat(); pet1.eat(); pet2.eat(); } }
|
Pet(父类):
1 2 3 4 5
| public class Pet { public void eat(){ System.out.println("Eat All"); } }
|
Dog(子类1):
1 2 3 4 5 6
| public class Dog extends Pet { @Override public void eat() { System.out.println("Eat bone");; } }
|
Cat(子类2):
1 2 3 4 5 6
| public class Cat extends Pet { @Override public void eat() { System.out.println("Eat fish"); } }
|
多态的作用
提高了代码的拓展性,使用父类类型作为方法形式参数,传递子类对象给方法,进行方法的调用。
听起来很难理解,我们来看例子就很容易理解这个意思的。
依然使用上面例子的三个类Pet
、Dog
、Cat
。
Demo2:
1 2 3 4 5 6 7 8 9 10 11
| public class Demo { public static void main(String[] args) { Dog dog = new Dog(); Cat cat = new Cat(); petEat(dog); petEat(cat); } public static void petEat(Pet pet){ pet.eat(); } }
|
多态的"局限"
当某个子类拥有独有的方法的时候,我们多态的写法就无法访问子类独有的方法了。
我们重写一下Cat
类:
1 2 3 4 5 6 7 8 9
| public class Cat extends Pet { @Override public void eat() { System.out.println("Eat fish"); } public void playBall(){ System.out.println("Cat is playing Ball"); } }
|
Demo:
1 2 3 4 5 6 7 8 9
| public class Demo { public static void main(String[] args) { Pet cat = new Cat(); cat.eat(); cat.playBall(); } }
|
类型转换
instanceof
关键词可以用来测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。
这种比较需要两边存在关系,否则连编译都没法通过。
1 2 3 4 5 6 7 8 9
| public class Demo { public static void main(String[] args) { Object object = new Cat(); System.out.println(object instanceof Cat); System.out.println(object instanceof Pet); System.out.println(object instanceof Object); System.out.println(object instanceof Dog); } }
|
我们在之前的学习中,我们知道,基本数据类型之间的转换,低转高可以自动转化,高转低需要强制转化,在多态的类型转化也是如此。
多态本身是子类向父类向上转换(自动转换)的过程,这个过程是默认的,因此上文提到的“局限”就出现了,所以我们需要一种强制转化的方法来是对象可以调用子类独有的方法。
1 2 3 4 5 6 7
| public class Demo { public static void main(String[] args) { Pet cat = new Cat(); ((Cat) cat).playBall(); } }
|
这种强制转化的方法,也可以使得子转父,但是在子转父的时候,我们就会损失子类的方法。