所以我在这里写了一个简单的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();
}
So Book是一个抽象类,由两个不同的类MyBook和ABook类扩展,如下所示。
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中存储在堆中)是否将其存储为upcasted Book对象。或者它是否将其存储为实际的MyBook和ABook类型的对象,以便在调用display方法时,JVM明确知道要调用MyBook或ABook上的方法?
P.S。是的例子有点不好但是假设我在MyBook和ABook上甚至没有类似的显示方法。即使这样,JVM仍然执行正确的显示方法。因此,请在上传时解释JVM中发生的事情。
答案 0 :(得分:1)
具体对象是ABook
,声明为Book
。
哪个display
方法选择在运行时解析 ,而不是编译时。
来自JLS:
如果要调用的方法是实例方法,则为实际 要调用的方法将在运行时确定,使用动态 方法查找(§15.12.4)。
基本上它意味着真实实例类型是在运行时获取的。如果在该类中覆盖该方法,则执行此方法,如果未覆盖该方法,则调用超类的方法。
答案 1 :(得分:1)
您正在混合对象和引用。您永远不会在ArrayList
中存储对象,只是将引用存储到对象中。引用可能具有比它指向的对象更宽泛的类型,但它永远不会更改对象的实际类型。
事实上,由于类型擦除,ArrayList<Book>
甚至不包含Book
类型的引用,它不知道它。它只包含Object
类型的引用,但这足以使ArrayList
能够正常工作,是boolean equals(Object)
中声明的方法java.lang.Object
。由于引用的类型不会更改实际对象的类型,因此通过类型为equals
的引用调用对象上的Object
仍会调用实际类型的最具体方法,如果{ {1}}方法已被覆盖。
您也可以在equals
中多次存储同一个对象,而“存储对象”是一个通俗的术语,您总是必须转换为“存储对该对象的引用”对象“为你自己。如果你这样做,仍然只有一个对象,列表将只包含多个引用。通过其中一个引用修改该对象将立即通过所有其他引用可见。
当对象的类型以特定于实现的方式创建并存储在对象中时,该对象的类型是固定的。这也是为什么运行时类型将类型缩小到更具体的类型可以验证正确性的原因。强制转换不会更改对象的类型,只有在证明了正确性后,它才会创建对具有更广泛类型的引用的更具体类型的同一对象的新引用。从List
检索引用时,这也会隐式发生;存储在列表中的ArrayList<Book>
类型的引用将转换为您将收到的Object
类型的引用。
答案 2 :(得分:0)
这个概念叫做多态性。 ABook是一本书 MyBook是一本书
您可以拥有一个ArrayList并拥有ABook和MyBook的内部对象。 但是每当你得到这个ArrayList的一个对象时,你只能调用book的方法,除非你向这个子类之一做一个向下转换。但是你必须在强制转换之前确定子类,因为你可能会得到一个CastException。
最后,如果调用override方法,在运行时他将始终调用object方法。在运行时,我们总是调用对象方法。