Java 中实例化对象的不同方式之间的差异
Difference between different ways in instantiating an object in Java
我有代码:
class Father{
String name="father";
void f(){System.out.print("father class");}
}
class Son extends Father{
String name = "son";
void f(){System.out.print("son class");}
void f2(){}
}
public class Test {
public static void main(String[] args) {
Father s = new Son();
System.out.println(s.name);// outputs father
s.f();// outputs "son class"
s.f2();// s does not have f2
}
}
我的问题是,执行 Father s = new Father() 或 Father s = new Son() 或 Son s = new Son() 有什么区别?
还有,为什么示例中的s.f2会报错呢?父亲必须执行 f2() 吗?
s.f2() 是语法错误,因为你告诉 JVM s 是父亲,而不是儿子。
在代码中,找不到Father中的f2方法class
class Father{
String name="father";
void f(){System.out.print("father class");}
}
但这并不代表代码有错,只是JVM不喜欢而已。
如果将 s.f2() 更改为
(Son)s.f2();
它会起作用
你处理的是引用类型(变量类型)和对象类型(实际引用的是什么)。 Java 编译器需要某种保证所引用的对象可以 运行 您正在调用的方法。为此,它查看 引用类型 。执行时,方法运行是对象类型.
的方法
简单地说:
Father f = new Father(); //Treated as a Father, behaves like a Father
Son s = new Son(); //Treated as a Son, behaves like a Son
Father q = new Son(); //Treated as a Father, behaves like a Son (sounds like my own father)
如果您通过说 (Son)q
将 cast q 转换为 Son,它将被编译器视为 Son
,除非该对象不是t 实际上 一个儿子,在这种情况下你会得到一个 ClassCastException
.
让我们采用一个更简单的概念,因为您的层次结构意味着所有 Son
都是 Father
,但并非所有 Father
都是 Son
(这不完全正确)。
让我们采用摘要 class Number
及其任何 children - 为简洁起见,我们可以使用 Integer
、Float
和 BigInteger
.
假设我们这样声明:
Number num = Float.NaN;
我们现在有一个 Float
实例,它被 Float
引用。我们可以对该实例做任何我们想做的事情,但 仅在 Number
.
的上下文中
Float
有一个有用的方法叫做 isNan
,它可以让我们看看我们的浮点数是否真的 是 一个数字。在 Number
的上下文中...该方法不存在。
这样做有好处 - 如果您不需要 child 引用的特殊性,您可以通过它的 parent class (或界面)。如果您想与之分离,这也会使您与 child 的 API 分离(参见 developing to an interface)。
好的,我知道这里哪里有混淆了。
在 java 中,您可以覆盖方法但不能覆盖 class 变量
牢记这条规则
所以当你
父亲 s = 新儿子();
对象 "s" 是父亲类型
正如我们所说的,它里面的变量不会被覆盖,只是方法
所以最终的结果是一个对象,它有来自父亲 class 的成员变量("name" 变量)和来自儿子 class 的方法(因为父亲只有 1 个方法并且儿子被覆盖了它)。
以及为什么 f2 不起作用
这是因为对象 "s" 的类型是父亲而不是儿子(父亲对象有 1 个方法被儿子 class 覆盖,除此之外它仍将是父亲对象)而父亲没有 f2 方法,这就是为什么你得到编译错误
我觉得用动物的例子更容易解释:
class Animal {
void printName() {
System.out.println("Animal");
}
}
class Dog extends Animal{
@Override
void printName() {
System.out.println("Dog");
}
}
class Cat extends Animal{
@Override
void printName() {
System.out.println("Cat");
}
void meow() {
System.out.println("meow");
}
}
当您扩展 classes 时,子 class 可以覆盖父的方法并且可以有自己的方法。在我的 Animal 示例中,通用 Animal 对象只能给出它的名字,但 Cat 对象可以给出它的名字和喵喵声。显然,喵喵方法是猫特有的,因为我们知道狗不能喵喵叫,而一般动物。
当你
Animal animal = new Cat();
您实际上创建了 Cat 的实例,但将其用作一般 Animal。因此,您的动物实例只有 Animal class 中可用的方法,但 Cat class 覆盖的方法的执行将委托给 Cat class。
如果你想执行 Cat 的特定方法,那么你需要将你的 Animal 转换为 Cat
(Cat) animal.meow();
在您的示例中,要调用 f2() 方法,您需要先将您的父亲对象转换为儿子
(Son)s.f2();
我有代码:
class Father{
String name="father";
void f(){System.out.print("father class");}
}
class Son extends Father{
String name = "son";
void f(){System.out.print("son class");}
void f2(){}
}
public class Test {
public static void main(String[] args) {
Father s = new Son();
System.out.println(s.name);// outputs father
s.f();// outputs "son class"
s.f2();// s does not have f2
}
}
我的问题是,执行 Father s = new Father() 或 Father s = new Son() 或 Son s = new Son() 有什么区别?
还有,为什么示例中的s.f2会报错呢?父亲必须执行 f2() 吗?
s.f2() 是语法错误,因为你告诉 JVM s 是父亲,而不是儿子。
在代码中,找不到Father中的f2方法class
class Father{
String name="father";
void f(){System.out.print("father class");}
}
但这并不代表代码有错,只是JVM不喜欢而已。
如果将 s.f2() 更改为
(Son)s.f2();
它会起作用
你处理的是引用类型(变量类型)和对象类型(实际引用的是什么)。 Java 编译器需要某种保证所引用的对象可以 运行 您正在调用的方法。为此,它查看 引用类型 。执行时,方法运行是对象类型.
的方法简单地说:
Father f = new Father(); //Treated as a Father, behaves like a Father
Son s = new Son(); //Treated as a Son, behaves like a Son
Father q = new Son(); //Treated as a Father, behaves like a Son (sounds like my own father)
如果您通过说 (Son)q
将 cast q 转换为 Son,它将被编译器视为 Son
,除非该对象不是t 实际上 一个儿子,在这种情况下你会得到一个 ClassCastException
.
让我们采用一个更简单的概念,因为您的层次结构意味着所有 Son
都是 Father
,但并非所有 Father
都是 Son
(这不完全正确)。
让我们采用摘要 class Number
及其任何 children - 为简洁起见,我们可以使用 Integer
、Float
和 BigInteger
.
假设我们这样声明:
Number num = Float.NaN;
我们现在有一个 Float
实例,它被 Float
引用。我们可以对该实例做任何我们想做的事情,但 仅在 Number
.
Float
有一个有用的方法叫做 isNan
,它可以让我们看看我们的浮点数是否真的 是 一个数字。在 Number
的上下文中...该方法不存在。
这样做有好处 - 如果您不需要 child 引用的特殊性,您可以通过它的 parent class (或界面)。如果您想与之分离,这也会使您与 child 的 API 分离(参见 developing to an interface)。
好的,我知道这里哪里有混淆了。
在 java 中,您可以覆盖方法但不能覆盖 class 变量
牢记这条规则
所以当你
父亲 s = 新儿子();
对象 "s" 是父亲类型
正如我们所说的,它里面的变量不会被覆盖,只是方法 所以最终的结果是一个对象,它有来自父亲 class 的成员变量("name" 变量)和来自儿子 class 的方法(因为父亲只有 1 个方法并且儿子被覆盖了它)。
以及为什么 f2 不起作用
这是因为对象 "s" 的类型是父亲而不是儿子(父亲对象有 1 个方法被儿子 class 覆盖,除此之外它仍将是父亲对象)而父亲没有 f2 方法,这就是为什么你得到编译错误
我觉得用动物的例子更容易解释:
class Animal {
void printName() {
System.out.println("Animal");
}
}
class Dog extends Animal{
@Override
void printName() {
System.out.println("Dog");
}
}
class Cat extends Animal{
@Override
void printName() {
System.out.println("Cat");
}
void meow() {
System.out.println("meow");
}
}
当您扩展 classes 时,子 class 可以覆盖父的方法并且可以有自己的方法。在我的 Animal 示例中,通用 Animal 对象只能给出它的名字,但 Cat 对象可以给出它的名字和喵喵声。显然,喵喵方法是猫特有的,因为我们知道狗不能喵喵叫,而一般动物。
当你
Animal animal = new Cat();
您实际上创建了 Cat 的实例,但将其用作一般 Animal。因此,您的动物实例只有 Animal class 中可用的方法,但 Cat class 覆盖的方法的执行将委托给 Cat class。 如果你想执行 Cat 的特定方法,那么你需要将你的 Animal 转换为 Cat
(Cat) animal.meow();
在您的示例中,要调用 f2() 方法,您需要先将您的父亲对象转换为儿子
(Son)s.f2();