文章目录
一、OOP三大特征之继承
1.继承基本介绍和示意图
继承可以解决代码复用,让我们的编程更加靠近人类思维.当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可。
2.继承的基本语法
class 子类 extends 父类{
……
}
-
子类就会自动拥有父类定义的属性和方法
-
父类又叫超类,基类。
-
子类又叫派生类。
3. 继承的深入讨论/细节问题
-
类的树形结构
如果C是B的子类,B又是A的子类,习惯上称C是A的子孙类,Java的类按继承关系形成树形结构(将类看作树上的结点),在这个树形结构中,根结点是Object类(Object是java. lang包中的类),即Object是所有类的祖先类。任何类都是Object类的子孙类,每个类(除了Object类)有且仅有一个父类,一个类可以有多个或零个子类,如果一个类(除Object类)的声明中没有使用extends关键字,这个类被系统默认为Object 的子类,即类声明“class A”与“class A extends Object”是等同的。 -
子类的继承性
1)子类和父类在同一包内的继承性
子类继承了除了private的所有的属性和方法,继承的成员变量和方法的访问权限保持不变
子类如果要访问父类的私有属性和方法要通过父类提供公共的方法去访问。
2)子类和父类不在同一包中的继承性
子类只会继承父类的protected和public访问权限的成员变量和方法,不会继承private和默认(友好)访问权限的成员变量和方法 -
子类对象的特点
当用子类的构造方法创建一个子类的对象时,不仅子类中声明的成员变量被分配了内存空间,而且父类的成员变量也都分配了内存空间,但只将其中一部分(即子类继承的那部分成员变量)作为分配给子类对象的变量。
也就是说,父类中的private成员变量尽管分配了内存空间,也不作为子类对象的变量,即子类不继承父类的私有成员变量。
同样,如果子类和父类不在同一包中,尽管父类的友好成员变量分配了内存空间,但也不作为子类对象的变量,即如果子类和父类不在同一包中,子类不继承父类的友好成员变量。
子类继承的方法只能操作子类继承的成员变量或未继承的成员变量,不可能操作子类新声明的变量。 -
子类必须调用父类的构造方法,完成父类的初始化。当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过
-
java所有类都是Object类的子类, Object是所有类的基类.
-
父类构造器的调用不限于直接父类!将一直往上追溯直到Object类(顶级父类)
-
子类最多只能继承一个父类(指直接继承),即java中是单继承机制。
思考:如何让A类继承B类和C类? 【A继承B,B继承C】 -
不能滥用继承,子类和父类之间必须满足is-a的逻辑关系
二、成员变量的隐藏与方法重写/覆盖
1.成员变量的隐藏
在编写子类时所声明的成员变量的名字和从父类继承来的成员变量的名字相同(声明的类型可以不同),在这种情况下子类就会隐藏所继承的成员变量。
子类隐藏继承的成员变量的特点如下:
(1)子类对象以及子类自己定义的方法操作与父类同名的成员变量是指子类重新声明的这个成员变量。
(2)子类对象仍然可以调用从父类继承的方法操作被子类隐藏的成员变量,也就是说,子类继承的方法所操作的成员变量一定是被子类继承或隐藏的成员变量
2.方法重写/覆盖
-
基本介绍
子类通过重写可以隐藏已经继承的方法 -
注意事项和使用细节
方法重写也叫方法覆盖,需要满足下面的条件-
子类的方法的形参列表,方法名称,要和父类方法的形参列表,方法名称完全一样。
-
子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类
-
子类方法不能缩小父类方法的访问权限,但可以提高访问权限
pubilc >protected>默认>private
-
三、Super关键字
-
基本介绍
super代表父类的引用,用于访问父类的属性、方法、构造器
-
基本语法
-
访问父类的属性,但不能访问父类的private属性
super.属性名;
-
访问父类的方法,不能访问父类的private方法
super.方法名(参数列表);
-
访问父类的构造器(这点前面用过):
super(参数列表);
只能放在构造器的第一句,只能出现一句!
-
-
super给编程带来的便利/细节
- 调用父类的构造器的好处(分工明确,父类属性由父类初始化,子类的属性由子
类初始化) - 当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须
通过super。如果没有重名,使用super、this、直接访问是一样的效果! - super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用
super去访问爷爷类的成员;如果多个基类(上级类)中都有同名的成员,使用super访问遵循就近原则。A->B->C,当然也需要遵守访问权限的相关规则 - 如果在子类的构造方法中显式地写出了super 关键字来调用父类的某个构造方法,那么编译器不再提供默认的super语句
- 如果类中定义了一个或多个构造方法,那么Java不提供默认的构造方法(不带参数的构造方法),因此当在父类中定义多个构造方法时应当包括一个不带参数的构造方法,以防子类省略super时出现错误
- 调用父类的构造器的好处(分工明确,父类属性由父类初始化,子类的属性由子
-
super和this的比较
No. | 区别点 | this | super |
---|---|---|---|
1 | 访问属性 | 访问本类中的属性,如果本类没有此属性则从父类中继续查找 | 从父类开始查找属性 |
2 | 调用方法 | 访问本类中的方法,如果本类没有此方法则从父类继续查找. | 从父类开始查找方法 |
3 | 调用构造器 | 调用本类构造器,必须放在构造器的首行 | 调用父类构造器,必须放在子类构造器的首行 |
4 | 特殊 | 表示当前对象 | 子类中访问父类对象 |
super()和 this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
四、final关键字
-
基本介绍
final可以修饰类、属性、方法和局部变量.
在某些情况下,程序员可能有以下需求,就会使用到final:
1)当不希望类被继承时,可以用final修饰.
2)当不希望父类的某个方法被子类覆盖/重写时,可以用final关键字修饰。
3)当不希望类的的某个属性的值被修改,可以用final修饰.
4)当不希望某个局部变量被修改,可以使用final修饰 -
final使用注意事项和细节讨论
-
final修饰的属性又叫常量,一般用xx_xx_xx来命名
-
final修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如下位置之一:
1)定义时:如public final double TAX_RATE=0.08;
2)在构造器中
3)在代码块中。 -
如果final修饰的属性是静态的,则初始化的位置只能是
1)定义时2)在静态代码块,不能在构造器中赋值。 -
final类不能继承,但是可以实例化对象。
-
如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承。
-
一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法。
-
final不能修饰构造方法(即构造器)
-
final和static往往搭配使用,效率更高,不会导致类加载.底层编译器做了优化处理。
-
包装类(Integer,Double,Float,Boolean等都是final),String也是final类。
-
五、OOP三大特征之多态
-
多[多种]态[状态]基本介绍
方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。
-
多态的具体体现
-
方法的多态
重写和重载就体现多态
-
对象的多态(核心,困难,重点)
1)一个对象的编译类型和运行类型可以不一致2)编译类型在定义对象时,就确定了,不能改变
3)运行类型是可以变化的.
4)编译类型看定义时=号的左边,运行类型看=号的右边
-
-
多态注意事项和细节讨论
-
多态的前提是:两个对象(类)存在继承关系
-
多态的向上转型
1)本质:父类的引用指向了子类的对象
2)语法:父类类型 引用名 = new 子类类型();
3)特点:编译类型看左边,运行类型看右边。
可以调用父类中的所有成员(需遵守访问权限),不能调用子类中特有成员,但是可以访问子类隐藏的成员变量和子类重写的实例方法。
如果调用子类重写了父类的某个实例方法,当对象的上转型对象调用这个实例方法时,调用的是子类重写的实例方法。如果调用子类重写了父类的静态方法,子类对象的上转型对象不能调用子类重写的静态方法,只能调用父类的静态方法。 -
多态的向下转型
1)语法:子类类型 引用名 = (子类类型) 父类引用;2)只能强转父类的引用,不能强转父类的对象
3)要求父类的引用必须指向的是当前目标类型的对象
4)当向下转型后,可以调用子类类型中所有的成员
举例
-
class 类人猿 {
void crySpeak(String s) {
System.out.println(s);
}
}
class People extends 类人猿 {
void computer(int a,int b) {
int c=a*b;
System.out.println(c);
}
void crySpeak(String s) {
System.out.println("***"+s+"***");
}
}
public class Example5_10 {
public static void main(String args[]) {
类人猿 monkey;
People geng = new People();
System.out.println(geng instanceof People); //输出为true
monkey = geng ; //monkey是People对象geng的上转型对象
System.out.println(monkey instanceof People); //输出为true
monkey.crySpeak("I love this game");//等同于geng.crySpeak("I love this game");
People people=(People)monkey; //把上转型对象强制转换为子类的对象
people.computer(10,10);
}
}
-
属性没有重写之说!属性的值看编译类型
-
instanceof比较操作符,用于判断象的类型是否为XX类型或XX类型的子类型
-
java的动态绑定机制
- 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型累定
- 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
-
多态的应用
1)多态数组
数组的定义类型为父类类型,里面保存的实际元素类型为子类类型2)多态参数
方法定义的形参类型为父类类型,实参类型允许为子类类型
六、抽象类
当父类的一些方法不能确定时,可以用abstract关键字来修饰该方法,这个方法就是抽象方法,用abstract来修饰该类就是抽象类。
-
用abstract关键字来修饰一个类时,这个类就叫抽象类
访问修饰符 abstract 类名{
} -
用abstract关键字来修饰一个方法时,这个方法就是抽象方法
访问修饰符 abstract 返回类型 方法名(参数列表); //没有方法体 -
抽象类不能用new标识符创建对象
-
但是可以用抽象类声明对象,该对象可以成为其子类对象的上转型对象,那么该对象就可以调用子类重写的方法
-
抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现抽象类
-
抽象类在框架和设计模式使用较多
-
抽象类可以有任意成员【抽象类本质还是类】,比如:非抽象方法、构造器、静态属性等等
-
抽象方法不能有主体,即不能实现.
-
如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类。
-
抽象方法不能使用private、final和 static来修饰,因为这些关键字都是和重写相违背的。
七、面向抽象编程
- 在设计程序时经常会使用abstract类,其原因是abstract类只关心操作,不关心这些操作具体的实现细节.可以使程序的设计者把主要精力放在程序的设计上,而不必拘泥于细节的实现(将这些细节留给子类的设计者),即避免设计者把大量的时间和精力花费在具体的算法。
- 在设计一个程序时,可以通过在abstract类中声明若干个abstract方法表明这些方法在整个系统设计中的重要性,方法体的内容细节由它的非abstract子类去完成
使用多态进行程序设计的核心技术之一是使用上转型对象,即将abstract类声明的对象作为其子类对象的上转型对象,那么这个上转型对象就可以调用子类重写的方法 - 所谓面向抽象编程,是指当设计某种重要的类时不让该类面向具体的类,而是面向抽象类,即所设计类中的重要数据是抽象类声明的对象,而不是具体类声明的对象。
八、开-闭原则
- 开-闭原则就是计设计的系统对扩展开放,对修改关闭。实际上,这句话的本质是指当系统中增加新的模块时不需要修改现有的模块。在设计系统时,应当首先考虑到用户需求的变化,将应对用户变化的部分设计为对扩展开放,而设计的核心部分是经过精心考虑之后确定下来的基本结构,这部分应当是对修改关闭的,即不能因为用户的需求变化而再发生变化,因为这部分不是用来应对需求变化的。如果系统的设计遵守了“开-闭原则”,那么这个系统一定是易维护的,因为在系统中增加新的模块时不必去修改系统中的核心模块