在 Java 中如何将向上转换的对象存储在堆中
How is upcasted object stored in Heap in Java
所以我在这里写了一个简单的java代码如下图。
package com.example.aakash;
public abstract class Book {
String title;
String author;
Book(String t,String a){
title=t;
author=a;
}
abstract void display();
}
所以 Book 是一个抽象 class,它将被两个不同的 class 扩展,称为 MyBook 和 ABook class,如下所示。
package com.example.aakash;
public class MyBook extends Book {
private int price;
MyBook(String t, String a,int p) {
super(t, a);
price = p;
}
@Override
public void display(){
System.out.println("Title: "+title);
System.out.println("Author: "+author);
System.out.println("Price: "+price);
}
}
package com.example.aakash;
public class ABook extends Book {
private int price;
ABook(String t, String a,int p) {
super(t, a);
price = p;
}
@Override
public void display(){
System.out.println("Title: "+title);
System.out.println("Author: "+author);
System.out.println("Price: "+price);
}
}
package com.example.aakash;
import java.util.ArrayList;
public class Main {
public static void main(String[] args){
ArrayList<Book> myArray = new ArrayList<Book>();
String bookName = "Book";
for(int i=1;i<=10;i++){
if(i%2==0){
String tempBook = bookName + i;
String author = "Author2";
Book temp = new MyBook(tempBook,author,i*50);
myArray.add(temp);
}else{
String tempBook = bookName + i;
String author = "Author1";
Book temp = new ABook(tempBook,author,i*50);
myArray.add(temp);
}
}
for(int i=0;i<10;i++){
Book temp = myArray.get(i);
temp.display();
System.out.println("--------------------------------------------------");
}
myArray.get(5).display();
}
}
好的,所以当我运行这个程序时,显示方法每次都打印正确的书、作者和价格。就像它们的存储方式一样。但是在 运行 时间,JVM 不知道数组列表中的 Book 对象是 MyBook 还是 ABook 类型。所以我的问题是调用显示方法如何每次都打印正确的书。 Book 对象的 ArrayList 如何存储在堆上? (即所有对象都存储在 Java 中的堆中)是否将其存储为向上转换的 Book 对象。或者它是否将它存储为实际的 MyBook 和 ABook 类型的对象,以便在调用 display 方法时,JVM 明确知道要调用 MyBook 或 ABook 上的方法?
P.S。是的,这个例子有点糟糕,但假设我在 MyBook 和 ABook 上什至没有类似的显示方法。即使这样 JVM 仍然执行正确的显示方法。所以请解释一下向上转换完成后 JVM 中发生了什么。
具体对象是一个 ABook
声明为 Book
。
哪个 display
方法 select 在运行时 解析 ,而不是编译时。
来自JLS:
If the method that is to be invoked is an instance method, the actual
method to be invoked will be determined at run time, using dynamic
method lookup (§15.12.4).
基本上就是在运行时取真实的实例类型。如果该方法在 class 中被覆盖,则执行此方法,如果未被覆盖,则调用 super class 的方法。
这个概念叫做多态。
书是书
MyBook 是一本书
您可以拥有一个 ArrayList 并拥有 ABook 和 MyBook 的内部对象。
但是每次你得到这个 ArrayList 的一个对象,你只能调用 book 的方法,除非你向下转型到这个子类之一。但是你必须在转换之前确定子类,因为你可能会得到一个CastException。
最后,如果你调用了一个override方法,在运行时他总会调用对象的方法。在运行时他我们总是调用对象方法。
您混淆了对象和引用。您永远不会将对象存储在 ArrayList
中,您只是将 reference 存储到对象中。引用的类型可能比它所指向的对象的类型更广泛,但这绝不会改变对象的实际类型。
事实上,由于类型擦除,一个ArrayList<Book>
甚至不包含Book
类型的引用,它对此一无所知.它只包含 Object
类型的引用,但足以让 ArrayList
正常工作,是在 java.lang.Object
中声明的方法 boolean equals(Object)
。由于引用的类型不会更改实际对象的类型,因此通过类型为 Object
的引用在对象上调用 equals
仍将调用实际类型的最具体方法,如果 equals
方法已被覆盖。
您也可以在 List
中多次存储同一个对象,而“存储一个对象”是一个口语化的术语,您 总是 必须将其翻译为“为自己存储对该对象的引用”。如果你这样做,仍然只有一个对象,列表将只包含对它的多个引用。通过这些引用之一修改该对象将立即通过所有其他引用可见。
对象的类型在创建时就已确定,并以特定于实现的方式存储在对象中。这也是为什么将类型缩小为更具体的运行时类型可以验证正确性的原因。强制转换并没有改变对象的类型,它只是在证明正确性之后,从具有更广泛类型的引用中创建对具有更具体类型的同一对象的新引用。从 ArrayList<Book>
检索引用时也会隐式发生这种情况;存储在列表中的 Object
类型的引用被转换为您将收到的 Book
类型的引用。
所以我在这里写了一个简单的java代码如下图。
package com.example.aakash;
public abstract class Book {
String title;
String author;
Book(String t,String a){
title=t;
author=a;
}
abstract void display();
}
所以 Book 是一个抽象 class,它将被两个不同的 class 扩展,称为 MyBook 和 ABook class,如下所示。
package com.example.aakash;
public class MyBook extends Book {
private int price;
MyBook(String t, String a,int p) {
super(t, a);
price = p;
}
@Override
public void display(){
System.out.println("Title: "+title);
System.out.println("Author: "+author);
System.out.println("Price: "+price);
}
}
package com.example.aakash;
public class ABook extends Book {
private int price;
ABook(String t, String a,int p) {
super(t, a);
price = p;
}
@Override
public void display(){
System.out.println("Title: "+title);
System.out.println("Author: "+author);
System.out.println("Price: "+price);
}
}
package com.example.aakash;
import java.util.ArrayList;
public class Main {
public static void main(String[] args){
ArrayList<Book> myArray = new ArrayList<Book>();
String bookName = "Book";
for(int i=1;i<=10;i++){
if(i%2==0){
String tempBook = bookName + i;
String author = "Author2";
Book temp = new MyBook(tempBook,author,i*50);
myArray.add(temp);
}else{
String tempBook = bookName + i;
String author = "Author1";
Book temp = new ABook(tempBook,author,i*50);
myArray.add(temp);
}
}
for(int i=0;i<10;i++){
Book temp = myArray.get(i);
temp.display();
System.out.println("--------------------------------------------------");
}
myArray.get(5).display();
}
}
好的,所以当我运行这个程序时,显示方法每次都打印正确的书、作者和价格。就像它们的存储方式一样。但是在 运行 时间,JVM 不知道数组列表中的 Book 对象是 MyBook 还是 ABook 类型。所以我的问题是调用显示方法如何每次都打印正确的书。 Book 对象的 ArrayList 如何存储在堆上? (即所有对象都存储在 Java 中的堆中)是否将其存储为向上转换的 Book 对象。或者它是否将它存储为实际的 MyBook 和 ABook 类型的对象,以便在调用 display 方法时,JVM 明确知道要调用 MyBook 或 ABook 上的方法?
P.S。是的,这个例子有点糟糕,但假设我在 MyBook 和 ABook 上什至没有类似的显示方法。即使这样 JVM 仍然执行正确的显示方法。所以请解释一下向上转换完成后 JVM 中发生了什么。
具体对象是一个 ABook
声明为 Book
。
哪个 display
方法 select 在运行时 解析 ,而不是编译时。
来自JLS:
If the method that is to be invoked is an instance method, the actual method to be invoked will be determined at run time, using dynamic method lookup (§15.12.4).
基本上就是在运行时取真实的实例类型。如果该方法在 class 中被覆盖,则执行此方法,如果未被覆盖,则调用 super class 的方法。
这个概念叫做多态。 书是书 MyBook 是一本书
您可以拥有一个 ArrayList 并拥有 ABook 和 MyBook 的内部对象。 但是每次你得到这个 ArrayList 的一个对象,你只能调用 book 的方法,除非你向下转型到这个子类之一。但是你必须在转换之前确定子类,因为你可能会得到一个CastException。
最后,如果你调用了一个override方法,在运行时他总会调用对象的方法。在运行时他我们总是调用对象方法。
您混淆了对象和引用。您永远不会将对象存储在 ArrayList
中,您只是将 reference 存储到对象中。引用的类型可能比它所指向的对象的类型更广泛,但这绝不会改变对象的实际类型。
事实上,由于类型擦除,一个ArrayList<Book>
甚至不包含Book
类型的引用,它对此一无所知.它只包含 Object
类型的引用,但足以让 ArrayList
正常工作,是在 java.lang.Object
中声明的方法 boolean equals(Object)
。由于引用的类型不会更改实际对象的类型,因此通过类型为 Object
的引用在对象上调用 equals
仍将调用实际类型的最具体方法,如果 equals
方法已被覆盖。
您也可以在 List
中多次存储同一个对象,而“存储一个对象”是一个口语化的术语,您 总是 必须将其翻译为“为自己存储对该对象的引用”。如果你这样做,仍然只有一个对象,列表将只包含对它的多个引用。通过这些引用之一修改该对象将立即通过所有其他引用可见。
对象的类型在创建时就已确定,并以特定于实现的方式存储在对象中。这也是为什么将类型缩小为更具体的运行时类型可以验证正确性的原因。强制转换并没有改变对象的类型,它只是在证明正确性之后,从具有更广泛类型的引用中创建对具有更具体类型的同一对象的新引用。从 ArrayList<Book>
检索引用时也会隐式发生这种情况;存储在列表中的 Object
类型的引用被转换为您将收到的 Book
类型的引用。