Objetos y referencias
nuevo objeto
El ejemplo más sencillo:
new Object();
En pocas palabras, new Object () es una instancia de tipo de objeto creado (ejemplo), asignado en la memoria de pila JVM
Para método público como un ejemplo, mira:
PD: si el método es público o privado / protegido / método de paquete, o método de construcción es, incluso en un bloque estático, las variables estáticas, variables de instancia, nuevo objeto de esta acción, es mucho lo mismo
public class Test {
public void fun1() {
Object o = new Object();
}
}
En el método fun1 de la clase de prueba se crea una instancia de un objeto, y se asigna a una variable de tipo Object, cuando se llama a este método, ¿qué pasó?
1. Realice javac Test.java
compilados a Test.class
archivos
2. ejecución javap -v Test.class
, se puede ver el compilado .class
archivo de código de bytes. Éstos son sólo fun1
public void fun1();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=1
0: new #2 // class java/lang/Object
3: dup
4: invokespecial #1 // Method java/lang/Object."<init>":()V
7: astore_1
8: return
LineNumberTable:
line 3: 0
line 4: 8
la sección del Código de enfoque
pila = 2 indica que el proceso requiere una profundidad de pila de operandos 2
locales = 2 indica que el proceso requiere de dos espacio variable local de Slot
Aquí es seguido por el desplazamiento y el correspondiente conjunto de instrucciones JVM, podemos analizar estos paso a paso conjunto de instrucciones para hacer algo
En primer lugar, la inicialización de la pila de operandos y el espacio variable local es tal que:
Juego de operaciones | código correspondiente |
---|---|
0: nuevo, crear una instancia de un tipo java.lang.Object, y su valor de referencia en la pila de operandos de la pila (PS: Esto no se refiere a un valor de referencia Object o ) de la pila de operandos: [libre], [ObjectRef] tabla de variables locales: [del presente], [libre] |
new Object () |
3: dup, copia la pila valor del operando pila, y apile el valor del operando en la pila de operando pila: [ObjectRef], [ObjectRef] tabla de variables locales: [el este], [libre] |
new Object () |
4: invokespecial, llamando a la java.lang.Object "
la pila de operandos: , [ObjectRef] [libre] tabla de variables locales: [el este], [libre] |
new Object () |
7: astore_1, el valor de referencia de la parte superior de la pila en las variables locales segunda pila de operandos: [libre], [libre] tabla de variables locales: [el este], [ObjectRef] |
Object o = new Object (), principalmente el operador de asignación |
8: retorno, desde el actual método devuelve void |
A partir del análisis de los pasos anteriores se puede encontrar, un método simple en una nueva operación de objeto, la JVM se ejecuta tres instrucciones, a saber:
- Crear el objeto y la pila de valor de referencia
- Copiar la parte superior del valor de la pila
- Llamar al constructor de la superclase
Este valor de referencia ObjectRef conducir más fácilmente a la ambigüedad, se suele decir que la referencia es a Object o = new Object()
la dar al operador la izquierda del objeto O , señalar que esta sentencia no crea una referencia, pero la referencia a la instancia del objeto, en las variables locales
VS no se asigna la asignación
Al crear objetos a utilizar, en esta situación
Object o = new Object();
o.toString();
Crear una instancia del tipo de objeto, y luego llamar a su toString
método
El mismo texto también puede ser de esta manera:
new Object().toString();
¿Hay alguna diferencia de estos dos métodos? Por instrucciones de la JVM para mirar
fuente:
public class Test {
public void invokeWithoutReference() {
new Object().toString();
}
public void invokeWithReference() {
Object o = new Object();
o.toString();
}
}
conjunto de instrucciones ( javap -v Test.class
conservando solamente la sección del conjunto de instrucciones):
public void invokeWithoutReference();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: new #2 // class java/lang/Object
3: dup
4: invokespecial #1 // Method java/lang/Object."<init>": ()V
7: invokevirtual #3 // Method java/lang/Object.toString:()Ljava/lang/String;
10: pop
11: return
public void invokeWithReference();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=1
0: new #2 // class java/lang/Object
3: dup
4: invokespecial #1 // Method java/lang/Object."<init>": ()V
7: astore_1
8: aload_1
9: invokevirtual #3 // Method java/lang/Object.toString:()Ljava/lang/String;
12: pop
13: return
No hay ninguna diferencia entre la referencia y es la referencia, en invokeWithReference
la generación de Object
los ejemplos, ejecutado astore_1
y aload_1
dos instrucciones, en donde:
astore_1
Shows un valor de referencia de pila se almacena en la segunda ranura de la mesa variable local, lo que representa el operador de asignación ( =
) hacen
aload_1
Dicho segundo tipo de referencia pila variable local inserta en la pila de operandos
Específicamente, en dos métodos diferentes de conjuntos de ejecución de instrucciones:
invokeWithoutReference
Juego de operaciones | código correspondiente |
---|---|
0: nuevo, crear una instancia de un tipo java.lang.Object, pila y su valor de referencia en el operando pila operando pila: [libre], [ObjectRef] tabla de variables locales: [el este] |
new Object () |
3: dup, copia la pila valor del operando pila, y apile el valor del operando en la pila de operando pila: [ObjectRef], [ObjectRef] tabla de variables locales: [el este] |
new Object () |
4: invokespecial, llamando a la java.lang.Object "
la pila de operandos: , [ObjectRef] [libre] tabla de variables locales: [el este] |
new Object () |
7: invokevirtual, método toString java.lang.Object invocación porque el método toString devuelve un valor, el resultado se realizará donde la pila se empuja en la pila de operandos: , [java.lang.String] [libre] tabla de variables locales : [el este] |
new Object () .toString (); |
10: pop, la parte superior de la pila pop valor de funcionamiento de la pila: [libre], [libre] locales tabla de variables: [del presente] |
new Object () .toString (); |
11: Vuelve, vuelve vacío desde el método actual |
invokeWithReference
Juego de operaciones | código correspondiente |
---|---|
0: nuevo, crear una instancia de un tipo java.lang.Object, y su valor de referencia en la parte superior de la pila de operando pila de la pila de operandos: [libre], [ObjectRef] tabla de variables locales: [el este], [inactivo ] |
new Object () |
3: dup, copia la pila valor del operando pila, y apile el valor del operando en la pila de operando pila: [ObjectRef], [ObjectRef] tabla de variables locales: [el este], [libre] |
new Object () |
4: invokespecial, llamando a la java.lang.Object "
la pila de operandos: , [ObjectRef] [libre] tabla de variables locales: [el este], [libre] |
new Object () |
7: astore_1, el valor de referencia de la parte superior de la pila en las variables locales segunda pila de operandos: [libre], [libre] tabla de variables locales: [el este], [ObjectRef] |
Objeto O = new Object (); |
8: aload_1,将第二个本地变量推入栈顶 操作数栈:[空闲], [objectref] 局部变量表:[this], [objectref] |
|
9: invokevirtual,调用java.lang.Object的toString方法,因为toString方法有返回值,所以这里会将执行的结果推入栈顶 操作数栈:[空闲], [java.lang.String] 局部变量表:[this], [objectref] |
new Object().toString(); |
12: pop,将栈顶数值弹出 操作数栈:[空闲], [空闲] 局部变量表:[this], [objectref] |
new Object().toString(); |
13: return,从当前方法返回void |
引用?
首先,什么是引用?
《深入理解JVM虚拟机》一书中多次对Java的引用进行了讨论
对象引用(reference类型,它不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)——《深入理解JVM虚拟机》 2.2.2 Java虚拟机栈
建立对象是为了使用对象,我们的Java程序需要通过栈上的reference数据来操作堆上的具体对象。——《深入理解JVM虚拟机》 2.3.3 对象的访问定位
一般来说,虚拟机实现至少都应当能通过这个引用做到两点,一是从此引用中直接或间接地查找到对象在Java堆中地数据存放地起始地址索引,二是此引用中直接或间接地查找到对象所属数据类型在方法区中的存储的类型信息 ——《深入理解JVM虚拟机》 8.2.1 局部变量表
对于new Object()
来说,JVM执行的new(0xbb)
指令天然的就会将新实例的引用压入操作数栈的栈顶
而Object o = new Object()
只是利用=
运算符,让JVM执行了astore_n
指令,将这个引用保存到了局部变量表中,以便我们以后可以直接通过o.xxx()
来对这个实例做一些操作
等到我们需要使用的时候,JVM再通过aload_n
将指定的局部变量表中的引用类型值推到操作数栈的栈顶进行后续操作
所以在我看来,Object o
其实是一个引用类型的本地变量
创建对象到底赋值吗?
回到初衷,是否定义一个引用类型的本地变量,没有一个绝对的优劣
Object o = new Object()
仅仅是比new Object()
多在局部变量表中保存了一个Object o
引用类型,但它可以让我们在创建了实例之后,重复对这个实例进行操作
new Object()
在进行了new Object().toString()
这种方式的调用之后,由于局部变量表中没有了该实例的引用,操作数栈中的那个两个由dup
产生的两个引用,也已经分别因为invokespecial
和invokevirtual
弹出栈了,所以这个对象已经没有指向它的引用了
如果我们对于实例只是一次性调用,那么直接new Object()
的方式也未尝不可