文档章节

Java类的初始化

浮躁的码农
 浮躁的码农
发布于 2016/04/19 10:11
字数 1635
阅读 39
收藏 3
点赞 1
评论 0

我们知道一个类(class)要被使用必须经过装载,连接,初始化这样的过程。下面先对这三阶段做一个简单的描述,之后会结合一个简单的例子来说明java中类的初始化过程。

在装载阶段,类装载器(Bootstrap ClassLoader 或者用户自定义的ClassLoader) 把编译形成的class文件载入内存,创建类相关的Class对象,这个Class对象封装了我们要使用的类的类型信息。

连接阶段又可以分为三个子步骤:验证、准备和解析。

验证就是要确保java类型数据格式 的正确性,并适于JVM使用。

准备阶段,JVM为静态变量分配内存空间,并设置默认值,注意,这里是设置默认值,比如说int型的变量会被赋予默认值0 。在这个阶段,JVM可能还会为一些数据结构分配内存,目的 是提高运行程序的性能,比如说方法表。

解析过程就是在类型的常量池中寻找类、接口、字段和方法的符号引用,把这些符号引用替换成直接引用。这个阶段可以被推迟到初始化之后,当程序运行的过程中真正使用某个符号引用的时候 再去解析它。

类会在首次被“主动使用”时执行初始化,为类(静态)变量赋予正确的初始值。在Java代码中,一个正确的初始值是通过类变量初始化语句或者静态初始化块给出的。而我们这里所说的主动使用 包括:

1.        创建类的实例

2.        调用类的静态方法

3.        使用类的非常量静态字段

4.        调用Java API中的某些反射方法

5.        初始化某个类的子类

6.        含有main()方法的类启动时

 

初始化一个类包括两个步骤:

1、  如果类存在直接父类的话,且直接父类还没有被初始化,则先初始化其直接父类

2、  如果类存在一个初始化方法,就执行此方法

注:初始化接口并不需要初始化它的父接口。

 

好,下面我们通过一些简单的例子来重点看一下初始化的过程 。

好,先上代码(超级简单的代码)。

package kevin.demo;
 
class Base {
    int a;
    static int b;
    static int c = 1;
 
    static {
       b=2;
       c = 3;
       System.out.println("Base:static init block invoked..");
    }
 
    void display() {
       System.out.println("Base:a=" + a + ",b=" + b + ",c=" + c);
    }
 
}
 
class Derived extends Base {
    int d;
    static int e = 4;
 
    static {
       e= 5;
       System.out.println("Derived:static init block invoked...");
    }
 
    void display() {
       super.display();
       System.out.println("Derived:d=" + d + ",e=" + e);
    }
}


 

好,这里我们定义了一个简单的基类Base,然后定义了一个类Derived继承它。这个已经简单到我不知道说什么了,直接上main方法吧。

 

public class Demo {
    public static void main(String[] args) {
       new Derived().display();
    }
}

 

好,我们执行这个方法,看会输出什么。。上个图看一下:

如图所示,虽然我们调用的子类的构造函数创建对象 ,但是父类中的有关初始化的语句和静态初始化块也会被执行或调用,这正与前面所说的:如果一个类的直接父类还没有被初始化,那么 先初始化它的父类。而为什么这些初始化的动作会执行,就是因为我们主动使用了Derived这个类。而我们上面列出了好几个主动使用的情况,好下面我们修改一下main方法,看上面所言是否正确。

注:因为我们在这里谈论的主题是类的初始化,所以关于对象的初始化过程我们这里暂不讨论。


好,看下修改后的Demo:

public class Demo {
    static int f;
    static{
       f=100;
       System.out.println("Demo:static init block invoked..and f="+f);
    }
   
    public static void main(String[] args) {
       System.out.println("maininvoked..");
    }}

好,看执行结果:

好,我们可以看到,含有main函数的类会首先执行初始化。

public class Demo {
    static int f;
    static{
       f=100;
       System.out.println("Demo:static init block invoked..and f="+f);
    }
   
    public static void main(String[] args) {
       System.out.println(Derived.e);
    }
}

下面再看一下,使用Derived的静态变量会不会引起初始化过程。。


果然,这也属于主动使用的一种,所以类执行了初始化,并且这里看的更加清楚 ,含有main方法的类首先被初始化,因为它是第一个被使用的。但要注意,这里的静态就是不能是final的,否则初始化不会执行。

 

我们在main方法中添加一个语句,如下

public static void main(String[] args) {
       System.out.println(Derived.e);
       new Derived().display();
    }

好, 这下看一下这个执行结果,能过这个,我主要 是想证明,类的初始化是只执行一次呢还是每次主动使用类时都执行。。

通过这个截图我们可以看到,我们添加了一条语句 ,但输出结果只添加了两行,调用Derived类的实例的display()方法只是执行了方法中的语句 ,并没有再执行初始化语句,因此我们可以总结出,一个类只有会被初始化一次,除非是它被重新装载。

 

Last but not least,让我们看一下静态嵌套类的初始化情况,它是要被主动使用的时候才初始化呢,还是它的外围类被主动使用的时候 它就会被初始化呢?

为了方便,我们就在Demo类中添加一个静态嵌套类Inner,如下

public class Demo {
    static int f;
    static {
       f = 100;
       System.out.println("Demo:static init block invoked..and f=" + f);
    }
   
    static class Inner{
       static int g ;
       static{
           g=666;
           System.out.println("Inner:static init block invoked..and g="+g);
       }
    }
 
    public static void main(String[] args) {
      
    }
}

看下main执行的结果:

我们可以看到,Inner类的静态初始化块并没有被执行,也就是说它并没有被初始化。好,现在在main函数中加上下面这句再看下执行结果:

System.out.println(Inner.g);

看截图:

这下,很明显,Inner的初始化进行了,所以说,它与外围类没有必然的联系,外围类的初始化并不能导致它被初始化, 要初始化静态嵌套类必须主动使用它,而主动使用其外围类并不等同于主动使用Static Nested Class.

 

好,上面就是我所知道的关于类的初始化的东西了,如果有错误的地方还请高手指点,我及时修改。。

By KevinJom

2011/11/14

本文转载自:http://blog.csdn.net/moreevan/article/details/6968718

共有 人打赏支持
浮躁的码农

浮躁的码农

粉丝 58
博文 714
码字总数 141530
作品 0
松江
程序员
谈java中类的加载、链接和初始化

Java字节代码的表现形式是字节数组(byte[]), 而Java类在JVM中的表现形式是java. lang. Class类的对象。 一个Java类从字节代码到能够在JVM中被运用, 需要经过加载、链接和初始化这三个步骤。...

风一样
2011/08/03
0
0
两道面试题,带你解析Java类加载机制

文章首发于【博客园-陈树义】,点击跳转到原文《两道面试题,带你解析Java类加载机制》 在许多Java面试中,我们经常会看到关于Java类加载机制的考察,例如下面这道题: 请写出最后的输出字符...

陈树义
06/12
0
0
两道面试题带你解析 Java 类加载机制

在许多Java面试中,我们经常会看到关于Java类加载机制的考察,例如下面这道题: class Grandpa{ } class Father extends Grandpa{ }class Son extends Father{ }public class Initialization...

06/13
0
0
JVM-ClassLoader

<谭锋>整理 为了支持跨平台的特性,java语言采用源代码编译成中间字节码,然后又各平台的jvm解释执行的方式。字节码采用了完全与平台无关的方式进行描述,java只给出了字节码格式的规范,并没...

项籍20130121
2013/07/11
0
0
用 JNI 进行 Java 编程(3)

从 C/C++ 程序调用 Java 代码 概述 JNI 允许您从本机代码内调用 Java 类方法。要做到这一点,通常必须使用 Invocation API 在本机代码内创建和初始化一个 JVM。下列是您可能决定从 C/C++ 代码...

Jerikc
2012/10/08
0
0
Java类加载及变量初始化过程

Java虚拟机如何把编译好的.class文件加载到虚拟机里面?加载之后如何初始化类?静态类变量和实例类变量的初始化过程是否相同,分别是如何初始化的呢?这篇文章就是解决上面3个问题的。 本文前...

kalo
2014/09/29
0
0
java百问---从Hello World中你可以学到什么?

public class HelloWorld { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub System.out.println("Hello World"); } } 1.为什么总是由一......

BravoZu
2014/02/07
0
0
从原理来理解继承关系的类初始化和实例化的顺序

就像之前的一个评论.我们学习的是思路. 很多人都知道继承关系的类的初始化和实例化的顺序,但如果忘记了怎么办? 如何找到自己的答案? 又如果遇到的问题是关于泛型的擦除问题,又该如何去分析?...

xpbug
2013/03/03
0
4
14、Java并发性和多线程-Java ThreadLocal

以下内容转自http://ifeve.com/java-theadlocal/: Java中的ThreadLocal类可以让你创建的变量只被同一个线程进行读和写操作。因此,尽管有两个线程同时执行一段相同的代码,而且这段代码又有...

easonjim
2017/06/16
0
0
升级到JDK9的一个BUG,你了解吗

概述 前几天在一个群里看到一个朋友发了一个demo,说是JDK的bug,昨天在JVM的一个群里又有朋友发了,觉得挺有意思,分享给大家,希望大家升级JDK的版本的时候注意下是否存在这样的代码,如果...

你假笨
06/06
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

实现异步有哪些方法

有哪些方法可以实现异步呢? 方式一:java 线程池 示例: @Test public final void test_ThreadPool() throws InterruptedException { ScheduledThreadPoolExecutor scheduledThre......

黄威
今天
0
0
linux服务器修改mtu值优化cpu

一、jumbo frames 相关 1、什么是jumbo frames Jumbo frames 是指比标准Ethernet Frames长的frame,即比1518/1522 bit大的frames,Jumbo frame的大小是每个设备厂商规定的,不属于IEEE标准;...

六库科技
今天
0
0
牛客网刷题

1. 二维数组中的查找(难度:易) 题目描述 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入...

大不了敲一辈子代码
今天
0
0
linux系统的任务计划、服务管理

linux任务计划cron 在linux下,有时候要在我们不在的时候执行一项命令,或启动一个脚本,可以使用任务计划cron功能。 任务计划要用crontab命令完成 选项: -u 指定某个用户,不加-u表示当前用...

黄昏残影
昨天
0
0
设计模式:单例模式

单例模式的定义是确保某个类在任何情况下都只有一个实例,并且需要提供一个全局的访问点供调用者访问该实例的一种模式。 实现以上模式基于以下必须遵守的两点: 1.构造方法私有化 2.提供一个...

人觉非常君
昨天
0
0
《Linux Perf Master》Edition 0.4 发布

在线阅读:https://riboseyim.gitbook.io/perf 在线阅读:https://www.gitbook.com/book/riboseyim/linux-perf-master/details 百度网盘【pdf、mobi、ePub】:https://pan.baidu.com/s/1C20T......

RiboseYim
昨天
1
0
conda 换源

https://mirrors.tuna.tsinghua.edu.cn/help/anaconda/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/conda config --add channels https://mir......

阿豪boy
昨天
1
0
Confluence 6 安装补丁类文件

Atlassian 支持或者 Atlassian 缺陷修复小组可能针对有一些关键问题会提供补丁来解决这些问题,但是这些问题还没有放到下一个更新版本中。这些问题将会使用 Class 类文件同时在官方 Jira bug...

honeymose
昨天
0
0
非常实用的IDEA插件之总结

1、Alibaba Java Coding Guidelines 经过247天的持续研发,阿里巴巴于10月14日在杭州云栖大会上,正式发布众所期待的《阿里巴巴Java开发规约》扫描插件!该插件由阿里巴巴P3C项目组研发。P3C...

Gibbons
昨天
1
0
Tomcat介绍,安装jdk,安装tomcat,配置Tomcat监听80端口

Tomcat介绍 Tomcat是Apache软件基金会(Apache Software Foundation)的Jakarta项目中的一个核心项目,由Apache、Sun和其他一些公司及个人共同开发而成。 java程序写的网站用tomcat+jdk来运行...

TaoXu
昨天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部