点进来你就是我的人了
博主主页:戳一戳,欢迎大佬指点!
博主沐沐:当你的才华撑不起你的野心的时候,你就应该静下心来学习!欢迎志同道合的朋友一起加油喔
目标梦想:进大厂,立志成为一个牛掰的Java程序猿,虽然现在还是一个哈哈
谢谢你这么帅气美丽还给我点赞!比个心
目录
前言
一.继承
1.什么是继承
2.继承的优缺点
3.对继承的理解
4.方法的重写
5.继承中的构造方法的调用
6.包的声明和使用
7.四种权限修饰符
8.再谈初始化
二.多态
1.多态的理解
2.向上转型和向下转型
3.instanceof 关键字
4.多态在子父类中的成员上的体现的特点
在上篇类和对象中我们讲到了封装,继封装之后,我们继续来讲一下面向对象的另外两个特性,继承和多态,为什么要有继承和多态呢?因为继承提高了代码的复用性,让类与类之间产生了关系,提供了另一个特征多态的前提,而多态则是提高了程序的扩展性!
继承是面向对象的三大特征之一,可以使得子类具有父类的属性和方法,还可以在子类中重新定义、追加属性和方法.
继承的格式:
public class 子类名 extends 父类名{}
父类:也被称为基类、超类
子类:也被称为派生类
范例:public class Student extends Parent{
成员变量;
成员方法;
}
2.1.好处:
提高了代码的复用性(多个类相同的成员属性和方法可以放到一个类当中)
提高了代码的维护性(如果方法的代码需要修改,只需修改一处即可)
2.2弊端:
继承让类与类之间产生了关系,类的耦合性也增加了,当父类发生变化时候,子类也不得不跟着变化,削弱了子类的独立性。
什么时候使用继承?(final/private修饰的类和构造方法是不能继承的)
当类与类之间存在着所属关系时,才具备了继承的前提。a是b中的一种。a继承b。苹果是水果中的一种。 苹果和水果(可以继承)
英文书中,所属关系:" is a "
注意:不要仅仅为了获取其他类中的已有成员进行继承。 狗和狮子(不可以继承)
所以判断所属关系,可以简单看,如果继承后,被继承的类中的功能,都可以被该子类所具备,那么继承成立。如果不是,不可以继承。
父类的由来:其实是由多个类不断向上抽取共性内容而来的。
java中对于继承,java只支持单继承。
单继承:一个类只能有一个父类。 多继承:一个类可以有多个父类。
为什么不支持多继承呢?
因为当一个类同时继承两个父类时,两个父类中有相同的功能,那么子类对象调用该功能时,运行哪一个呢?因为父类中的方法中存在方法体。
但是java支持多重继承。A继承B B继承C C继承D。
多重继承的出现,就有了继承体系。体系中的顶层父类是通过不断向上抽取而来的。它里面定义的该体系最基本最共性内容的功能。
所以,一个体系要想被使用,直接查阅该系统中的父类的功能即可知道该体系的基本用法。那么想要使用一个体系时,需要建立对象。建议建立最子类对象,因为最子类不仅可以使用父类中的功能。还可以使用子类特有的一些功能。
简单说:对于一个继承体系的使用,查阅顶层父类中的内容,创建最底层子类的对象。
父类和子类出现后,类中的成员都有了哪些特点:
1:成员变量
当子父类中出现一样的属性时,子类类型的对象,调用该属性,调用的是子类的属性值。
如果想要调用父类中的属性值,需要使用一个关键字:super
This:代表是本类类型的对象引用。
Super:代表是子类所属的父类中的内存空间引用。
1, super.成员名 访问父类的成员
2,super.方法名 访问父类的方法
3,super() 调用父类的构造方法
注意:子父类中通常是不会出现同名成员变量的,因为父类中只要定义了,子类就不用在定义了,直接继承过来用就可以了。
2:成员方法
当子父类中出现了一模一样的方法时,建立子类对象会运行子类中的方法。好像父类中的方法被覆盖掉一样。所以这种情况,是方法的另一个特性:重写(复写,覆盖)
什么时候使用重写呢?当一个类的功能内容需要修改时,可以通过重写来实现。
【1】重写:
发生在子类和父类中,当子类对父类提供的方法不满意的时候,要对父类的方法进行重写。【2】重写有严格的格式要求:
子类的方法名字和父类必须一致,参数列表(个数,类型,顺序)也要和父类一致。【3】父类中私有方法,子类不能被继承(私有的方法不能被重写)
子类方法访问权限不能比父类低(public>protected>默认>私有的)
@Override
是一个注解(注解后面会学习到)
可以帮我们检查重写方法声明的正确性
下面代码感受一下重写:
public class Person { //父类
public void sleep(){
System.out.println("我喜欢睡觉");
}
public void eat(){
System.out.println("我喜欢吃火锅");
}
}
public class Student extends Person { //子类
public void study(){
System.out.println("我喜欢学习");
}
@override
public void eat(){
System.out.println("我喜欢吃肉蟹煲"); //重写的父类吃的方法
}
}
注意:重载和重写的区别:
重载:在同一个类中,当方法名相同,形参列表不同的时候 多个方法构成了重载
重写:在不同的类中,子类对父类提供的方法不满意的时候,要对父类的方法进行重写。
有没有发现子类构造方法运行时,先运行了父类的构造方法。为什么呢?
原因:子类的所有构造方法中的第一行,其实都有一条隐身的语句super();
super: 从父类继承过来的属性的引用,有的书上也说super是父类的引用,但是这种说法其实是不准确的。而super():则是在调用父类中空参数的构造方法。
为什么子类对象初始化时,都需要调用父类中的构造方法呢?(为什么要在子类构造函数的第一行加入这个super()?)
因为子类继承父类,会继承到父类中的数据,所以必须要看父类是如何对自己的数据进行初始化的。所以子类在进行对象初始化时,先调用父类的构造方法,这就是子类的实例化过程。
总结:
1.子类中所有的构造方法都会默认访问父类中的空参数的构造方法,因为每一个子类构造方法内的第一行都有默认的语句super();
2.如果父类中没有空参数的构造方法,那么子类的构造方法内,必须通过super语句指定要访问的父类中的构造方法。
3.如果子类构造方法中用this来指定调用子类自己的构造方法,那么被调用的构造方法也一样会访问父类中的构造方法。
package(声明包):包其实就是文件夹。
作用:对类进行分类管理。
包的定义格式
格式:package 包名 (多级包用.分开)
范例:package com.day1
import(导入包)
使用不同包下的类时,使用的时候需要写类的全路径,为了简化带包的操作,我们就可以使用import
导包的格式:
1.格式:import com.day1.m1.*
1 |
访问级别 |
访问控制修饰符 |
同类 |
同包 |
不同包子类 |
不同包 |
2 |
公开 |
public |
可以 |
可以 |
可以 |
可以 |
3 |
受保护的 |
protected |
可以 |
可以 |
可以 |
不可以 |
4 |
默认 |
default |
可以 |
可以 |
不可以 |
不可以 |
5 |
私有的 |
private |
可以 |
不可以 |
不可以 |
不可以 |
学完继承后大家也就明白了前面讲的代码块的执行过程了吧
1、父类静态代码块优先于子类静态代码块执行,且是最早执行
2、父类实例代码块和父类构造方法紧接着执行
3、子类的实例代码块和子类构造方法紧接着再执行
4、第二次实例化子类对象时,父类和子类的静态代码块都将不会再执行
不同对象调用同一个方法会产生不同的效果。方法本身就具备多态性,某一种事物有不同的具体的体现。
多态的好处:提高了程序的扩展性。
多态的弊端:当父类引用指向子类对象时,虽然提高了扩展性,但是只能访问父类中具备的方法,不可以访问子类中特有的方法。(前期不能使用后期产生的功能,即访问的局限性)
多态的前提和体现:
1.必须要有关系,比如继承、或者实现。
2.有重写操作。
3.父类引用或者接口的引用指向了自己的子类对象。//Animal a = new Dog();
多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法。
属性看声明(属性没有多态性) 方法看对象
用父类声明子类对象 Dog是Animal的一种
向上转型:将子类对象赋给父类类型的变量。向下转型:需要强转
动态绑定机制:会自动找到引用所指向的对象所在的类中的方法
子类中特有的方法只能用子类类型的变量来调用
多态的出现,思想上也应该发生转变:以前是创建对象并指挥对象做事情。有了多态以后,我们可以找到对象的共性类型,直接操作共性类型做事情即可,这样可以指挥一批对象做事情,即通过操作父类或接口实现。
向上转型:实际就是创建一个子类对象,将其当成父类对象来使用。
语法格式:父类类型 对象名 = new 子类类型()
animal是父类类型,但可以引用一个子类对象,因为是从小范围向大范围的转换。向下转型:由于一个子类对象经过向上转型之后,无法调用子类独有的方法,此时,将父类引用再还原为子类对象即可,即向下转换。
语法格式:对象名 = (子类类型)父类类型
public class TestAnimal {
public static void main(String[] args) {
Cat cat = new Cat("团团",2);
Dog dog = new Dog("乐乐", 1);
// 向上转型
Animal animal = cat;
animal.eat();
// 向下转型
// 程序可以通过编译,但运行时抛出异常---因为:animal实际指向的是猫
// 现在要强制还原为狗,无法正常还原,运行时抛出:ClassCastException
dog = (Dog)animal;
// animal本来指向的就是猫,因此将animal还原为猫也是安全的
cat = (Cat)animal;
}
}
其实到这里,我们可以发现,向上转型是向下转型的前提,并且只有当我们向上转型时引用的子类的对象类型,与我们向下转型时想转型成为的类的类型一样时,才能正确的完成向下转型(或者说之前必须已经引用了这个类)。
虽然有向下转型的方法,我们一般最好也不要用,因为很容易出错,如果使用建议先使instanceof 关键字加以判断!
如果想用子类对象的特有方法,必须发生向下转型,前提是判断父类是否引用子类对象?
可以通过一个关键字 instanceof ;//判断该对象是否被父类引用
格式:<对象 instanceof 类型> ,判断一个对象是否是该类的实例。
student instanceof Person = true;//person类引用了student类的对象
4.1 成员变量:在多态中,子父类成员变量同名。
在编译时期:参考的是引用型变量所属的类中是否有调用的成员。(编译时不产生对象,只检查语法错误)
运行时期:也是参考引用型变量所属的类中是否有调用的成员。
简单一句话:无论编译和运行,成员变量参考的都是引用变量所属的类中的成员变量。
再说的更容易记忆一些:成员变量 --- 编译运行都看 = 左边。
4.2 成员方法。
编译时期:参考引用型变量所属的类中是否有调用的方法。
运行时期:参考的是对象所属的类中是否有调用的方法。
为什么是这样的呢?因为在子父类中,对于一模一样的成员方法,有一个特性:重写。
简单一句:成员函数,编译看引用型变量所属的类,运行看对象所属的类。
更简单:成员方法--- 编译看 = 左边,运行看 = 右边。
4.3 静态方法。
编译时期:参考的是引用型变量所属的类中是否有调用的成员。
运行时期:也是参考引用型变量所属的类中是否有调用的成员。
为什么是这样的呢?因为静态方法,其实不所属于对象,而是所属于该方法所在的类。
调用静态的方法引用是哪个类的引用调用的就是哪个类中的静态方法。
简单说:静态方法--- 编译运行都看 = 左边。