手把手教你学会Java中的面向对象之继承与多态(炼气篇)
继承
继承的定义是啥
别急,家人们,我们先来想一个问题:
如果我们要定义一个狗类,是不是应该这样去写呀?
- 定义狗的名字
- 定义狗的年龄
- 定义狗的行为(吃)
public class Dog {
public String name;//狗的名字
public int age; //狗的年龄
//狗会做出的行为(动作)
public void eat(){
System.out.println("吃");
}
}
如果我们要定义一个猫类,应该这样去写叭!
- 定义猫的的名字
- 定义猫的年龄
- 定义猫的行为(吃)
//定义一个猫类
class Cat{
public String name; //猫的名字
public int age; //猫的年龄
//猫的行为(吃)
public void eat(){
System.out.println("吃");
}
}
如果我们要定义一个鸡类,是不是也要这样去写呐?
- 定义鸡的名字
- 定义鸡的年龄
- 定义鸡的行为(吃)
//定义一个鸡类
class chicken{
public String name; // 鸡的名字
public int age; //鸡的年龄
//鸡的行为(吃)
public void eat(){
System.out.println("吃");
}
}
此时此刻,聪明的你一定已经发现问题了,如果把所有的动物都写一个类,
那岂不是要累死,还好人类足够聪明(爱偷懒)
我们慢慢观察就可以发现其中的规律!
所谓的狗类,猫类,鸡类都有着共同的属性(名字和年龄)以及共同的行为(吃)
那这些类从本质上都属于动物类,所以我们只需要定义一个动物类(包括名字,年龄,吃)的类
然后让狗类,猫类,鸡类继承这个动物类就OK啦
- 定义一个动物类
- 定义动物的属性(名字,年龄)
- 定义动物的行为(吃)
//定义了一个父类,名叫动物的父类
public class Animal {
public String name; //定义动物的名字
public int age; //定义动物的年龄
//定义动物的行为(吃)
public void eat(){
System.out.println("吃");
}
}
继承的目的
为了提高代码的复用性
小tips:
- 子类会继承父类的成员方法和成员变量
- 子类不能继承父类的构造方法
- 子类也不能继承父类的static修饰的方法和属性(静态方法和静态属性属于类,不属于对象)
super关键字
super的作用:为了在子类方法中访问父类的成员
例子:
public class Dog extends Animal {
public String name = "狗";//狗的名字
public int age = 10; //狗的年龄
public Dog() {
}
//狗会做出的行为(动作)
public void eat(){
System.out.println(super.name); //会访问父类Animal的姓名
super.eat(); //会访问父类eat的方法
System.out.println("==========");
System.out.println("狗吃");
}
}
class Text{
public static void main(String[] args) {
//定义一个Dog对象
Dog dog = new Dog();
dog.eat(); //调用eat()方法
}
}
小tips:
- super只能访问从父类继承过来的成员
- super只是一个关键字,不是父类对象的引用
- 作用是为了在子类的方法中访问父类的方法
- super在构造方法中不能与this()同时存在,都只能在第一行
super与this的区别
相同点:
- super与this都关键字
- 都只能在非静态方法中使用,来访问非静态成员
- 都只能是在构造方法中的第一条语句,不能同时存在
不同点:
- this表示当前对象的引用, super表示在子类方法中访问父类成员
- 在构造方法中this()表示在本类中调用其他的构造方法
- super()表示调用父类的构造方法
- 构造方法中一定有super(),(默认有), 而this(),我们不写则没有
如果一个类不想被继承,则使用final关键字来修饰
多态
多态定义
为了去完成一些行为,不同的对象会产生不同的状态,
即:父类引用指向了子类的对象
Animal dog = new Dog();
向上转型
定义:将子类对象赋值给父类引用
向上转型的两种方式:
1.直接赋值
Animal dog = new Dog();
2.方法传参和返回值
public class Text1 {
public static void main(String[] args) {
Cat cat = new Cat("小猫",23);
fun2(cat); //调用cat的eat()方法
Animal animal = fun1();
System.out.println(animal.name);
}
//父类作为方法的返回值,可以接收任意子类
public static Animal fun1(){
Dog dog = new Dog("小狗",234);
return dog;
}
//父类作为方法的参数,可以传输任意子类
public static void fun2(Animal animal){
animal.eat();
}
}
向上转型的缺点
这个向上转型, 不能够去调用子类独有的,特有的方法
动态绑定
父类引用指向了子类型对象,并且调用了子类重写的方法
Animal animal = new Dog(); //父类型的引用animal指向了子类型的对象
animal.eat(); //调用了子类重写过后的eat()方法
例子:
//定义父类Animal
class Animal {
public String name = "动物"; //定义动物的名字
public int age = 10; //定义动物的年龄
//定义动物的行为(吃)
public void eat(){
System.out.println("动物吃");
}
}
//子类狗继承父类Animal
class Dog extends Animal {
public String name = "狗";//狗的名字
public int age = 10; //狗的年龄
public Dog() {
}
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
//重写父类的同名的eat()方法
public void eat(){
System.out.println("狗吃");
}
}
class Text2 {
public static void main(String[] args) {
Animal animal = new Dog();
animal.eat(); //运行结果为子类的eat()狗吃
}
}
静态绑定与动态绑定的区别
静态绑定(编译时绑定):
表示编译器在编译代码的时候就能够确定
调用的是父类的方法还是子类的方法(方法重载)
动态绑定(运行时绑定):
父类引用调用子类的重写方法,就叫做动态绑定
为什么?
Animal animal = new Dog();
animal.eat();
编译时看左边(Animal),运行时看右边(Dog)
编译器在编译过程中发现
这个eat()方法在编译过程中调用的是父类的Animal类,
但是在编译结束之后;
在运行过程中发现这个方法已经被子类重写了,于是就会去调用子类重写之后的方法
如何查看编译过程
1.第一步:在代码中鼠标右击,去找到该代码的目录,点击Show in Explorer
2.第二步:进入out文件夹,进入.class文件中,输入cmd
3.第三步:输入javap -c Text2(名字)
4.查看main方法
5.分析代码,在编译时,确实调用了Animal类的eat()方法
但是在运行时发现子类重写了eat()方法,
于是就调用了子类重写的eat()方法.
这个就是编译时看左,运行时看右.
重写
什么是重写
当父类的方法无法满足子类的要求是,就必须在子类重写这个方法
上面的例子中eat()方法就被重写了
重写的条件
- 重写的方法名必须和父类的方法相同
- 参数列表必须相同
- 返回值必须相同
重写与重载的区别
重写 | 重载 | |
---|---|---|
方法名 | 相同 | 相同 |
参数列表 | 相同 | 不同 |
返回值 | 相同(或具有继承关系) | 不影响(没所谓) |
访问符 | 相同(或只降不升,严格) | 不影响(没所谓) |
重写的注意事项
- private的方法不能重写
- static的方法不能重写
- final的方法不能重写
- 构造方法不能重写
- 返回值可不同但必须有继承关系
向下转型
父类类型被强转为子类类型(非常不安全哦)
可以使用instanceof关键字来判断
总结
继承
-
目的是为了提高代码的复用性
-
使用过extends关键字来继承
-
super的目的是为了在子类中访问父类的成员
多态
多态核心: 是一种思想,父类引用指向子类对象,调用同名的重写方法来表现出不同的行为
多态发生的过程
- 第一步向上转型
- 第二步通过父类引用调用子类重写的方法
多态的作用
- 降低圆复杂度
- 增强代码的扩展度
多态的缺点
- 只有方法可以使用多态,属性不可以
- 构造方法不能使用多态