5

如果我将 JungleCat 作为 Cat 的子类(JungleCat extends Cat),然后我说:

JungleCat cat1 = new JungleCat();
Cat cat2 = new Cat();
Cat cat3 = new JungleCat();
JungleCat cat4 = new Cat(); //this one is illegal, right?
JungleCat cat5;

我想知道 , , , 和 的cat1对象cat2类型cat3cat4什么cat5?我还想知道为什么在实例化一个对象时会有冗余:为什么在实例化一个对象时需要列出两种对象类型。

如果这是一个非常基本的问题,我很抱歉,但这是我只想一劳永逸地知道的事情,并且有一个很好的答案和很好的推理,我知道我可以在这里期待(而不是雅虎问答,等):P

4

6 回答 6

5

在下面的声明中: -

JungleCat cat1 = new JungleCat();

您可以将其分为两部分:-

JungleCat cat1;  // This creates a reference of type `JungleCat`
cat1 = new JungleCat();   // This creates an object of type `JungleCat`.

现在,您正在进行cat1引用,指向JungleCat对象。引用只不过是指向所创建对象的链接,因此您可以访问它们。

您也可以像这样创建对象:-

new JungleCat();   // Will create an unnamed object

但是,在上述情况下,您将只能在实例化的地方使用方法和属性。但是,稍后,由于您没有访问该对象的引用,因此您也无法访问它的属性。


现在,让我们继续第二个声明:-

Cat cat = new JungleCat();

在这里,您可以猜到,您有 type 的引用和 typeCat - Super Class的对象JungleCat。这就是我们所说Polymorphism的。

因此,基本上,您可以创建任何超类型的引用,并使其指向任何子类型的对象。这很容易理解—— “因为 JungleCat 只是一只猫。所以,你总是可以有一个 Cat 参考点指向 JungleCat”。

反过来,这不是真的。例如: -

JungleCat ref = new Cat();

现在这是无效的。因为 aCat不一定是 a JungleCat。它可以是任何其他猫。所以,你不能让你的JungleCat参考点指向一个Cat对象。


现在这是您真正关心的问题:-

我想知道 cat1、cat2、cat3、cat4 和 cat5 的对象类型是什么

好吧,cat1, cat2.. 不是对象,而是指向某些对象的引用。并且你可以从上面的解释中推断出它们各自的引用类型。

对象类型是对象创建语句的 RHS 上使用的类型。与new关键字一起使用的类型是Object. 您可以有不同类型的引用指向相同的对象类型。

因此,您可以同时拥有cat1, 和cat2指向同一对象类型的引用。

于 2012-12-14T05:28:30.357 回答
2

当您实例化一个对象时,您必须指定

  1. 您的新变量能够引用的引用类型或类型(类)是什么
  2. 要初始化一个对象,我们需要指定对象的类型(类)

所以基本上你需要创建一个 JungleCat 对象并创建一个指向它的引用。指向它的引用能够指向 Cat(JungleCat、PetCat 和所有猫),以防万一

Cat cat3 = new JungleCat();

只有 JungleCats 如果

JungleCat cat3 = new JungleCat();

关于

object types of cat1, cat2, cat3, cat4, and cat5?

对象类型将是您从分配运算符右侧分配给它的内容。

关于

JungleCat cat4 = new Cat(); //this one is illegal, right?

是的,因为猫不一定是 JungleCat(我们只知道它是一只猫),但 JungleCat 肯定是一只猫。这就是为什么 Cat cat= new JungleCat();有效

于 2012-12-14T05:20:59.690 回答
2

cat1, cat2, cat3, cat4, 和的对象类型是cat5什么?

我不知道在这种情况下“对象类型”是什么意思。我建议改为根据声明的类型运行时类型来考虑事情。

  • 声明的类型cat1JungleCat; 的运行时类型cat1也是JungleCat
  • 声明的类型cat2Cat; 的运行时类型cat2也是Cat
  • 声明的类型cat3Cat; 的运行时类型cat3JungleCat
  • 声明的类型cat4JungleCat; 的运行时类型cat4Cat
    • 是的,这是非法的。如果没有明确的向下转型,它将无法编译;使用显式强制转换......它可能会但它会在运行时引发异常。
  • 声明的类型cat5JungleCat; 它没有运行时类型。

“冗余”的重点是因为 Java 是静态类型的,并且还支持动态调度。静态类型要求变量具有声明的类型;动态分派允许运行时类型与声明的类型不同。

例如,您可以在程序执行过程中将多个对象分配给同一个变量:

Cat c1 = new Cat();
c1 = new JungleCat();

声明的类型c1决定了可以通过该变量调用哪些方法:

Cat c1 = new Cat();
c1.purr(); // compiles without error
c1 = new JungleCat();
c1.purr(); // compiles without error

同样,它确定哪些方法不能通过该变量调用:

JungleCat jc1 = new JungleCat();
Cat c1 = jc1;
jc1.roar(); // compiles without error - JungleCats can roar!
c1.roar(); // compile error! Cats don't roar

这允许变量的行为因运行时类型(多态性)而异,同时仍具有编译时检查。

于 2012-12-14T05:28:56.760 回答
0

丛林猫 cat4 = new Cat(); //这个是非法的,对吧?

是的。派生类引用不能指向它的基类。

我想知道 cat1、cat2、cat3、cat4 和 cat5 的对象类型是什么?

getClass方法知道。

为什么在实例化对象时需要列出两种对象类型。

这被称为polymorphism。即一个基类引用可以指向它的任何派生类。

于 2012-12-14T05:24:41.393 回答
0

- Polymorphism作为子类的对象的工作被分配给超类对象引用变量

-由于这是一个class polymorphism类必须在同一个继承树中,并且子类必须是超类型的。

所以

Cat c = new JungleCat();   // Will work

但,

JungleCat c = new Cat();   // Won't work
于 2012-12-14T05:25:50.090 回答
0

这是多态性背后的基本原理。每个对象的类型是出现在表达式左侧的内容,但是,它的行为与右侧的内容一样。例如,让我们考虑一个这样的函数:

public static void Claw(Cat cat)
{
    cat.claw();
}

现在,我们假设我们的类是这样的:

public class Cat
{
    //...

    public void claw()
    {
        System.out.println("Cat claw");
    }
}

public class JungleCat extends Cat
{
    //...

    @Override
    public void claw()
    {
        System.out.println("Jungle cat claw");
    } 
 }

现在,让我们实例化一些对象:

Cat c = new Cat();
Cat j = new JungleCat();

传入我们的static Claw函数:

Claw(c); //Prints "Cat claw"
Claw(j); //Prints "Jungle cat claw"

假设我们像这样修改了函数:

public static void Claw(JungleCat junglecat)
{
    junglecat.claw();
}

然后Claw(c)显然不会编译,因为它是一个Cat. 怎么样Claw(j)?这也不会编译,因为我们已经说过它也是 type Cat。我们知道它实际上是一个JungleCat,但我们已经告诉编译器“好吧,把它当作一个普通Cat类型来对待”。

在许多这些初学者示例中经常缺少的是为什么有人想要这样做的动机。猫和动物等通常是不好的例子。举个更好的例子,假设我们有一些代码根据运行的操作系统来做不同的事情;说移动文件。Linux如果它/usr/binC:\Windows\System32. 所以我们定义一个这样的接口:

public interface FileMover
{
    public void move(File f);
}

public class LinuxMover implements FileMover
{
    public void move(File f)
    {
       //Move our file to /usr/bin
    }
}

public class WindowsMover implements FileMover
{
    public void move(File f)
    {
       //Move our file to C:\Windows\System32
    }
 }

现在,直到运行时我们才知道这段代码将在哪个系统上运行——所以我们可以进行运行时检查并在运行时实例化正确的类:

//Pseudocode

FileMover fm;
if(OperatingSystem == LINUX) {
    fm = new LinuxMover();
} 
else if(OperatingSystem == WINDOWS) { 
    fm = new WindowsMover();
}

fm.move();

同样,任何函数都可以简单地采用 a FileMover- 他们不在乎是什么类型- 因此我们不必在每次想要使用不同类型时编写两个函数定义FileMover,例如doSomething(WindowsFileMover m)and doSomething(LinuxFileMover m),我们可以简单地将其替换为一个函数doSomething(FileMover m),传入正确的类型,它将“正常工作”。

这可能比问题所需的信息多得多,但希望它也能让您对为什么这些事情以比通常的猫和狗更具体的方式完成有一些直觉。

于 2012-12-14T05:41:05.733 回答