关于oop:为什么java.lang.object不是抽象的?

Why java.lang.Object is not abstract?

本问题已经有最佳答案,请猛点这里访问。

Possible Duplicate:
Java: Rationale of the Object class not being declared abstract

为什么对象类是Java中所有的基类,而不是抽象的?

我有这个问题很长时间了,在这里问这个问题纯粹是出于好奇,仅此而已。我的代码或任何人的代码中没有任何东西是破坏的,因为它不是抽象的,但我想知道为什么它们使它具体化?

为什么会有人想要这个对象类的"实例"(而不是它的存在a.k.a.引用)?一种情况是使用对象实例进行锁定的不好的同步代码(至少我用过一次)。我的坏消息)。

对象类的"实例"有什么实际用途吗?它的实例化如何适合OOP?如果他们将其标记为抽象的(当然,在为其方法提供实现之后),会发生什么?


如果没有java.lang.Object的设计者告诉我们,我们必须根据自己的意见来回答。有几个问题可以问,这可能有助于澄清。好的。

对象的任何方法都能从抽象中获益吗?好的。

可以说有些方法会从中受益。以hashCode()equals()为例,如果把这两个概念都抽象化,那么对这两个概念的复杂性就不会那么沮丧了。这将要求开发人员了解它们应该如何实现它们,使它们更加一致(更有效的Java)。但是,我更倾向于认为hashCode()equals()clone()属于独立的选择性抽象(即接口)。其他方法,如wait()notify()finalize()等,都非常复杂和/或是本地的,所以最好已经实现了,而且不会从抽象中受益。好的。

所以我想答案是否定的,对象的任何方法都不会从抽象中受益。好的。

将对象类标记为抽象类是否有好处?好的。

假设所有方法都实现了,标记对象抽象的唯一效果就是它不能被构造(即new Object()是一个编译错误)。这会有好处吗?我认为"对象"这个词本身是抽象的(你能在你周围找到任何可以完全描述为"对象"的东西吗?)因此,它将适合面向对象的范式。然而,这是纯粹主义的一面。可以说,强制开发人员为任何具体的子类(甚至是空的子类)选择一个名称将导致代码更好地表达他们的意图。我认为,从范式的角度来说,完全正确的做法是,对象应该被标记为abstract,但归根结底,没有真正的好处,这是一个设计偏好的问题(实用主义与纯粹性)。好的。

使用一个简单的对象进行同步的实践是否足够具体化?好的。

许多其他的答案都是关于构建一个简单的对象来用于synchronized()操作。虽然这可能是一种常见且被接受的实践,但我不认为这是一个足够好的理由,如果设计者希望对象是抽象的,那么就可以避免对象是抽象的。其他答案也提到了在我们想要在某个对象上同步的任何时候,我们都必须声明一个单独的空对象子类,但这是站不住的——一个空的子类可以在sdk中提供(java.lang.Lock或其他),可以在我们想要同步的任何时候构造。这样做还有一个额外的好处,那就是创建一个更强有力的意图声明。好的。

是否有任何其他因素可能会受到使对象抽象化的不利影响?好的。

有几个领域,从纯粹的设计角度出发,可能影响了选择。不幸的是,我对它们的了解还不够,无法扩展它们。但是,如果其中任何一项对决定产生影响,我都不会感到惊讶:好的。

  • 性能
  • 安全性
  • JVM实现的简单性

还有其他原因吗?好的。

有人提到它可能与反射有关。然而,反射是在物体设计完成后引入的。所以它是否影响反射是没有意义的——这不是原因。仿制药也一样。好的。

还有一个令人难忘的点,java.lang.object是由人类设计的:他们可能犯了错误,他们可能没有考虑过这个问题。没有没有没有没有瑕疵的语言,这可能是其中之一,但如果是的话,那就不是一个大问题。我想我可以放心地说,没有雄心壮志,我不太可能参与设计这种广泛使用的技术的关键部分,尤其是持续15年的技术。多年来一直很强大,所以这不应该被视为批评。好的。

说了这句话,我就把它抽象化了;-p好的。

总结基本上,就我所见,这两个问题的答案是"为什么java.lang.object是具体的?"或者(如果是这样)"为什么java.lang.object是抽象的?"是……"为什么不呢?".好的。好啊。


java.lang.Object的普通实例通常用于锁定/同步场景,这是公认的做法。

还有——为什么它是抽象的?因为它作为一个实例本身并不能完全发挥作用?它真的能和一些抽象的成员有关吗?别这么想。因此,首先把它抽象化的论点是不存在的。事实并非如此。

以动物的经典层次结构为例,其中有一个抽象类Animal,使Animal类抽象的理由是,动物的一个实例实际上是"无效的"——因为缺少一个更好的单词动物(即使它的所有方法都提供了基本实现)。对于Object,情况并非如此。首先,没有压倒性的理由将其抽象化。


我可以考虑几个案例,其中Object的实例很有用:

  • 锁定和同步,就像你和其他评论提到的。这可能是一种代码味道,但我一直看到对象实例以这种方式使用。
  • 作为空对象,因为除实例本身之外,equals始终返回false。
  • 在测试代码中,尤其是在测试集合类时。有时,用虚拟对象而不是null来填充集合或数组是最容易的。
  • 作为匿名类的基实例。例如:
    Object o = new Object() {...code here...}


从我读到的每一篇文章来看,Object似乎不需要具体化,实际上应该是抽象的。

不仅不需要具体化,而且在阅读了更多的内容之后,我相信不是抽象的Object与基本继承模型冲突——我们不应该允许具体类的抽象子类,因为子类只应该添加功能。显然,Java中不是这样的,我们有EDCOX1的抽象子类0。


我认为它应该已经被宣布为抽象的,但是一旦它被完成并发布,很难撤销而不会造成很多的痛苦。

"如果将非抽象类更改为声明为抽象类,则尝试创建该类新实例的现有二进制文件将在链接时抛出InstantiationError,或者(如果使用反射方法)在运行时抛出InstantiationException;因此,不建议对广泛分布的类进行这种更改。"."


我不明白为什么大多数人似乎都认为制作一个完全功能类是一个好主意,它以一种使用完全抽象的方式实现它的所有方法。
我宁愿问为什么要把它抽象化?它有什么不该做的吗?它是否缺少了一些应该具备的功能?这两个问题都可以用"否"来回答,它本身就是一个完全工作的类,使其抽象化只会导致人们实现空类。

1
public class UseableObject extends AbstractObject{}

useableobject继承自抽象对象,可以实现它,它不添加任何功能,它存在的唯一原因是允许访问对象公开的方法。
我也不同意在"差"同步中的使用。使用私有对象来同步访问比使用同步(这个)更安全,而且比Java UITL并发的锁类更容易使用。


有时您需要一个没有自己状态的普通对象。虽然这些物体乍一看似乎没用,但它们仍然有效用,因为每一个都有不同的特性。TNIS在一些场景中很有用,其中最重要的是锁定:您希望协调两个线程。在爪哇中,使用一个被用作锁的对象来实现这一点。对象不需要任何状态,它的存在足以使它成为一个锁:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class MyThread extends Thread {
   private Object lock;
   public MyThread(Object l) { lock = l; }

   public void run() {

      doSomething();
      synchronized(lock) {
          doSomethingElse();
      }
   }
}

Object lock = new Object();
new MyThread(lock).start();
new MyThread(lock).start();

在这个例子中,我们使用一个锁来防止两个线程同时执行doSomethingElse()

如果对象是抽象的,我们需要一个锁,那么我们就必须在不添加任何方法或字段的情况下对它进行子类化,这样我们就可以实例化锁。

想一想,这里有一个双重问题:假设对象是抽象的,它会定义任何抽象方法吗?我想答案是否定的。在这种情况下,将类定义为抽象类没有多大价值。


在我看来,这里有一个简单的实用性问题。将类抽象化会剥夺程序员做某事的能力,即实例化它。对于一个抽象类,你不能做任何你不能做的事情。(好吧,您可以在其中声明抽象函数,但在本例中,我们不需要有抽象函数。)因此,通过使其具体化,您可以使其更灵活。

当然,如果通过使其具体化而造成一些积极的伤害,那么"灵活性"将是一个缺点。但我想不出任何使对象可实例化的主动伤害。(是否"可实例化"一词?不管怎样),我们可以讨论某人对原始对象实例的任何给定使用是否是一个好主意。但即使你能说服我,我所见过的每一个原始对象实例的使用都是一个坏主意,那仍然不能证明那里可能没有好的用途。所以,如果它不伤害任何东西,而且它可能会有帮助,即使我们现在不能想到它实际上会有帮助的方法,为什么要禁止它呢?


我认为迄今为止所有的答案都忘记了Java 1是什么样的。在Java 1中,你不能制作一个匿名类,所以如果你只是为了某个目的而需要一个对象(同步或空占位符),那么你就必须为此声明一个类,然后一堆代码就有了这些额外的类。更直接地向前,只允许对象的直接实例化。

当然,如果你现在在设计Java,你可能会说每个人都应该这样做:

1
 Object NULL_OBJECT = new Object(){};

但这不是1.0中的一个选项。


我怀疑设计者不知道未来人们使用对象的方式,因此不想通过强制程序员在不必要的地方创建一个额外的类来限制他们,例如对于互斥锁、键等。


它还意味着它可以在数组中实例化。在1.5天前,这将允许您拥有通用的数据结构。在某些平台上仍然是这样(我在考虑J2ME,但我不确定)


对象需要具体化的原因。

  • 反射请参见object.getClass()。

  • 通用用途(前Java 5)

  • 比较/输出请参见object.toString()、object.equals()、object.hashcode()等。

  • 同步化请参见object.wait()、object.notify()等。

  • 尽管已经替换了两个区域,但仍然需要一个具体的父类来向每个Java类提供这些特性。

    对象类用于反射,因此代码可以对不确定类型的实例调用方法,即"object.class.getDeclaredMethods()"。如果对象是抽象的,那么想要参与的代码必须实现所有抽象方法,然后客户机代码才能对其使用反射。

    根据Sun的说法,抽象类是一个声明为抽象的类,它可以包含也可以不包含抽象方法。抽象类不能被实例化,但可以被子类化。这也意味着您不能调用方法或访问抽象类的公共字段。

    抽象根类示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    abstract public class AbstractBaseClass
    {
        public Class clazz;

        public AbstractBaseClass(Class clazz)
       {
           super();
           this.clazz = clazz;
       }
    }

    AbstractBaseClass的一个子类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class ReflectedClass extends AbstractBaseClass  
    {
        public ReflectedClass()
        {
           super(this);
        }

        public static void main(String[] args)  
        {
            ReflectedClass me = new ReflectedClass();
        }
    }

    这不会编译,因为在构造函数中引用"this"是无效的,除非它调用同一类中的另一个构造函数。如果我将其更改为:

    1
    2
    3
    4
       public ReflectedClass()
       {
           super(ReflectedClass.class);
       }

    但这只起作用,因为ReflectedClass有一个父级("Object"),它是1)Concrete,2)有一个字段来存储其子级的类型。

    更典型的反射示例是在非静态成员函数中:

    1
    2
    3
    4
    public void foo()
    {
        Class localClass = AbstractBaseClass.clazz;  
    }

    除非将字段"clazz"更改为静态,否则将失败。对于对象的类字段,这不起作用,因为它应该是特定于实例的。对象具有静态类字段是没有意义的。

    现在,我确实尝试了下面的更改,它是有效的,但有点误导。它仍然需要扩展基类才能工作。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public void genericPrint(AbstractBaseClass c)
    {
        Class localClass = c.clazz;
        System.out.println("Class is:" + localClass);
    }

    public static void main(String[] args)
    {
        ReflectedClass me = new ReflectedClass();
        ReflectedClass meTwo = new ReflectedClass();

        me.genericPrint(meTwo);
    }

    Java5之前的泛型(比如数组)是不可能的。

    1
    2
    3
    Object[] array = new Object[100];
    array[0] = me;
    array[1] = meTwo;

    在接收到实际对象之前,需要构造实例作为占位符。


    我怀疑简短的答案是,集合类在Java泛型之前的几天内丢失了类型信息。如果集合不是泛型的,那么它必须返回一个具体的对象(并且在运行时向下强制转换为以前的任何类型)。

    因为将一个具体的类变为抽象类会破坏二进制兼容性(如上面提到的那样),所以保留了具体的对象类。我想指出的是,在任何情况下,它都不是为实现同步而创建的;虚拟类也可以工作。

    设计缺陷从一开始就不包括泛型。很多设计批评都是针对这个决定及其后果的。[哦,还有数组子类型规则。]


    它不是抽象的,因为每当我们创建一个新的类时,它会扩展对象类,如果它是抽象的,那么您需要实现对象类的所有方法,这是开销…该类中已经实现了方法…