Java中类加载过程全面解析

类文件加载的顺序

1、先加载执行父类的静态变量及静态初始化块(执行先后顺序按排列的先后顺序)

2、再加载执行本类的静态变量及静态初始化块

只要类没有被销毁,静态变量及静态初始化块只会执行1次,后续再对该类进行其他操作也不会再执行这两个步骤。

类实例创建过程

只有在调用new方法时才会创建类的实例

1、按照上面类文件加载的顺序(类已被加载则跳过此步)

2、父类的非静态变量及非静态初始化块

3、父类的构造方法

4、本类的非静态变量及非静态初始化块

5、本类的构造方法

4、类实例销毁时候,首先销毁子类部分,再销毁父类部分

静态方法和非静态方法都是被动调用

即系统不会自动调用执行。所以用户没有调用时都不执行,主要区别在于静态方法可以直接用类名直接调用(实例化对象也可以),而非静态方法只能先实例化对象后才能调用。

相关概念

static关键字:

是一个修饰符,用于修饰成员(成员变量和成员函数)

被修饰后的成员具备以下特点:

随着类的加载而加载(类一加载,静态数据就立即在内存中加载空间)

随着类的消失而消失,说明它的生命周期最长

优先于对象存在(对象消失了,static还在)

静态先存在,对象后存在

被所有对象所共享

节约内存空间

当成员被静态修饰后,除了可以被对象调用外,还可以直接被类名调用

写法:类名.静态成员

使用注意

静态方法只能访问静态成员(方法和变量)

非静态方法既可以访问静态也可以访问非静态

静态方法中不可以写this,super关键字

因为静态优先于对象存在,所以静态方法中不可以出现this

主函数是静态的

publicstaticvoidmain(String[]args){}

何时使用静态?

要从两方面下手:因为静态修饰的内容有成员变量和函数。

何时定义静态变量(类变量)

当对象中出现共享数据时,该数据被静态所修饰。对象中的特有数据要定义成非静态存在于堆内存中。

何时定义静态函数

当功能内部没有访问到非静态数据(对象特有数据),该功能可以定义成静态。

静态利弊

利:

1、对对象的共享数据单独空间的存储,节省空间。没有必要每个对象都存储一份。

2、可以直接被类名调用

弊:

1、生命周期过长

2、访问出现局限性(只能访问静态)

内存结构

Java程序在运行时,需要在内存中的分配空间。为了提高运算效率,有对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方式。

栈内存

用于存储局部变量当数据使用完,所占空间会自动释放

堆内存

数组和对象(实体),通过new建立的实例都存放在堆内存中(成员变量随着对象的建立而建立,存在于对象所在的堆内存中)每一个实体都有内存地址值(变量通过地址引用)实体中的变量都有默认初始化值实体不再被使用,会在不确定的时间内被垃圾回收器回收(垃圾回收机制)

方法区,本地方法区,寄存器

验证

加载顺序 父类静态变量=1 父类非静态变量=1 子类静态变量=1 子类非静态变量=1
【父类调用父类静态方法】 Parent.pStaticMethod();      
父类静态初始化块一 2      
父类静态初始化块二 3      
父类静态方法 4      
【子类调用子类静态方法】 Child.cStaticMethod();      
子类静态初始化块一 5   2  
子类静态初始化块二 6   3  
子类静态方法 7   4  
【子类实例化】 Child c=new Child();      
父类非静态初始化块一 8 2    
父类非静态初始化块二 9 3    
父类构造方法 10 4    
子类非静态初始化块一 11 5 5 2
子类非静态初始化块二 12 6 6 3
子类构造方法 13 7 7 4
【父类实例化子类对象】 Parent p=new Child();      
父类非静态初始化块一 14 2    
父类非静态初始化块二 15 3    
父类构造方法 16 4    
子类非静态初始化块一 17 5 8 2
子类非静态初始化块二 18 6 9 3
子类构造方法 19 7 10 4
加载顺序 父类静态变量=1 父类非静态变量=1 子类静态变量=1 子类非静态变量=1
【子类实例化】 Child c=new Child();      
父类静态初始化块一 2      
父类静态初始化块二 3      
子类静态初始化块一 4   2  
子类静态初始化块二 5   3  
父类非静态初始化块一 6 2    
父类非静态初始化块二 7 3    
父类构造方法 8 4    
子类非静态初始化块一 9 5 4 2
子类非静态初始化块二 10 6 5 3
子类构造方法 11 7 6 4
【父类实例化子类对象】 Parent p=new Child();      
父类非静态初始化块一 12 2    
父类非静态初始化块二 13 3    
父类构造方法 14 4    
子类非静态初始化块一 15 5 7 2
子类非静态初始化块二 16 6 8 3
子类构造方法 17 7 9 4
【父类调用父类静态方法】 Parent.pStaticMethod();      
父类静态方法 18      
【子类调用子类静态方法】 Child.cStaticMethod();      
子类静态方法 19   10  
public class ClassTest {
  public static void main (String args[]) {
    System.out.println("【子类实例化】|Child c=new Child();");
    Child c=new Child();
    System.out.println("【父类实例化子类对象】|Parent p=new Child();");
    Parent p=new Child();
    System.out.println("【父类调用父类静态方法】|Parent.pStaticMethod();");
    Parent.pStaticMethod();
    System.out.println("【子类调用子类静态方法】|Child.cStaticMethod();");
    Child.cStaticMethod();
  }
}
public class ClassTest2 {
  public static void main (String args[]) {
    System.out.println("【父类调用父类静态方法】|Parent.pStaticMethod();");
    Parent.pStaticMethod();
    System.out.println("【子类调用子类静态方法】|Child.cStaticMethod();");
    Child.cStaticMethod();
    System.out.println("【子类实例化】|Child c=new Child();");
    Child c=new Child();
    System.out.println("【父类实例化子类对象】|Parent p=new Child();");
    Parent p=new Child();
  }
}
public class Parent {
  // 父类静态变量
  static int m = 1;
  // 父类非静态变量
  int n = 1;

  // 静态语句块1
  static {
    m++;
    // j++; 父类非静态变量不能在静态语句块中使用
    System.out.println("父类静态初始化块一|" + m);
  }
  // 静态语句块2
  static {
    m++;
    System.out.println("父类静态初始化块二|" + m);
  }

  // 构造函数
  public Parent() {
    m++;
    n++;
    System.out.println("父类构造方法|" + m + "|" + n);
  }

  // 非静态语句块
  {
    m++;
    n++;
    System.out.println("父类非静态初始化块一|" + m + "|" + n);
  }

  // 非静态语句块
  {
    m++;
    n++;
    System.out.println("父类非静态初始化块二|" + m + "|" + n);
  }

  // 非静态方法
  public void pMethod() {
    m++;
    n++;
    System.out.println("父类非静态方法|" + m + "|" + n);
    return;
  }

  // 静态方法
  public static void pStaticMethod() {
    m++;
//   j++; 父类非静态变量不能在静态方法中使用
    System.out.println("父类静态方法|" + m);
    return;
  }

  @Override
  protected void finalize() throws Throwable {
    super.finalize();
    System.out.println("销毁父类|");
  }
}
public class Child extends Parent {
  // 静态变量
  static int i = 1;
  // 非静态变量
  int j = 1;

  // 静态语句块1
  static {
    m++;
    i++;
    // j++; 非静态变量不能在静态语句块中使用
    System.out.println("子类静态初始化块一 " + "|" + m + "||" + i);
  }
  // 静态语句块2
  static {
    m++;
    i++;
    System.out.println("子类静态初始化块二 " + "|" + m + "||" + i);
  }

  // 构造函数
  public Child() {
    m++;
    n++;
    i++;
    j++;
    System.out.println("子类构造方法 " + "|" + m + "|" + n + "|" + i + "|" + j);
  }

  // 非静态语句块
  {
    m++;
    n++;
    i++;
    j++;
    System.out.println("子类非静态初始化块一" + "|" + m + "|" + n + "|" + i + "|" + j);
  }
  // 非静态语句块
  {
    m++;
    n++;
    i++;
    j++;
    System.out.println("子类非静态初始化块二" + "|" + m + "|" + n + "|" + i + "|" + j);
  }

  // 非静态方法
  public void pMethod() {
    m++;
    n++;
    i++;
    j++;
    System.out.println("子类继承非静态方法" + "|" + m + "|" + n + "|" + i + "|" + j);
    return;
  }

  // 静态方法
  public static void pStaticMethod() {// 静态方法不能被继承
    m++;
    i++;
    // j++; 非静态变量不能在静态方法中使用
    return;
  }

  // 非静态方法
  public void cMethod() {
    m++;
    n++;
    i++;
    j++;
    System.out.println("子类非静态方法" + "|" + m + "|" + n + "|" + i + "|" + j);
    return;
  }

  // 静态方法
  public static void cStaticMethod() {
    m++;
    i++;
    // j++; 非静态变量不能在静态方法中使用
    System.out.println("子类静态方法 " + "|" + m + "||" + i);
    return;
  }

  @Override
  protected void finalize() throws Throwable {
    super.finalize();
    System.out.println("销毁子类|");
  }
}

总结

以上就是本文关于Java中类加载过程全面解析的全部内容,希望对大家有所帮助。如有问题可以随时留言,小编会及时回复大家的。期待您的宝贵意见。

(0)

相关推荐

  • java 详解类加载器的双亲委派及打破双亲委派

    java 详解类加载器的双亲委派及打破双亲委派 一般的场景中使用Java默认的类加载器即可,但有时为了达到某种目的又不得不实现自己的类加载器,例如为了达到类库的互相隔离,例如为了达到热部署重加载功能.这时就需要自己定义类加载器,每个类加载器加载各自的类库资源,以此达到资源隔离效果.在对资源的加载上可以沿用双亲委派机制,也可以打破双亲委派机制. 一.沿用双亲委派机制自定义类加载器很简单,只需继承ClassLoader类并重写findClass方法即可.如下例子: ①先定义一个待加载的类Test,它

  • 详解Java中类的加载顺序

    本文介绍的是Java中类的加载顺序,下面来看看详细的介绍: 1.虚拟机在首次加载Java类时,会对静态初始化块.静态成员变量.静态方法进行一次初始化 2.只有在调用new方法时才会创建类的实例 3.类实例创建过程:按照父子继承关系进行初始化,首先执行父类的初始化块部分,然后是父类的构造方法:再执行本类继承的子类的初始化块,最后是子类的构造方法 4.类实例销毁时候,首先销毁子类部分,再销毁父类部分 示例 public class Parent { public static int t = par

  • 深入理解Java 类加载全过程

    Java类加载全过程 一个java文件从被加载到被卸载这个生命过程,总共要经历4个阶段: 加载->链接(验证+准备+解析)->初始化(使用前的准备)->使用->卸载 其中加载(除了自定义加载)+链接的过程是完全由jvm负责的,什么时候要对类进行初始化工作(加载+链接在此之前已经完成了),jvm有严格的规定(四种情况): 1.遇到new,getstatic,putstatic,invokestatic这4条字节码指令时,加入类还没进行初始化,则马上对其进行初始化工作.其实就是3种情况

  • 浅谈Java 类中各成分加载顺序和内存中的存放位置

    一.什么时候会加载类? 使用到类中的内容时加载:有三种情况 1.创建对象:new StaticCode(); 2.使用类中的静态成员:StaticCode.num=9;  StaticCode.show(); 3.在命令行中运行:java StaticCodeDemo 二.类所有内容加载顺序和内存中的存放位置 利用语句进行分析: 1.Person p=new Person("zhangsan",20); 该句话所做的事情: 1.在栈内存中,开辟main函数的空间,建立main函数的变量

  • java 类加载与自定义类加载器详解

    类加载 所有类加载器,都是ClassLoader的子类. 类加载器永远以.class运行的目录为准. 读取classpath根目录下的文件有以下几种方式: 1 在Java项目中可以通过以下方式获取classspath下的文件: public void abc(){ //每一种读取方法,使用某个类获取Appclassloader ClassLoader cl = ReadFile.class.getClassLoader(); URL url = cl.getResource("a.txt&quo

  • Java类加载基本过程详细介绍

    Java类加载基本过程详细介绍 基本过程: 根据类的全限定名称加载定义类的二进制字节流. 将字节流代表的静态存储结构转化为方法区的运行时数据结构 内存中生成一个代表这个类的java.lang.Class对象,作为方法去这个类的各种数据访问入口 数组类本身不通过类加载器创建,由java虚拟机直接创建,数组类的元素类型由类加载器加载. 数组类的元素类型:数组去掉所有维度后的类型, 文件格式验证: 0xCAFEBABY 魔数开头: 主次版本号当前虚拟机可处理: 常量类型: 索引执行类型: utf8编码

  • Java中类加载过程全面解析

    类文件加载的顺序 1.先加载执行父类的静态变量及静态初始化块(执行先后顺序按排列的先后顺序) 2.再加载执行本类的静态变量及静态初始化块 只要类没有被销毁,静态变量及静态初始化块只会执行1次,后续再对该类进行其他操作也不会再执行这两个步骤. 类实例创建过程 只有在调用new方法时才会创建类的实例 1.按照上面类文件加载的顺序(类已被加载则跳过此步) 2.父类的非静态变量及非静态初始化块 3.父类的构造方法 4.本类的非静态变量及非静态初始化块 5.本类的构造方法 4.类实例销毁时候,首先销毁子类

  • java中类加载与双亲委派机制详解

    目录 类加载是什么 类加载器 双亲委派机制 BootStrapClassLoader ExtClassLoader AppClassLoader 为什么使用双亲委派机制 全盘负责委托机制 自定义类加载器 打破双亲委派机制 类加载是什么 把磁盘中的java文件加载到内存中的过程叫做类加载 当我们用java命令运行某个类的main函数启动程序时,首先需要通过类加载器把主类加载到JVM. 有如下 User 类 package dc.dccmmtop; public Class User { publi

  • Java中LinkedList原理代码解析

    本文研究的主要是Java中LinkedList原理的相关内容,具体介绍如下. 一句话概括,Java中的LinkedList其实就是使用双向链表,LinkedList的基本操作就是对双向链表的操作. 上面可以清晰的看出,链表中每个元素对应一个节点,节点里面包含三部分,一个是前一个节点的引用,一个是元素内容,一个是后一个节点的引用. 向链表中添加元素的过程就是在链表尾部追加一个节点 void linkLast(E e) { final Node<E> l = last; final Node<

  • GC参考手册二java中垃圾回收原理解析

    内存碎片整理 每次执行清除(sweeping), JVM 都必须保证不可达对象占用的内存能被回收重用.但这(最终)有可能会产生内存碎片(类似于磁盘碎片), 进而引发两个问题: 写入操作越来越耗时, 因为寻找一块足够大的空闲内存会变得非常麻烦. 在创建新对象时, JVM在连续的块中分配内存.如果碎片问题很严重, 直至没有空闲片段能存放下新创建的对象,就会发生内存分配错误(allocation error). 要避免这类问题,JVM 必须确保碎片问题不失控.因此在垃圾收集过程中, 不仅仅是标记和清除

  • Java中后台线程实例解析

    本文研究的主要是Java中后台线程的相关问题,具体介绍如下. 以前从来没有听说过,java中有后台线程这种东西.一般来说,JVM(JAVA虚拟机)中一般会包括俩种线程,分别是用户线程和后台线程.所谓后台线程(daemon)线程指的是:在程序运行的时候在后台提供的一种通用的服务的线程,并且这种线程并不属于程序中不可或缺的部分.因此,当所有的非后台线程结束的时候,也就是用户线程都结束的时候,程序也就终止了.同时,会杀死进程中的所有的后台线程.反过来说,只要有任何非后台线程还在运行,程序就不会结束.不

  • java中fork-join的原理解析

    ForkJoinTask就是ForkJoinPool里面的每一个任务.他主要有两个子类:RecursiveAction和RecursiveTask.然后通过fork()方法去分配任务执行任务,通过join()方法汇总任务结果, 这就是整个过程的运用.他有两个子类,使用这两个子类都可以实现我们的任务分配和计算. (1)RecursiveAction 一个递归无结果的ForkJoinTask(没有返回值) (2)RecursiveTask 一个递归有结果的ForkJoinTask(有返回值) For

  • java中CopyOnWriteArrayList源码解析

    目录 简介 继承体系 源码解析 属性 构造方法 add(Ee)方法 add(intindex,Eelement)方法 addIfAbsent(Ee)方法 get(intindex) remove(intindex)方法 size()方法 提问 总结 简介 CopyOnWriteArrayList是ArrayList的线程安全版本,内部也是通过数组实现,每次对数组的修改都完全拷贝一份新的数组来修改,修改完了再替换掉老数组,这样保证了只阻塞写操作,不阻塞读操作,实现读写分离. 继承体系 public

  • java中VO的使用解析

    目录 java中VO的使用 场景 java里VO是什么 1.PO:persistant object 持久对象 2.VO:value object值对象 java中VO的使用 场景 现在我们需要从数据库中查询用户列表t_user,对应的实体类如下: import io.swagger.annotations.ApiModelProperty; public class User { @ApiModelProperty(value = "用户id") private String use

  • Java中Synchronized的用法解析

    简单介绍 synchronized是Java中的关键字,是一种同步锁.它修饰的对象有以下几种: 1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象: 2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象: 3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象: 4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对

  • Java中对XML的解析详解

    先简单说下前三种方式: DOM方式:个人理解类似.net的XmlDocument,解析的时候效率不高,占用内存,不适合大XML的解析: SAX方式:基于事件的解析,当解析到xml的某个部分的时候,会触发特定事件,可以在自定义的解析类中定义当事件触发时要做得事情:个人感觉一种很另类的方式,不知道.Net体系下是否有没有类似的方式? StAX方式:个人理解类似.net的XmlReader方式,效率高,占用内存少,适用大XML的解析: 不过SAX方式之前也用过,本文主要介绍JAXB,这里只贴下主要代码

随机推荐