目录
前言
Hi~ 你好!
欢迎点击我的博客 : )
这是我的学习总结,希望也能帮助到你
我的博客地址:hi~,我在这里~,欢迎关注哦,
三连不迷路,优质内容持续更新!
一.多态
polymorphism
1. 多态的概念
有多种形态:同一件事情,发生在不同对象身上,就会产生不同的结果
2. 多态实现条件
实现多态必须同时满足以下条件:
1.必须在继承体系下
2.子类必须对父类中的方法进行重写
3.通过父类的引用调用重写的方法
public class TestAnimal2 {
public static void main(String[] args) {
new Animal("老虎",16).eat();//直接调用父类的方法
Animal animal1 = new Cat("小橘",4);
animal1.eat();//通过父类的引用调用重写的方法
Animal animal2 = new Dog("大黄",7);
animal2.eat();//通过父类的引用调用重写的方法
}
}
老虎在淦饭
小橘在吃鱼
大黄在吃骨头
多态的体现 : 在运行代码时,当传递不同类对象的时,会调用对应类中的方法
public class Animal {//父类
String name;
int age;
public Animal(String name, int age) {//Animal的构造方法
this.name = name;
this.age = age;
}
void eat(){
System.out.println(name+"在淦饭");
}
}
public class Cat extends Animal {
public Cat(String name,int age){//cat的构造方法,来自传递父类的参数
super(name,age); //在构造子类前,要先构造完父类
}
@Override
void eat() {//重写父类的eat方法
System.out.println(name+"在吃鱼");
}
}
public class Dog extends Animal{
public Dog(String name, int age) {
super(name, age);
}
@Override
void eat() {
System.out.println(name+"在吃骨头");
}
}
public class TestAnimal {
public static void eatTest(Animal animal){//传参进行参数转型
//通过父类的引用调用重写的方法
animal.eat();
}
// 编译器在编译代码时,并不知道要调用Dog 还是 Cat 中eat的方法
// 等程序运行起来后,形参animal引用的具体对象确定后,才知道调用哪个方法
// 此处的形参类型必须时父类类型才可以
public static void main(String[] args) {
Cat cat = new Cat("Tom",5);
Dog dog = new Dog("旺财",6);
new Animal("狐狸",3).eat();//直接调用父类的est方法
eatTest(new Animal("企鹅",8));//通过eatTest方法
eatTest(cat);//通过父类的引用调用子类重写的方法
eatTest(dog);
}
}
狐狸在淦饭
企鹅在淦饭
Tom在吃鱼
旺财在吃骨头
编译器在编译代码时,并不知道要调用Dog 还是 Cat 中eat的方法 等程序运行起来后,
形参animal引用的具体对象确定后,才知道调用哪个方法
3. 重写
重写是子类对父类非静态、非 private修饰,非final 修饰,非构造方法等的实现过程进行重新编写 , 返回值和形参都不能改变重写的好处在于子类可以根据需要,定义特定于自己的行为
3.1 方法重写的规则:
1.子类重写父类方法时,必须与父类方法原型一致:返回值类型 方法名 (参数列表) 要完全一致2.被重写的方法返回值类型可以不同,但是必须是具有父子关系的 (协变类型)
3.子类的访问权限要 >= 父类的访问权限 private<默认<protected<public
如果父类方法被public修饰,则子类中重写该方法就不能声明为 protected
4.父类中static(静态方法属于类)、private修饰的方法、final修饰的方法(密封方法)、构造方法都不能被重写5.重写的方法, 可以使用 @Override 注解, 能帮住校验拼写
3.2 重写和重载的区别
区别 | 重写(override) | 重载(overload) |
参数列表 | 不能修改 | 不行修改 |
返回值类型 | 不能修改(除非继承关系) | 可以修改 |
访问;限定符 | 不能做更严格的限制 | 可以修改 |
3.3 静态/动态绑定
静态绑定: 也称为前期绑定(早绑定),即在编译时,根据用户所传递实参类型就确定了具体调用那个方法。典型代表函数重载。
动态绑定: 也称为后期绑定 ( 晚绑定 ),即在编译时,不能确定方法的行为,需要等到程序运行时,才能够确定具体调用哪个类的方法
动态绑定的前提:
1.向上转型2.方法重写3.通过父类引用,调用重写的方法.
4. 向上转移和向下转型
4.1 向上转型
创建一个子类对象,将其当成父类对象来使用
父类类型 对象名 = new 子类类型()
Animal animal1 = new Cat("Tom",5);
animal是父类类型,但可以引用一个子类对象,因为是从小范围向大范围的转换。
使用:1.直接赋值 2.方法传参 3. 方法返回
作返回值:返回任意子类对象:
public static Animal askAnimal(String var){ if("狗".equals(var)){ return new Dog("哮天犬",3000); } else if ("猫".equals(var)) { return new Cat("狸花猫",3); }else { return null; } } // 编译器在编译代码时,并不知道要调用Dog 还是 Cat 中eat的方法 // 等程序运行起来后,形参animal引用的具体对象确定后,才知道调用哪个方法 // 此处的形参类型必须时父类类型才可以 public static void main(String[] args) { Animal animal1 = new Cat("Tom",5); Animal animal2= new Dog("旺财",6); new Animal("狐狸",3).eat();//直接调用父类的est方法 eatTest(new Animal("企鹅",8));//通过eatTest方法 eatTest(animal1);//通过父类的引用调用子类重写的方法 eatTest(animal2); System.out.println("============"); Animal animal3 = askAnimal("狗"); Animal animal4 = askAnimal("猫"); animal3.eat(); animal4.eat(); System.out.println("============="); eatTest(animal3); eatTest(animal4); } } 狐狸在淦饭 企鹅在淦饭 Tom在吃鱼 旺财在吃骨头 ============ 哮天犬在吃骨头 狸花猫在吃鱼 ============= 哮天犬在吃骨头 狸花猫在吃鱼
4.2 向下转型
将一个子类对象经过向上转型之后当成父类方法使用,无法调用子类的方法,但有时可能需要调用子类特有的方法,此时:将父类引用再还原为子类对象即可,即向下转换
向下转型用的比较少,而且不安全,万一转换失败,运行时就会抛异常。Java中为了提高向下转型的安全性,引入了 instanceof ,如果该表达式为true,则可以安全转换。
public static void main(String[] args) {
Cat cat = new Cat("红猫",8);
cat.eat();
//向上转型
Animal animal1 =new Dog("史努比",50);//子类的类型给到父类
//不管给的是什么,一定是动物
// animal1.notsleep()//无法调用子类特有的方法
//向下转型
Dog dog = (Dog) animal1;//父类的类型给到了子类
dog.notSleep();
//不安全,能骗过编译器
if (animal1 instanceof Cat){//instanceof关键字判断animal1是不是Cat类型
Cat cat1 = (Cat) animal1;//这样不会报错,如果是Cat类型, 进行sleep方法
cat1.sleep();
}
// Cat cat1 = (Cat) animal1;//ClassCastException类型转换异常
// cat1.sleep();//animal本身引用的是狗,强转成猫,但之前给的是狗,报错
//Dog cannot be cast to Cat
System.out.println("=============");
//向上转型
Animal animal = cat;
animal.eat();
cat.sleep();
System.out.println("========");
animal = dog;
animal.eat();
}
}
红猫在吃鱼
史努比不想睡觉
=============
红猫在吃鱼
红猫想睡觉
========
史努比在吃骨头
5. 多态的优缺点
优点
1. 能够降低代码的 " 圈复杂度 ", 避免使用大量的 if - else2. 可扩展能力更强
缺点:代码的运行效率降低。
1. 属性没有多态性当父类和子类都有同名属性的时候,通过父类引用,只能引用父类自己的成员属性2. 构造方法没有多态性
6. 避免在构造方法中调用重写的方法
class A{
public A(){
func();
}
public void func(){
System.out.println("A.func");
}
}
class B extends A{
private int num = 1;
@Override
public void func() {
System.out.println("B.func"+num);
}
}
public class Test {
public static void main(String[] args) {
B b = new B();
}
}
B.func0
当在父类的构造方法当中, 去调用父类和子类重写的方法的时候,此时会调用子类
用尽量简单的方式使对象进入可工作状态, 尽量不要在构造器中调用方法 ( 如果这个方法被子类重写 , 就会触发动态绑定, 但是此时子类对象还没构造完成 ), 可能会出现一些隐藏的但是又极难发现的问题 .
二. 抽象类
1.抽象类的概念
所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的如果 一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类
2.抽象类语法
abstract class Shape2{//抽象类
abstract public void draw();//抽象方法
public void func(){
}
}
一个类如果被 abstract 修饰称为抽象类,抽象类中被 abstract 修饰的方法称为抽象方法,抽象方法不用给出具体的实现体。
抽象类也是类,内部可以包含普通方法和属性,甚至构造方法
3. 抽象类特性
1. 抽象类不能直接实例化对象
Shape shape = new Shape();
// 编译出错
Error:(30, 23) java: Shape是抽象的; 无法实例化
2. 抽象方法不能是 private 的
abstract class Shape {
abstract private void draw();
}
// 编译出错
Error:(4, 27) java: 非法的修饰符组合: abstract和private
3. 抽象方法不能被final和static修饰,因为抽象方法要被子类重写
public abstract class Shape {
abstract final void methodA();
abstract public static void methodB();
}
编译报错:
Error:(20, 25) java: 非法的修饰符组合: abstract和final
Error:(21, 33) java: 非法的修饰符组合: abstract和static
4. 抽象类必须被继承,并且继承后子类要重写父类中的抽象方法,否则子类也是抽象类, 必须要使用 abstract修饰
5. 抽象类中不一定包含抽象方法,但是有抽象方法的类一定是抽象类
6. 抽象类中可以有构造方法,供子类创建对象时,初始化父类的成员变量
4 抽象类的作用
抽象类本身不能被实例化, 要想使用, 只能创建该抽象类的子类. 然后让子类重写抽象类中的抽象方法
使用抽象类相当于多了一重编译器的校验,如果误用成父类了, 使用普通类编译器是不会报错的. 但是父类是抽象类就会在实例化的时候提示错误, 尽早发现问题
后记
看到这里,希望能帮到你~
您的点赞 ,收藏 ,关注 是我创作的最大动力!
同时也欢迎在评论区进行交流,共同进步~