Java 面向对象 继承 多态 重写 覆盖
目录
目录
Java 面向对象 继承 多态 重写 覆盖
基础概念
JAVA 面向对象的三大特征
- 封装 encapsulation
- 继承 inheritance
- 继承的缺点:增加了代码间的
耦合
度,使用不灵活 - 使用原则:对类的
功能的扩展
,要多用组合
,少用继承
- 继承的缺点:增加了代码间的
- 多态 polymorphism
final 关键字
- final 可以修饰类,方法,变量
- final 修饰的
类
不可以被继承 - final 修饰的
方法
不可以被覆盖 - final 修饰的
变量
是一个常量,只能被赋值一次
对象的实例化过程
- JVM 读取指定的路径下的.
class
文件,并加载进内存,并会先加载其父类 - 在堆内存中开辟空间,并分配地址
- 并在对象空间中,给对象的属性进行【默认初始化】
- 调用对应的
构造函数
进行初始化 - 在构造函数中,第一行会先调用
父类的构造函数
进行初始化 - 父类初始化完毕后,再对子类的属性进行【显示初始化】
- 再进行
子类构造函数
的特定初始化 - 初始化完毕后,将地址值赋值给引用变量
构造方法
- 构造函数的函数名与类名相同,没有返回值类型
- 若在构造函数前面加上
返回值类型
,就是一个普通的函数了。允许一个普通函数和构造函数同名同参数列表
- 多个构造函数是以
重载
的形式存在的 - private 修饰的构造函数不被子类继承
- 若没有显示定义构造函数,则存在默认的
无参构造函数
- 若已显示定义构造函数,则不再有默认的无参构造函数,除非显示定义了无参构造函数
- 父类构造函数中调用的函数遵循多态原则
- 即:父类构造函数中调用的【非静态方法】实际执行的是子类中重写的方法
this 和 super
- this 代表当前对象的引用,就是【函数所属对象】的引用
- 简单说,哪个对象调用了 this 所在的函数,this 就代表哪个对象
- 当成员变量和局部变量重名时,可以用 this 来区分
- this 也可以用于
在构造函数中
用于调用其他构造函数
,但只能定义在构造函数的第一行,因为初始化动作要先执行
- 子类构造函数的第一条语句,默认会首先访问父类中空参的构造函数
- 也就是说,子类构造函数的第一行默认为
super()
语句 - 如果子类构造函数中第一行使用
super(参数)
语句调用了父类其他的构造函数,那么默认的super()
语句就没有了 - 如果子类构造函数中第一行使用
this()
语句调用了其他的构造函数,同样默认的super()
语句也没有了 - 若父类没有空参的构造函数,或者为私有,则子类构造函数的第一行必须显示的通过
super
或this
语句访问其他构造函数 - 总之,子类对象在进行初始化前,肯定都会先访问父类的构造函数对要初始化的子类对象进行初始化
- 因为
super
和this
都要求放在第一行,所以两者必须有且只能有一个
多态
Fu fu = new Zi();
- 子类继承了其父类中所有非私有的
成员变量和成员方法
- 子类中定义的
成员变量
和父类中定义的成员变量相同时,则父类中的成员变量会被隐藏 - 仅
非静态方法
才具有多态的特性,即运行时的调用的是子类
的方法 - 对于
静态方法或成员变量
,不具有多态的特性,即运行时调用的都是父类
的成员 - 对于强制类型转换后的对象,如
ZI z =(ZI) f
,则z.成员
访问的全部是子类
的成员,这和多态没关系
- 不允许一个类中存在
函数名、参数列表
完全相同,但返回值
不同的两个函数 - 同样道理,不允许子类中存在和父类
函数名、参数列表
相同,但返回值
不同的函数 - 同样道理,也不允许子类中存在和父类
函数名、参数列表
相同,但一个为静态
一个为非静态
的函数 - 允许子类从父类中继承的方法的作用域大于父类,但不允许小于父类
- 一定要注意,
成员变量
(包括静态的和非静态的)没有多态的概念 - 父类中有的成员变量,子类既可以继承后直接拿来用,也可以重新定义一个
- 重写定义一个新的成员变量后,就和父类原有的成员变量没关系了
- 同样的道理,父类的
静态成员变量
、final 类型的变量
在子类中均可以重新定义成一个新的变量
示例代码 - 多态
继承关系
class Fu {
public String name = "父类的成员变量";
public String getName() {
show(); //【1】这里一定要注意,这里调用的是【子类】的方法,这就是多态
return name; //【2】这里一定要注意,【任何情况】下,这里引用的都是当前类的name,而不是子类的name
}
protected void show() {
System.out.println("父类的方法");
}
}
class Zi extends Fu {
public String name = "子类的成员变量"; // 这是子类自己定义的name,和父类中的name没有一点点关系!
@Override
public String getName() {
System.out.println("------------");
return super.getName(); // 这是子类默认的实现方式
}
@Override
protected void show() {
System.out.println("子类的方法");
}
}
测试代码
Fu fu = new Zi();
System.out.println(fu.name); // 父类的成员变量
System.out.println(fu.getName()); // 父类的成员变量
Zi zi = (Zi) fu;
System.out.println(zi.name); // 子类的成员变量
System.out.println(zi.getName()); // 父类的成员变量
执行结果
父类的成员变量
------------
子类的方法
父类的成员变量
子类的成员变量
------------
子类的方法
父类的成员变量
示例代码 - 多态2
继承关系
class Fu {
public String name = "父类的成员变量";
public String name2 = "父类的成员变量2";
public static String name3 = "父类的【静态】成员变量";
public String getName() {
return name;
}
public String getName2() {
return name2;
}
}
class Zi extends Fu {
public String name;
public Zi() {
name = "【子类的成员变量】";
name2 = "继承的成员变量2"; // 这里的赋值实际上是对【父类】成员的赋值
name3 = "继承的【静态】成员变量";
}
}
测试代码
Fu fu = new Zi();
Zi zi = (Zi) fu;
System.out.println(fu.name + " - " + zi.name); // 父类的成员变量 - 子类的成员变量
System.out.println(fu.getName() + " - " + zi.getName()); // 父类的成员变量 - 父类的成员变量
System.out.println(fu.getName2() + " - " + zi.getName2()); // 继承的成员变量2 - 继承的成员变量2
System.out.println(Fu.name3); // 继承的【静态】成员变量
示例代码 - 初始化顺序
继承关系
class Fu {
Fu() {
System.out.println("父类的构造方法");
method();
staticMethod();
}
void method() {
System.out.println("父类的普通方法");
}
static void staticMethod() {
System.out.println("父类的静态方法");
}
}
class Zi extends Fu {
Zi() {
//super(); // 默认是有这么一行代码的,所以才会先调用父类的构造方法
System.out.println("子类的构造方法");
}
@Override
void method() {
System.out.println("子类重写的父类的普通方法");
}
static void staticMethod() { // 这里就不能用【@Override】标识,已经说明,这个完全是子类自己定义的方法
System.out.println("子类的静态方法");
}
}
测试代码
new Zi();
父类的构造方法
子类重写的父类的普通方法 【多态特性】
父类的静态方法 【非多态】
子类的构造方法
2016-09-12
本文来自博客园,作者:白乾涛,转载请注明原文链接:https://www.cnblogs.com/baiqiantao/p/5865540.html