子父类中构造函数的特点:
Class Fu
{
Fu()
{
Sop(“Fu run”);
}
Fu(int x)
{
Sop(“fu run...”+x);
}
}
Class Zi extends Fu
{
Zi()
{ //super();其实这里省略了这条语句,调用的就是父类中的空参数的构造函数。而this()则代表本类中的构造函数(这句是隐式的)访问的是空参的父类中的构造函数,如果父类中的构造函数不是空参的,那么super()里面也需要传参数,不然会出问题。
Sop(“Zi run”);
}
}
Class ExtendsDemo4
{
Public static void main(String[]args)
{
New Zi();
}
}
运行结果为:fu run
Zi run
构造函数没有覆盖之说,也没有继承之说。子类的实例化(对象)过程:子类中所有的构造函数默认都会访问父类中的空参数的构造函数。
Class Fu
{
Fu()
{
Sop(“A Fu run”);
}
Fu(int x)
{
Sop(“B fu run...”+x);
}
}
Class Zi extends Fu
{
Zi()
{ //super();其实这里省略了这条语句,调用的就是父类中的空参数的构造函数。而this()则代表本类中的构造函数(这句是隐式的)访问的是空参的父类中的构造函数,如果父类中的构造函数不是空参的,那么super()里面也需要传参数,不然会出问题。
Sop(“ C Zi run”);
}
Zi(int x)
{
Sop(“D zi run”);
}
}
Class ExtendsDemo4
{
Public static void main(String[]args)
{
New Zi(6);
}
}
打印结果为:AD(这个我自己做对了,稍微看看就行)
为什么子类实例化的时候要访问父类中的构造函数呢?
那是因为子类继承了父类,获取了父类中内容(属性),所以在使用父类内容之前,要先看父类是如何对自己的内容进行初始化的。所以子类在构造对象时,必须访问父类中的构造函数。为了完成这个必须的动作,就在子类的构造函数中加入了super()语句。如果父类中没有定义空参数构造函数,那么子类的构造函数就必须要用super明确要调用父类哪个构造函数。(总之就是要匹配了)
注意:super语句必须要定义在子类函数的第一行,因为父类的初始化函数必须要先完成。
This()是调用本类中的构造函数。子类构造函数中如果使用this调用本类构造函数时,super就没有了,因为super和this都只能定义第一行。但是可以保证的是,子类中最少有一个构造函数访问父类的构造函数。
权限:类名若有public的话,那么里面的构造函数就默认也是public。父类其实也有爹,就是Object。Object类是java中最顶层的类,是java的上帝。
子类的实例化过程(图解)
注:对代码进行修改后,只需ctrl+S保存后,不需要关闭文件,文件打开状态,即可对文件进行编译操作。
下面这个例子跟自己想象中的运行结果有点反差(务必要弄懂这个示例):
关键就是子父类显式、隐式初始化问题:
为什么子类实例化的时候要访问父类中的构造函数呢?
那是因为子类继承了父类,获取了父类中内容(属性),所以在使用父类内容之前,要先看父类是如何对自己的内容进行初始化的。所以子类在构造对象时,必须访问父类中的构造函数。为了完成这个必须的动作,就在子类的构造函数中加入了super()语句。如果父类中没有定义空参数构造函数,那么子类的构造函数就必须要用super明确要调用父类哪个构造函数。(总之就是要匹配了)
注意:super语句必须要定义在子类函数的第一行,因为父类的初始化函数必须要先完成。
This()是调用本类中的构造函数。子类构造函数中如果使用this调用本类构造函数时,super就没有了,因为super和this都只能定义第一行。但是可以保证的是,子类中最少有一个构造函数访问父类的构造函数。
权限:类名若有public的话,那么里面的构造函数就默认也是public。父类其实也有爹,就是Object。Object类是java中最顶层的类,是java的上帝。
子类的实例化过程(图解)
注:对代码进行修改后,只需ctrl+S保存后,不需要关闭文件,文件打开状态,即可对文件进行编译操作。
下面这个例子跟自己想象中的运行结果有点反差(务必要弄懂这个示例):
关键就是子父类显式、隐式初始化问题:一个对象的实例化过程(就是刚刚分析内存图解的过程):
1. JVM会读取指定路径下的Person.class文件,并加载进内存,并会先加载Person的父类。(如果有直接父类的的情况下)
2. 在堆内存中开辟空间,分配地址。
3. 并在对象空间中,对对象中的属性进行默认初始化。
4. 调用对应的构造函数进行初始化。
5. 在构造函数中,第一行会先到调用父类中的构造函数进行初始化。
6. 父类初始化完毕后,再对子类的属性进行显示初始化。
7. 再进行子类构造函数的特定初始化。
8. 初始化完毕后,将地址值赋值给引用变量。
Class Zi extends fu
{
Int num=8;
Zi()
{
Super(); //这一步的时候num=0;
//到这里的时候,num就已经为8了。
num=10;//然后到这里的时候,num就为10了。(初始化的顺序)
}
}
Final关键字
用个示例来感受下:
class Fu
{
Void method()
{
//调用底层系统的资源
}
}
Class Zi extends Fu
{
Void method()
{
Sop(“haha”); //继承的弊端可以打破父类的封装性,一继承一覆盖。
}
}
Class FinalDemo
{
Public static void main()
{
Sop(“hello world”);
}
}
Final关键字就是用于解决这个问题的。不让你继承。只需要final class fu
1. final是一个修饰符,可以修饰类、方法、变量
2. Final修饰的类不可以被继承(编译会出错)
3. 但是如果类中有部分想被继承,该怎么办呢? 此时只需将final加在方法上,也即是final修饰的方法不可被覆盖
4. 开发常用:修饰变量时:
Final修饰的变量是一个常量,只能赋值一次。
Final double PI=3.14;(不能直接用3.14,阅读性,必须要取名字)
Final默认显示初始化值,没有默认初始化值。一般成员被final了,一般都会加static,其他看自己的需求。
为什么要用final修饰变量?
其实在程序中如果一个数据是固定的,那么直接使用即可。但是阅读性差,所以该数据起个名称而已。而且这个变量名称不能变化,所以加上final固定。
写法规范:
常量所有字母都大写,多个单词,中间用下划线连接。
抽象类
抽象函数所在的类也必须标志为抽象的。这也就是抽象类的由来。当一个类没有足够的信息来描述该事物,该事物就可抽象。
抽象类的特点:
1. 方法只有声明没有实现时,该方法就是抽象方法,需要被abstract修饰。抽象方法必须定义在抽象类中。该类必须也被abstract修饰。
2. 抽象类不可以实例化。(因为里面根本没有运行体,调用抽象方法没有意义)
抽象类必须有其子类覆盖了所有的抽象方法后,该子类才可以实例化
3. 抽象类必须有其子类覆盖了所有的抽象方法后,该子类才可以实例化。
五个问题
1.抽象类中有构造函数吗?
我猜对了,嘿嘿。有,抽象类中也是可以有构造函数的。虽然抽象类不能实例化,但是抽象类被继承之后,它的派生类(子类)可以实例化。
2. 抽象类可以不定义抽象方法呢?
可以,但是很少见。目的是不让该类创建对象。AWT的适配器就是这种类,通常这个类中的方法有方法体,但是没有内容。
3. 抽象关键字不能和哪些关键字共存?(非法修饰符组合)
Final我答对了(水火不容的),因为final不让覆盖,而abstract可以覆盖,所以矛盾。
Private不行,抽象方法需要覆盖,但是若私有后,人家都不知道你有没有方法,还谈什么覆盖操作。
Static也不行,成员若是静态,对象都不需要创建了,类名就可以直接调用。
4. 一般类中不可定义抽象方法,只能定义非抽象方法。
抽象类中可定义抽象方法,同时也可以定义非抽象方法。一般类可以被实例化,而抽象类不可以被实例化。
5. 抽象类一定是父类吗?
这个好理解。因为需要子类覆盖其方法后才能对子类实例化。
接口定义
当一种抽象类中的方法都是抽象的时候,这时可以将该抽象类用一种形式定义和表示,就是interface。定义接口使用的关键字不是class,是interface。但是编译完后还是.class文件。Interface仅仅换了一种表现方式。
对于接口中参加的成员:而且这些成员都有固定的修饰符
1. 全局常量:public static final
2. 抽象方法:public abstract
由此可见,接口中的成员都是公共的权限。即使漏写了修饰符,编译器会给你补齐。但是最好不要这样做,阅读性较差。
接口实现
之前抽象类,后面的类可以继承。而现在接口里面全是抽象的,全部都得重写。所以叫做实现。(类与类之间继承关系,类与接口之间是实现关系)
接口不可以实例化,只能实现了接口的子类并覆盖了接口中所有的抽象方法,该子类才可以实例化(也就是创建对象)。
接口的多实现
在java中不直接支持多继承,因为会出现调用的不确定性。所以java将多继承机制进行改良,在java中变成了多实现。一个类可以实现多个接口。
Interface A
{
Public static void show();
{
}
}
Interface Z
{
Public static void showZ()
{
}
}
Class Test implements A,Z
{
}
若接口A,B中的均是一样的方法show(),可以在Test中同时覆盖。多继承就是因为有方法体,会导致运行结果不一样。而现在接口里面都没有方法体了,故而可行。(Java的改良),因为现在没有不确定性了。
Interface A
{
Public void show();
}
Interface Z
{
Public void show(); //若改为int的话,则会编译失败,下面根本都没有对应的覆盖。可能不服气,在下面Test类中加上public int show() {} ,还是会编译失败,因为Test里实现的时候返回都不知道返回什么类型的值,2个返回值类型不同的show方法。人家都不知道该返回哪一个。
}
Class Test implements A,Z
{
Public void show()
{
}
}
接口的细节
Class Test2 extends Q implements A,Z 这样功能就丰富多了。
接口的出现避免了但继承的局限性。接口与接口之间是什么关系呢?(两个都是抽象的)
Interface CC
{
Void show();
}
Interface MM
{
Void method();
}
Interface QQ extends CC,MM
{
Void function();
}//接口与接口之间是继承关系,而且接口可以多继承。接口可以被类多实现,接口之间可以多继承。
Class WW implements QQ
{
//这就需要覆盖3个方法
Public void show()
{}
Public void method()
{}
Public void function()
{}
}
接口的特点
Ps2(早期鼠标的接口那种圆形的)----现在的USB接口,其实说白了,接口就是对我暴露的规则,是程序的功能拓展,接口的出现降低了耦合性,接口可以用来多实现。
接口和抽象类的区别
相同点:都是向上抽取而来的。
不同点:1.抽象类需要被继承,而且只能是单继承。接口需要被实现,而且可以多实现。
2.抽象类中可以定义抽象方法和非抽象方法,子类继承后可以直接使用非抽象方法。接口中只能定义抽象方法,必须由子类实现。
3.抽象类的继承是is a关系,在定义该体系的共性内容;而接口的实现是like a关系,在定义该体系的额外功能。
举个例子:
犬按功能分:导盲犬、搜爆犬等
Abstract class犬
{
Abstract void 吼叫();
}
Class 导盲犬 extends 犬
{
Public void 吼叫()
{
}
Public void 导盲()
{
}
}
还可以如下这样做:
Abstract class 犬
{
Abstract void 吼叫();
}
Abstract class 导盲
{
Abstract void 导盲();
}
Class 导盲犬 extends 犬 interface 导盲
//但是不能多继承,现在要想办法,可以让他们2个变成接口,就可以多实现了。直接让他们2个多接口,但是导盲犬本身属于犬的一种还是继承犬比较合适,导盲属于犬的额外功能。可以让其继承一个再实现另外一个。若是直接在下面里面写死了导盲,可是可以,但是不利于程序的拓展性。
{
Public void 吼叫()
{
}
Public void 导盲()
{
}
}