java - new + 类名,一定需要申明一个对象吗?
问题描述
public class CodeBlock02{ { System.out.println('第一代码块');}public CodeBlock02() {System.out.println('构造方法');}{ System.out.println('第二构造块'); } public static void main(String[] args) { new CodeBlock02(); new CodeBlock02(); new CodeBlock02(); }}
在这里, new CodeBlock02(); 或者换成 CodeBlock02 code = new CodeBlock02();他们是一样的吗!
问题解答
回答1:先明确几个概念,java代码是跑在jvm中的,而jvm的内存区域划分为这么几个模块:
程序计数器(Program Counter Register):程序计数器是一个比较小的内存区域,用于指示当前线程所执行的字节码执行到了第几行,可以理解为是当前线程的行号指示器。字节码解释器在工作时,会通过改变这个计数器的值来取下一条语句指令。
虚拟机栈(JVM Stack):一个线程的每个方法在执行的同时,都会创建一个栈帧(Statck Frame),栈帧中存储的有局部变量表、操作站、动态链接、方法出口等,当方法被调用时,栈帧在JVM栈中入栈,当方法执行完成时,栈帧出栈。
本地方法栈(Native Method Statck):本地方法栈在作用,运行机制,异常类型等方面都与虚拟机栈相同,唯一的区别是:虚拟机栈是执行Java方法的,而本地方法栈是用来执行native方法的,在很多虚拟机中(如Sun的JDK默认的HotSpot虚拟机),会将本地方法栈与虚拟机栈放在一起使用。
堆区(Heap):堆区是理解Java GC机制最重要的区域,没有之一。在JVM所管理的内存中,堆区是最大的一块,堆区也是Java GC机制所管理的主要内存区域,堆区由所有线程共享,在虚拟机启动时创建。堆区的存在是为了存储对象实例,原则上讲,所有的对象都在堆区上分配内存(不过现代技术里,也不是这么绝对的,也有栈上直接分配的)。
方法区(Method Area):(也被称为永久代),方法区是各个线程共享的区域,用于存储已经被虚拟机加载的类信息(即加载类时需要加载的信息,包括版本、field、方法、接口等信息)、final常量、静态变量、编译器即时编译的代码等。
直接内存(Direct Memory):直接内存并不是JVM管理的内存,可以这样理解,直接内存,就是JVM以外的机器内存,比如,你有4G的内存,JVM占用了1G,则其余的3G就是直接内存,JDK中有一种基于通道(Channel)和缓冲区(Buffer)的内存分配方式,将由C语言实现的native函数库分配在直接内存中,用存储在JVM堆中的DirectByteBuffer来引用。由于直接内存收到本机器内存的限制,所以也可能出现OutOfMemoryError的异常。
明白这几个基本概念以后再来看看题主疑惑的地方。其实题主疑惑的是在java中,对象的引用是个什么东西,它们和对象的实例化过程有什么关系。
别急我们先来分析下java中一个引用是怎么实现的:
一个Java的引用访问涉及到3个内存区域:JVM栈,堆,方法区。
以最简单的本地变量引用:Object obj = new Object()为例:
Object obj表示一个本地引用,存储在JVM栈的本地变量表中,表示一个reference类型数据;
new Object()作为实例对象数据存储在堆中;
堆中还记录了Object类的类型信息(接口、方法、field、对象类型等)的地址,这些地址所执行的数据存储在方法区中;
具体的实现方式有很多种,句柄是其中一种,关系如图所示。
看到这里应该就明白了。类本身的信息,类实例数据,以及指向对象的引用信息分别放在 java 的方法区和栈区以及堆区。
在题主的例子中:
CodeBlock02 code = new CodeBlock02();
code 就是存放在本地变量表的一个引用,它指向堆中的对象实例数据。而这个对象实例数据,就是通过new CodeBlock02() 取到的。
再具体一点:
1. 你写的 CodeBlock02.java 文件存放了 CodeBlock02 类的定义,当 jvm 的类加载器加载这个java文件的时候,将其中的类型定义语句存放在了 jvm 的方法区中。2. 但是这个时候并没有在堆中生成这个对象的实例,也就是说,这个时候因为没有对象,你并不能调用 CodeBlock02 类的非静态方法。3. 什么时候获取的对象呢?就是在用 new 关键字执行了本类的构造方法以后 new CodeBlock02() 从这时候开始通过 new 关键字和类的构造器, jvm 在虚拟机的堆区创建了一个 CodeBlock02 类的实例,并返回这个实例的引用,同时你也可以通过这个引用调用它的非静态方法了。
综上所述,code 就是你用来接收 new 出的实例的的“遥控器”,它指向这个对象在堆区的具体位置。
回答2:你需要理解 java 的引用
CodeBlock02 code = new CodeBlock02();
左边这个叫做 CodeBlock02 类型的变量。
右边这个叫做 CodeBlock02 类型的对象。
你也可以让这个变量依次指向两个类型相同的不同对象。
CodeBlock02 code;CodeBlock02 code1 = new CodeBlock02();CodeBlock02 code2 = new CodeBlock02();code = code1;//code.doSomething(); 相当于 code1.doSomething();code = code2;//code.doSomething(); 相当于 code2.doSomething();
你甚至可以让这个类型的变量指向这个类型的子类的对象:
MyClass m = new SubMyClass(); //SubMyClass 继承于 MyClass
还可以这样直接在 new 出来的对象上调用方法:
new CodeBlock02().doSomething();回答3:
两个都是声明对象 楼主问的应该是赋值
如果后面不对这个值继续操作的话 赋不赋值都是一样的
new CodeBlock02() // 声明了之后不赋值,没有办法后续对这个对象继续操作CodeBlock02 code = new CodeBlock02(); // 把声明的对象赋值给一个变量,可以进行后续操作
回答4:左边的是对象的引用变量,右边的是在内存实际分配的对象。