我是Java新手。我创建了一个超类和一个从其扩展的子类:
class Car {
private String name;
private int wheels;
public Car(String name, int wheels) {
this.name = name;
this.wheels = wheels;
}
}
class Ford extends Car {
public Ford(String name, int wheels) {
super(name, wheels);
}
}
然后在main方法中,我可以创建一个超类和一个子类的实例:
Car car = new Car("car", 4);
Ford ford = new Ford("ford", 6);
Car ford2 = new Ford("ford", 6);
我只是想问一下,以福特开头或句子开头是Car的实例是否有区别?如您所见,福特和福特2创建不同。请注意,我知道ford和ford2是不一样的,因为它们是具有不同指针的引用变量。我只是想知道创建它们的语法。 谢谢
编辑:
我发现了差异,但不了解其背后的逻辑。
我不太明白为什么,福特2仍然是福特的一个实例
答案 0 :(得分:2)
到目前为止,我真的不喜欢这些答案。他们使简单的声音变得复杂。
变量只是参考的持有人。
从示例中可以看到,变量的类型不必与它所保存的引用的确切类型匹配。变量必须比其引用等于或更通用(或者,如果您愿意,等于或更不具体)。 Car
比Ford
更笼统,因此分配是可以的。
每个引用都可以由Object
变量保存,因为这是Java中最通用的类型。
Object foo = new Car(1, 6);
Object bar = "hello world";
现在,使用这些变量的困难在于我们只能从Object
类中调用方法。通过将变量声明为Object
,当我尝试访问内容时,无论那里实际存储了什么,我都只能使用Object
中的方法。
Object
中有几种方法(例如toString
),所以也许可以,但是在大多数情况下,分配给Object
并不是很有用。
因此,我们对变量的定义越具体,就越有可能获得更多的功能。
当你说
Car car = new Ford("ford", 6);
可能存在某些特定于Ford的功能,当从此变量引用它时,我们将无法访问。我们总是可以稍后通过投射实例来解决该问题,但是除非绝对必要,否则您应该避免这样做。
但是,我们越通用,我们的代码就越灵活。如果一个方法仅将福特汽车作为参数,那将有很大的局限性。如果可以将任何汽车作为参数,那么它会更加灵活。
通常,当为变量选择正确的类型时,您可以从脑海中开始,尽可能使用最通用的类型,然后使其更加具体,直到适合您的用例为止。
例如,如果您随后可以更喜欢Iterable
而不是Collection
,而不喜欢Collection
而不是List
,并且喜欢List
而不是{{1 }}。
答案 1 :(得分:1)
Ford ford = new Ford("ford", 6);
Car ford2 = new Ford("ford", 6);
两个对象变量都引用福特类对象。但是对象变量的类型不同。就像福特是福特的类型,而福特2是汽车的类型。
它称为通用对象创建。普遍建议创建对象引用,因为您可以通过分配继承同一父类的不同子类来更改对象引用类型。
除了以下提到的区别外,这种情况也是有用的。
Car car = new Car("car", 4);
此car对象只能访问Car类方法。
Ford ford = new Ford("ford", 6);
此对象可以访问两个类的所有方法。但是,假设您在两个类中都具有如下所示的1种方法
public static void a(){
system.out.println("static method");
}
如果福特对象调用方法a()。它将运行子类中存在的方法。因为福特变量是福特子类的类型。
Car ford2 = new Ford("ford", 6);
此对象可以访问两个类的所有方法。但是,假设您在两个类中都具有如下所示的1种方法
public static void a(){
system.out.println("static method");
}
如果福特对象调用方法a()。它将运行超类中存在的方法。因为ford2变量是福特超类的类型。
答案 2 :(得分:0)
您可以在Ford
上调用任何ford
方法。
您可以在Car
上调用任何ford2
方法。
答案 3 :(得分:0)
从技术上讲,它们并不相同-==
将给出false
-但它们持有相同的数据(不相同意味着修改一个不会影响另一个),并且它们是相同的类型。但是,您只能通过将ford2
类(如果有的话)中的类型强制转换为Ford
(在此状态下,位于{{1 }}只能调用Ford
的方法。
答案 4 :(得分:0)
我只想问实例之间是否有区别 福特和福特2?还是完全一样?
最佳答案是:这取决于。
这完全取决于您所说的“相同”。
在Java中,new
运算符是创建新对象所必需的。因此,这两个分配会将新创建的对象分配给变量ford
和ford2
。这些对象将具有不同的引用,因此在JVM中具有不同的标识。结果,以下内容将得出false
:
boolean b = (ford == ford2);
但是,可以说这两个对象具有相同的属性,因为它们的实例字段包含相同的值。就目前而言,该表达式也将得出false
:
boolean b = (ford.equals(ford2));
因为两种类型的已分配变量(即equals()
和Object
)都从Car
继承的默认Ford
方法执行了与评估时相同的引用比较在之前的声明中。如果equals()
类中的Ford
方法被重写以执行值比较(而不是默认的引用比较),如下所示:
@Override boolean equals(Object ob) {
if (!(ob instanceof Ford)) return false;
Ford f = (Ford) ob;
return this.name.equals(f.name) && this.wheels == f.wheels;
}
然后下面的表达式将得出true
:
boolean b = ford.equals(ford2);
TL; DR:由变量ford
和ford2
引用的对象具有相同的类型(即它们都是Ford
),相同的属性但具有不同的标识。如果如图所示为类Ford
定义了一个值比较,则它们将具有相同的值,否则就没有。