1.运行 TestInherits.java 示例,观察输出,注意总结父类与子类之间构造方法的调用关系修改Parent构造方法的代码,显式调用GrandParent的另一个构造函数,注意这句调用代码是否是第一句,影响重大!
package 继承; class Grandparent { public Grandparent() { System.out.println("GrandParent Created."); } public Grandparent(String string) { System.out.println("GrandParent Created.String:" + string); } } class Parent1 extends Grandparent { public Parent1() { //super("Hello.Grandparent."); System.out.println("Parent Created"); // super("Hello.Grandparent."); } } class Child1 extends Parent1 { public Child1() { System.out.println("Child Created"); } } public class TestInherits { public static void main(String args[]) { Child1 c = new Child1(); } }
运行截图:
2.为什么子类的构造方法在运行之前,必须调用父类的构造方法?能不能反过来?为什么不能反过来?
构造函数(constructor)是一种特殊的方法 。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中 。特别的一个类可以有多个构造函数 ,可根据其参数个数的不同或参数类型的不同来区分它们 即构造函数的重载。构造函数的功能主要用于在类的对象创建时定义初始化的状态。构造一个对象,先调用其构造方法,来初始化其成员函数和成员变量。子类拥有父的成员变量和成员方法,如果不调用,则从父类继承而来的成员变量和成员方法得不到正确的初始化。不能反过来调用也是这个原因,因为父类根本不知道子类有什么变量而且这样一来子类也得不到初始化的父类变量,导致程序运行出错!
3.现在有三个类: class Mammal{}
class Dog extends Mammal {}
class Cat extends Mammal{}
针对每个类定义三个变量并进行初始化 Mammal m=null ; Dog d=new Dog(); Cat c=new Cat();
下列语句哪一个将引起编译错误?为什么?哪一个会引起运行时错误?为什么? m=d; d=m; d=(Dog)m; d=c; c=(Cat)m;
①d=m;②d=c;③c=(Cat)m
①子类对象可以直接赋给基类变量。
基类对象要赋给子类对象变量,必须执行类型转换,
②其语法是:子类对象变量=(子类名)基类对象名;
③转换混乱。如果类型转换失败Java会抛出以下这种异常:ClassCastException
4.参看ExplorationJDKSource.java示例
此示例中定义了一个类A,它没有任何成员: class A { }
示例直接输出这个类所创建的对象 public static void main(String[] args) { System.out.println(new A()); }
public class ExplorationJDKSource { /** * @param args */ public static void main(String[] args) { System.out.println(new A()); } } class A{}
运行截图:
valueOf方法内部又调用Object.toString方法:
public String toString() {
return getClass().getName() +"@" +
Integer.toHexString(hashCode());
}
5.来看一段代码(示例Fruit.java ):
注意最后一句,一个字串和一个对象“相加”,得到以下结果:
public class Fruit { public String toString() { return "Fruit toString."; } public static void main(String args[]) { Fruit f=new Fruit(); System.out.println("f="+f); System.out.println("f="+f.toString()); } }
运行结果:
6.运行以下测试代码
1. 左边的程序运行结果是什么?
2. 你如何解释会得到这样的输出?
3. 计算机是不会出错的,之所以得 到这样的运行结果也是有原因的, 那么从这些运行结果中,你能总 结出Java的哪些语法特性?
1.运行结果:
2.通过强制类型转换子类的字符将父类的字符覆盖了
3.当子类与父类拥有一样的方法,并且让一个父类变量引用一个子类对象时,到底调用哪个方法,由对象自己的“真实”类型所决定,这就是说:对象是子类型的,它就调用子类型的方法,是父类型的,它就调用父类型的方法。
如果子类与父类有相同的字段,则子类中的字段会代替或隐藏父类的字段,子类方法中访问的是子类中的字段(而不是父类中的字段)。如果子类方法确实想访问父类中被隐藏的同名字段,可以用super关键字来访问它。
如果子类被当作父类使用,则通过子类访问的字段是父类的。
7.思索: 这种编程方式有什么不合理的地方吗?
public class Zoo { public static void main(String args[]) { Feeder f = new Feeder("С��"); Animal[] ans = new Animal[16]; //����ԱС��ι��һֻʨ�� ans[0] = new Lion(); //����ԱС��ι��ʮֻ���� for (int i = 0; i < 10; i++) { ans[1 + i] = new Monkey(); } //����ԱС��ι��5ֻ���� for (int i = 0; i < 5; i++) { ans[11 + i] = new Pigeon(); } f.feedAnimals(ans); } } class Feeder { public String name; Feeder(String name) { this.name = name; } public void feedAnimals(Animal[] ans) { for (Animal an : ans) { an.eat(); } } } abstract class Animal { public abstract void eat(); } class Lion extends Animal { public void eat() { System.out.println("�Ҳ�����˭�ҳ��⣡"); } } class Monkey extends Animal { public void eat() { System.out.println("��ʲô���ԣ�����ϲ���㽶��"); } } class Pigeon extends Animal { public void eat() { System.out.println("��Ҫ���ʣ�����ÿ��ֻ��һ����ס�"); } }
运行结果: