很考验人的java内存加载面试题

源代码如下,求结果

public class MemoryAnalyse {
    public static int k = 0;
    public static MemoryAnalyse t1 = new MemoryAnalyse("t1");
    public static MemoryAnalyse t2 = new MemoryAnalyse("t2");
    public static int i = print("i");
    public static int j = print("j");
    public static int n = 99;

    {
        print("constructor code");
    }

    static {
        print("static code");
    }

    public static int print(String s) {
        System.out.println("i=" + i + "   " + s + "  k=" + k + "  n=" + n
                + "   j=" + j);
        ++i;
        ++k;
        ++n;
        return i;
    }

    public MemoryAnalyse(String string) {
        print(string);
    }

    public static void main(String[] args) throws ClassNotFoundException {
        MemoryAnalyse d = new MemoryAnalyse("T");
    }
}

然而结果是这个

i=0   constructor code  k=0  n=0   j=0
i=1   t1  k=1  n=1   j=0
i=2   constructor code  k=2  n=2   j=0
i=3   t2  k=3  n=3   j=0
i=4   i  k=4  n=4   j=0
i=5   j  k=5  n=5   j=0
i=6   static code  k=6  n=99   j=6
i=7   constructor code  k=7  n=100   j=6
i=8   T  k=8  n=101   j=6

有没有很惊讶,结果竟然这么复杂.好,下面我们分析一下,在分析之前,先普及下不怎么用的基础知识

代码块和静态代码块何时运行问题:

  代码块在创建对象时运行
  静态代码块在类加载时运行

大家都知道static是属于类的并非对象,也就是说static修饰的东西都会在class加载到方法区时就存在在那里.所以方法区中类加载时内存过程如下

1.当类刚加载时会全部加载到方法区时,此时所有变量全部未实例化.

2.实例化参数t1

此时因为代码块在创建对象时执行,且在构造函数之前执行,所以先执行代码块

{
print("constructor code");
}

  因为此时所有的变量都为默认值,所以执行后打印结果为

i=0   constructor code  k=0  n=0   j=0

此时i,n,k的值都已经自加一,值为1

然后实例化调用构造函数

public MemoryAnalyse(String string) {   //这里string为t1
        print(string);
}

  构造函数调用结果如下

  i=1   t1  k=1  n=1   j=0
  此时i,n,k的值都已经自加一,值为2

3.实例化参数t2

和第一步一样在构造函数之前执行代码块

{
print("constructor code");
}

i=2   constructor code  k=2  n=2   j=0

此时i,n,k的值都已经自加一,值为3

然后实例化调用构造函数

public MemoryAnalyse(String string) {   //这里string为t2
        print(string);
}

  构造函数调用结果如下

i=3   t2  k=3  n=3   j=0

此时i,n,k的值都已经自加一,值为4

4.初始化参数i

  这里直接调用print("i")函数,得到结果为

  i=4   i  k=4  n=4   j=0

  此时i,k,j值为5,注意i的值不是通过自加一变成5的,而是通过函数的返回值赋给i的

  5.初始化参数j

  

  这里和上一步一样,执行print("j"),然后把函数的返回值赋给j,打印结果为

  i=5   j  k=5  n=5   j=0

  此时j的值已经为6

到这里类加载的内部参数变化就完成了,我们可以用加载类的方式调用一下

    public static void main(String[] args) throws ClassNotFoundException {
        //MemoryAnalyse d = new MemoryAnalyse("T");
        Class.forName("MemoryAnalyse");
    }

Class.forName(类名字符串)是手动加载类到方法区,得到结果为

i=0   constructor code  k=0  n=0   j=0
i=1   t1  k=1  n=1   j=0
i=2   constructor code  k=2  n=2   j=0
i=3   t2  k=3  n=3   j=0
i=4   i  k=4  n=4   j=0
i=5   j  k=5  n=5   j=0
i=6   static code  k=6  n=99   j=6  //这行的出现是因为static代码段在类加载时执行.n=99是因为类加载了,n的初值为99把之前的值覆盖掉了

然后改为我们之前的demo

public static void main(String[] args) throws ClassNotFoundException {
        MemoryAnalyse d = new MemoryAnalyse("T");
        //Class.forName("MemoryAnalyse");
    }

执行结果为

i=0   constructor code  k=0  n=0   j=0
i=1   t1  k=1  n=1   j=0
i=2   constructor code  k=2  n=2   j=0
i=3   t2  k=3  n=3   j=0
i=4   i  k=4  n=4   j=0
i=5   j  k=5  n=5   j=0
i=6   static code  k=6  n=99   j=6
i=7   constructor code  k=7  n=100   j=6    //代码块执行
i=8   T  k=8  n=101   j=6     //构造函数执行

最后两行的出现就很简单了,一个是代码块的,一个是构造函数的

这么一分析是不是简单了很多

时间: 2024-10-12 15:36:38

很考验人的java内存加载面试题的相关文章

看起来很懵的java内存加载面试

java内存加载机制一直以为掌握的还不错,今天被考验到了,一时间竟然很懵 http://p.baidu.com/itopic/main/qlog?qid=d5236162636533646239363600&type=questionloghttp://p.baidu.com/itopic/main/qlog?qid=db236162636332613639663600&type=questionloghttp://p.baidu.com/itopic/main/qlog?qid=e023

Android中apk加固完善篇之内存加载dex方案实现原理(不落地方式加载)

一.前言 时隔半年,困扰的问题始终是需要解决的,之前也算是没时间弄,今天因为有人在此提起这个问题,那么就不能不解决了,这里写一篇文章记录一下吧.那么是什么问题呢? 就是关于之前的一个话题:Android中apk加固技术实现 关于这个问题,之前的一篇文章已经说过了,没有了解的同学可以点击这里:Android中apk加固技术实现 请务必仔细的看完这篇文章,不然今天说的内容会感觉很蛋疼的,因为今天的文章就是为了解决当初的加固技术遗留的问题,这里先大致来说一下加固apk的原理吧,先来看一张图: 看到这张

socket实现人多聊天与Java代码加载过程

第一部分是java代码加载过程 关于java代码加载过程,今天调试了阿里巴巴一份代码,如下: /** * 加载方法不等于执行方法,初始化变量则会赋值 * 类加载顺序应为 加载静态方法-初始化静态变量-执行静态代码块 * 实例化时 先加载非静态方法-实例化非静态变量-执行构造代码块-执行构造函数 * @author panteng * */ public class StaticTest { /**第一个加载*/ public static int k = 0; /**第二个加载,因为是new一个

Java热加载的实现

应用服务器一般都支持热部署或者热加载(Hot Deployment或者Hot Swap),即更新代码保存时把新编译类替换旧的类,后面的程序就执行新类中的代码.这也是由各种应用服务器的独有的类加载器层次实现的. 那如何在我们的程序中也实现这种热加载功能呢? 即要在不重启JVM虚拟机的情况下,换个类,JVM就知道加载这个新类,执行新类中的逻辑. 首先大致了解一下类加载器: 标准 Java 启动器的类加载器层次 1. 引导类加载器(bootstrap):加载内核 API,如 rt.jar(java.l

java如何加载本地的dll文件

首先,应当明确,dll有两类:(1)Java所依赖的dll和,(2)dll所依赖的dll.正是由于第(2)种dll的存在,才导致了java中加载dll的复杂性大大增加,许多说法都是这样的,但我实验的结果却表明似乎没有那么复杂,后面会予以详细阐述. 其次,Java中加载dll的方式也有两种:(1)通过调用System.loadLibrary(String filename)和,(2)通过调用System.load(String filename)方法.其底层都是通过使用ClassLoader中的l

内存加载DLL

1.前言 目前很多敏感和重要的DLL(Dynamic-link library) 都没有提供静态版本供编译器进行静态连接(.lib文件),即使提供了静态版本也因为兼容性问题导致无法使用,而只提供DLL版本,并且很多专业软件的授权部分的API,都是单独提供一个DLL来完成,而主模块通过调用DLL中的接口来完成授权功能.虽然这些软件一般都采用了加壳和反调试等保护,但是一旦这些功能失去作用,比如脱壳,反反调试,HOOK API或者干脆写一个仿真的授权DLL(模拟授权DLL的所有导出函数接口),然后仿真

避免缓存,Java动态加载配置文件

Java动态加载配置文件 关键:每次读取都要重新生成流 今天无意间在项目的代码中看到如下这样一段简单加载配置文件的代码: Properties prop = new Properties(); InputStream in = PropertiesTest.class.getClassLoader().getResourceAsStream("/config.properties"); prop.load(in); 其实代码本身是没有什么问题的 问题就是用这种方式来读取配置文件,会存在

原来热加载如此简单,手动写一个 Java 热加载吧

1. 什么是热加载 热加载是指可以在不重启服务的情况下让更改的代码生效,热加载可以显著的提升开发以及调试的效率,它是基于 Java 的类加载器实现的,但是由于热加载的不安全性,一般不会用于正式的生产环境. 2. 热加载与热部署的区别 首先,不管是热加载还是热部署,都可以在不重启服务的情况下编译/部署项目,都是基于 Java 的类加载器实现的. 那么两者到底有什么区别呢? 在部署方式上: 热部署是在服务器运行时重新部署项目. 热加载是在运行时重新加载 class. 在实现原理上: 热部署是直接重新

Java 热加载

1. 什么是热加载 热加载是指可以在不重启服务的情况下让更改的代码生效,热加载可以显著的提升开发以及调试的效率,它是基于 Java 的类加载器实现的,但是由于热加载的不安全性,一般不会用于正式的生产环境. 2. 热加载与热部署的区别 首先,不管是热加载还是热部署,都可以在不重启服务的情况下编译/部署项目,都是基于 Java 的类加载器实现的. 那么两者到底有什么区别呢? 在部署方式上: 热部署是在服务器运行时重新部署项目. 热加载是在运行时重新加载 class. 在实现原理上: 热部署是直接重新