共计 12823 个字符,预计需要花费 33 分钟才能阅读完成。
原创公众号:「bigsai」关注这个有情怀的程序员 除公众号以外回绝任意擅自转载
文章收录在 bigsai 公众号和回车课堂
课程导学
在 Java 课堂中,所有老师不得不提到 面向对象 (Object Oriented),而在谈到面向对象的时候,又不得不提到面向对象的三大特色:封装、 继承 、多态。三大特色紧密联系而又有区别,本课程就带你学习 Java 的 继承。
你可能不晓得继承到底有什么用,但你大概率曾有过这样的经验:写 Java 我的项目 / 作业时候创立很多类似的类,类中也有很多雷同的办法,做了很多反复的工作量,感觉很臃肿。而正当应用继承就能大大减少反复代码,进步代码复用性。
继承的初相识
学习继承,必定是先从广的概念理解继承是什么以及其作用,而后才从细的方面学习继承的具体实现细节,本关就是带你先疾速理解和了解继承的重要概念。
### 什么是继承
继承 (英语:inheritance)是面向对象软件技术中的一个概念。它使得 复用以前的代码非常容易,可能大大缩短开发周期,升高开发费用。
Java 语言是十分典型的面向对象的语言,在 Java 语言中 继承就是子类继承父类的属性和办法,使得子类对象(实例)具备父类的属性和办法,或子类从父类继承办法,使得子类具备父类雷同的办法。父类有时也叫基类、超类;子类有时也被称为派生类。
咱们来举个例子:咱们晓得动物有很多种,是一个比拟大的概念。在动物的品种中,咱们相熟的有猫 (Cat)、狗(Dog) 等动物,它们都有动物的个别特色(比方可能吃货色,可能发出声音),不过又在细节上有区别(不同动物的吃的不同,叫声不一样)。在 Java 语言中实现 Cat 和 Dog 等类的时候,就须要继承 Animal 这个类。继承之后 Cat、Dog 等具体动物类就是子类,Animal 类就是父类。
为什么须要继承
你可能会疑难 为什么须要继承?在具体实现的时候,咱们创立 Dog,Cat 等类的时候实现其具体的办法不就能够了嘛,实现这个继承仿佛使得这个类的构造不那么清晰。
如果仅仅只有两三个类,每个类的属性和办法很无限的状况下的确没必要实现继承,但事件并非如此,事实上一个零碎中往往有很多个类并且有着很多相似之处,比方猫和狗同属动物,或者学生和老师同属人。各个类可能又有很多个雷同的属性和办法,这样的话如果每个类都从新写不仅代码显得很乱,代码工作量也很大。
这时继承的劣势就进去了:能够间接应用父类的属性和办法,本人也能够有本人新的属性和办法满足拓展,父类的办法如果本人有需要更改也能够重写。这样 应用继承不仅大大的缩小了代码量,也使得代码构造更加清晰可见。
所以这样从代码的层面上来看咱们设计这个残缺的 Animal 类是这样的:
class Animal
{
public int id;
public String name;
public int age;
public int weight;
public Animal(int id, String name, int age, int weight) {
this.id = id;
this.name = name;
this.age = age;
this.weight = weight;
}
// 这里省略 get set 办法
public void sayHello()
{System.out.println("hello");
}
public void eat()
{System.out.println("I'm eating");
}
public void sing()
{System.out.println("sing");
}
}
而 Dog,Cat,Chicken 类能够这样设计:
class Dog extends Animal// 继承 animal
{public Dog(int id, String name, int age, int weight) {super(id, name, age, weight);// 调用父类构造方法
}
}
class Cat extends Animal{public Cat(int id, String name, int age, int weight) {super(id, name, age, weight);// 调用父类构造方法
}
}
class Chicken extends Animal{public Chicken(int id, String name, int age, int weight) {super(id, name, age, weight);// 调用父类构造方法
}
// 鸡下蛋
public void layEggs()
{System.out.println("我是老母鸡下蛋啦,咯哒咯!咯哒咯!");
}
}
各自的类继承 Animal 后能够间接应用 Animal 类的属性和办法而不须要反复编写,各个类如果有本人的办法也可很容易地拓展。上述代码中你须要留神 extends 就是用来实现继承的。
继承的分类
继承分为单继承和多继承,Java 语言只反对类的单继承,但能够通过实现接口的形式达到多继承的目标。咱们先用一张表概述一下两者的区别,而后再开展解说。
定义 | 优缺点 | |
---|---|---|
单继承 |
一个子类只领有一个父类 | 长处:在类层次结构上比拟清晰 毛病:构造的丰盛度有时不能满足应用需要 |
多继承(Java 不反对,但能够用其它形式满足多继承应用需要) |
一个子类领有多个间接的父类 | 长处:子类的丰盛度很高 毛病:容易造成凌乱 |
单继承
单继承,是一个子类只领有一个父类,如咱们下面讲过的 Animal 类和它的子类。单继承在类层次结构上比拟清晰,但毛病是构造的丰盛度有时不能满足应用需要。
多继承(Java 不反对,但能够实现)
多继承,是一个子类领有多个间接的父类。这样做的益处是子类领有所有父类的特色,子类的丰盛度很高,然而毛病就是容易造成凌乱。下图为一个凌乱的例子。
Java 尽管不反对多继承,然而 Java 有三种实现多继承成果的形式,别离是 外部类、多层继承和实现接口。
外部类 能够继承一个与外部类无关的类,保障了外部类的独立性,正是基于这一点,能够达到多继承的成果。
多层继承:子类继承父类,父类如果还继承其余的类,那么这就叫 多层继承。这样子类就会领有所有被继承类的属性和办法。
实现接口 无疑是满足多继承应用需要的最好形式,一个类能够实现多个接口满足本人在丰富性和简单环境的应用需要。类和接口相比,类就是一个实体,有属性和办法,而接口更偏向于一组办法。举个例子,就拿斗罗大陆的唐三来看,他存在的继承关系可能是这样的:
如何实现继承
实现继承 除了下面用到的 extends外,还能够用 implements 这个关键字实现。上面,让我给你逐个解说一下。
extends 关键字
在 Java 中,类的继承是繁多继承,也就是说一个子类只能领有一个父类,所以 extends 只能继承一个类。其应用语法为:
class 子类名 extends 父类名{}
例如 Dog 类继承 Animal 类,它是这样的:
class Animal{} // 定义 Animal 类
class Dog extends Animal{} //Dog 类继承 Animal 类
子类继承父类后,就领有父类的非公有的 属性和办法 。如果不明确,请看这个案例,在 IDEA 下创立一个我的项目,创立一个 test 类做测试,别离创立 Animal 类和 Dog 类,Animal 作为父类写一个 sayHello() 办法,Dog 类继承 Animal 类之后就能够调用 sayHello()办法。具体代码为:
class Animal {public void sayHello()// 父类的办法
{System.out.println("hello,everybody");
}
}
class Dog extends Animal// 继承 animal
{ }
public class test {public static void main(String[] args) {Dog dog=new Dog();
dog.sayHello();}
}
点击运行的时候 Dog 子类能够间接应用 Animal 父类的办法。
implements 关键字
应用 implements 关键字能够变相使 Java 领有多继承的个性,应用范畴为类实现接口的状况,一个类能够实现多个接口 (接口与接口之间用逗号离开)。Java 接口是一系列办法的申明, 一个接口中没有办法的具体实现。子类实现接口的时候必须重写接口中的办法。
咱们来看一个案例,创立一个 test2 类做测试,别离创立 doA 接口和 doB 接口,doA 接口申明 sayHello()办法,doB 接口申明 eat()办法,创立 Cat2 类实现 doA 和 doB 接口,并且在类中须要重写 sayHello()办法和 eat()办法。具体代码为:
interface doA{void sayHello();
}
interface doB{void eat();
// 以下会报错 接口中的办法不能具体定义只能申明
//public void eat(){System.out.println("eating");}
}
class Cat2 implements doA,doB{
@Override// 必须重写接口内的办法
public void sayHello() {System.out.println("hello!");
}
@Override
public void eat() {System.out.println("I'm eating");
}
}
public class test2 {public static void main(String[] args) {Cat2 cat=new Cat2();
cat.sayHello();
cat.eat();}
}
Cat 类实现 doA 和 doB 接口的时候,须要实现其申明的办法,点击运行后果如下,这就是一个类实现接口的简略案例:
继承的特点
继承的次要内容就是子类继承父类,并重写父类的办法。应用子类的属性或办法时候,首先要创立一个对象,而对象通过 构造方法 去创立,在构造方法中咱们可能会调用子父类的一些属性和办法,所以就须要提前把握 this 和 super 关键字。创立完这个对象之后,在调用 重写 父类的办法,并区别重写和重载的区别。所以本节依据 this、super 关键字—> 构造函数—> 办法重写—> 办法重载的程序进行解说。
this 和 super 关键字
this 和 super 关键字是继承中 十分重要的知识点,别离示意以后对象的援用和父类对象的援用,两者有很大类似又有一些区别。
this 示意以后对象,是指向本人的援用。
this. 属性 // 调用成员变量,要区别成员变量和局部变量
this.() // 调用本类的某个办法
this() // 示意调用本类构造方法
super 示意父类对象,是指向父类的援用。
super. 属性 // 示意父类对象中的成员变量
super. 办法() // 示意父类对象中定义的办法
super() // 示意调用父类构造方法
此外,this 和 super 关键字只能呈现在非 static 润饰的代码中。
this()和 super()都只能在 构造方法 的第一行呈现,如果应用 this()示意调用以后类的其余构造方法,应用 super()示意调用父类的某个构造方法,所以两者只能依据本人应用需要抉择其一。
写一个小案例,创立 D1 类和子类 D2 如下:
class D1{public D1() {}// 无参结构
public void sayHello() {System.out.println("hello");
}
}
class D2 extends D1{
public String name;
public D2(){super();// 调用父类构造方法
this.name="BigSai";// 给以后类成员变量赋值
}
@Override
public void sayHello() {System.out.println("hello,我是"+this.name);
}
public void test()
{super.sayHello();// 调用父类办法
this.sayHello();// 调用以后类其余办法}
}
public class test8 {public static void main(String[] args) {D2 d2=new D2();
d2.test();}
}
执行的后果为:
构造方法
构造方法是一种非凡的办法,它是一个与类同名的办法 。对象的创立就通过构造方法来实现,其次要的性能是实现对象的初始化。但在继承中 构造方法是一种比拟非凡的办法(比方不能继承),所以要理解和学习在继承中构造方法的规定和要求。
构造方法可分为有参结构和无参结构,这个能够依据本人的应用需要正当设置构造方法。但继承中的构造方法有以下几点须要留神:
父类的构造方法不能被继承:
因为构造方法语法是 与类同名 ,而继承则不更改办法名,如果子类继承父类的构造方法,那显著与构造方法的语法抵触了。比方 Father 类的构造方法名为 Father(),Son 类如果继承 Father 类的构造方法 Father(),那就和构造方法定义: 构造方法与类同名 抵触了,所以在子类中不能继承父类的构造方法,但子类会调用父类的构造方法。
子类的结构过程必须调用其父类的构造方法:
Java 虚拟机 结构子类对象前会先结构父类对象,父类对象结构实现之后再来结构子类特有的属性,这被称为 内存叠加 。而 Java 虚拟机结构父类对象会执行父类的构造方法,所以子类构造方法必须调用 super() 即父类的构造方法。就比方一个简略的继承案例应该这么写:
class A{
public String name;
public A() {// 无参结构}
public A (String name){// 有参结构}
}
class B extends A{public B() {// 无参结构
super();}
public B(String name) {// 有参结构
//super();
super(name);
}
}
如果子类的构造方法中没有显示地调用父类构造方法,则零碎默认调用父类无参数的构造方法。
你可能有时候在写继承的时候子类并没有应用 super()调用,程序仍然没问题,其实这样是为了节俭代码,零碎执行时会主动增加父类的无参结构形式,如果不信的话咱们对下面的类稍作批改执行:
办法重写(Override)
办法重写也就是子类中呈现和父类中截然不同的办法 (包含返回值类型,办法名,参数列表),它建设在继承的根底上。你能够了解为办法的 外壳不变,然而核心内容重写。
在这里提供一个简略易懂的办法重写案例:
class E1{public void doA(int a){System.out.println("这是父类的办法");
}
}
class E2 extends E1{
@Override
public void doA(int a) {System.out.println("我重写父类办法,这是子类的办法");
}
}
其中 @Override 注解显示申明该办法为注解办法,能够帮你查看重写办法的语法正确性,当然如果不加也是能够的,但倡议加上。
对于重写,你须要留神以下几点:
从重写的要求上看:
- 重写的办法和父类的要统一(包含返回值类型、办法名、参数列表)
- 办法重写只存在于子类和父类之间,同一个类中只能重载
从拜访权限上看:
- 子类办法不能放大父类办法的拜访权限
- 子类办法不能抛出比父类办法更多的异样
- 父类的公有办法不能被子类重写
从动态和非动态上看:
- 父类的静态方法不能被子类重写为非静态方法
- 子类能够定义于父类的静态方法同名的静态方法,以便在子类中暗藏父类的静态方法(满足重写束缚)
- 父类的非静态方法不能被子类重写为静态方法
从形象和非形象来看:
- 父类的形象办法能够被子类通过两种路径重写(即实现和重写)
- 父类的非形象办法能够被重写为形象办法
当然,这些规定可能波及一些修饰符,在第三关中会具体介绍。
办法重载(Overload)
如果有两个办法的 办法名雷同,但参数不统一,那么能够说一个办法是另一个办法的重载。办法重载规定如下:
- 被重载的办法 必须扭转参数列表(参数个数或类型或程序不一样)
- 被重载的办法能够扭转返回类型
- 被重载的办法能够扭转拜访修饰符
- 被重载的办法能够申明新的或更广的查看异样
- 办法可能在同一个类中或者在一个子类中被重载
- 无奈以返回值类型作为重载函数的辨别规范
重载能够通常了解为实现同一个事件的办法名雷同,然而参数列表不同其余条件也可能不同。一个简略的办法重载的例子,类 E3 中的 add()办法就是一个重载办法。
class E3{public int add(int a,int b){return a+b;}
public double add(double a,double b) {return a+b;}
public int add(int a,int b,int c) {return a+b+c;}
}
办法重写和办法重载的区别:
办法重写和办法重载名称上容易混同,但内容上有很大区别,上面用一个表格列出其中区别:
区别点 | 办法重写 | 办法重载 |
---|---|---|
构造上 | 垂直构造,是一种父子类之间的关系 | 程度构造,是一种同类之间关系 |
参数列表 | 不能够批改 | 能够批改 |
拜访修饰符 | 子类的拜访修饰符范畴必须大于等于父类拜访修饰符范畴 | 能够批改 |
抛出异样 | 子类办法异样必须是父类办法异样或父类办法异样子异样 | 能够批改 |
继承与修饰符
Java 修饰符的作用就是对类或类成员进行润饰或限度,每个修饰符都有本人的作用,而在继承中可能有些非凡修饰符使得被润饰的属性或办法不能被继承,或者继承须要一些其余的条件,上面就具体介绍在继承中一些修饰符的作用和个性。
Java 语言提供了很多修饰符,修饰符用来定义类、办法或者变量,通常放在语句的最前端。次要分为以下两类:
- 拜访修饰符
- 非拜访修饰符
这里拜访修饰符次要解说 public,protected,default,private 四种访问控制修饰符。非拜访修饰符这里就介绍 static 修饰符,final 修饰符和 abstract 修饰符。
拜访修饰符
public,protected,default(无修饰词),private修饰符是面向对象中十分重要的知识点,而在继承中也须要懂得各种修饰符应用规定。
首先咱们都晓得不同的关键字作用域不同,四种关键字的作用域如下:
同一个类 | 同一个包 | 不同包子类 | 不同包非子类 | |
---|---|---|---|---|
private | ✅ | |||
default | ✅ | ✅ | ||
protect | ✅ | ✅ | ✅ | |
public | ✅ | ✅ | ✅ | ✅ |
- private:Java 语言中对拜访权限限度的 最窄 的修饰符,个别称之为“公有的”。被其润饰的属性以及办法 只能被该类的对象拜访,其子类不能拜访,更不能允许跨包拜访。
- default:(也有称 friendly)即不加任何拜访修饰符,通常称为“默认拜访权限“或者“包拜访权限”。该模式下,只容许在同一个包中进行拜访。
- protected:介于 public 和 private 之间的一种拜访修饰符,个别称之为“爱护拜访权限”。被其润饰的属性以及办法只能被类自身的办法及子类拜访,即便子类在不同的包中也能够拜访。
- public:Java 语言中拜访限度 最宽 的修饰符,个别称之为“公共的”。被其润饰的类、属性以及办法不仅能够跨类拜访,而且容许跨包拜访。
Java 子类重写继承的办法时,不能够升高办法的拜访权限 , 子类继承父类的拜访修饰符作用域不能比父类小 ,也就是更加凋谢,如果父类是 protected 润饰的,其子类只能是 protected 或者 public,相对不能是 default(默认的拜访范畴) 或者 private。所以在继承中须要重写的办法不能应用 private 修饰词润饰。
如果还是不太分明能够看几个小案例就很容易搞懂,写一个 A1 类中用四种修饰词实现四个办法,用子类 A2 继承 A1,重写 A1 办法时候你就会发现父类公有办法不能重写,非公有办法重写应用的修饰符作用域不能变小(大于等于)。
正确的案例应该为:
class A1 {private void doA(){ }
void doB(){}//default
protected void doC(){}
public void doD(){}
}
class A2 extends A1{
@Override
public void doB() {}// 继承子类重写的办法拜访修饰符权限可扩充
@Override
protected void doC() {}// 继承子类重写的办法拜访修饰符权限可和父类统一
@Override
public void doD() {}// 不可用 protected 或者 default 润饰
}
还要留神的是,继承当中子类抛出的异样必须是父类抛出的异样或父类抛出异样的子异样。上面的一个案例四种办法测试能够发现子类办法的异样不可大于父类对应办法抛出异样的范畴。
正确的案例应该为:
class B1{public void doA() throws Exception{}
public void doB() throws Exception{}
public void doC() throws IOException{}
public void doD() throws IOException{}
}
class B2 extends B1{
// 异样范畴和父类能够统一
@Override
public void doA() throws Exception {}
// 异样范畴能够比父类更小
@Override
public void doB() throws IOException {}
// 异样范畴 不能够比父类范畴更大
@Override
public void doC() throws IOException {}// 不可抛出 Exception 等比 IOException 更大的异样
@Override
public void doD() throws IOException {}
}
非拜访修饰符
拜访修饰符用来管制拜访权限,而非拜访修饰符每个都有各自的作用,上面针对 static、final、abstract 修饰符进行介绍。
static 修饰符
static 翻译为“动态的”,可能与变量,办法和类一起应用,称为动态变量,静态方法 (也称为类变量、类办法)。如果在一个类中应用 static 润饰变量或者办法的话,它们 能够间接通过类拜访,不须要创立一个类的对象来拜访成员。
咱们在设计类的时候可能会应用静态方法,有很多工具类比方 Math
,Arrays
等类外面就写了很多静态方法。static 修饰符的规定很多,这里仅仅介绍和 Java 继承相干用法的规定:
- 构造方法不容许申明为 static 的。
- 静态方法中不存在以后对象,因此不能应用 this,当然也不能应用 super。
- 静态方法不能被非静态方法重写(笼罩)
- 静态方法能被静态方法重写(笼罩)
能够看以下的案例证实上述规定:
源代码为:
class C1{
public int a;
public C1(){}
// public static C1(){}// 构造方法不容许被申明为 static
public static void doA() {}
public static void doB() {}
}
class C2 extends C1{public static void doC()// 静态方法中不存在以后对象,因此不能应用 this 和 super。{//System.out.println(super.a);
}
public static void doA(){}// 静态方法能被静态方法重写
// public void doB(){}// 静态方法不能被非静态方法重写
}
final 修饰符
final 变量:
- final 示意 ” 最初的、最终的 ” 含意,变量一旦赋值后,不能被从新赋值。被 final 润饰的实例变量必须显式指定初始值(即不能只申明)。final 修饰符通常和 static 修饰符一起应用来创立类常量。
final 办法:
- 父类中的 final 办法能够被子类继承,然而不能被子类重写。申明 final 办法的次要目标是避免该办法的内容被批改。
final 类:
- final 类不能被继承,没有类可能继承 final 类的任何个性。
所以无论是变量、办法还是类被 final 润饰之后,都有代表最终、最初的意思。内容无奈被批改。
abstract 修饰符
abstract 英文名为“形象的”,次要用来润饰类和办法,称为抽象类和形象办法。
形象办法:有很多不同类的办法是类似的,然而具体内容又不太一样,所以咱们只能抽取他的申明,没有具体的办法体,即形象办法能够表白概念但无奈具体实现。
抽象类 : 有形象办法的类必须是抽象类,抽象类能够表白概念然而无奈结构实体的类。
抽象类和形象办法内容和规定比拟多。这里只提及一些和继承无关的用法和规定:
- 抽象类也是类,如果一个类继承于抽象类,就不能继承于其余的(类或抽象类)
- 子类能够继承于抽象类,然而肯定要实现父类们所有 abstract 的办法。如果不能齐全实现,那么子类也必须被定义为抽象类
- 只有实现父类的所有形象办法,能力是残缺类。
比方咱们能够这样设计一个 People 抽象类以及一个形象办法,在子类中具体实现:
abstract class People{public abstract void sayHello();// 形象办法
}
class Chinese extends People{
@Override
public void sayHello() {// 实现形象办法
System.out.println("你好");
}
}
class Japanese extends People{
@Override
public void sayHello() {// 实现形象办法
System.out.println("口你七哇");
}
}
class American extends People{
@Override
public void sayHello() {// 实现形象办法
System.out.println("hello");
}
}
Object 类和转型
提到 Java 继承,不得不提及所有类的根类:Object(java.lang.Object)类,如果一个类没有显式申明它的父类(即没有写 extends xx),那么默认这个类的父类就是 Object 类,任何类都能够应用 Object 类的办法,创立的类也可和 Object 进行向上、向下转型,所以 Object 类是把握和了解继承所必须的知识点。而 Java 向上和向下转型在 Java 中使用很多,也是建设在继承的根底上,所以 Java 转型也是把握和了解继承所必须的知识点。
Object 类概述
- Object 是类层次结构的 根类,所有的类都隐式的继承自 Object 类。
- Java 所有的对象都领有 Object 默认办法
- Object 类的构造方法有一个,并且是 无参结构
Object 是 java 所有类的父类,是整个类继承构造的顶端,也是最形象的一个类。像 toString()、equals()、hashCode()、wait()、notify()、getClass()等都是 Object 的办法。你当前可能会常常碰到,但其中遇到更多的就是 toString()办法和 equals()办法,咱们常常须要重写这两种办法满足咱们的应用需要。
toString()办法示意返回该对象的字符串,因为各个对象结构不同所以须要重写,如果不重写的话默认返回 类名 @hashCode
格局。
如果重写 toString()办法后 间接调用 toString()办法就能够返回咱们自定义的该类转成字符串类型的内容输入,而不须要每次都手动的拼凑成字符串内容输入,大大简化输入操作。
equals()办法次要比拟两个对象是否相等,因为对象的相等不肯定非要严格要求两个对象地址上的雷同,有时内容上的雷同咱们就会认为它相等,比方 String 类就重写了 euqals() 办法,通过字符串的内容比拟是否相等。
向上转型
向上转型 : 通过子类对象(小范畴) 实例化父类对象(大范畴),这种属于主动转换。用一张图就能很好地示意向上转型的逻辑:
父类援用变量指向子类对象后,只能应用父类已申明的办法,但办法如果被重写会执行子类的办法,如果办法未被重写那么将执行父类的办法。
向下转型
向下转型 : 通过父类对象(大范畴) 实例化子类对象 (小范畴),在书写上父类对象须要加括号()
强制转换为子类类型。但父类援用变量理论援用必须是子类对象能力胜利转型,这里也用一张图就能很好示意向上转型的逻辑:
子类援用变量指向父类援用变量指向的对象后 (一个 Son() 对象),就实现向下转型,就能够调用一些子类特有而父类没有的办法。
在这里写一个向上转型和向下转型的案例:
Object object=new Integer(666);// 向上转型
Integer i=(Integer)object;// 向下转型 Object->Integer,object 的本质还是指向 Integer
String str=(String)object;// 谬误的向下转型,尽管编译器不会报错然而运行会报错
子父类初始化程序
在 Java 继承中,父子类初始化先后顺序为:
- 父类中动态成员变量和动态代码块
- 子类中动态成员变量和动态代码块
- 父类中一般成员变量和代码块,父类的构造函数
- 子类中一般成员变量和代码块,子类的构造函数
总的来说,就是 动态 > 非动态,父类 > 子类,非构造函数 > 构造函数。同一类别(例如一般变量和一般代码块)成员变量和代码块执行从前到后,须要留神逻辑。
这个也不难理解,动态变量也称类变量,能够看成一个全局变量,动态成员变量和动态代码块在类加载的时候就初始化,而非动态变量和代码块在对象创立的时候初始化。所以动态快于非动态初始化。
而在创立子类对象的时候须要先创立父类对象,所以父类优先于子类。
而在调用构造函数的时候,是对成员变量进行一些初始化操作,所以一般成员变量和代码块优于构造函数执行。
至于更深层次为什么这个程序,就要更深刻理解 JVM 执行流程啦。上面一个测试代码为:
class Father{public Father() {System.out.println(++b1+"父类构造方法");
}// 父类构造方法 第四
static int a1=0;// 父类 static 第一 留神程序
static {System.out.println(++a1+"父类 static");
}
int b1=a1;// 父类成员变量和代码块 第三
{System.out.println(++b1+"父类代码块");
}
}
class Son extends Father{public Son() {System.out.println(++b2+"子类构造方法");
}// 子类构造方法 第六
static {// 子类 static 第二步
System.out.println(++a1+"子类 static");
}
int b2=b1;// 子类成员变量和代码块 第五
{System.out.println(++b2 + "子类代码块");
}
}
public class test9 {public static void main(String[] args) {Son son=new Son();
}
}
执行后果:
结语
好啦,本次继承就介绍到这里啦,Java 面向对象三大特色之一继承——优良的你曾经把握。再看看 Java 面向对象三大个性:封装、继承、多态。最初问你能大抵理解它们的特色嘛?
封装:是对类的封装,封装是对类的属性和办法进行封装,只对外裸露办法而不裸露具体应用细节,所以咱们个别设计类成员变量时候大多设为公有而通过一些 get、set 办法去读写。
继承:子类继承父类,即“子承父业”,子类领有父类除公有的所有属性和办法,本人还能在此基础上拓展本人新的属性和办法。次要目标是 复用代码。
多态:多态是同一个行为具备多个不同表现形式或状态的能力。即一个父类可能有若干子类,各子类实现父类办法有多种多样,调用父类办法时,父类援用变量指向不同子类实例而执行不同办法,这就是所谓父类办法是多态的。
最初送你一张图捋一捋其中的关系吧。
原创不易,bigsai 请你帮两件事帮忙一下:
- 一键三连 反对一下,您的必定是我在平台创作的源源能源。
- 微信搜寻「bigsai」,关注我的公众号 ( 原创干货博主),不仅免费送你电子书,我还会第一工夫在公众号分享常识技术。加我还可拉你进力扣打卡群一起打卡 LeetCode。
- 最近在将历史文章和图解整顿成电子书,行将第一工夫给大家收费放送。