目录
1.多态
1.1多态的概念
多态是什么?
多态是同一个行为具有不同的表现形式或形态的能力
同一个方法可以根据发送对象的不同而采取不同的行为方式
举个例子来说:大家生活中常见的打印机,打印机分为彩色打印机和黑白打印机,在彩色打印机的情况下打出的是彩色,在黑白打印机的情况下打出的是黑白。
总的来说就是:多态就是事物的多种形态,一个对象在不同条件下所表现的不同形式
1.2如何实现多态
多态存在的条件:
1.在多态中必须存在有继承或实现关系的子类和父类
2.子类对父类的某些方法进行重新定义
3.通过父类的引用调用重写的方法
1.3多态的格式
- 父类类名 变量名 = new 子类名();
- 通过变量名.方法名(),这种格式来调用在子类中重写的方法
1.4多态中的成员变量
多态中的成员变量:编译运行看左边,即看的是父类的里面的变量。
来给大家举个例子:
这个是父类:
class Animal{
int age = 15;//父类
}
这个是子类:
class Dog extends Animal{
int age = 10;//子类
}
这个是启动项:
public class Test {
public static void main(String[] args) {
//父亲类型 变量名 = new 子类对象
Animal animal = new Dog();
System.out.println(animal.age);
}
}
让我们来运行一下,来看看结果 :
多态里面的成员方法:编译看左边,运行看右边
给大家举个例子:
这个是父类:
class Animal{
void eat(){
System.out.println("吃东西");//父类
}
这个是子类:
class Dog extends Animal{
@Override
void eat() {
System.out.println("吃狗粮");//子类
}
}
这个是启动项:
public class Test {
public static void main(String[] args) {
//父亲类型 变量名 = new 子类对象
Animal animal = new Dog();
animal.eat();
}
}
让我们来看一下运行结果:
当子类方法与父类的方法有相同的名称、返回类型和参数列表,此时会发生重写现象,这时候会调用子类的成员方法来取代父类的实现。
Tip:这里只是针对调用子类和父类方法名相同的成员方法所会发生的情况。
有人会问:当重写的方法很多时,有没有很快捷的方法帮我生成重写的代码?
我的回答:有的!
鼠标右键找到这个生成(我这里时下了汉化模组,英文版是Generate):
然后点击重写方法(英文版是Override Methods):
就欧克了。
1.5重写的注意事项
重写的要求:
1.方法名必须相同
2.参数列表必须相同(类型,个数,顺序)
3.返回值必须相同,如果想要返回值类型不同,则返回关系类型必须为父子类关系
重写的注意事项 :
1.父类被private修饰的方法,不能进行重写
2.父类被static修饰的方法,不能进行重写
3.父类被final修饰的方法,不能进行重写,此时这个方法被叫作密封方法
4.构造方法不能发生重写
5.访问修饰限定符 private < 默认权限(既什么都不写)< protected < public子类的访问修饰限定符权限一定要大于等于父类,才能发生重写现象
我们把重写这个运行过程叫做动态绑定 。
1.6多态的特点
- 当子类和父类存在相同的成员变量时,此时编译器会访问父类的成员变量
- 当子类和父类存在相同的成员方法时,此时会发生重写现象,此时编译器会访问子类当中的成员方法
- 当子类中有的方法,而父类没有时,此时则不能访问子类中独有的方法
- 子类和父类存在同名的静态的成员函数时,访问的是父类的成员函数
- 以上这些特点,均是在多态的情况下发生的
那有人会问:子类特有的方法,难道父类真的没有办法访问嘛?
我的回答是:办法当然是有的。
我们先看下这个例子:
当我们尝试去调用子类特有的方法是,编译器报错了,这是为啥了?还记得我们前面说的嘛,在调用成员方法是,编译看左,运行看右 ,在编译过程中,没有发现父类中有bark这个方法,所以编译系统就报错了,那如何解决呢?请接着往下看。
2.引用类型转换
2.1向上转型
什么是向上转型?
向上转型是Java中常用的类型转换方式之一,也称为自动类型转换。这种类型转换发生在继承关系中父子类之间,通常是将子类对象转换为父类对象
这里我们有三种方法可以发生向上转型:
1.直接赋值
2.方法传参的方式
3.返回值
那就我来给大家一一来列举
2.1.1直接赋值
class Animal{
String name;
int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public void eat(){
System.out.println(name + "正在吃");
}
@Override
public String toString() {
return "Animal{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
class Dog extends Animal{
public Dog(String name, int age) {
super(name, age);
}
public void wangWang(){
System.out.println(name+"正在汪汪");
}
}
public class Test {
public static void main(String[] args) {
Animal animal = new Dog("小狗",10);//直接赋值
animal.eat();
}
}
就像图中这样,直接赋值就好了。
2.1.2方法传参
就像图中这样。
2.1.3返回值
如图所示:
以上就是向上转型的三种方式,从上面三种方式看出: 向上转型无法调用子类特有的方法!
他只能用来调用父类自己的方法,除非发生重写现象,否则无法调用。
2.2向下转型
什么是向下转型?
将一个子类对象向上转型之后可以当成父类对象使用,如果需要调用子类特有的方法,则需要将父类对象在转换为子类对象即可。这就是向下转型
这里我们来以代码进行说明:
class Animal{
String name;
int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public void eat(){
System.out.println(name + "正在吃");
}
@Override
public String toString() {
return "Animal{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
class Dog extends Animal{
public Dog(String name, int age) {
super(name, age);
}
public void special(){
System.out.println("子类特有的方法");
}
public void wangWang(){
System.out.println(name+"正在汪汪");
}
}
public class Test {
public static void main(String[] args) {
Animal animal = new Dog("小狗",10);
Dog dog = (Dog)animal;//这里发生强制转换,将父类强制转换为子类。
dog.special();
dog.eat();
dog.wangWang();
}
运行结果如图所示:
这里我们看到,不仅将父类的方法调用出来,而且还把子类特有的方法一起运行出来。这就是向下转型的厉害之处。但再厉害的东西还是有缺点的。接下来我们就讲一讲
2.2.1向下转型的缺点
虽然我们可以通过向下转型来调用子类独有的方法,但也会产生下面问题
当我们新增加一个类Cat时,看看会发生什么。
这是子类新增的对象代码:
class Cat extends Animal{
public Cat(String name, int age) {
super(name, age);
}
public void special1(){
System.out.println("子类特有的方法1");
}
public void miaoMiao(){
System.out.println(name+"正在喵喵叫");
}
}
当我们把启动项的代码改动一下,看看会发生什么:
改成如下图所示:
public class Test {
public static void main(String[] args) {
Animal animal = new Cat("小猫",10);
Dog dog = (Dog)animal;//这里发生强制转换,将父类强制转换为子类。
dog.special();
dog.eat();
dog.wangWang();
}
此时我们运行下代码,发现报错了
此时报错的原因是ClassCastException,即类型转换异常。那为什么会发生这种现象了,请听我解释:
- 在启动项中,向上转型的过程,Aniaml aniaml = new Cat();animal这个对象是由子类Cat所构造出来的
- 而在向下转型的过程中 ,Dog dog = (Dog)animal 把animal这个对象转变成Dog类的对象
- 而Dog类和Cat类都是Animal类的子类
- 但通过上面这些步骤,将子类Cat的对象cat变成兄弟类的对象dog,此时不符合向下转型的定义了,故报错
那我们如何可以避免这种情况呢?请跟着我往下看。
2.2.2向下转型的防范
如果我们想要避免上述情况,我们可以用到instanceof,instanceof作用就是检测其变量类型是否属于该数据类型或者为它的子类类型,我们直接举例说明:
public class Test {
public static void main(String[] args) {
Animal animal = new Cat("小猫",10);
if(animal instanceof Cat){
Cat cat = (Cat)animal;
cat.special1();
}
else{
Dog dog = (Dog)animal;
dog.special();
}
就像图中这样,如果引用的是Cat这个子类对象,则会打印出Cat中特有的方法,如果引用的是Dog这个子类对象,则会打印出Dog中特有的方法,运行结果如下:
完整版代码:
class Animal{
String name;
int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public void eat(){
System.out.println(name + "正在吃");
}
@Override
public String toString() {
return "Animal{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
class Cat extends Animal{
public Cat(String name, int age) {
super(name, age);
}
public void special1(){
System.out.println("子类特有的方法1");
}
public void miaoMiao(){
System.out.println(name+"正在喵喵叫");
}
}
class Dog extends Animal{
public Dog(String name, int age) {
super(name, age);
}
public void special(){
System.out.println("子类特有的方法");
}
public void wangWang(){
System.out.println(name+"正在汪汪叫");
}
}
public class Test {
public static void main(String[] args) {
Animal animal = new Cat("小猫",10);
if(animal instanceof Cat){
Cat cat = (Cat)animal;
cat.special1();
}
else{
Dog dog = (Dog)animal;
dog.special();
}
}
3.结语
第一次写这么多的内容,如有不好的地方请大家指出。希望大家都会被自己满意的offer录取。