Java中new一个对象到底经历了什么?我们从内存的方面来分析它们.String定义的两种方式内存怎么安排的

一、前提知识

jvm的内存区域组成

Java把内存分为两种: 1: 栈内存,  2: 堆内存

1: 在函数定义的基本类型变量对象的引用变量都在函数的栈内存中分配

2: 堆内存用来存放由new创建的对象数组以及对象的实例变量

堆和栈的优缺点

1: 堆的优势是: 可以动态分配内存大小,生存期也不必事先告诉编译器,因为它是在运行是动态分配内存的.

缺点: 就是要在运行时动态分配内存, 存取速度较慢,

2; 栈的优势是: 存取速度比堆要快,仅次于直接位于CPU中的寄存器. 栈中数据也可以共享

缺点: 存在栈中的数据大小与生存期必须是确定的,缺乏灵活性.

 栈中有一个很重要的特殊性, 就是存在栈中的数据可以共享

例如

int a = 3;
int b = 3;

编译器处理int a = 3时,首先它会在栈中创建一个变量为a的引用, 然后查找有没有字面值为3的地址, 没找到就开避一个存放3这个字面值的地址, 然后将a指向3的地址.

接着处理int b = 3;跟a是一样的(这时候就会出现a与b同时指向3的情况). 如果后面要改变a的值为4只是将a的引用指向字面值为4的地址即可.

二、创建对象的内存分配情况

在Java中创建一个对象包括对象的声明实例化两步

以Student student = new Student("小明", 18);为例

2.1、对象的声明

 1: 声明一个对象student时,将在栈内存为对象的引用变量student分配内存空间, 但student值为空, 称student是一个空对象. 空对象不能够使用, 因为它还没有引用任何实体.

2.2、对象的实例化

对象实例化时的内存模型

执行new Student("小明", 18);

2.1: 在堆内存中为类的成员变量width, height分配内存, 并将其初始化为各数据类型的默认值.

2.2: 接着进行初始化, 在Java对象初始化过程中,主要涉及三种执行对象初始化的结构,分别是 实例变量初始化实例代码块初始化(static修饰的代码块,或者非static修饰的代码块) 以及 构造函数初始化。 (这三种初始化并不是按照顺序进行的).调用构造方法为成员变量赋值. 返回堆内存中对象的引用(相当于首地址)给引用变量student, 以后就可以通过student来引用堆内存中的对象了.

三、当创建多个不同的对象实例

一个类通过使用new运算符可以创建多个不同的对象实例, 这些对象实例将在堆中被分配不同的内存空间. 改变其中一个对象的状态不会影响其它对象的状态. 此时将在堆内存中分别为两个对象的成员变量name, age分配内存空间, 两个对象在堆内存中占据的空间互不相同. 如果相同, 则在堆内存中只创建了一个对象实例, 在栈中创建了两个对象的引用, 两个对象的引用同时指向一个对象实例.

四、String是一个特殊的包装类数据, 可以用一下两种方式创建

例题1: 

String b = "abc";
String c = "abc";

前提知识: JVM运行的时候,将内存分为两个部分,一部分是堆, 另一部分是栈. 堆中存放的是创建的对象, 而栈中存放的则是方法调用过程中局部变量或引用. 而设计Java字符串对象内存实现的时候, 在堆中又开辟了一块很小的内存, 其被称为字符串常量, 专门用来存放特定的字符串对象.

(1): 在栈中创建一个a的引用对象

(2): 查看字符串常量池中是否存在内容于"abc"相同的字符串对象.

(3): 若没有,则新创建一个包含改内容的字符串对象, 并让引用变量指向改对象. 例如创建字符串b的时候,字符串常量池中没有,则创建一个新对象,并让引用b指向改对象.

(4): 若已经存在包含改内容的字符串对象,则让字符串引用直接指向改对象. 例如,在创建字符串c的时候,字符串常量池中已经有包含改内容的对象了,所以引用c直接指向已有的对象.

注: 字符串引用b和c因为指向同一个对象,所以"=="测试成立,返回true;

 例题2: 

String a = "abc";
String b = new String ("abc");

(1): 在栈中创建a,b两个引用对象

(2): 然后在堆(不是常量池)中创建一个包含指定内容的字符串对象,并将字符串引用指向改对象.例如在上述代码中,使用new创建字符串b,其会直接在堆中创建一个内容为"abc"的字符串对象,并将引用b指向改对象.

jvm对new String中有一个优化的处理: 使用intern()方法,使用之后就会把b引用对象指向常量池中的字符串对象,而不是堆中的字符串对象,则a,b的地址都会是一样的

b = b.intern();
System.out.println(b==a)  //true

(3): 去字符串常量池中查看,是否有包含改内容的对象.

(4): 若有,则将new出来的字符串对象与字符串常量池中内容相同的对象联系起来.例如,本例中b所指向的对象与a中所指向的联系起来.

(5): 若没有,则在字符串常量池中再创建一个包含该内容的字符串对象,并将堆中的对象与字符串常量池中新创建出来的对象联系起来.

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
package com.ljl.org.test4; /** *@DEMO:Interview *@Author:jilongliang *@Date:2013-4-17 * * 分别使用Runnable接口和Thread类编程实 编写一应用程序创建两个线程一个线程打印输出1—1000之间所有的奇数(Odd Number) * 另外一个线程打印输出1-1000之间所有的偶数(Even Number)要求两个线程随机休眠一 段时间后 继续打印输出下一个数 * * 创建线程有两种方式: 1.实现Runnable接口 2.继承Thread类 * 实现方式和继承方式有啥区别? * 实现方式的好处:避免了单继承的局限性 在定义线程时. * 建议使用实现方式 * 区别: * 继承Thread:线程代码存放Thread子类run方法 实现 * Runnable:线程代码存放接口的子类的run方法 * wait释放资源,释放锁 * sleep释放资源,不释放锁 */ @SuppressWarnings("all") public class Thread1 { public static void main(String[] args) { //方法一 /* OddNumber js = new OddNumber(); js.start(); EvenNumber os = new EvenNumber(); os.start(); while (true) { if (js.i1 == 1000 || os.i2 == 1000) { System.exit(-1); } } */ //方法二 OddNum on=new OddNum(); EvenNum en=new EvenNum(); new Thread(on).start(); new Thread(en).start(); while (true) { if (on.i1 == 1000 || en.i2 == 1000) { System.exit(-1); } } } } /** * ============================继承Thread的线程=============================== */ class EvenNumber extends Thread { int i2; @Override public void run() { for (i2 = 1; i2 <= 1000; i2++) { if (i2 % 2 == 0) { System.out.println("偶數" + i2); } try { sleep((int) (Math.random() * 500) + 500); } catch (Exception e) { } } } } class OddNumber extends Thread { int i1; @Override public void run() { for (i1 = 1; i1 <= 1000; i1++) { if (i1 % 2 != 0) { System.out.println("奇數" + i1); } try { sleep((int) (Math.random() * 500) + 500); } catch (Exception e) { } } } } /** * ============================实现Runnable的线程=============================== */ @SuppressWarnings("all") class OddNum implements Runnable { int i1; @Override public void run() { for (i1 = 1; i1 <= 1000; i1++) { if (i1 % 2 != 0) { System.out.println("奇數" + i1); } try { new Thread().sleep((int) (Math.random() * 500)+500); } catch (Exception e) { } } } } @SuppressWarnings("all") class EvenNum implements Runnable { int i2; @Override public void run() { for (i2 = 1; i2 <= 1000; i2++) { if (i2 % 2 == 0) { System.out.println("偶數" + i2); } try { /**在指定的毫秒数内让当前正在执行的线程休眠 * Math.random()一个小于1的随机数乘于500+500,随眠时间不会超过1000毫秒 */ //new Thread().sleep((int) (Math.random() * 500)+500); new Thread().sleep(1000);//也可以指定特定的参数毫秒 } catch (Exception e) { } } } }
Java内存划分成两种:一种是栈内存,另一种是堆内存。在函数定义的一些基本类型的变量和对象的引用变量都是在函数的 栈内存分配,当在一段代码块定义一个变量时,Java 就在栈为这个变量分配内存空间,当超过变量的作用域后,Java 会自动 释放掉为该变量分配的内存空间,该内存空间可以立即被另作它用。 堆内存用来存放由 new 创建的对象和数组,在堆分配的内存,由 Java 虚拟机的自动垃圾回收器来管理。在堆产生了一个数 组或者对象之后,还可以在栈定义一个特殊的变量,让栈的这个变量的取值等于数组或对象在堆内存的首地址,栈的这个 变量就成了数组或对象的引用变量,以后就可以在程序使用栈的引用变量来访问堆的数组或者对象,引用变量就相当于是为 数组或者对象起的一个名称。引用变量是普通的变量,定义时在栈分配,引用变量在程序运行到其作用域之外后被释放。而数组 和对象本身在堆分配,即使程序运行到使用 new 产生数组或者对象的语句所在的代码块之外,数组和对象本身占据的内存不会 被释放,数组和对象在没有引用变量指向它的时候,才变为垃圾,不能在被使用,但仍然占据内存空间不放,在随后的一个不确定 的时间被垃圾回收器收走(释放掉)。这也是 Java 比较占内存的原因。 实际上,栈的变量指向堆内存的变量,这就是 Java 的指针!
面试题包括以下十九部分:Java 基础、容器、多线程、反射、对象拷贝、Java Web 模块、异常、网络、设计模式、Spring/Spring MVC、Spring Boot/Spring Cloud、Hibernate、Mybatis、RabbitMQ、Kafka、Zookeeper、MySql、Redis、JVM 。 目录: 一、Java 基础 1.JDK 和 JRE 有什么区别? 2.== 和 equals 的区别是什么? 3.两个对象的 hashCode()相同,则 equals()也一定为 true,对吗? 4.final 在 java 有什么作用? 5.java 的 Math.round(-1.5) 等于多少? 6.String 属于基础的数据类型吗? 7.java 操作字符串都有哪些类?它们之间有什么区别? 8.String str="i"与 String str=new String(“i”)一样吗? 9.如何将字符串反转? 10.String 类的常用方法都有那些? 11.抽象类必须要有抽象方法吗? 12.普通类和抽象类有哪些区别? 13.抽象类能使用 final 修饰吗? 14.接口和抽象类有什么区别? 15.java IO 流分为几种? 16.BIO、NIO、AIO 有什么区别? 17.Files的常用方法都有哪些? 二、容器 18.java 容器都有哪些? 19.Collection 和 Collections 有什么区别? 20.List、Set、Map 之间的区别是什么? 21.HashMap 和 Hashtable 有什么区别? 22.如何决定使用 HashMap 还是 TreeMap? 23.说一下 HashMap 的实现原理? 24.说一下 HashSet 的实现原理? 25.ArrayList 和 LinkedList 的区别是什么? 26.如何实现数组和 List 之间的转换? 27.ArrayList 和 Vector 的区别是什么? 28.Array 和 ArrayList 有何区别? 29.在 Queue poll()和 remove()有什么区别? 30.哪些集合类是线程安全的? 31.迭代器 Iterator 是什么? 32.Iterator 怎么使用?有什么特点? 33.Iterator 和 ListIterator 有什么区别? 34.怎么确保一个集合不能被修改? 三、多线程 35.并行和并发有什么区别? 36.线程和进程的区别? 37.守护线程是什么? 38.创建线程有哪几种方式? 39.说一下 runnable 和 callable 有什么区别? 40.线程有哪些状态? 41.sleep() 和 wait() 有什么区别? 42.notify()和 notifyAll()有什么区别? 43.线程的 run()和 start()有什么区别? 44.创建线程池有哪几种方式? 45.线程池都有哪些状态? 46.线程池 submit()和 execute()方法有什么区别? 47.在 java 程序怎么保证多线程的运行安全? 48.多线程锁的升级原理是什么? 49.什么是死锁? 50.怎么防止死锁? 51.ThreadLocal 是什么?有哪些使用场景? 52.说一下 synchronized 底层实现原理? 53.synchronized 和 volatile 的区别是什么? 54.synchronized 和 Lock 有什么区别? 55.synchronized 和 ReentrantLock 区别是什么? 56.说一下 atomic 的原理? 四、反射 57.什么是反射? 58.什么是 java 序列化?什么情况下需要序列化? 59.动态代理是什么?有哪些应用? 60.怎么实现动态代理? 五、对象拷贝 61.为什么要使用克隆? 62.如何实现对象克隆? 63.深拷贝和浅拷贝区别是什么? 六、Java Web 64.jsp 和 servlet 有什么区别? 65.jsp 有哪些内置对象?作用分别是什么? 66.说一下 jsp 的 4 种作用域? 67.session 和 cookie 有什么区别? 68.说一下 session 的工作原理? 69.如果客户端禁止 cookie 能实现 session 还能用吗? 70.spring mvc 和 struts 的区别是什么? 71.如何避免 sql 注入? 72.什么是 XSS 攻击,如何避免? 73.什么是 CSRF 攻击,如何避免? 七、异常 74.throw 和 throws 的区别? 75.final、finally、finalize 有什么区别? 76.try-catch-finally 哪个部分
教学目标 理解数据抽象和数据隐藏 创建类 能够创建和使用对象 能够控制对实例变量和方法的访问 方法的重载 构造函数的使用 理解this引用的用法 理解Java的垃圾收集机制 static方法和域的使用 类的组合 包的创建和使用 第1页/共37页 java面向对象程序设计全文共37页,当前为第1页。 4.1 面向对象程序设计的思想 4.1.1 OOP思想 4.1.2 用类实现抽象数据类型:时钟类 4.1.3 类成员:构造函数、方法和域 第2页/共37页 java面向对象程序设计全文共37页,当前为第2页。 4.1.1 OOP思想 面向对象编程技术 将数据及对数据的操作封装在一起而形成了类,类是描述相同类型的对象集合。 。面向对象编程(object-oriented programming,OOP)就是定义这些类。 类作为抽象的数据类型用于创建类的对象。 程序的执行,表现为一组对象之间的交互通信。对象之间通过公共接口进行通信,从而完成系统功能。对象的公共接口是该对象的应用程序编程接口(Application Programming Interface,API),把对象的内部详细信息隐藏起来,使得对象变得抽象,将这种技术称为数据的抽象化。 Java语言是完全面向对象的语言,程序的结构由一个以上的类组成。所有的过程都被封装起来,并将它们称之为方法。 第3页/共37页 java面向对象程序设计全文共37页,当前为第3页。 4.1.2 用类实现抽象数据类型:时钟类 类作为一种抽象的数据类型,封装了对象的数据属性和动态行为,被用来定义类的对象。 下面我们将通过一个具体的实例来理解什么是类,如何定义类、创建对象和使用对象。 时钟类的设计: (1) 数据属性: 时(int hour); 分 (int minute); 秒(int second)。 (2) 行为: 设置时间 setTime() ; 显示时间 toUniversalString() 、 toStandardString() ; 走时 (暂时不考虑); 第4页/共37页 java面向对象程序设计全文共37页,当前为第4页。 4.1.2 用类实现抽象数据类型:时钟类(续) 例4-1 时钟类的实现。 该例子包含两个文件Time1.java和TimeTest1.java。 Time1.java是用于定义时钟类。 TimeTest1类是一个用于测试时钟类的包含main方法的主类,在main方法将创建Time1类的一个对象,并调用对象的公共方法。 第5页/共37页 java面向对象程序设计全文共37页,当前为第5页。 4.1.2 用类实现抽象数据类型:时钟类(续) Time1.java文件的代码如下: import java.text.DecimalFormat; public class Time1 extends Object { private int hour; private int minute; private int second; public Time1(){ setTime( 0, 0, 0 ); } public void setTime( int h, int m, int s ){ hour = ( ( h >= 0 && h < 24 ) ? h : 0 ); minute = ( ( m >= 0 && m < 60 ) ? m : 0 ); second = ( ( s >= 0 && s < 60 ) ? s : 0 ); } 时 分 秒 设置时间 第6页/共37页 java面向对象程序设计全文共37页,当前为第6页。 4.1.2 用类实现抽象数据类型:时钟类(续) public String toUniversalString(){ //toUniversalString方法以24时制格式显示时间 DecimalFormat twoDigits = new DecimalFormat( "00" ); return twoDigits.format( hour ) + ":" + twoDigits.format( minute ) + ":" + twoDigits.format( second ); } public String toStandardString(){ //toStandardString方法以12时制格式显示时间 DecimalFormat twoDigits = new DecimalFormat( "00" ); return ( (hour == 12 "" hour == 0) ? 12 : hour % 12 ) + ":" + twoDigits.format( minute ) + ":" + twoDigits.f

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值