new对象实际是在干吗,懂了后String相关面试题随便推导

目录java

对象的建立,内存布局以及访问定位,推导String必问面试题答案程序员

JVM 遇到了 new 对象以后作了什么?

package com.kuaizhan.web.utils;
 /**  * @author by zengzhiqin  * 2020-07-15  */ class Person {  String name;   public void say(String name) {  System.out.println("hello " + name);  } } public class TestPerson {  public static void main(String[] args){  Person person;  person = new Person();  person.say("特朗普");  } } 复制代码

以此为例,结合以前说到的类生命周期来讲(不懂的朋友能够看前面的文章Java类的生命周期,不懂这个都很差意思和别人说我是搞JAVA的 ),当执行到 new Person 的时候:web

  1. 首先去常量池中看可否根据这个类的全路径找到这个类的信息,查看是否加载过,解析,初始化过。若是没有,则先进行类加载过程;
  2. 接下来JVM为新的 Person对象分配内存,对象所需的内存大小在类加载的时候就能够彻底肯定,由于一个对象具备什么属性,属性大小(int,byte,long等所占字节大小)均可以进行肯定; 分配内存 = 从虚拟机设置的堆内存里面划分一块新对象所需的肯定大小的内存

具体的划分方式根据各家垃圾收集器不一样也采用不一样的划分方式,主要两种:面试

方式一:指针碰撞(假设JAVA堆内存是整整齐齐的,用过的站一块儿,没用过的站一块儿,那么在中间分界地方放个指针,每次新分配往没用过的那边移动一点就行了。这种方式须要垃圾收集器维护这个用过的和没用过的内存,由于运行过程当中可能中间随时有使用过的内存被回收了就出现了缺口,须要垃圾收集器进行整理内存)编辑器

方式二:空闲列表(JAVA堆空闲内存之间是这里缺一块那里缺一块的,虚拟机维护一个空闲内存列表,记录下来哪些空闲哪些占用了,分配的时候从空闲列表里面找)布局

  1. 分配完内存,JVM 将拿到内存的数据类型都初始化为默认值;
  2. JVM 对对象头进行设置,例如对象是哪一个类的实例,如何找到类的元数据信息,对象hash 码,GC分代年龄信息等,都属于对象头信息;
  3. ”clinit“方法统一赋值对象的属性,前面第三步初始化是默认值,例如 int i = 4 d在第三步是初始化为int的默认值0,这里是将其赋值为你定义的4;
  4. 在栈中新建对象引用,并将其指向堆中新建的对象实例。

举个栗子

仍是这段代码:flex

package com.kuaizhan.web.utils;
 /**  * @author by zengzhiqin  * 2020-07-15  */ class Person {  String name;   public void say(String name) {  System.out.println("hello " + name);  } } public class TestPerson {  public static void main(String[] args){  Person person;  person = new Person();  person.say("特朗普");  } } 复制代码
代码对应对象调用的内存
代码对应对象调用的内存

调用过程:优化

  1. JVM去方法区寻找Person类信息
  2. 若是找不到,Classloader加载Person类信息进入内存方法区
  3. 在堆内存中建立Person对象,并持有方法区中Person类的类型信息的引用
  4. 把person添加到执行main()方法的主线程java调用栈中,指向堆空间中的内存对象
  5. 执行person.sayHello()时,JVM根据person定位到堆空 间的Person实例
  6. 根据Person实例在方法区持有的引用,定位到方法区 Person类型信息,得到sayHello() 字节码,执行此方法。执行,打印出结果。

注意:url

  • 当 new Person() 的时候,虚拟机作了两件事情:
  1. 在堆上产生一个实例,假设实例地址是 0x22;
  2. 将变量地址 0x11 指向堆实例的地址 0x22;

new Person()

JVM堆、栈和方法区
JVM堆、栈和方法区

局部变量 person 其实就是一个reference引用,说白了就是一个相似 0x11 的地址,存在于局部变量表里面(局部变量表在栈区,不记得的朋友能够看看上一篇讲JVM内存分布的文章Java跨平台根本缘由,面试必问JVM内存模型白话文详解来了)。spa

引用指向关系: 0x11 => 0x22 => 0x33

reference1 就是局部变量person,指向堆区的实例;而后 new Person() 指向方法区里面的 Person类元数据信息包括sayHello方法。

当调用sayHello的时候,步骤以下三步:

  1. 首先能拿到 person 也就是reference1,其地址是0x11;
  2. 0x11 地址指向堆里面的 new Person(),new Person 拥有方法区类存放地址假设 0x22;
  3. 0x22 指向的是方法区的 Person类元数据信息,元数据信息里面是包括 sayHello() 方法的地址,最后完成调用。

从虚拟机内存推导String面试高频题答案

String 面试高频题,其实都是靠推导出来的,记永远是记不清的:

  1. 建立了几个对象?
String str = "1" + "2" + "3";
 答案:一个对象,编译时候会进行字符串折叠,算是一个优化,之前确实是四个对象,“1”,“2”,“3”,“123” 字符串折叠:若是是常量相加,通俗理解就是先加,而后去常量池找,有直接返回,没有就建立 复制代码
  1. 打印结果?
String s1 = "hello";
String s2 = "world"; String s3 = "hello world";  System.out.print(s3 == "hello" + " world") true,根据上面的分析,都是比较常量池的值,是同一个,true System.out.print(s3 == s1 + s2); false,变量相加,只要有一个变量,那么都要先给变量开空间,就成了地址比较 复制代码
  1. 打印结果?

String str2 = new String("Trump"); 建立了几个对象?

String str1 = "Trump"; 
直接去常量池建立一个Trump,栈里面保存引用直接指向常量池”Trump“  String str2 = new String("Trump"); 建立几个对象分状况(String 不可变): 1. 一个对象(若是常量池中已经存在”Trump”,堆里面建立个对象就能够,栈里面来个引用指向堆) 2. 两个对象(若是常量池中不存在”Trump“,先在常量池里面建立”Trump“,而后堆里面new一个对象,最后栈里面来个引用指向堆)  System.out.print(str1 == str2); 比较的栈里面的引用地址,指向的东西都不同怎么多是同样的,果断 false System.out.print(str1.equals(str2)); 比较的常量池里面的值,都是Trump, true 复制代码

公众号下篇内容预告: 垃圾回收,JAVA程序员的福音呐

往期推荐:

Java跨平台根本缘由,面试必问JVM内存模型白话文详解来了

从JVM设计者的角度来看.class文件结构,一文弄懂.class文件的身份地位

Java类的生命周期,不懂这个都很差意思和别人说我是搞JAVA的

欢迎批评指正,有收获的朋友点个在看或者分享鼓励一下吧,十分感谢~

关注我,一块儿成长
关注我,一块儿成长
相关文章
相关标签/搜索