未涉及虚拟机

0、<init>与<clinit>的区别

1、类的加载过程

2、类的使用方式

3、类的加载来源

4、重载之泛型参数不同可以吗

5、参考

引子

记得上次中秋一哥们写个需求,没写完。他中秋过后还请一天假,有点错,打电话叫我帮他继续搞。

由于测试支撑,叫到我加班了。第二天过来看,打开页面直接报错,再次点击报错就不一样了。前次报错是有代码行的,第二次直接页面说类发现什么的错。

看了下代码,类似如下:

 package san;

 import java.io.FileNotFoundException;
import java.util.logging.Level;
import java.util.logging.Logger; import javax.xml.bind.JAXBContext;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement; //每个类有一个Log4j的静态日志成员
//这里是单例
public class ClassWithStatic {
private static ClassWithStatic instance = new ClassWithStatic();
private static Logger logger = Logger.getLogger(ClassWithStatic.class.getName()); private ClassWithStatic() {
JAXBContext jc;
try {
//do something that maybe throw IOExption;
throw new FileNotFoundException();
} catch (Exception e) {
logger.log(Level.ALL, "xxx", e);
}
} /**
* @return the instance
*/
public static ClassWithStatic getInstance() {
return instance;
} public void doSomeThing() {
System.out.println("doSomeThing");
} public static void main(String[] args) {
ClassWithStatic.getInstance().doSomeThing();
}
} @XmlRootElement(name = "Scenes")
class Scenes{
@XmlElement(name = "id", required = true)
protected String id; /**
* @return the id
*/
public String getId() {
return id;
} /**
* @param id the id to set
*/
public void setId(String id) {
this.id = id;
} }

报错

Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.NullPointerException
at san.ClassWithStatic.<init>(ClassWithStatic.java:21)
at san.ClassWithStatic.<clinit>(ClassWithStatic.java:12)

这是和静态成员初始化顺序有关。

基础知识如下:

Java代码中一个类初始化顺序:static变量 --  其他成员变量  --  构造函数 三者的调用先后顺序:

初始化父类Static --> 子类的Static (如果是类实例化,接下来还会: 初始化父类的其他成员变量->父类构造方法->子类其他成员变量->子类的构造方法)。

系统默认值的给予比通过等号的赋予先执行。

一个类中的static变量或成员变量的初始化顺序,是按照声明的顺序初始化的。

测试类

 public class ClassWithStatic extends Super{
private static int iTest0 = 0;
private static ClassWithStatic instance = new ClassWithStatic();
private static int iTest1;
private static int iTest2 = 0;
static {
System.out.println(ClassWithStatic.class.getName() + " : static{}");
iTest0++;
iTest1++;
iTest2++;
} public ClassWithStatic() {
System.out.println(this.getClass().getName() + " : Constuctor.");
iTest0++;
iTest1++;
iTest2++;
} /**
* @return the instance
*/
public static ClassWithStatic getInstance() {
return instance;
} public void doSomeThing() {
System.out.println("iTest0 = " + iTest0);
System.out.println("iTest1 = " + iTest1);
System.out.println("iTest2 = " + iTest2);
} public static void main(String[] args) {
//private static void main(String[] args)
//Error: Main method not found in class san.ClassWithStatic, please define the main method as:
// public static void main(String[] args)
// or a JavaFX application class must extend javafx.application.Application
System.out.println("public static void main(String[] args)");
ClassWithStatic.getInstance().doSomeThing(); }
} class Super {
private static Super instance = new Super();
private static int iTest1;
private int iTest2 = 0;
static {
System.out.println(Super.class.getName() + " : static{}");
iTest1++;
} public Super() {
System.out.println(this.getClass().getName() + " : Constuctor.");
iTest2++;
}
}

结果:

san.Super : Constuctor.
san.Super : static{}
san.ClassWithStatic : Constuctor.
san.ClassWithStatic : Constuctor.
san.ClassWithStatic : static{}
public static void main(String[] args)
iTest0 = 2
iTest1 = 2
iTest2 = 1

这里两遍子类构造,为了区分,改一下构造函数里的打印语句代码。

 public class ClassWithStatic extends Super{
private static int iTest0 = 0;
private static ClassWithStatic instance = new ClassWithStatic();
private static int iTest1;
private static int iTest2 = 0;
static {
System.out.println(ClassWithStatic.class.getName() + " : static{}");
iTest0++;
iTest1++;
iTest2++;
} public ClassWithStatic() {
System.out.println(ClassWithStatic.class.getName() + " : Constuctor with this = " + this);
iTest0++;
iTest1++;
iTest2++;
} /**
* @return the instance
*/
public static ClassWithStatic getInstance() {
return instance;
} public void doSomeThing() {
System.out.println("iTest0 = " + iTest0);
System.out.println("iTest1 = " + iTest1);
System.out.println("iTest2 = " + iTest2);
} public static void main(String[] args) {
//private static void main(String[] args)
//Error: Main method not found in class san.ClassWithStatic, please define the main method as:
// public static void main(String[] args)
// or a JavaFX application class must extend javafx.application.Application
System.out.println("public static void main(String[] args)");
ClassWithStatic.getInstance().doSomeThing(); }
} class Super {
private static Super instance = new Super();
private static int iTest1;
private int iTest2 = 0;
static {
System.out.println(Super.class.getName() + " : static{}");
iTest1++;
} public Super() {
System.out.println(Super.class.getName() + " : Constuctor with this = " + this);
iTest2++;
}
}

结果:

san.Super : Constuctor with this = san.Super@15db9742
san.Super : static{}
san.Super : Constuctor with this = san.ClassWithStatic@6d06d69c
san.ClassWithStatic : Constuctor with this = san.ClassWithStatic@6d06d69c
san.ClassWithStatic : static{}
public static void main(String[] args)
iTest0 = 2
iTest1 = 2
iTest2 = 1

public class ClassWithStatic extends Super {
protected static int iTest0 = Super.iTest0 + 1;
private static ClassWithStatic instance = new ClassWithStatic();
protected static int iTest1;
private static int iTest2 = 0;
static {
System.out.println(ClassWithStatic.class.getName() + " : static{}");
iTest1++;
iTest2++;
} public ClassWithStatic() {
System.out.println(ClassWithStatic.class.getName() + " : Constuctor with this = " + this);
iTest1++;
iTest2++;
} /**
* @return the instance
*/
public static ClassWithStatic getInstance() {
return instance;
} public void doSomeThing() {
System.out.println("ClassWithStatic.iTest0 = " + iTest0);
System.out.println("ClassWithStatic.iTest1 = " + iTest1);
System.out.println("ClassWithStatic.iTest2 = " + iTest2);
} public static void main(String[] args) {
//private static void main(String[] args)
//Error: Main method not found in class san.ClassWithStatic, please define the main method as:
// public static void main(String[] args)
// or a JavaFX application class must extend javafx.application.Application
System.out.println("public static void main(String[] args)"); ClassWithStatic.getInstance().doSomeThing();
System.out.println("Super.iTest0 = " + Super.iTest0);
System.out.println(Const.constanceString);//对类的静态变量进行读取、赋值操作的。static,final且值确定是常量,是编译时确定的,调用的时候直接用,不会加载对应的类
System.out.println("------------------------");
Const.doStaticSomeThing();
}
} class Super {
protected static int iTest0;
private static Super instance = new Super();
protected static int iTest1 = 0;
static {
System.out.println(Super.class.getName() + " : static{}");
iTest0 = ClassWithStatic.iTest0 + 1;//
} public Super() {
System.out.println(Super.class.getName() + " : Constuctor with this = " + this + ", iTest0 = " + iTest0);
iTest1++;
}
} class Const {
public static final String constanceString = "Const.constanceString";
static {
System.out.println(Const.class.getName() + " : static{}");
}
public static void doStaticSomeThing() {
System.out.println(Const.class.getName() + " : doStaticSomeThing();");
}
}

san.Super : Constuctor with this = san.Super@15db9742, iTest0 = 0
san.Super : static{}
san.Super : Constuctor with this = san.ClassWithStatic@6d06d69c, iTest0 = 1
san.ClassWithStatic : Constuctor with this = san.ClassWithStatic@6d06d69c
san.ClassWithStatic : static{}
public static void main(String[] args)
ClassWithStatic.iTest0 = 2
ClassWithStatic.iTest1 = 2
ClassWithStatic.iTest2 = 1
Super.iTest0 = 1
Const.constanceString
------------------------
san.Const : static{}
san.Const : doStaticSomeThing();

0、<init>与<clinit>的区别

其实:

在编译生成class文件时,会自动产生两个方法,一个是类的初始化方法<clinit>, 另一个是实例的初始化方法<init>

<clinit>:在jvm第一次加载class文件时调用,包括静态变量初始化语句和静态块的执行

<init>:在实例创建出来的时候调用,包括调用new操作符;调用Class或java.lang.reflect.Constructor对象的newInstance()方法;调用任何现有对象的clone()方法;通过java.io.ObjectInputStream类的getObject()方法反序列化。

简要说明下final、static、static final修饰的字段赋值的区别:

  • static修饰的字段在类加载过程中的准备阶段被初始化为0或null等默认值,而后在初始化阶段(触发类构造器<clinit>)才会被赋予代码中设定的值,如果没有设定值,那么它的值就为默认值。
  • final修饰的字段在运行时被初始化(可以直接赋值,也可以在实例构造器中赋值),一旦赋值便不可更改;
  • static final修饰的字段在Javac时生成ConstantValue属性,在类加载的准备阶段根据ConstantValue的值为该字段赋值,它没有默认值,必须显式地赋值,否则Javac时会报错。可以理解为在编译期即把结果放入了常量池中。

1、类的加载过程

类加载的时机就很简单了:在用到的时候就加载(和系统内存管理差不多,一个进程都是写时复制CopyOnWrite)。下来看一下类加载的过程:

加载->验证->准备->解析->初始化->使用->卸载

所有的Java虚拟机实现必须在每个类或接口被Java程序 “首次主动使用”时才初始化他们。

有必要提一点的是:准备和初始化

准备

准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中分配。对于该阶段有以下几点需要注意:

1、这时候进行内存分配的仅包括类变量(static),而不包括实例变量,实例变量会在对象实例化时随着对象一块分配在Java堆中。

2、这里所设置的初始值通常情况下是数据类型默认的零值(如0、0L、null、false等),而不是被在Java代码中被显式地赋予的值。

假设一个类变量的定义为:

  public static int value = 3;

那么变量value在准备阶段过后的初始值为0,而不是3,因为这时候尚未开始执行任何Java方法,而把value赋值为3的putstatic指令是在程序编译后,存放于类构造器<clinit>()方法之中的,所以把value赋值为3的动作将在初始化阶段才会执行。

详细查看:【深入Java虚拟机】之四:类加载机制 http://blog.csdn.net/ns_code/article/details/17881581

2、类的使用方式

Java 程序对类的使用方式可分为两种 :

类的使用方式
主动
使用
创建类的实例
遇到
new、
getstatic、
putstatic、
invokestatic
这四条字节码指令时,
如果类还没有进行过初始化,
则需要先触发其初始化。
Test a = new Test();
访问某个类或接口的非编译期静态变量,
或者对该非编译期静态变量赋值
读写某个类的静态变量 
int b = a.staticVariable;
或a.staticVariable=b;
调用类的静态方法
调用某个类的静态方法 Test.doSomething();
反射
 
Class.forName("xxxx")
初始化一个类的子类(不是对父类的主动使用就初始化子类,
这样的话生成一个Object类,那岂不是每个类都要初始化)
 
Child.class、Parent.class,初始化Child时,
就是对Parent的主动使用,先初始化父类
Java虚拟机启动时被标明为启动类的类
 
就是main方法那个所在类
被动
使用
除了以上六种情况,其他使用Java类的方式都被看作是对
类的被动使用,都不会导致类的初始化
   
       
•主动使用(六种)
– 创建类的实例 -------Test a = new Test(); – 访问某个类或接口的非编译期静态变量,或者对该非编译期静态变量赋值 -------读写某个类的静态变量 int b = a.staticVariable;或a.staticVariable=b; – 调用类的静态方法 -------调用某个类的静态方法 Test.doSomething(); – 反射(如 Class.forName (“ com.shengsiyuan.Test ”) ) -------比如Class.forName("xxxx"); – 初始化一个类的子类(不是对父类的主动使用就初始化子类,这样的话生成一个Object类,那岂不是每个类都要初始化) -------Child.class、Parent.class,初始化Child时,就是对Parent的主动使用,先初始化父类 – Java虚拟机启动时被标明为启动类的类( Java Test ) -------就是main方法那个所在类
•被动使用

除了以上六种情况,其他使用Java类的方式都被看作是对类的被动使用,都不会导致类的初始化  
前3个可以归一类:遇到new、getstatic、putstatic、invokestatic这四条字节码指令时,如果类还没有进行过初始化,则需要先触发其初始化。
主要说下开始:当jvm启动时,用户需要指定一个要执行的主类(包含static void main(String[] args)的那个类),则jvm会先去初始化这个类。

友链:【深入Java虚拟机】之三:类初始化 http://blog.csdn.net/ns_code/article/details/17845821

3、类的加载来源


•  类的加载指的是将类的 .class 文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个  java.lang.Class  对象,用来封装类在方法区内的数据结构    

•  加载 .class 文件的方式   

   –  从本地系统中直接加载   

   –  通过网络下载 .class 文件(URLClassLoader)   

   –  从 zip、jar 等归档文件中加载 .class 文件   

   –  从专有数据库中提取 .class 文件   

   –  将 Java源文件动态编译为 .class 文件 
 

4、重载之泛型参数不同可以吗

泛型编译后是一样的类型。

java5后新特性方便了书写,字面误导,需要深刻了解一下class。

    //返回值参数重载吗
public class Overload {
public int method(List<String> ls){
return 0;
}
/**
* Method method(List<Integer>) has the same erasure method(List<E>) as another method in type Demo.Overload
*/
public boolean method(List<Integer> li){
return false;
}
/**
* 会有问题吗,编译后是什么样的呢。。。
*/
public int method(Integer s){
return s;
}
public int method(String i){
return 0;
}
}

重载条件:

在Java语言中,要重载一个方法,除了要与原方法具有相同的简单名称外,还要求必须拥有一个与原方法不同的(不包含返回值的)特征签名,特征签名就是一个方法中各个参数在常量池中的字段符号引用的集合,也就是因为返回值不会包含在特征签名之中,因此Java语言里无法仅仅依靠返回值的不同来对一个已有方法进行重载。

如果进行过Android的native编程,便会非常熟悉这种签名,因为Android源码中可以看到。

用方法描述符描述方法时,按照先参数后返回值的顺序描述,参数要按照严格的顺序放在一组小括号内,如方法 int getIndex(String name,char[] tgc,int start,int end,char target)的描述符为“(Ljava/lang/String[CIIC)I”。

重载调用选择:

另外,根据参数选择重载方法时,是静态时需要确定的,即编译后就确定。

参考:http://blog.csdn.net/ns_code/article/details/17965867
public class Demo {
/**
* I am human
* I am human
*/
public static void main(String[] args){
Demo sp = new Demo();
Human man = sp.new Man();
Human woman = sp.new Woman();
sp.say(man);
sp.say(woman);
} class Human{
}
class Man extends Human{
}
class Woman extends Human{
}
public void say(Human hum){
System.out.println("I am human");
}
public void say(Man hum){
System.out.println("I am man");
}
public void say(Woman hum){
System.out.println("I am woman");
} }


5、参考:
http://www.cnblogs.com/tianchi/archive/2012/11/11/2761631.htmlhttp://www.cnblogs.com/o-andy-o/archive/2013/06/06/3120298.htmlhttp://blog.csdn.net/ns_code/article/details/17675609

java中类加载顺序(深入Java)的更多相关文章

  1. [转]JAVA程序执行顺序,你了解了吗:JAVA中执行顺序,JAVA中赋值顺序

    本文主要介绍以下两块内容的执行顺序,熟悉的大虾可以直接飘过. 一.JAVA中执行顺序 静态块 块 构造器 父类构造器 二.JAVA中赋值顺序 静态块直接赋值 块直接赋值 父类继承的属性已赋值 静态变量 ...

  2. ClassLoader Java中类加载出现在哪个阶段,编译期和运行期? 类加载和类装载是一样的吗

    1.ClassLoader Java中类加载出现在哪个阶段,编译期和运行期? 类加载和类装载是一样的吗? :当然是运行期间啊,我自己有个理解误区,改正后如下:编译期间编译器是不去加载类的,只负责编译而 ...

  3. Java 的类加载顺序

    Java 的类加载顺序 一.加载顺序:先父类后子类,先静态后普通 1.父类的静态成员变量初始化 2.父类的静态代码块 3.子类的静态成员变量初始化 4.子类的静态代码块 5.父类的普通成员变量初始化 ...

  4. java 中类加载器

    jar 运行过程和类加载机制有关,而类加载机制又和我们自定义的类加载器有关,现在我们先来了解一下双亲委派模式. java 中类加载器分为三个: BootstrapClassLoader 负责加载 ${ ...

  5. 第七节:详细讲解Java中的日期,java.util.date

    前言 大家好,给大家带来详细讲解Java中的日期,java.util.date的概述,希望你们喜欢 类Date Java.lang.Object->java.util.Date public c ...

  6. 在java中使用JMH(Java Microbenchmark Harness)做性能测试

    文章目录 使用JMH做性能测试 BenchmarkMode Fork和Warmup State和Scope 在java中使用JMH(Java Microbenchmark Harness)做性能测试 ...

  7. Java中类加载和反射技术实例

    我们知道一个对象在运行时有两种类型,一个是编译类型,一个是运行时类型.在程序运行时,往往是需要发现类和对象的真实的信息的.那么如何获的这种信息呢? 其一,如果我们在编译和运行时都知道类型的具体信息,这 ...

  8. java中类加载的全过程及内存图分析

    类加载机制: jvm把class文件加载到内存,并对数据进行校验.解析和初始化,最终形成jvm可以直接使用的java类型的过程. (1)加载 将class文件字节码内容加载到内存中,并将这些静态数据转 ...

  9. 【Java】Java中的Collections类——Java中升级版的数据结构【转】

    一般来说课本上的数据结构包括数组.单链表.堆栈.树.图.我这里所指的数据结构,是一个怎么表示一个对象的问题,有时候,单单一个变量声明不堪大用,比如int,String,double甚至一维数组.二维数 ...

随机推荐

  1. 面试准备——Zookeeper

    转自https://www.cnblogs.com/shan1393/p/9479109.html 1. Zookeeper是什么框架 分布式的.开源的分布式应用程序协调服务,原本是Hadoop.HB ...

  2. 【PL/SQL编程基础】

    [PL/SQL编程基础]语法: declare 声明部分,例如定义变量.常量.游标 begin 程序编写,SQL语句 exception 处理异常 end: / 正斜杠表示执行程序快范例 -- Cre ...

  3. mongodb权限机制以及扩展

    mongodb权限机制 启动权限机制之前要先在MONGODB中添加管理员账号: 1. 创建账号 重装安装一个mongodb,安装时添加一个 --auth参数: 先把安装好的从服务中删除掉(删除之后数据 ...

  4. php官方微信接口大全

    微信入口绑定,微信事件处理,微信API全部操作包含在这些文件中.内容有:微信摇一摇接口/微信多客服接口/微信支付接口/微信红包接口/微信卡券接口/微信小店接口/JSAPI <?php class ...

  5. 编辑被标记为“只读”的Word文档

    从邮件接收到的Word文档,打开时总是被标记为“只读”,在阅读时对其进行编辑,但不能保存,会提示文档为只读的.要想对其进行编辑并保存,需要进行一定的操作. 进入文件所在的目录,鼠标右键点击Word文档 ...

  6. PTA 08-图8 How Long Does It Take (25分)

    题目地址 https://pta.patest.cn/pta/test/16/exam/4/question/674 5-12 How Long Does It Take   (25分) Given ...

  7. shell的if-else的基本用法

    if 语句通过关系运算符判断表达式的真假来决定执行哪个分支.Shell 有三种 if ... else 语句: if ... fi 语句: if ... else ... fi 语句: if ... ...

  8. 【bzoj3123】[Sdoi2013]森林 倍增LCA+主席树+启发式合并

    题目描述 输入 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数.第三行包含N个非负 ...

  9. [LOJ#2270][BZOJ4912][SDOI2017]天才黑客

    [LOJ#2270][BZOJ4912][SDOI2017]天才黑客 试题描述 SD0062 号选手小 Q 同学为了偷到 SDOI7012 的试题,利用高超的黑客技术潜入了 SDOI 出题组的内联网的 ...

  10. POJ 3469 Dual Core CPU ——网络流

    [题目分析] 构造一个最小割的模型. S向每一个点连Ai,每一个点向T连Bi. 对于每一个限制条件,在i和j之间连一条Cij的双向边即可. 然后求出最小割就是最少的花费. 验证最小割的合理性很容易. ...