目录
多态
在Java中多态就是在完成一件事的时候,不同人去完成产生的结果不同
比方说打印,我们就是要打印一个东西,如果我们交给彩色打印机,那么会打印出来彩色的,而交给黑白打印机的话则会打印出来黑白的。
实现多态的条件
- 有继承关系
- 子类会重写父类的方法
- 会发生向上转型
在Java中的体现则是,当代码运行时,传递不同的类对象时返回的结果不同。
重写
重写(覆盖),子类对父类非静态,非private,非final的方法的核心部分进行重新编写
- 重写时,方法名字不可以变,返回类型不可以改变,参数列表不可以改变。只可以更改核心部分的代码。
- 对于修饰方法的修饰符可以有变化,但是就是子类的访问权限不能低于父类的访问权限。
- 返回类型也可以修改,只不过需要构成父子类关系,如下:父类中的返回值类型为A,子类的返回值类型为B,两者构成了父子关系,所以可以修改返回类型。
class A {
public A eat() {
return new A();
}
}
class B extends A {
@Override
public B eat() {
return new B();
}
}
- 我们可以在重写方法的时候,在方法上加上注解@Override,这个注解的作用就是帮助我们检查,我们方法名写没写错等。
重写和重载
这个知识点也经常被作为面试题,当然很多学校也很喜欢考,在以前的文章中介绍过重载,在这就不过说了。
重载是在一个类中呈现多态性
重写是在父类和子类继承的时候呈现多态性
静态绑定和动态绑定
静态绑定:也可以称为前期绑定(早绑定),在编译的时候,根据用户传递的参数来确定调用哪个方法。(方法重载就是典型的静态绑定)
动态绑定:后期绑定(晚绑定),在编译的时候并不能确定方法的行为,只有真正运行的时候才能知道方法具体是执行哪个类的哪个方法。(向上转型就发生了动态绑定)
向上转型和向下转型
向上转型
向上转型:创建一个子类对象,再将其赋值给父类。小范围转向大范围
父类 对象名 = new 子类();
Eg:Animal animal = new cat();
我们来谈一谈向上转型的内部原理:我在这画了一个大概的图,希望你们能看懂。
看上图,我们应该有了些许了解,那么我们也可以知道,对于子类而言,在堆中存储的子类对象有两部分:继承下来的和子类自己的内容。下面重点来喽!
向上转型调用的到底是父类的还是子类的呢
1.由于我们进行了向上转型,所以定义的变量只可以访问子类的继承的部分内容。
2 .由于向上转型发生了动态绑定,所以当调用子类父类同名的方法的时候,本来该调用父类的,但由于执行时会调用子类的。
3 .由于向上转型只可以访问子类的继承部分,所以根本访问不到子类特有的方法。(这也就是向上转型的一大弊端)。
这里借鉴我看过的博主的一个图片,我感觉这个图片很好,很方便理解。
向上转型可以用下面三种方法来应用。
1.直接赋值
2.方法传参
3.作为返回值
有这三种场景可以发生向上转型,我们来看代码吧
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 + "吃饭");
}
}
class Cat extends Animal {
public Cat(String name, int age) {
super(name,age);
}
@Override
public void eat() {
System.out.println(name + "吃鱼");
}
}
class Dog extends Animal {
public Dog(String name, int age) {
super(name,age);
}
@Override
public void eat() {
System.out.println(name + "吃骨头");
}
}
public class JavaSE730 {
//2.通过参数来向上转型,用父类来接受,可以介绍任何的子类
public static void eatFood(Animal a){
a.eat();
}
//3.当成返回值来向上转型
public static Animal buyAnimal(String var){
if("狗" == var){
return new Dog("狗狗",1);
}else if("猫" == var){
return new Cat("猫猫", 1);
}else{
return null;
}
}
public static void main(String[] args) {
Animal cat = new Cat("元宝",2); // 1. 直接赋值:子类对象赋值给父类对象
Dog dog = new Dog("小七",3);
eatFood(cat);
eatFood(dog);//2.将子类dog对象传过去,那边用Animal接收,也发上向上转型
}
}
向下转型
我们上面提到了,向上转型的话,我们不能访问子类独有的内容,而我们有时又想要访问独有的内容,这就引出来了向下转型。向下转型即将父类引用对象还原为子对象。
Cat cat = new Cat("元宝",2);
Animal animal = cat;//向上转型
cat = (Cat)animal;//向下转型
向下转型并不常用,因为不安全。如果原来子类为猫,向上转型后再向下转型为猫没有问题,因为原来就为猫类;但是假设原来为狗类向下转型为猫类则就出错了,这也正是不安全的地方。狗一定是动物而动物并不一定是狗。
instanceof
如果向下转型的时候子类不对应的话,会抛出异常,在Java中我们可以通过instanceof进行处理异常。
if(animal instanceof Cat){//判断向上转型时的子类是否为cat,是进行下面操作
cat = (Cat)animal;
cat.mew();
}
抽象类
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
上面那个了解就可以,下面内容才是重要的!
1.被abstract修饰的类为抽象类,被abstract修饰的方法为抽象方法,抽象方法不用有方法体并且要有分号。
abstract class A {
abstract void func();
}
2.抽象类也可以包含普通方法和属性,也可以有构造方法。
3.抽象类不可以进行实例化。
4.抽象类存在的意义就是被继承。
5.抽象方法不可以被private修饰,也不能被final和static修饰,因为抽象方法要保证能重写。
6.抽象类不可以被final修饰,要保证抽象类能被继承。
7.抽象类必须被继承,并且继承后子类要重写父类中的抽象方法,否则子类也是抽象类,必须要使用 abstract 修饰。
8.抽象类中不一定包含抽象方法,但是有抽象方法的类一定是抽象类。