@w1024020103
2017-02-28T15:34:08.000000Z
字数 6060
阅读 535
CS61B
UCBerkeley
public class Animal {
protected String name, noise;
protected int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
this.noise = "Huh?";
}
public String makeNoise() {
if (age < 5) {
return noise.toUpperCase();
} else {
return noise;
}
}
public void greet() {
System.out.println("Animal " + name + " says: " + makeNoise());
}
public class Cat extends Animal {
public Cat(String name, int age) {
super(name, age); // Call superclass’ constructor.
this.noise = "Meow!"; // Change the value of the field.
}
@Override
public void greet() {
System.out.println("Cat " + name + " says: " + makeNoise());
}
}
public class Dog extends Animal {
public Dog(String name, int age) {
super(name, age);
noise = "Woof!";
}
@Override
public void greet() {
System.out.println("Dog " + name + " says: " + makeNoise());
}
public void playFetch() {
System.out.println("Fetch, " + name + "!");
}
}
public static void main(String[] args) {
Animal a = new Animal("Pluto", 10);
Cat c = new Cat("Garfield", 6);
Dog d = new Dog("Fido", 4);
a = new Dog("Spot", 10);
d = a;
}
Cat
class when Cat
extends Animal
.java继承中对构造函数是不继承的,只是调用(隐式或显式)。
以下是例子:
public class FatherClass {
public FatherClass() {
System.out.println(100);
}
public FatherClass(int age) {
System.out.println(age);
}
}
public class SonClass extends FatherClass{
public SonClass() {
}
public SonClass(int c) {
System.out.println(1234);
}
public static void main(String[] args) {
SonClass s = new SonClass(66);
}
}
编译后执行结果如下是什么呢?
分析:
SonClass s = new SonClass(66);
执行这句时,调用
`public SonClass(int c) {
System.out.println(1234);//系统会自动先调用父类的无参构造函数(super())
}
在这个构造函数中,等价于
public SonClass(int c) {
super();//必须是第1行,否则不能编译
System.out.println(1234);
}
所以结果是 100
1234
如果子类构造函数是这样写的
public SonClass(int c) {
super(22);//必须是第1行,否则不能编译
//显式调用了super后,系统就不再调用无参的super()了;
System.out.println(1234);
}
执行结果是 22
1234
总结1:构造函数不能继承,只是调用而已。
如果父类没有无参构造函数,创建子类时,不能编译,除非在构造函数代码体中第一行,必须是第一行显式调用父类有参构造函数
如下:
SonClass (){
super(777);//显示调用父类有参构造函数
System.out.println(66);
}
如果不显示调用父类有参构造函数,系统会默认调用父类无参构造函数super();
但是父类中没有无参构造函数,那它不是不能调用了。所以编译就无法通过了。
总结2:创建有参构造函数后,系统就不再有默认无参构造函数。
如果没有任何构造函数,系统会默认有一个无参构造函数。
2.Consider what would happen for these two lines?
a = new Dog("Spot", 10);
d = a;
The static type
of d is Dog
while thestatic type
of a is Animal
. Dog
is a subclass of Animal
, so this assignment will fail at compile time because not all Animals are Dogs.
We can fix that by using a cast:
d = (Dog) a;
This represents a promise to the compiler that at runtime, a will be bound to an object that is compatible with the Dog type.
What is static type
?
There are two main differences between dynamic typing
and static typing
that you should be aware of when writing transformation scripts.
First, dynamically-typed
languages perform type checking at runtime, while statically typed
languages perform type checking at compile time. This means that scripts written in dynamically-typed
languages (like Groovy) can compile even if they contain errors that will prevent the script from running properly (if at all). If a script written in a statically-typed
language (such as Java) contains errors, it will fail to compile until the errors have been fixed.
Second, statically-typed
languages require you to declare the data types of your variables before you use them, while dynamically-typed
languages do not. Consider the two following code examples:
// Java example
int num;
num = 5;
// Groovy example
num = 5
Both examples do the same thing: create a variable called num and assign it the value 5. The difference lies in the first line of the Java example, int num;, which defines num's data type as int. Java is statically-typed, so it expects its variables to be declared before they can be assigned values. Groovy is dynamically-typed and determines its variables' data types based on their values, so this line is not required.
Dynamically-typed languages are more flexible and can save you time and space when writing scripts. However, this can lead to issues at runtime. For example:
// Groovy example
number = 5
numbr = (number + 15) / 2 // note the typo
The code above should create the variable number with a value of 5, then change its value to 10 by adding 15 to it and dividing it by 2. However, number is misspelled at the beginning of the second line. Because Groovy does not require you to declare your variables, it creates a new variable called numbr and assigns it the value number should have. This code will compile just fine, but may produce an error later on when the script tries to do something with number assuming its value is 10.
source : Dynamic typing vs. static typing
3 . An Exercise in Inheritance Misery
Cross out any lines that cause compile-time errors, and put an X through runtime errors (if any).
What does the main program (in class D) output after removing these lines? Note: There are many cases covered here and possibly not enough time to finish in discussion. Remember that solutions
will be posted online later this week.
class A {
public int x = 5;
public void m1() {System.out.println("Am1-> " + x);}
public void m2() {System.out.println("Am2-> " + this.x);}
public void update() {x = 99;}
}
class B extends A {
public void m2() {System.out.println("Bm2-> " + x);}
public void m2(int y) {System.out.println("Bm2y-> " + y);}
public void m3() {System.out.println("Bm3-> ") + "called";}
}
class C extends B {
public int y = x + 1;
public void m2() {System.out.println("Cm2-> " + super.x);}
//public void m4() {System.out.println("Cm3-> " + super.super.x);} can’t do super.super
public void m5() {System.out.println("Cm5-> " + y);}
}
class D {
public static void main (String[] args) {
<C> // B a0 = new A(); a0 must be B or a subclass of B.
<C> // a0.m1(); a0 is invalid
<C> // a0.m2(16); a0 is invalid
A b0 = new B();
System.out.println(b0.x); [5]
b0.m1(); [Am1-> 5]
b0.m2(); [Bm2-> 5]
<C> // b0.m2(61); m2(int y) not defined in static type
B b1 = new B();
b1.m2(61); [Bm2y-> 61]
b1.m3(); [Bm3-> called]
A c0 = new C();
c0.m2(); [cm2-> 5]
<C> // C c1 = (A) new C(); Can’t assign c1 to an A.
A a1 = (A) c0;
C c2 = (C) a1;
c2.m3(); [Bm3-> called]
<C> // c2.m4(); C.m4() is invalid
c2.m5(); [Cm5-> 6]
((C) c0).m3(); [Bm3-> called]
<C NOT R> //(C) c0.m3(); This would cast the result of what the method returns.
b0.update();
b0.m1(); [Am1-> 99]
}
}
Pay attention to the following cases:
b0.m2(61); //m2(int y) not defined in static type
多态存在的三个必要条件
继承
重写
父类引用指向子类对象
比如:
Parent p = new Child();
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
多态就是指父类的某个方法被子类重写时,可以各自产生自己的功能行为。同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。就是父类引用可以持有子类对象。这时候只能调用父类中的方法,而子类中特有方法是无法访问的,因为这个时候(编译时)你把他看作父类对象的原因,但是到了运行的时候,编译器就会发现这个父类引用中原来是一个子类的对像,所以如果父类和子类中有相同的方法时,调用的会是子类中的方法,而不是父类的。
可以这么说:编译时看父类,运行时看子类。运行时的多态性就是指直到系统运行时,才根据实际情况决定实现何种操作。C#中,运行时的多态性通过虚成员实现。
父类A a = new 父类的子类B();
a.方法();
//这就是多态
resouces:
Java中的封装、继承与多态
Java 多态
用Java学编程-翁恺
tips:
利用Sublime Text的多行编辑功能删除掉行首的序号。
在Sublime Text中打开或者粘贴你想清理的代码,然后选中所有行
选中多行
按下Ctrl + Shift + L(Command + Shift + L)--------- 可以同时编辑这些行
用左右方向键把光标移动到行首,然后按下 Delete键 或者 Backspace退格键 来删除行号。