摘要:很多时候提到类加载,大家总是没法马上回忆起顺序,这篇文章会用一个例子为你把类加载的诸多问题一次性澄清。

本文分享自华为云社区《用1个例子加5个问题,一次性搞清java中的类加载问题【奔跑吧!JAVA】》,原文作者:breakDraw 。

很多时候提到类加载,大家总是没法马上回忆起顺序,这篇文章会用一个例子为你把类加载的诸多问题一次性澄清。

Java类的加载顺序

引用1个网上的经典例子,并做稍许改动,以便大家更好地理解。

原例子引用自:https://blog.csdn.net/zfx2013/article/details/89453482

public class Animal {private int i = test();private static int j  = method();static {System.out.println("a");}Animal(){System.out.println("b");}{System.out.println("c");}public int test(){System.out.println("d");return 1;}public static int method(){System.out.println("e");return 1;}
}
public class Dog extends Animal{{System.out.println("h");}private int i = test();static {System.out.println("f");}private static int j  = method();Dog(){System.out.println("g");}public int test(){System.out.println("i");return 1;}public static int method(){System.out.println("j");return 1;}public static void main(String[] args) {Dog dog = new Dog();System.out.println();Dog dog1 = new Dog();}
}

执行这段main程序,会输出什么?
答案是
eafjicbhig
icbhig

为了方便大家一个个细节去理解, 我换一种方式去提问。
Q: 什么时候会进行静态变量的赋值和静态代码块的执行?
A:

  • 第一次创建某个类或者某个类的子类的实例
  • 访问类的静态变量、调用类的静态方法
  • 使用反射方法forName
  • 调用主类的main方法(本例子的第一次静态初始化其实属于这个情况,调用了Dog的main方法)
    注: 类初始化只会进行一次, 上面任何一种情况触发后,之后都不会再引起类初始化操作。

Q:初始化某个子类时,也会对父类做静态初始化吗?顺序呢?
A:如果父类之前没有被静态初始化过,那就会进行, 且顺序是先父类再子类。 后面的非静态成员初始化也是如此。
所以会先输出eafj。

Q: 为什么父类的method不会被子类的method重写?
A: 静态方法是类方法,不会被子类重写。毕竟类方法调用时,是必定带上类名的。

Q: 为什么第一个输出的是e而不是a?
A: 因为类变量的显示赋值代码和静态代码块代码按照从上到下的顺序执行。
Animal的静态初始化过程中,method的调用在static代码块之前,所以先输出e再输出a。
而Dog的静态初始化过程中,method的调用在static代码块之后,因此先输出f,再输出j

Q: 没有在子类的构造器中调用super()时,也会进行父类对象的实例化吗?
A: 会的。会自动调用父类的默认构造器。 super()主要是用于需要调用父类的特殊构造器的情况。
因此会先进行Animal的对象实例化,再进行Dog的对象实例化

Q: 构造方法、成员显示赋值、非静态代码块(即输出c和h的那2句)的顺序是什么?
A:

  1. 成员显示赋值、非静态代码块(按定义顺序)
  2. 构造方法
    因此Animal的实例化过程输出icb(如果对输出i有疑问,见下面一题)
    接着进行Dog的实例化,输出hig

Q: 为什么Animal实例化时, i=test()中输出的是i而不是d?
A:因为你真正创建的是Dog子类,Dog子类中的test()方法由于签名和父类test方法一致,因此test方法被重写了。
此时即使在父类中调用,也还是用使用子类Dog的方法。除非你new的是Animal。

Q: 同上题, 如果test方法都是private或者final属性, 那么上题的情况会有变化吗??
A:
因为private和final方法是不能被子类重写的。
所以Animal实例化时,i=test输出d。

总结一下顺序:

  1. 父类静态变量显式赋值、父类静态代码块(按定义顺序)
  2. 子类静态变量显式赋值、子类静态代码块(按定义顺序)
  3. 父类非静态变量显式赋值(父类实例成员变量)、父类非静态代码块(按定义顺序)
  4. 父类构造函数
  5. 子类非静态变量(子类实例成员变量)、子类非静态代码块(按定义顺序)
  6. 子类构造函数。

类加载过程

Q:类加载的3个必经阶段是:
A:

  1. 加载(类加载器读取二进制字节流,生成java类对象)
  2. 链接(验证,分配静态域初始零值)
  3. 初始化(前面的题目讲的其实就是初始化时的顺序)
    更详细的如下:

被动引用中和类静态初始化的关系

Q:new某个类的数组时,会引发类初始化吗?
像下面输出什么

public class Test {static class A{public static int a = 1;static{System.out.println("initA");}}public static void main(String[] args) {A[] as = new A[5];}
}

A:
new数组时,不会引发类初始化。
什么都不输出。

Q:引用类的final静态字段,会引发类初始化吗?
像下面输出什么?

public class Test {static class A{public static final int a = 1;static{System.out.println("initA");}}public static void main(String[] args) {System.out.println("A.a=" + A.a);}
}

A: 不会引发。
不会输出initA。 去掉final就会引发了。
(注意这里必须是基本类型常量, 如果是引用类型产量,则会引发类初始化)

Q:子类引用了父类的静态成员,此时子类会做类初始化嘛?
如下会输出什么

public class Test {static class A{public static int a = 1;static{System.out.println("initA");}}static class B extends A{static {System.out.println("initB");}}public static void main(String[] args) {System.out.println("B.a=" + B.a);}
}

A:
子类不会初始化。
打印initA,却不会打印initB。

类加载器

双亲委派

类加载时的双亲委派模型,不知道能怎么出题。。。反正就记得优先去父类加载器中看类是否能加载。

就贴个图吧:

注意,上面的图有问题。
Bootsrap不是ClassLoader的子类,他是C++编写的。
而ExtClassLoader和AppClassLoader都是继承自ClassLoader的

Q:java中, 是否类和接口的包名和名字相同, 那么就一定是同一个类或者接口?
A:错误。
1个jvm中, 类和接口的唯一性由 二进制名称以及它的定义类加载器 共同决定。
因此2个不同的加载器加载出来相同的类或接口时, 实际上是不同的。

点击关注,第一时间了解华为云新鲜技术~

一次性搞清Java中的类加载问题相关推荐

  1. java中的类加载器有,Java自定义的类加载器,java自定义加载,在java中类加载器有以...

    Java自定义的类加载器,java自定义加载,在java中类加载器有以 在java中类加载器有以下几种java虚拟机自带的加载器 1)根类加载器(Bootstrap,c++实现)2)扩展类加载器(Ex ...

  2. 通过这一篇文章,可以把Java中的类加载器了解的七七八八了

    对于每个开发人员来说,java.lang.ClassNotFoundExcetpion这个异常几乎都遇到过,而追求其该异常的来源的话,就免不了谈一谈Java的类加载器了.本文就基于启动类加载器.扩展类 ...

  3. java中的类加载与双亲委派机制

    类加载是什么 把磁盘中的java文件加载到内存中的过程叫做类加载 当我们用java命令运行某个类的main函数启动程序时,首先需要通过类加载器把主类加载到JVM. 有如下 User 类 package ...

  4. java 查找链表中间元素_如何在Java中一次性查找Java中链表的中间元素

    如何在一次传递中找到LinkedList的中间元素?这是一个 Java 和非Java程序员面试时经常被问到的编程问题.这个问题类似于检查回文或计算阶乘,有时也会要求编写代码.为了回答这个问题,候选人必 ...

  5. 随笔18 java中的类加载器

    类的加载是由类加载器完成的,类加载器包括:根加载器( BootStrap ).扩展加载器( Extension ).系统加载器( System )和用户自定义类加载器( java.lang.Class ...

  6. 20、java中的类加载机制

    1.类加载机制是什么? 类加载机制指的就是jvm将类的信息动态添加到内存并使用的一种机制. 2.那么类加载的具体流程是什么呢? 一般说类加载只有三步:加载.连接和初始化,其中连接包括验证.准备和解析, ...

  7. Java中的类加载机制

    目录 类加载器介绍 JVM类加载过程 类加载器介绍 首先类的加载是由类加载器完成的,类加载器包括:根加载器(Bootstrap).拓展加载器(Extension).系统加载器(System)和用户自定 ...

  8. Java中的类加载器(ClassLoader)及类的加载机制

    类加载器就是用来将class文件加载到内存中的一个java类! 系统默认有三个类加载器! ①BootStrap:这不是java类,是java虚拟机在启动的时候加载的第一个类,这个加载器用来加载核心类, ...

  9. Java反射 - 动态类加载和重载

    可以使用Java在运行时加载和重新加载类,虽然它不像人们希望的那样简单. 本文将解释何时以及如何在Java中加载和重新加载类. ClassLoader Java应用程序中的所有类都使用java.lan ...

最新文章

  1. 大型商业银行数据库连接池选型策略
  2. Note:理解CGI、FastCGI、PHP-CGI和PHP-FPM
  3. HTML5新增了哪些标签
  4. 中常用的数据结构_C语言实现常用数据结构:基本概念(第0篇
  5. 自动 更新SVN目录文件.bat
  6. eclispe修改project Explorer字体大小
  7. 【BIM入门实战】Win11平台上Revit 2018_x64简体中文版图文安装与卸载完整教程
  8. jquery ajax 解决跨域访问问题
  9. 三个数据集与deploy.prototxt
  10. php 非侵入式,Tideways和xhgui打造PHP非侵入式监控平台
  11. 重拾Excel之为什么
  12. Java系统架构的演化之路
  13. Unity2019中文补丁下载
  14. 口令破解:kali字典工具的使用:CUPP、Crunch、Hydra
  15. Nvivo 自动编码语言包
  16. 冰汽朋克侦查机器人_寒霜朋克全系统解析攻略 冰汽时代全法典全科技详解
  17. 三国演义中的十大谎言 VS 程序员十大口头禅
  18. linux下载tkinter模块,Linux升级Python提示Tkinter模块找不到解决
  19. 利用DHT网络原理制作bt采集
  20. Oracle报错1031,dataguard 归档日志报1031错误的一次处理

热门文章

  1. 深度学习笔记(35) 滑动窗口的卷积实现
  2. linux ntfs chkdsk,ntfs格式磁盘提示无法运行chkdsk解决办法
  3. java jdbc代码_javajdbc代码解决
  4. 数据库连接字符串 mysql_数据库连接字符串(MySQL,SQL Server,Oracle)
  5. html5点击事件_lt;dialoggt; | HTML5 对话框标签
  6. 动画函数优化,为任意元素添加任意多个属性
  7. python开发之路---第二章之--函数之匿名函数
  8. 读取数据库并写入excel表中 发送邮件
  9. delete操作字符串会报错吗?
  10. win和mac系统Sublime Text 3配置编译c和c++