继承
继承的定义:
继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。通俗的来说继承是当我们在定义若干个类的时候,发现某一些类中具有相同的属性和行为;那么,我们就可以将这些相同的部分进行抽取,独立生成另外一个类,那么这个生成出来的类我们称之为是父类,其他的被抽取内容的类称之为子类。子类与父类之间就是所谓的继承关系 用 extends来表示。
图示:对于学生和工人他们都有共同的作为人存在的属性与行为,对这些相同的部分进行抽取,生成独立的一个类。
代码:
//测试类
class ExtendsDemo01{
public static void main(String[] args){
Student a=new Student();
Worker b=new Worker();
a.study();
a.eat();
b.work();
}
}
//人共有的属性和行为
class Person{
String name;
int age;
void eat(){
System.out.println("人都要吃喝");
}
}
//学生
class Student extends Person{
//学习
void study(){
System.out.println("学生学习");
}
}
//工人
class Worker extends Person{
//工作
void work(){
System.out.println("工人工作");
}
}
继承的特点:
(1)在Java当中 继承是单继承。所谓单继承就是一个子类只能有一个父类,但是一个父类可以有若干个子类当然,在C++中,继承是多继承的,不太符合实际的社会问题,所以Java更加符合现实编程。但是不代表Java当中就没有多继承的时候!如果是类与类之间 必须是单继承;如果是接口与接口之间,可以是多继承。
(2)既然有了继承,那么在Java当中就有了继承体系,常用的继承体系 异常体系 集合体系 IO体系 网络体系 多线程体系。 在Java继承体系当中,所有类的最终父类都是Object!如果我们在定义一个类的时候,没有显著声明父类的时候 那么该类的父类默认是Object。
(3)一个子类只能有一个父类,而一个父类可以有多个子类。
(4)子类可以可以使用父类的属性和方法 ,但是父类不能够访问子类独有的属性和方法。注意:在分析设计继承的时候 一定要符合社会常识问题; 子类 和 父类 之间 必须是 is 、a 关系 子类必须父类的一种 同一系列的。
子父类中成员变量/静态变量的特点:
(1)如果只有子类有且非私有 那么就调用子类的
(2) 如果只有父类有且非私有 那么就调用父类的
(3) 如果子父类都有且非私有 那么就调用子类的
(4)子类.属性 顺序:子类对象成员->子类静态->父类成员->父类静态
(5)子类成员函数在内部调用变量时 局部变量->子类对象成员->子类静态->父类成员->父类静态
子父类中构造函数的特点:
现象:子类的构造函数在调用运行的时候 先执行父类的构造函数。
在子类的构造函数当中,有一句默认的super(...)隐藏了,而且这句代码必须是在第一行
对super的调用必须是构造器中的第一个。原因是,既然子类继承自父类 那么必然会继承到父类当中的一些数据,所以,在子类构造函数之中,必须先让父类把这些数据进行一些初始化才能继承给子类
注意:父类的构造函数被调用,但不代表父类就被创建对象了!所以this是当前对象的引用,而super不是父类对象的引用,而是父类空间的引用。并且注意super(...) 如果父类没有默认无参构造函数 那么子类构造函数中super()失效了。所以在调用父类构造函数时,一定要注意父类构造函数的参数情况!适时修改super(...)。
this(...)是当前类中 本类构造函数调用本类构造函数的方式
super(...)是本类构造函数调用父类构造函数的方式
都必须在第一行 那么这两冲突不?
如果本类构造函数当中不存在调用关系 那么每一个本类构造函数第一句都是super(...);
如果本类构造函数当中存在调用关系,那么最后被调用的那个构造函数第一句绝对是super(...);
this(...)的调用是单向的还是递归的?是单向的,那么最后被调用的第一句绝对就是super(...) 。
子父类中成员函数的特点:
(1)如果只有子类有且非私有 那么就调用子类的。
(2)如果只有父类有且非私有 那么就调用父类的。
(3)如果子父类都有且非私有 那么就调用子类的。
代码示例:
//测试类
class ExtendsDemo02{
public static void main(String[] args){
Zi zi=new Zi();//创建对象
System.out.println(zi.num1);//20
System.out.println(zi.num2);//30
System.out.println(zi.num3);//50
zi.show();//调用函数
zi.sing();//调用函数
}
}
//父类
class Fu {
int num1=10;
static int num2=40;//静态成员变量
static int num3=50;
Fu(){
super();//Object(){} 显示初始化父类空间中的成员变量
System.out.println("Fu constructor1......");
}
Fu(int num){
System.out.println("Fu constructor2......");
}
void sing(){
System.out.println("Fu sing......");
System.out.println("爸爸我会唱红歌");
}
}
//子类
class Zi extends Fu{
int num1=20;
static int num2=30;
Zi(){
this(0,0);
System.out.println("Zi constructor1......");
}
Zi(int num1){
this(0,0);
System.out.println("Zi constructor2......");
}
Zi(int num1,int num2){
super();
System.out.println("Zi constructor3......");
}
void show(){
//局部变量和成员变量如果重名了 this区分
System.out.println(num1+","+num2+","+num3);
//局部变量/成员变量和父类变量重名了 super区别 super.xxx => 你爸爸的xxx
System.out.println(super.num1+","+super.num2+","+num3);
//System.out.println(super.super.num1);
//System.out.println(num4);
}
}
/*结果
Fu constructor1......
Zi constructor3......
Zi constructor1......
20
30
50
20,30,50
10,40,50
Fu sing......
爸爸我会唱红歌
*/
重写
函数重写:在子父类中,同名函数
函数有什么组成:函数声明(权限 类型 返回值类型 函数名 参数列表)+函数体({}里面的内容)
重写的意义在于哪?在于子类继承了父类的函数声明(功能),但是子类可以将该函数的具体实现进行优化或更改。举个例子来说就是 郭德纲会说相声 ,郭麒麟也会说相声,这是子承父业!
(1)保留父类的功能声明 子类可以进一步优化
(2)保留父类的功能声明 子类可以重新定义
重写当中应该注意到的一些细节:
(1)函数重名 但是参数列表不同 不构成重写关系。
(2)函数重名 参数列表相同 构成重写关系 返回值类型也必须是一样的。
(3)子类重写父类函数时,权限>=父类权限。
(4)当然 如果父类函数权限为private 子类压根就继承不到 何谈重写。
代码示例:
//测试类
class ExtendsDemo03{
public static void main(String[] args){
IPhoneXPlusProMax iniuB=new IPhoneXPlusProMax();
iniuB.call();
iniuB.open5G();
Zi zi=new Zi();
zi.show(10);
zi.show();
}
}
//父类
class Fu{
void show(){
System.out.println("Fu show...");
}
private void haha(){} //去洗头房
}
//子类
class Zi extends Fu{
//不是重写的
void show(int num){
System.out.println("Zi show1...");
}
public void show(){
System.out.println("Zi show2...");
}
public void haha(){} //去洗头房 对象的特有行为
}
//有重写
//父类
class IPhone4s{
public void call(){
System.out.println("基本语音通话......");
}
}
//IPhone4s子类
class IPhone5s extends IPhone4s{
public void call(){
super.call();
System.out.println("添加视频通话......");
}
}//IPhone5s的子类
class IPhone6s extends IPhone5s{
public void call(){
super.call();
System.out.println("添加卡通动画......");
}
}
//IPhone6s的子类
class IPhoneXPlusProMax extends IPhone6s{
//当前对象重写的函数
@Override
public void call(){
System.out.println("虚拟现实+全息投影 通话...");
}
//当前对象的特有函数
public void open5G(){
System.out.println("支持5G网络......");
}
}
/*
代码实现
虚拟现实+全息投影 通话...
支持5G网络......
Zi show1...
Zi show2...
*/
多态
所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。
多态指多种状态:就是指一个对象可以有多种状态(他在继承体系中的位置变化) 对于D->C->B->A->Animal,子类继承父类其中位置的变化只能是向上变 但不能低于自身。
例如:对于C而言 可以当做B来看 也可以当做A来看 也可以当成Animal
小明 男人 人 动物
小明的学校开家长会 小明不想让他爸爸去 他让大明去 大明就是小明他哥;大明冒充Dad去参加家长会的话,那么所表现出来的行为 是不是应该符合他爸行为。不同的视角,有不同的角色。但是无论是在哪个视角场合 对象本身没变,只不过我们需要对象在不同的场合表现出相对应的行为。在Java当中 多态代码表现 就是 父类引用指向子类的对象。
多态的前提:继承 重写。
多态的好处:
(1)对代码的功能进行了可扩展性。
(2)子类对象可以当做父类对象去使用 但是有限制 只能调用父类函数或重写的函数。
(3)不能使用子类特有的行为。
多态当中 成员变量的特点:只能访问父类中的成员变量
多态当中 成员函数的特点:
(1)如果子类没有重写 且父类中有 调用父类的。
(2)如果子类有重写 调用子类重写的。
(3)如果不存在重写 父类没有 那就报错了!
代码示例:
//测试类
class DuoTaiDemo01{
public static void main(String[] args){
//父类引用指向子类的对象
Dady d=new Son();
d.chouyan();
d.hejiu();
d.tangtou();
//d.lol();
System.out.println(d.num);
d.xiToufang();
}
}
//父类
class Dady{
int num=20;
void chouyan(){
System.out.println("爸爸抽烟~");
}
void hejiu(){
System.out.println("爸爸喝酒~");
}
void tangtou(){
System.out.println("爸爸烫头~");
}
void xiToufang(){
System.out.println("爸爸洗头房~");
}
}
//子类
class Son extends Dady{
int num=10;
void chouyan(){
System.out.println("儿子抽烟~");
}
void hejiu(){
System.out.println("儿子喝酒~");
}
void tangtou(){
System.out.println("儿子烫头~");
}
void lol(){ //对象的特有行为
System.out.println("儿子玩lol~");
}
}
引用数据类型转换
向上转型和向下转型:强制转换和自动转换
向上转型对应着:自动转换
向下转型对应着:强制转换
满足条件:两者之间要存在继承或者是实现关系(接口)
抽象类
抽象类:模糊不清的类 不具体的类
当我们在抽取一个父类的时候,发现子类当中的一些共同方法在父类中无法进行具体的实现
并且这些方法只能在子类中具体实现时,父类当中的这些函数就只保留函数声明即可,不必写函数体
那么此时这个函数就是 抽象函数! 有抽象函数的类 就是抽象类!
特点:
1.抽象类能否创建对象?不能 对象本身是一个具体的东西 而抽象类中含有不具体的内容
2.抽象类必须作为父类存在吗?必须 抽象类就等着被继承呢!
3.抽象类和一般类有什么区别?就是一般类的特殊情况 唯一的区别只在于抽象类中有抽象函数!
4.抽象类中必须有抽象函数吗?不一定 AWT界面开发
5.abstract这个关键字不能和那些关键字共存?private static 不能与abstract共存。 private,抽象函数就等着被子类重写实现呢不能有private; static 静态是优先于对象存在的,在加载静态数据的时候 肯定是具体的数据,如果抽象函数可以被静态 那么我们还需要对象干什么。即能共存。
instanceof 关键字
用于判断:对象是否属于后面的类型,如果属于则返回true 如果不属于则返回false
代码示例:
//测试类
class DuoTaiDemo02{
public static void main(String[] args){
Dog d=new Dog();
Cat c=new Cat();
Pig p=new Pig();
feed(d);//调用函数
feed(c);
feed(p);
}
//Animal a=new Dog();
//Animal a=new Cat(); 向上类型转换
public static void feed(Animal a){
a.eat();
a.jiao();
//ClassCastException 类型转换异常
//判断对象的本质类型 instanceOf
//在这里只有猪才拱白菜,其他动物不会
if(a instanceof Pig){
Pig pp=(Pig)a;
pp.gongBaiCai();
}
if(a instanceof Dog){
Dog dd=(Dog)a;
dd.lookDoor();
}
if(a instanceof Cat){
Cat cc=(Cat)a;
cc.catchMouse();
}
}
}
/*
运行结果
狗吃狗粮......
狗汪汪汪......
狗看家~......
猫吃鱼......
猫喵喵喵......
猫捉老鼠~......
猪啥都吃......
猪哼哼哼......
猪拱白菜......
*/
class Animal{
void eat(){
}
void jiao(){
}
}
//子类
class Pig extends Animal{
//重写
void eat(){
System.out.println("猪啥都吃......");
}
void jiao(){
System.out.println("猪哼哼哼......");
}
//特有属性
void gongBaiCai(){
System.out.println("猪拱白菜......");
}
}
//子类
class Dog extends Animal{
//重写
void eat(){
System.out.println("狗吃狗粮......");
}
void jiao(){
System.out.println("狗汪汪汪......");
}
//特有属性
void lookDoor(){
System.out.println("狗看家~......");
}
}
//子类
class Cat extends Animal{
//重写
void eat(){
System.out.println("猫吃鱼......");
}
void jiao(){
System.out.println("猫喵喵喵......");
}
//特有属性
void catchMouse(){
System.out.println("猫捉老鼠~......");
}
}