将一种对象类型分配给另一种对象类型时,Java 变量如何工作?

How does Java variables work when assigning one object type to another object type?

我不太确定如何立即解释我的疑问,所以我将从一个例子开始。我有下一行:

Object myObject = null;
Integer myInteger = 10;

myObject = myInteger;

基本上,我知道我可以将一个 subclass 对象分配给一个 superclass 对象(在这种情况下,myInteger 类型 Integer 到类型 ObjectmyObject)。到目前为止一切顺利,但现在...假设我想获得 myObject.

的 class
System.out.println("myObject: " + myObject.getClass());

它打印出这个:

myObject: class java.lang.Integer 

它说是类型 Integer!

我知道变量实际上存储了对内存中实际对象的引用,所以方法 getClass() 返回实际对象的 class。但是后来...

  1. 为什么我不能通过 myObject 访问 class Integer 的方法,因为它指向一个类型的对象Integer?我的意思是,为什么我不能做这样的事情? myObject.intValue();

  2. 如果我只能通过myObject访问Object的方法(即使它指向[=14=类型的对象]),Java如何知道通过myObject可以调用哪些方法?变量本身存储一种 class?

基本上,我想知道Java 变量中存储了哪些信息,它只是一个参考吗?或者它也有一个 class 名字?如果变量已经分配了对 subclass 对象的引用,它如何知道可以调用哪些方法?

我希望我的疑问是清楚的,并原谅我的语法。

你的问题的答案基本上归结为两个关键点:“编译器不允许你做危险的事情”和“编译器没有你想象的那么聪明”。

Why can't I access the methods of the class Integer through myObject since is pointing to an object of type Integer?

知道因为你看到行 myObject = myInteger;,所以你知道 myObject 将有一个 intValue() 方法.

当编译器看到您调用 myObject.intValue(); 时,它不会像您那样返回几行来找出 myObject 实际引用的内容。它只看到它的类型是 Object,并允许您仅使用 Object 中定义的可用内容。由于它不回顾 myObject 实际指的是什么,就编译器而言,myObject 可能 指的是 Integer ,但它也可以引用 String,因为你可以这样做:

myObject = "Hello";

String 没有 intValue() 方法。编译器说:“安全总比遗憾好”,并且不允许您调用 intValue。你可以通过转换告诉编译器你更了解:

((Integer)myObject).intValue();

how does Java know which methods can be called through myObject?

由表达式的类型决定。表达式 myObject 的类型为 Object,因为变量 myObject 的类型为 Object。另一方面,表达式 ((Integer)myObject) 的类型是 Integer.

考虑这个类比:您提供具有三层“安全级别”的存储服务:普通商品、贵重物品和奢侈品。您有一个在线系统,可以让您的客户预订 space 并根据他们想要存储的内容进行付款。根据等级,您的服务和预订要求不同:

普货:

  • 你在共享仓库中获得space
  • 您按物品数量支付

有价值的物品:

  • 您在共享仓库
  • 中获得存储空间space
  • 您可以通过摄像头监控获得额外的安全保障
  • 您按物品数量支付

奢侈品:

  • 您将获得带有位置标签的专用存储空间 space
  • 您有摄像头监控 保安人员 24/7 全天候监视您的物品。
  • 您按物品价值支付。

现在,假设您的客户将物品放在标有您的预订系统发布的参考编号的盒子中,并且您的保安人员会在现场检查物品。


让我们看看您的流程步骤:

在线系统:

  • 让您的客户声明他们要购买的等级
  • 为声明的预订预留储藏室
  • 根据客户的预订,使用正确的付款方式收取费用
  • 提供显示物理存储基本详细信息的跟踪系统

保安:

  • 检查客户在盒子中展示的实际实物物品
  • 确保物品与预订一致,但如果客户选择,黄金可以存放在普通物品区,但为了保险起见,旧塑料杯不能带入奢侈品。

您如何看待这些场景?

  • 客户使用您的在线系统预订普通货物仓储并完成预订。当他们回来检查他们的物品状态时:系统会让他们看到 24/7 全天候看管他们物品的保安的名字吗?
    提示:不,系统知道他们有普通商品等级,不提供专门的看守服务。
  • 同一客户将金币放入箱子,并在现场出示。
    • 在线系统会让他们看到 24/7 守卫或视频片段的详细信息吗?
      提示:没有(仍然),因为系统知道是普货预订。
    • 检查他们的保安会知道盒子里装的是金子而不是廉价物品吗?
      提示:是的。但是,客户仍然无法获得奢侈品服务。对吗?
  • 另一位客户进行了“奢侈品”预订,但赠送了一个装有旧塑料杯的盒子。
    • 您的在线系统会阻止他们这样做吗?
      提示:不,它不能,因为它不知道客户的想法,也无法检查项目。
    • 你们现场的守卫能阻止这个箱子被登记吗?
      提示:是的,因为他们可以看到实物并确定它到底是什么。而且因为保险禁止在较高等级的低价值物品,警卫可以阻止这种情况。

Java也是如此(粗略地说):

  • 声明变量并赋予其类型 => 进行预订并声明商品种类
  • 为声明的变量赋值 => 将物品放入盒子中并将其交给警卫检查
  • 在声明的变量上调用方法 => 使用服务或在线跟踪(您无法获得您的等级无权获得的服务:即使您在普通商品中拥有黄金,也无法获得视频镜头)

换句话说,在线系统就像编译器,物理位置就像运行时。
在Java中,您在线预订的层类型称为“静态类型”,它是编译时(预订时)已知的类型。您放入盒子中的实际类型被称为“运行时 class”,它是动态的(就像普通商品等级盒子可以放入黄金或中等价值的物品,如电视)。

就像盒子一样,只有运行时知道变量的actual/runtime class:只有现场工作人员才知道盒子里是金子。当您调用 myObject.getClass() 时,这正是您所要求的:myObjectruntime class(这是 class 其构造函数被调用或与之一起使用 new;或者在某些情况下,文字的 class,例如 10 for intInteger ,但这是另一天的故事)。但是即使在运行时已知它是一个 Integer,编译器(在线系统,还记得吗?)不会让你调用 Integer 上的方法,因为 myObject 被声明为 Object:即使盒子里装的是钻石,在线系统也不会让你看到普货预订的视频。您可以强制编译器接受 IntegermyObject 的方法并进行类型转换,如 所示,这有点像在现场升级到奢侈品,之后在线系统将使您获得更高级别的服务:)


这个类比在某些方面可能很荒谬,例如编译器会阻止您将不兼容或更广泛类型的值分配给变量……但这只是一个例子,限量一个。