九、 Java程序初始化的顺序(二)
之前的一篇博客里我写了关于在一个类中的程序初始化顺序,但是在Java的面向对象里,类之间还存在着继承的关系。所以关于程序的初始化顺序,我们可以再细划分为:父类静态变量,父类的静态代码块,父类构造器,父类非静态变量,父类非静态代码块,子类静态变量,子类静态代码块,子类构造器,子类非静态成员变量和子类非静态代码块。
本篇博客我们讨论的就是关于程序初始化的过程中,上述的成员在初始化加载先后顺序。 在此前我们讨论得出的结论:在一个类中,Java程序加载的顺序是:静态变量-->静态代码块-->非静态变量-->非静态代码块-->构造器. 父类的代码:
public class SuperClass {
//父类与子类都在一个包中,这里我们就使用default修饰符
//这是一个父类的静态变量,此时还是初始化的默认值null
static String superStaticVariale;
// 静态代码块,给String赋值
static {
superStaticVariale = "父类静态代码块赋值成功";
System.out.println("此时运行的是父类的静态代码块:"+superStaticVariale);
}
// 无参构造,覆盖静态代码块的值
SuperClass(){
superStaticVariale = "父类构造器赋值成功";
System.out.println("此时运行的是父类的构造器:"+superStaticVariale);
}
//定义一个非静态变量
String superVariale;
// 定义一个非静态代码块
{
superVariale = "父类非静态代码块赋值";
System.out.println("此时运行的是父类的非静态代码块:"+superVariale);
}
}
子类的代码:
public class SubClass extends SuperClass{
static String subStaticVariale;
// 静态代码块,给String赋值
static {
subStaticVariale = "子类静态代码块赋值成功";
System.out.println("此时运行的是子类的静态代码块:"+subStaticVariale);
}
// 无参构造,覆盖静态代码块的值
SubClass(){
superStaticVariale = "子类构造器赋值成功";
System.out.println("此时运行的是子类的构造器:"+superStaticVariale);
}
//定义一个非静态变量
String subVariale;
// 定义一个非静态代码块
{
subVariale = "子类非静态代码块赋值";
System.out.println("此时运行的是子类非静态代码块:"+subVariale);
}
}
测试代码:
public class Main {
public static void main(String[] args) {
SubClass s = new SubClass();
}
}
运行结果:
```
此时运行的是父类的静态代码块:父类静态代码块赋值成功
此时运行的是子类的静态代码块:子类静态代码块赋值成功
此时运行的是父类的非静态代码块:父类非静态代码块赋值
此时运行的是父类的构造器:父类构造器赋值成功
此时运行的是子类非静态代码块:子类非静态代码块赋值
此时运行的是子类的构造器:子类构造器赋值成功
```
很显然,在继承关系中,代码的加载顺序是:父类的静态变量-->父类的静态代码块-->子类静态变量-->子类的静态代码块-->父类非静态变量-->父类的非静态代码块-->父类的构造器-->子类非静态变量-->子类非静态代码块-->子类构造器 进一步测试:
public class Main {
public static void main(String[] args) {
SubClass s = new SubClass();
SubClass s1 = new SubClass();
SubClass s2 = new SubClass();
}
}
运行结果:
```
此时运行的是父类的静态代码块:父类静态代码块赋值成功
此时运行的是子类的静态代码块:子类静态代码块赋值成功
此时运行的是父类的非静态代码块:父类非静态代码块赋值
此时运行的是父类的构造器:父类构造器赋值成功
此时运行的是子类非静态代码块:子类非静态代码块赋值
此时运行的是子类的构造器:子类构造器赋值成功
此时运行的是父类的非静态代码块:父类非静态代码块赋值
此时运行的是父类的构造器:父类构造器赋值成功
此时运行的是子类非静态代码块:子类非静态代码块赋值
此时运行的是子类的构造器:子类构造器赋值成功
此时运行的是父类的非静态代码块:父类非静态代码块赋值
此时运行的是父类的构造器:父类构造器赋值成功
此时运行的是子类非静态代码块:子类非静态代码块赋值
此时运行的是子类的构造器:子类构造器赋值成功
```
得出结论:
父类与子类的静态代码都只执行一次,然后非静态代码块与构造器是组合出现的。 简化一下代码:
public class Main {
public static void main(String[] args) {
C c= new C();
}
}
class A{
A(){
System.out.println("A的无参构造器");
}
}
class B extends A{
// B(int a){
B(){
System.out.println("B的无参构造器");
}
}
class C extends B{
C(){
System.out.println("C的无参构造器");
}
}
运行结果:
```text
A的无参构造器
B的无参构造器
C的无参构造器
```
调用C的构造器生成C的实例对象会从最上级的父类的无参构造器开始逐层调用,那么我们的类都继承了一个超级父类Object,也就是在我们最初的错误代码中,我们调用Student的无参构造创建一个对象时,首先会调用这个对象的父类Object的无参构造器,
class Student{
String name;
{
name = "老大";
}
Student(){
this(name);//这样会报错
super();
System.out.println("题目要求写一个无参的构造器");
}
Student(String name){
this.name = name;
System.out.println(name);
}
}
子类实例化默认调用父类的无参构造器,也就是如上this调用在super()之前(实际中这两者不会同时出现),name此时是非静态属性,此时会报错错误: 无法在调用超类型构造器之前引用name。
class Student{
static String name;
{
name = "老大";
}
Student(){
this(name);
System.out.println("题目要求写一个无参的构造器");
}
Student(String name){
this.name = name;
System.out.println(name);
}
}
当name是静态属性时,代码块是非静态时,编译通过,调用子类的无参构造器时this(name),输出结果是:
```text
null
题目要求写一个无参的构造器
```
此时的this()调用实参构造并没有赋值成功。
class Student{
static String name;
static{
name = "老大";
}
Student(){
this(name);
System.out.println("题目要求写一个无参的构造器");
}
Student(String name){
this.name = name;
System.out.println(name);
}
}
此时运行结果:
```text
老大
题目要求写一个无参的构造器
```
这样赋值成功。由此证明我们的结论是正确的,this()是在子类父类构造器之前进行的操作super(),当子类代码块是非静态时,子类非静态代码块会在执行父类构造器之后执行,所以this(name)时name还没有被赋值,所以打印是null。 结论:
1. 一个类中可以在无参构造器中调用此类的有参构造器(顺序反过来);
2. 在执行子类的无参构造器时会默认调用最高级父类无参构造,并逐级调用直至子类的无参构造;
3. Java程序的加载顺为父类的静态变量-->父类的静态代码块-->子类静态变量-->子类的静态代码块-->父类非静态变量-->父类的非静态代码块-->父类的构造器-->子类非静态变量-->子类非静态代码块-->子类构造器,且静态变量或代码块无论构造器调用多少次,他只会执行一次,后面再调用构造器则会执行非静态属性及代码块构造器。 最后关于为什么子类会调用父类的构造器,这个从设计着的角度来看是为了给从父类继承的属性初始化,子类需要知道父类是如何给属性初始化的。
九、 Java程序初始化的顺序(二)的更多相关文章
- Java程序初始化的顺序
Java程序初始化的顺序 java程序初始化工作可以在许多不同的代码块中来完成(例如:静态代码块.构造函数等),他们执行的顺序如下: 父类静态变量 父类静态代码块 子类静态变量 子类静态代码块 父类非 ...
- 《Java程序员面试笔试宝典》之Java程序初始化的顺序是怎样的
在Java语言中,当实例化对象时,对象所在类的所有成员变量首先要进行初始化,只有当所有类成员完成初始化后,才会调用对象所在类的构造函数创建对象. Java程序的初始化一般遵循以下三个原则(以下三原则优 ...
- 《Java程序猿面试笔试宝典》之Java程序初始化的顺序是如何的
在Java语言中.当实例化对象时.对象所在类的全部成员变量首先要进行初始化,仅仅有当全部类成员完毕初始化后,才会调用对象所在类的构造函数创建对象. Java程序的初始化一般遵循以下三个原则(以下 ...
- 八、 Java程序初始化的顺序(一)
今天在写构造器方法的时候,遇到了一个小问题,由这个问题引发了一连串的思考,在一个Java类中变量与类的初始化执行顺序是什么样的呢?## 发现问题 class Student{ private Stri ...
- java程序初始化顺序
使用场景: 在java程序中,当实例化对象时,对象的所在类的所有成员变量首先要进行初始化,只有当所有类成员完成初始化后, 才会调用对象所在类的构造函数创建对象. 初始化的原则: (1)静态对象优先于 ...
- Java中程序初始化的顺序
1,在一个类的内部(不考虑它是另一个类的派生类):很多人认为,类的成员变量是在构造方法调用之后再初始化的,先不考虑这种观点的正确性,先看一下下面的代码: class Test01...{ public ...
- Java的初始化执行顺序(父类static变量->子类static变量->父类成员变量->父类构造器->成员变量->构造器->main函数)
1. 引言 了解Java初始化的顺序,有助于理解Java的初始化机制和内存机制. 顺序:父类static变量->子类static变量->父类成员变量->父类构造器->成员变量- ...
- 实现Java程序跨平台运行十二个注意事项
[转自] http://blog.chinaunix.net/uid-20550186-id-1927257.html 使用Java语言编写应用程序最大的优点在于"一次编译,处处运行&quo ...
- JAVA学习第十九课(java程序的异常处理 (二))
异常处理的捕捉形式: 这是能够对异常进行针对性处理的方式 六.try.catch的理解 详细格式: try { //须要被检測异常的代码 } catch(异常类 变量)//改变量用于接受发生异常的对象 ...
随机推荐
- HDU 5971 二分图判定
Wrestling Match Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)T ...
- mac terminal基本命令
文件目录 首先要清楚几个文件目录: " / " :根目录 " ~ " :用户主目录的缩写.例如当前用户为esther,那么" ~ "展开来 ...
- jni 调用
Event 0 on null Unexpected event 0 on /storage/emulated/0/Books/null
- Java中BigInteger类型
BigInteger是java.math包提供的处理大整数类型,实现了大整数的存储,四则运算,判断素数的方法,求幂,求模,求逆元,求最大公约数等方法.本文主要分析下BigInteger对于大整数的存储 ...
- 极简配置phpstorm+xdebug进行断点调试
以前调试的时候各种var_dump()就能得到结果,现在入手别人开发的工作,由于不了解业务和代码逻辑,又要去修改bug,就造成了修改bug效率低,所以又拾起来了xdbug,顺便总结了一下phpstor ...
- Migrate a Domain-based Namespace to Windows Server 2008 Mode
TechNet Library Scripting with Windows PowerShell Windows and Windows Server Automation with Windows ...
- Bat windows 批处理 常用命令
设置全屏: To make all bat files fullscreen: reg add HKCU\Console\ /v Fullscreen /t REG_DWORD /d /f To ma ...
- leetcode NO.349 两个数组的交集 (python实现)
来源 https://leetcode-cn.com/problems/intersection-of-two-arrays/ 题目描述 给定两个数组,写一个函数来计算它们的交集. 例子: 给定 nu ...
- sql server 韩文查询匹配失败
在SQL Server 中查询韩文信息时,没有匹配到对应的信息,检查程序后发现字段类型是nvarchar类型的没有问题, 打开存储过程后找到问题了:原来是拼接后的查询语句存储在一个varchar变量中 ...
- Struts2 改变语言状态
只要在请求中增加 request_locale=en_US 参数,就可以实现语言的切换,内部由拦截器实现