一.继承的概述及特点
继承的概述:将多个类的共性内容,抽取到一个独立的类中, 独立的类和这多个类就产生了一种继承关系.
这多个类称为子类,独立的类称为父类extends关键字:表示继承
格式:class Zi extends Fu{
}
继承的特点:
- 在java中只支持单继承,不支持多继承
别的语言可以支持多继承:class Zi extends Fu,Mu{ } - 在java中支持多层继承
- 在java中只支持单继承,不支持多继承
/*
* 多层继承
* */
//爷爷类
class GrandFather{
public void show1(){
System.out.println("grandfather");
}
}
//父亲类继承爷爷类
class Father extends GrandFather{
public void show2(){
System.out.println("father");
}
}
//儿子类继承父亲类
class Son extends Father{
public void show3(){
System.out.println("son");
}
}
public class Demo1 {
public static void main(String[] args){
//创建一个儿子类的对象
Son s = new Son();
//多层继承,父亲类继承爷爷类,儿子类继承父亲类.因此儿子类可以调用父亲类的方法
s.show1();
s.show2();
s.show3();
}
}
二.继承的好处及注意事项
继承的好处:
- 提高代码的复用性
- 提高代码维护性
- 让类与类产生一种关系,是多态的前提
继承的注意事项:
- 子类不能继承父类私有的成员(成员方法/成员变量)
- 子类不能继承父类的构造方法,但可以间接的通过super关键字去访问父类的构造方法
三.继承中成员变量的关系
- 类的成员:成员变量,成员方法,构造方法
- 子类和父类,同名不同名的成员变量:
- 不同名的成员变量:分别输出即可
- 同名的成员变量:
先在子类成员方法的局部位置查找,有这个变量就输出
若在子类成员方法的局部位置找不到,就在子类的成员位置查找,有就输出
若在子类的成员位置找不到,就在父类的成员位置查找,有就输出
若在子类的成员位置找不到,就没有这个变量,报错
总结:查找顺序:子类局部位置 —>子类成员位置 —>父类成员位置
//父类
class Fu{
//父类成员位置
int num = 10;
}
//子类
class Zi extends Fu{
//子类的成员位置
int num = 20;
public void show(){
//子类成员方法中的局部位置
int num = 30;
System.out.println(num);
}
}
//测试类
public class Demo2 {
public static void main(String[] args){
Zi z = new Zi();
z.show();
}
}
四.super关键字
- super关键字:存储父类空间的一个标识,即父类的引用或者父类的对象
- this关键字和super 的区别以及他们的应用场景
- 区别:
this关键字:当前类的对象
super关键字:父类的引用 - 应用场景:
访问成员变量:this.成员变量 — super.成员变量
访问构造方法:this(..) — super(..)
访问成员方法:this.成员方法() — super.成员方法()
- 区别:
五.继承中构造方法的关系
- 构造方法之间的关系:
- 子类的(有参/无参)构造方法默认的访问父类中的无参构造方法
- 子类中的构造方法的第一句话被隐藏:super();
- 为什么子类的构造方法会默认的访问父类的无参构造?
因为子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化
/*
* 子类中所有的构造方法默认都会访问父类中空参数的构造方法
* */
class Fu3{
public Fu3(){
System.out.println("这是父类的无参构造方法");
}
public Fu3(String name){
System.out.println("这是父类的有参构造方法");
}
}
class Zi3 extends Fu3{
public Zi3(){
System.out.println("子类的无参构造方法");
}
public Zi3(String name){
System.out.println("子类的有参构造方法");
}
}
//测试类
public class Demo3{
public static void main(String[] args) {
//创建子类的对象
Zi3 z = new Zi3() ;
System.out.println("---------------");
Zi3 z1 = new Zi3("tom") ;
}
}
子父类初始化问题:先让父类进行初始化,然后子类进行初始化.
对象初始化问题:默认初始化 —>显示初始化 —>构造方法初始化
看程序写结果:
class X {
Y b = new Y();
X() {
System.out.print("X");
}
}
class Y {
Y() {
System.out.print("Y");
}
}
//测试类
public class Z extends X{
Y y = new Y();
Z() {
System.out.print("Z");
}
public static void main(String[] args) {
new Z();
}
输出:YXYZ
分析:
在main方法中,new Z()对Z类进行初始化之前,先对Z的父类X进行数据初始化,在X类中先输出Y,再输出X.然后在对Z进行初始化,输出Y,Z
3. 子类中的构造方法会默认的访问父类中的无参构造方法,那么如果父类中没有无参构造方法,会出现什么问题?如何解决?
问题:报错
解决:在父类中给出无参构造方法
使用super关键字去显访问父类中的带参构造
使用在子类构造方法中使用this关键字,间接的使用父类中的代参构造
六.方法重写
- 方法重写:子类出现了和父类一模一样的方法声明
- 方法重写的应用:当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容。
//手机类
class Phone{
public void Call(){
System.out.println("打电话");
}
}
//新的手机类,延续了手机类打电话的功能,又有特有的发短信的功能
class NewPhone extends Phone{
public void Call(){
System.out.println("打电话");
}
public void Message(){
System.out.println("发短信");
}
}
public class Demo1 {
public static void main(String[] args){
NewPhone n =new NewPhone();
n.Call();
n.Message();
}
}
3. 方法重写和方法重载的区别:
a.方法重载(overload):在同一个类中,方法名相同,参数列表不同(参数个数/参数类型),与返回值没有关系
b.方法重写(override):子类的方法和父类的一模一样,返回值也一样
七.final关键字
- final关键字:最终,可以修饰类,成员变量,成员方法
- final的用法:
- 修饰类:类不可被继承
- 修饰方法:方法不可被重写
- 修饰变量:变量变为常量(自定义常量),只可被赋值一次
//父类
class Father{
public int num = 100;
//只可赋值一次
public final int num2 = 200 ;
public void method(){
//局部变量和成员变量名称一致,就近原则
int num = 20 ;
int num2 = 50 ;
System.out.println("show Father....");
System.out.println(num2);
}
final public void function(){
System.out.println("function");
}
}
//子类
class Son extends Father{
public void show() {
System.out.println("show Son...");
}
//报错,该类在父类中被final修饰,不可重写
// public void function(){
// System.out.println("function");
// }
}
public class Demo1 {
public static void main(String[] args) {
Son s = new Son() ;
//报错,此时num2不能在更改,已经是常量
// s.num2 = 100;
System.out.println(s.num2);
s.function();
s.method();
}
}
3. 关于final修饰变量(基本数据类型/引用类型)的问题:
a.final修饰基本数据类型:基本数据类型的值不改变,变量此时是常量
b.final修饰引用类型:引用类型的地址值不发生改变,对象中堆内存的值可以改变
class Number{
int num1 = 10;
}
public class Demo2 {
public static void main(String[] args){
int x = 20;
x = 30;
System.out.println(x);
//final修饰基本数据类型
final int y = 40;
//报错,final修饰基本数据类型,此时变量为常量,不可再赋值
// y = 50;
//final修饰引用类型
final Number n = new Number();
//final修饰引用类型变量,引用类型变量的值可以改变
n.num1 = 100;
n.num1 = 200;
System.out.println(n.num1);
//报错,属于新建了一个对象,在堆内存中开辟了一个新空间
// n = new Number();
}
}
3. final初始化时机问题:
a.final去修饰变量,这个变量是常量,只能赋值一次
b.被final修饰变量:在调用构造方法之前,被final修饰的变量去使用它