Java 之继承和 final 关键字
- 继承的概述
- 继承的特点
- super 关键字
- 函数覆盖
- 子类的实例化过程
- final 关键字
1. 继承的概述
继承是类与类之间的关系.
继承的好处:
- 提高了代码的复用性
- 让类与类之间产生了关系, 给第三个特征多态提供了前提
继承按直接父类的个数分为两种:
- 单继承: 一个子类只能有一个直接父类
- 多继承: 一个子类可以有多个直接父类, 此继承方式在 java 中不允许.
java 对此种继承方式进行了改良, 即通过"多实现"的方式来实现的.
// 不直接支持多继承的原因: 多个父类中有相同成员, 会产生调用的不确定性
class A
{
void show()
{
System.out.println("a");
}
}
class B
{
void show()
{
System.out.println("b");
}
}
class C extends A,B
{
}
new C().show(); // 由于 C 继承了 A 和 B, 此时,出现调用的不确定性
继承体系
- java 支持多重继承, 即 C 继承 B, B 继承 A.
当要使用一个继承体系时
- 查看该体系中的顶层类(如上图中的 A 类), 了解该体系的基本功能
- 创建体系中的最子类对象(如上图中的 D 类), 完成功能的使用
什么时候定义继承呢?
- 当类与类之间存在着所属关系时, 就定义继承.
- 所属关系: is a 关系
例如:狗类是犬科类的一种,狗类就可以继承犬科类.
2. 继承的特点
在子父类中,成员的特点体现
- 成员变量
- 成员函数
- 构造函数
成员变量
- 当本类的成员变量和局部变量同名用 this 区分;
当子父类中的成员变量同名用 super 区分. - this 和 super 的用法很相似.不同之处在于
- this: 代表一个本类对象的引用
- super: 代表一个父类的空间, 不代表父类对象
- 子类不能直接访问父类中私有内容, 但是可以通过 get 方法间接访问父类中私有内容
class Fu
{
int num = 4;
}
class Zi extends Fu
{
int num = 5;
void show()
{
System.out.println(this.num+"..."+super.num);
}
}
class ExtendsDemo
{
public static void main(String[] args)
{
Zi z = new Zi();
z.show();
}
}
// 示例: 写出程序结果
class Super
{
int i = 0;
public Super(String a) // 带参数构造函数
{
System.out.println("A");
i = 1;
}
public Super() // 空参数构造函数
{
System.out.println("B");
i += 2;
}
}
class Demo extends Super
{
public Demo(String s) // 带参数构造函数
{
System.out.println("C");
i += 5; // 子类中没有定义 i, 直接访问父类的 i
}
public static void main(String[] args)
{
int i = 4;
Super d = new Demo("A");
System.out.println(d.i);
}
}
// 输出结果: B C 7
成员函数
当子父类中出现成员函数一模一样的情况, 会运行子类的函数, 这种现象称为覆盖操作. 这是函数在子父类中的特性.
覆盖操作注意事项:
- 子类方法覆盖父类方法时, 子类成员函数权限必须要大于等于父类成员函数时的权限.
权限分为三种: public, private 和 默认权限. - 静态只能覆盖静态, 或被静态覆盖
- 必须保证子类和父类函数一模一样, 即 返回值类型相同, 函数名一样, 参数列表一样.
// 示例1: 写出程序结果
class Super
{
public int get(){return 4;}
}
class Demo extends Super
{
public long get() {return 5;} // 编译失败, 函数调用的不确定性
public static void main(String[] args)
{
Super s = new Demo();
System.out.println(s.get());
}
}
// 示例2: 写出错误答案错误的原因, 用单行注释的方式
class Demo
{
int show(int a, int b){return 0;}
}
下面哪些函数可以存在于 Demo 的子类中
A. public int show(int a, int b){return 0;} // 可以, 函数覆盖
B. private int show(int a, int b){return 0;} // 不可以, 权限不够
C. private int show(int a, long b){return 0;} // 可以, 子类特有方法
D. public short show(int a, int b){return 0;} // 不可以, 调用的不确定性
E. static int show(int a, int b){return 0;} // 不可以, 静态只能覆盖静态
函数的两个特性:
- 重载: 发生在同一个子类中
- 覆盖: 也称为重写(覆写), override
什么时候使用覆盖操作?
当对一个类进行子类的扩展时, 子类需要保留父类的功能声明,
但是要定义子类中该功能的特有内容时, 就使用覆盖操作完成.
// 早期手机来电显示, 只能显示电话号码
class ExtendsDemo
{
public static void main(String[] args)
{
Phone p = new Phone();
p.show
}
}
class Phone
{
void show()
{
System.out.println("number"); //来电显示电话号码
}
}
// 手机升级, 增加来电显示姓名, 大头贴和电话号码
// 此时,相当于代码升级,不应该在源代码上过分修改.
class NewPhone extends Phone // 继承父类
{
void show() // 函数覆盖
{
System.out.println("name"); // 增加姓名
System.out.println("pic"); // 增加大头贴
// System.out.println(number);
super.show(); // 调用父类 show() 方法
}
}
构造函数
class Fu
{
Fu() //父类构造函数
{
System.out.println("fu run");
}
}
class Zi extends Fu
{
Zi() // 子类构造函数
{
System.out.println("zi run");
}
}
class ExtendsDemo
{
public static void main(String[] args)
{
new Zi();
}
}
运行结果:
在子类构造对象时, 发现访问子类构造函数时, 父类的构造函数也运行了.
为什么呢?
原因是: 在子类的构造函数中第一行有一个默认的隐式语句, super(); 调用的就是父类中的空参数的构造函数.
如果父类中的构造函数有参数, 需要使用 super 指定.
为什么子类实例化的时候要访问父类中的构造函数呢?
因为子类继承了父类, 获取到了父类中的内容(属性), 所以在使用父类内容之前,要先看父类是如何对自己
的内容进行初始化的.所以子类在构造对象时, 必须访问父类中的构造函数.为类完成这个必须的动作, 就在
子类的构造函数中加入了 super() 语句.
如果父类中没有定义空参数构造函数, 那么子类的构造函数必须用 super 明确要调用父类中哪个构造函数.
同时, 子类的构造函数中如果使用 this 调用类本类构造函数时,那么 super 就没有了, 因为 super 和
this 都只能定义在第一行, 所以只能有一个. 但是可以保证的是, 子类中肯定会有其他的构造函数访问父类
的构造函数.
注意: super 语句必须定义在子类构造函数的第一行, 因为父类的初始化动作要先完成.
// 子类实例化过程: 子类中所有的构造函数默认都会访问父类中的空参数的构造函数
class Fu
{
Fu()
{
System.out.println("A");
}
Fu(int x)
{
System.out.println("B"+ x);
}
}
class Zi extends Fu
{
Zi()
{
System.out.println("C");
}
Zi(int x)
{
System.out.println("D"+ x)
}
}
class ExtendsDemo
{
public static void main(String[] args)
{
new Zi(6);
}
}
// 输出结果: A D6
// 子类初始化,必须先访问父类的构造函数
class Fu
{
Fu()
{
System.out.println("A");
}
Fu(int x)
{
System.out.println("B"+ x);
}
}
class Zi extends Fu
{
Zi()
{
super(); // 通过这个构造函数,默认的隐式语句 super(); 访问父类中的空参数构造函数
System.out.println("C");
}
Zi(int x)
{
this(); // 访问本类中其他构造函数
System.out.println("D"+ x)
}
}
class ExtendsDemo
{
public static void main(String[] args)
{
new Zi(6);
}
}
4. 子类的对象实例化过程
class Fu
{
Fu() // 构造函数
{
super(); // 默认隐式语句
show();
return;
}
void show()
{
System.out.println("fu show");
}
}
class Zi extends Fu
{
int num = 8;
Zi() // 默认构造函数
{
super();
return;
}
void show()
{
System.out.println("zi show..."+num);
}
}
class ExtendsDemo
{
public static void main(String[] args)
{
Zi z = new Zi();
}
}
// 运行结果: zi show...0
一个对象实例化过程
以 Person p = new Person(); 为例:
- JVM 会读取指定的路径下的 Person.class 文件, 并加载进内存,
并会先加载 Person 的父类(如果有直接的父类的情况下) - 在堆内存中, 开辟空间, 分配内存地址
- 在对象空间中, 对对象中的属性进行默认初始化
- 调用对应的构造函数进行初始化
- 在构造函数中, 第一行会先调用父类中的构造函数进行初始化
- 父类初始化完毕后, 在对子类的属性进行显示初始化
- 再进行子类构造函数的特定初始化
- 初始化完毕后, 将地址值赋给引用变量
5. final 关键字
- final 是一个修饰符, 可以修饰类, 方法, 变量
- final 修饰的类不可以被继承
- final 修饰的方法不可以被覆盖
- final 修饰的变量是一个常量, 只能赋值一次
- 写法规范: 常量所有单词都大写, 若有多个单词, 使用下划线(_)连接
- 内部类只能访问被 final 修饰的局部变量
参考资料
Java 之继承和 final 关键字的更多相关文章
- Java的继承,final关键字,super关键字
1.继承的初始化顺序: 父类—>父类的初始化对象中的属性—>父类的构造方法—>子类—>子类的初始化对象中的属性—>子类的构造方法 若有构造方法:则先执行属性,再执行构造方 ...
- “全栈2019”Java第五十一章:继承与final关键字详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- 《JAVA学习笔记 (final关键字)》
[14-9]面向对象-final关键字 /* 继承的弊端,打破封装性. 不让其他类继承该类,就不会有重写. 怎么能实现呢?通过Java中的一个关键子来实现,final(最终化). [final关键字] ...
- Java的static和final关键字的用法
static关键字的用法 static的意思是"'静态的",在java里面可用于修饰属性和方法. static关键字的应用应注意以下几种情形: 1.static作用于某个字段,一个 ...
- 2017.10.23 Java 面向对象深入学习---final 关键字、static关键字、匿名对象等
今日内容介绍 1.final 关键字 2.static 关键字 3.匿名对象 4.内部类 5.包的声明与访问 6.访问修饰符 7.代码块 第一节课 01(面向对象)final关键字概念.avi 02: ...
- 深入java面向对象二:final关键字
文章内容源于对<疯狂java讲义>及<疯狂Java:突破程序员基本功的16课>学习和总结. 一. final成员变量 final 修饰变量时,表示该变量一旦获取了值就不可以改变 ...
- java学习面向对象之final关键字
之前我们讲过继承的相关知识了,继承就是子类继承父类的属性和方法并且还可以覆盖父类的方法.但是这样有一个缺陷是什么呢,就是当我们一个类当中涉及一些封装的核心的东西或者对整个系统非常关键的方法或者类的时候 ...
- (五)《Java编程思想》——final关键字
可能使用final的三种情况:数据.方法.类. 1.final数据 final 常量必须是基本类型数据,且在定义时须赋值: 一个既是static又是final的域只占据一段不能改变的存储空间,只有一份 ...
- 安卓开发(Java)中关于final关键字与线程安全性
前言 学习新知识固然重要,但是时常往回看看,温故知新是很必要的.回顾一下线程安全性和final关键字. 正文 从Java 5开始,final keyword一个特殊用法是在并发库中一个非常重要且经常被 ...
随机推荐
- Hibernate一级缓存和二级缓存具体解释
一.一级缓存二级缓存的概念解释 (1)一级缓存就是Session级别的缓存,一个Session做了一个查询操作,它会把这个操作的结果放在一级缓存中.假设短时间内这个 session(一定要同一个ses ...
- atitit。全局变量的设计与实现 java php的异同
atitit.全局变量的设计与实现 java php的异同 1. 超级全局(Superglobals)"变量 1 2. 全局变量和"global"关键字 1 3. 更加好 ...
- 关于Qt半自动内存管理的思考及实验
一时兴起,对Qt感了兴趣,决心想要研究一下. 按网上资料配好环境,Windows 7 64bit + Qt 5.3.1 + VS2010. 根据<C++ GUI Qt4 编程>这本书,写出 ...
- Android Studio编译的时候提示Gradle无法下载的解决方案
首先,打开android studio项目 找到项目目录gradle\wrapper\gradle-wrapper.properties这个文件.内容如下:#Wed Apr 10 15:27:10 P ...
- linux web.py spawn-fcgi web.py 配置
本来要用uwsgi,但是...介于以前说过...这台服务器略老...redhat 3的系统...确实很老,没法用yum,没法安装很多东西,打算自己编译uwsgi,但是编译各种错误...花了快一天,最后 ...
- 02 SQL 执行
sql 被保存在 share pool 后, 开始解析, 解析包括语句的语法, 检验及对象, 以确认该用于是否有该对象的权限, 如果这些都通过了, 接下来就要看这个语句之前是否被执行过, 如果是, o ...
- Bootstrap Multiselect 设置 option
$.ajax({ type: 'post', url: "helper/ajax_search.php", data: {models: decodeURIComponent(br ...
- java 学习笔记--mybatis 三剑客(mybatis)
Java项目中使用Mybatis入门程序 wanna 关注 2017.03.23 14:33* 字数 270 阅读 1243评论 0喜欢 5 MyBatis 是支持定制化 SQL.存储过程以及高级映射 ...
- 用CSS调整scrollbar(滚动条)的配色
可以通过调整CSS的方式,来给滚动条换色. 代码如下: .uicss-cn{ height:580px;overflow-y: scroll; scrollbar-face-color:#EAEAEA ...
- Storm集成Kafka编程模型
原创文章,转载请注明: 转载自http://www.cnblogs.com/tovin/p/3974417.html 本文主要介绍如何在Storm编程实现与Kafka的集成 一.实现模型 数据流程: ...