继承(上):
1. 提高了代码的重用性
2. 让类与类之间产生关系,有了这个关系,才有多态性
注意:千万不要为了获取其他功能,简化代码而继承:
必须是类与类之间有所属关系才可以继承,所属关系 is a。
在java语言中只能是单继承,不支持多继承。
因为:多继承容易带来安全隐患,当多个父类有相同的功能,
当功能内容不同时,子对象不确定运行哪一个。
但是java保留这种机制,用另一种形式表示:多实现。
并且存在多层继承,也就是一个继承体系。
如何使用一个继承体系中的功能?
要想使用体系,先查阅体系父类的描述,因为父类中定义的是体系中共性功能。
通过了解共性功能,就可以知道该体系的基本功能。
为什么具体调用时,创建最子类的对象?
1. 因为有可能父类不能创建对象。
2. 创建子类对象可以使用最多功能,包括基本的还有特有的
简单的一句话: 查阅父类功能,创建子类对象使用功能。
聚集:has a
1. 聚合:球员 和 球队
2. 组合:更紧密 人 和 手
1.变量
如果子类中出现非私有的同名成员变量时
子类要访问本类中的变量,用this
子类要访问父类中的同名变量,用super
super 和 this的使用基本使用几乎一致
this代表的是本类对象的引用
super代表的是父类对象的引用
clas Fu{
public String num = 4;
}
class Zi extends Fu{
int num = 5;
public void show(){
S.o.p(num);
}
}
输出结果:5
假如注销public String num = 5;
输出结果 : 4
结论:super 和 this 指向同一个对象,父类(super)的引用指向子类的对象。
子父类中的函数(重写,覆盖)
当子类出现和父类一模一样的函数时,
当子类对象调用该函数,会运行子类函数的内容。
如同父类的函数被覆盖一样。
这种情况就叫做重写(覆盖)
当子类继承父类,沿袭父类的功能,到子类中
但是子类虽然具备该功能,但是功能的内容却和父类不一致,
这时,没有必要定义新的功能,而是使用覆盖特殊,保留父类的功能定义,并且重写功能的内容
public class Tel{
public void show (){
s.o.p("number");
}
}
class NewTel extends Tel{
public NewTel(){
super(); //第一行必须
}
public void show(){
super.show();
s.o.p("name");
s.o.p("pic");
}
}
覆盖:1. 子类覆盖父类,必须保证子类权限大于等于父类权限,才可以覆盖,否则编译失败
2. 静态只能覆盖静态
子父类构造函数:
不能覆盖:因为覆盖名称要一模一样,而构造函数是跟类名走,不可能存在覆盖。
子类对象进行初始化时,同时也会调用父类的构造函数。
因为在子类的构造函数中默认会调用父类的构造函数通过super();
为什么子类一定要访问父类的构造函数
因为父类中的数据在子类中直接获取,所以子类建立时需要查看父类是如何
对这些数据进行初始化的。所以子类在初始化时,先要访问一下夫列的构造函数
如果要访问父类中指定的构造函数,可以通过手动的定义的super语句的方式制定
注意:super语句在子类构造函数的第一行
结论: 子类的所有的构造函数默认都会访问父类空参数的构造函数
因为子类每一个构造函数的第一行内都有隐式的父类构造函数
当父类中没有空参数的构造函数,在子类中必须手动的通过super语句形式来指
定访问父类中 的构造函数,当然子类的构造函数的第一行也可以手动的指定this语句来访问
本类中的构造函数。子类中的构造函数至少有一个构造函数访问父类的构造函数
final 关键字:作为一个修饰符,相当于一个死锁永远打不开,也变不了。
1. 可以修饰类,函数,变量。
2. 被final修饰的类不可以被继承。避免被继承,被子类复写功能。
3. 被final修饰的方法都不可以 被复写。
4. 被final修饰的变量是一个常量,只能赋值一次,即可以修饰成员变量,又可以修饰局部变量
当在描述事物时,一些数据的出现值是固定的,那么这时为了增强阅读性,都会给这个取一个名字
而且这个值不需要更改,所以加上final修饰。常量的书写规范是所有的单词都要大写,并且每个单词之间用下划线分隔开.
5. 内部类定义在类中的局部位置时只能访问局部被final修饰的变量。
------------------------------final问题-------------------------
在内部类中访问局部变量,编译器实际上会为该内部类创建一个成员变量,以及带有参数的构造方法,然后将该变 量传入构造方法,也就是说外面的变量和类里面的变量就是名字相同而已,此时你无论修改哪一个都对另外一个不产生影响,这样就出现矛盾了,防止这种现象就规 定只准用final
你可以自己定义构造方法然后传进去,就没必要定义final变量
成员变量的话实际上内部类访问就是用类似于Test.this.xxx的……
该说的都被你说了,我再补充一点:就是内部类在使用局部变量的时候为什么要创建一个改局部变量的拷贝呢?
原因就是:局部变量在方法结束后生命周期就结束了,但是内部类的对象却不是,所以内部类中使用局部变量的话,就需要改变量的一份拷贝。但是既然是拷贝,就 会出现两边值(局部变量和局部变量的拷贝)不一致的情况,所以要确保同步最直接的方法就是不对该变量做修改,最终的结果就是你的疑问了,“内部类引用局部 变量为什么要求必须是final的”。
当然了“全局变量”(你的意思应该是 实例变量 )的生命周期比局部变量相对要长,不会出现该问题,也就不需要拷贝,当然也就不需要强制为final来确保值同步了
--------------------------------
继承(下):
抽象类
当多个类中出现相同的功能,但是功能主体不一样。
这是可以进行向上抽取,这时只能抽取功能定义,而不抽取功能主体。
抽象类的特点:
1. 抽象方法一定在抽象类中。
2. 抽象方法和抽象类都必须被abstract关键字修饰。
3. 抽象类不可以用new创建对象,因为调用抽象方法没有意义。
4. 抽象类中方法要想被使用,必须由子类复写这个类的所有的抽象方法后,被子类对象调用
如果子类只覆盖了部分的抽象方法,那么该子类还是个抽象类。
特殊:抽象类中可以不定义抽象方法,这样做仅仅是不让该类创建对象。
模版方法设计模式
什么是模版方法?
在定义动能时,功能的一部分是确定的,但是有一部分是不确定,而确定的部分在使用不确定的部分时。
那么这时就将不确定的部分暴露出去,由子类去完成。
示例代码:
public class Demo2 {
public static void main(String args[]){
new GetTimes().getTime();
}
}
abstract class GetTime{
public void getTime(){//final 确保方法不会被重写
Long start = System.currentTimeMillis();
doThing();
Long end = System.currentTimeMillis();
System.out.println("工作时间 = " + -(start - end));
}
public abstract void doThing();//暴露一个抽象方法给子类去实现想要做的具体事情
}
class GetTimes extends GetTime{
public void doThing(){//子类实现父类的抽象方法,去干具体想干的事情
for(int i = 0 ; i < 10000; i ++)
System.out.print(i);
}
}
接口
当抽象类中的方法都是抽象的,那么该类可以通过接口的形式表示。
class 用于定义类 interface用于定于接口
接口定义时,格式特点:
1. 接口的常见定义: 常量,抽象方法
2. 接口中的成员都是有固定的修饰符
常量: public static final
方法: public abstract
记住:接口中的成员都是public的
接口: 是不可以被创建对象,因为有抽象方法。
需要在被子类实现,子类对接口中的抽象方法全部覆盖以后,子类才可以实例化。
否则子类还是一个抽象类
· 接口可以被类多实现,也是对多继承转换形式。
多态:事物的多种体现形态
父类的引用指向了自己的子类的对象。
前提:类与类之间有关系,要么继承或实现。
好处:提高了代码的扩展性
弊端:但是只能使用父类的引用指向子类和父类同样的方法。
类型转换:向上转型 向下转型
instanceof 用于判断对象是属于哪种类型。
多态(父类的引用指向子类对象)在代码中的特点(注意事项):
在多态中非静态成员函数的特点:
在编译时期: 参阅引用型变量所属类中是否有调用的方法。如果有,编译通过,否则编译失败。
在运行时期: 参阅对象所属的类中是否有调用的方法。
简单的总结:成员函数在多态调用时,编译看左边,运行看右边。
在多态中,非静态成员变量的特点:
无论编译和运行,都参考左边(引用型变量所属的类)
在多态中,静态成员变量/函数的特点:
无论编译和运行,都参照左边。
java对非静态方法的调用是采取动态绑定(运行时才判断)的方法,
对静态成员、属性的调用是采取静态绑定(编译时就判断)的方法。
---------------------示例代码----------------------------------------
class Fu{ public String name = "Fu"; public static String address= "Fu address"; public void method1(){ System.out.println("Fu method1"); } public void method2(){ System.out.println("Fu method2"); } public static void method4(){ System.out.println("static Fu method4"); } } class Zi extends Fu{ public static String address= "Zi address"; public String name = "Zi"; public void method1(){ System.out.println("Zi method1"); } public void method2(){ System.out.println("Zi method2"); } public void method3(){ System.out.println("Zi method3"); } public static void method4(){ System.out.println("static Zi method4"); } public void method5(){ System.out.println(name); } public void method6(){ System.out.println(super.name); } public void method7(){ System.out.println(name); } } public class Test{ public static void main(String args[]){ /*关于非静态/和静态函数的演示*/ //Fu z = new Zi(); //z.method3();//编译会报错 //z.method2();//Zi method2 /*为什么会出现下面的这种情况? 因为静态方法是静态绑定好的, 无论是在什么情况在最初的时候就已经在静态方法区把静态方法和类绑在一起,所以静态方法不存在覆盖 不管引用指向哪个对象不没有用。 而非静态方法是在new对象的时候把非静态方法和对象绑定一起,通过对象去调用非静态方法。 */ //z.method4(); //static Fu method4 //Zi z1 = new Zi(); //z1.method4();//static Zi method4 /*成员变量的演示*/ Zi z2 = new Zi(); System.out.println(z2.name);//Zi //(在堆内存中同时存在两个变量, //那么会先找定义引用的那个类的那个变量值 和 方法不同) Fu z4 = new Zi(); System.out.println(z4.name);//Fu /*为什么会出现这种情况? 在非静态成员变量中,对象的引用去取成员变量是根据左边的类去找相对应的值 在静态成员变量中是根据静态绑定的原理一样一开始就把类和静态成员绑定在一起。 不管自己指向哪个对象 静态的成员或方法都是直接和类进行绑定在一起称之为静态绑定 绑定回去好好看下下........... java对非静态方法的调用是采取动态绑定(编译期判断但运行时才绑定)的方法, 对静态成员、属性的调用是采取静态绑定(编译时就绑定并且判断)的方法。 */ //Fu z3 = new Zi(); //System.out.println(z3.address);//Fu address //Zi z4 = new Zi(); //System.out.println(z4.address);//Zi address } }
接口和多态的综合使用(接口回调)
举例:主板 PCI 卡
-----------------示例代码----------------------------
class MainBoard{ public void start(){ System.out.println("MainBoard start!"); } public void controllerCard(PCI p){//向外暴露一个接口只要是实现了该接口的方法就可以被调用执行 if(p != null){ p.open(); p.close(); } } } class SourdCard implements PCI{ public void open(){ System.out.println("SourdCard open"); } public void close(){ System.out.println("SourdCard close"); } } interface PCI{ public abstract void open(); public abstract void close(); } public class Test2{ public static void main(String args[]){ MainBoard mb = new MainBoard(); mb.start(); mb.controllerCard(new SourdCard() ); } }
内部的访问规则:
内部类可以直接访问外部类的成员,包括私有。
外部类要想访问内部类,必须建立对象。
之所以可以直接访问外部类的成员,因为内部类持有外部类的引用,格式:Outer.this.x
访问格式:
在内部类定义在外部类中的成员位置上,而且是非私有,可以在外部的其他类中。
可以直接建立内部类的对象。
格式:1. 外部类.内部类 变量名 = 外部对象.内部对象;
Outer.Inner in = new Outer().new Inner();
2. 当内部类在成员位置上,就可以被成员修饰符修饰。
比如 private:将内部类在外部类中进行封装。
static: 内部类具有了static特性。
当内部类被static修饰时,只能直接访问外部类的static成员,
出现了想问局限。
3.在外部类中,如何直接访问static内部类中非静态成员?
new Outer.Inner().function();
4. 在外部类中,如何直接访问static内部类中的静态成员?
Outer.Inner.function();
注意:
当在内部类中定义了静态成员,那么内部类必须是静态内部类。(内部类中不能有静态声明除非内部类也是静态的)。
当外部类中的静态方法访问内部类中的方法时,那么内部类必须为静态的。
什么时候使用内部类:
当描述的事物时,事物内部还有事物,该事物就应该用内部类封装:人-人体(private)
内部类中定义在局部时,
1. 不可被成员修饰符修饰。
2. 可以直接访问外部类中的成员,因为他还持有外部类的引用,
但是是不可以访问他所在的局部变量,只能访问被final修饰过的局部变量 。
匿名内部类:
1. 匿名内部类其实就是内部类的简写格式。
2. 定义匿名内部类的前提:
内部类必修是继承一个类或者是接口
3. 匿名内部类的格式:
new 父类或者接口(){定义子类的内容}
4. 其实匿名内部类就是一个匿名子类对象,而且这个对象有点胖,也可以说是带内容的对象。
5. 在匿名内部类中可以写其他成员,但每次只能调用一个方法,
但可以帮他取个名字用父类的引用指向子类的对象。
使用情况: 当使用的方法的参数类型是一个接口类型时,直接向调用方法传入一个匿名对象。
面试题:假如我没有父类,也没有接口让我去实现,还能写匿名内部类吗?
答案 :能
public class{
new Object(){
public void function(){}
}
}
jvm默认的异常处理机制就是调用printStackTrace方法
对于多异常的处理。应该是定义更具体的异常来捕捉捕捉问题
捕获异常代码块出现继承关系 应该把被继承的异常放在子类异常块的后面
throw 和 throws的区别
throw是是用在函数上,而throws是使用在函数内
throw后面跟的是异常对象,而throws跟的是异常类可以 多个
对异常的分类:
1.编译时被检测到的异常。
(值得去处理的异常,或者是希望得到调用者处理,并不影响运算意向)
2.编译时不被检测到的异常(运行时异常RuntimeException以及他的子类)
(不值得去处理的,交给jvm去处理,出现这种异常假如处理的会影藏异常影响了
运算意向,eg :角标越界、内存溢出、除0运算、空指针)