new + 类名,一定需要申明一个对象吗?

宋虎子
  • 35
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();
他们是一样的吗!

回复
阅读 6.7k
4 个回答

先明确几个概念,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、对象类型等)的地址,这些地址所执行的数据存储在方法区中;

具体的实现方式有很多种,句柄是其中一种,关系如图所示。
clipboard.png

看到这里应该就明白了。类本身的信息,类实例数据,以及指向对象的引用信息分别放在 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 出的实例的的“遥控器”,它指向这个对象在堆区的具体位置。

chaneyzorn
  • 588

你需要理解 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();

两个都是声明对象 楼主问的应该是赋值

如果后面不对这个值继续操作的话 赋不赋值都是一样的

new CodeBlock02() // 声明了之后不赋值,没有办法后续对这个对象继续操作
CodeBlock02 code = new CodeBlock02(); // 把声明的对象赋值给一个变量,可以进行后续操作

左边的是对象的引用变量,右边的是在内存实际分配的对象。

宣传栏