- 实验:利用IDE的debug功能给例6.4和例6.5的new语句设置断点,使用单步调试(step into/step over)跟踪子类对象实例化(初始化)的执行顺序,并总结该过程。
例6.4:
class AddClass {
private int x=0,y=0,z=0;//5执行非静态代码块的默认初始化
AddClass (int x) {//4执行构造函数,但不先执行内部代码块
this.x=x;//6
}
AddClass (int x,int y) {
this(x);//3
this.y=y;//7
}
AddClass (int x,int y,int z) {
this(x,y);// 2
this.z=z;//8
}
public int add() {
return x+y+z;
}
}
public class SonAddClass extends AddClass{
int a=0,b=0,c=0;//9 已经执行完父类的构造函数,接下来需要先执行本类的非静态代码,再执行本类构造函数内部的代码
SonAddClass (int x) {
super(x); a=x+7;
}
SonAddClass (int x,int y){
super(x,y); a=x+5; b=y+5;
}
SonAddClass (int x, int y,int z){
super(x,y,z);//1 执行此构造函数,先调用父类的构造方法
a=x+4;//10 执行本类构造函数内的代码
b=y+4;//11
c=z+4;//12
}//super(x,y,z)如果去掉会如何?//15
public int add() {
System.out.println("super:x+y+z="+super.add());
return a+b+c;
}
public static void main(String[] args){
SonAddClass p1=new SonAddClass (2,3,5);//0对此句进行debug,下面用数字来表示过程
SonAddClass p2=new SonAddClass (10,20);//13
SonAddClass p3=new SonAddClass (1);
System.out.println("a+b+c="+p1.add());
System.out.println("a+b="+p2.add());
System.out.println("a="+p3.add());
}
}
例6.5 step into若子类构造方法中没有显式调用父类构造方法,则在产生子类的对象时, 系统在调用子类构造方法的同时,默认调用父类无参构造方法。
class Pare {
int i=3;//4执行非静态代码块的默认初始化
Pare(){//3 执行构造函数,但不先执行内部代码块
System.out.println("call super()");//5
}//6
}
class Construct extends Pare{
int i = 10;//7执行非静态代码块的默认初始化
Construct(){ //2 执行构造函数,但不先执行内部代码块
System.out.println("execute Construct()");//8
}//9
Construct(int num){
this(); //如果去掉此句呢?//1执行此类的无参构造函数
System.out.println("execute Construct(int)");//10
}//11
public static void main(String[] args){
Construct ct = new Construct(9);//0 12
System.out.println(ct.i);
}
}
-
如何实现两个对象之间互发消息,请举例说明。
可以通过引用的方式。当一个类在域变量中声明另外一个对象时,两个对象之间就可以互发消息。
-
谈谈组合与继承的区别以及两者的使用场景(即什么时候宜用组合 ?什么时候宜用继承?)。
组合与继承的区别:
继承是子类继承父类,父类的所有属性和方法都可以被子类访问和调用,就是"is-a"的关系。组合是指将已存在的类型作为一个新建类的成员变量类型,两个类之间无"is-a"的关系。
使用场景:
当你只需要使用另外一个类的方法时使用组合。但是如果你需要使用另外一个类的作用时但你不想被其他的类访问用继承。 -
Java中的运行时多态的含义是什么?有什么作用?请举例说明。
运行时的多态:一个接口,多个方法 -
使用接口改写例6.8中的程序。
-
自定义一个类,覆写equals方法,以满足自身业务需求
public class Key {
private int id;
public Key(int id) {
this.id = id;
}
public int getId() {
return id;
}
@Override
public boolean equals(Object o) {
return this.getId()==((Key)o).getId();
}
public static void main(String[] args){
Key key1 = new Key(1);
Key key2 = new Key(1);
System.out.println(key1.equals(key2));
}
}
- 举例说明运算符instanceof的使用场景。
instanceof:二元运算符。 用法:boolean result = object instanceof class判断对象是否为特定类的实例
class Uncle{}
class Pare{}
class Pare1 extends Pare{}
class Pare2 extends Pare1{}
class Pare3 {
public static void main(String[] args){
Uncle u = new Uncle();
Pare p = new Pare();
Pare1 p1 = new Pare1();
Pare2 p2 = new Pare2();
//本类对象是本类的实例
if ( p instanceof Pare)
{System.out.println("p instanceof Pare");}
//子类对象是父类的实例
if (!( p1 instanceof Pare))
{System.out.println("p1 not instanceof Pare");}
else
{System.out.println("p1 instanceof Pare");}
//父类对象不是子类的实例
if (p1 instanceof Pare2)
{System.out.println("p1 instanceof Pare2");}
else
{System.out.println("p1 not instanceof Pare2");}
/*if (p instanceof Uncle)
{System.out.println("p instanceof Uncle");}
else
{System.out.println("p not instanceof Uncle");}*/
if (null instanceof String)
{System.out.println("null instanceof String");} else
{System.out.println("null not instanceof String");}
- 谈谈抽象类与接口的异同以及两者的使用场景。
答:(1)相同点:
1)两者都不能被实例化
2)接口的实现类和抽象类的子类都必须实现了接口或抽象类中的抽象方法后才能被实例化
(2)不同点:
1)关键字不同,接口用interface,抽象类用abstract class;类实现接口用implements,类继承抽象类用extends
2)接口允许一个类实现多个接口,抽象类只允许单继承
3)接口中只有全局常量(public static final)和抽象方法(public abstract)(java 8允许静态方法和默认方法),抽象类中可以包含普通类中所有可以包含的东西以外加抽象方法(抽象类中也可以没有抽象方法)
4)接口中的访问权限只能是public,抽象类允许多种访问权限
5)接口不能有构造方法,抽象类一定有构造方法
6)接口强调功能的实现,其设计理念是“has-a”关系,并运用于实现比较常用的功能,方便日后维护或者添加删除方法,抽象类强调所属关系,其设计理念是“is-a”关系,倾向于充当公共类的角色,不适用于日后重新对里面的代码进行修改
(3)使用场景:
抽象类强调所属关系,多用于在同类事物中有无法具体描述的方法的场景,所以当子类和父类之间存在逻辑上的层次结构时,推荐使用抽象类。
接口强调功能的实现,多用于不同类之间,定义不同类之间的通信规则,所以当希望支持差别较大的两个或者多个对象之间的特定交互行为时,应该使用接口;另外使用接口完全可以实现与抽象类相同的功能,使用接口可以大大降低软件系统的耦合度,应该优先考虑使用接口。