第六章小结

第六章小结

1. 实验:利用IDE的debug功能给例6.4和例6.5的new语句设置断点,使用单步调试(step into/step over)跟踪子类对象实例化(初始化)的执行顺序,并总结该过程。

//例6.4
class AddClass {
  private int x=0,y=0,z=0;

  AddClass (int x) {
    this.x=x; 
  }
  AddClass (int x,int y) {
     this(x);  
     this.y=y; 
  } 
AddClass (int x,int y,int z) {
     this(x,y); 
     this.z=z; 
  } 

  public int add() {
     return x+y+z; 
  }
}
public class SonAddClass extends AddClass{
     int a=0,b=0,c=0;
     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);  a=x+4;   b=y+4;  c=z+4;
     }
     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); 
        SonAddClass p2=new SonAddClass (10,20);
        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());
    }
}
//运行结果:
/** super:x+y+z=10
a+b+c=22
super:x+y+z=30
a+b=40
super:x+y+z=1
a=8 */

Debug运行顺序:
首先从主函数进入后访问 SonAddClass p1=new SonAddClass (2,3,5); 随后进入SonAddClass 类中寻找与之相对应的构造方法,随后执行 super(x,y,z), 然后去到父类相应的函数 AddClass (int x,int y,int z),中,执行 this(x,y) 语句,然后再去到 AddClass (int x,int y) 中, 执行this(x) 语句,然后再去到AddClass (int x) 中,然后执行初始化字段语句private int x=0,y=0,z=0; 然后在执行 this.x = x 语句,然后返回到函数 AddClass (int x,int y) 执行this.y = y 语句,然后返回到函数 AddClass (int x,int y,int z) 执行this.z= z 语句,然后返回到 函数 SonAddClass (int x,int y,int z) 中 ,然后执行子类的初始化字段语句 int a=0,b=0,c=0; ,然后在执行 a=x+4; b=y+4; c=z+4; 语句,最后返回主函数中,剩余两个 new 语句,顺序类似。

//例6.5
class Pare {
    int i=3;
    Pare(){ i=6;System.out.println("call super()");}
}

class Construct extends Pare{
    int i = 10; 
   Construct(){
   		System.out.println("execute Construct()");
   }
   Construct(int num){
   		this();
   		System.out.println("execute Construct(int)");
   	}
   public static void main(String[] args){
    Construct ct = new Construct(9);
    System.out.println(ct.i);
  }
}
//运行结果:
/** 
call super()
execute Construct()
execute Construct(int)
10
*/

Debug运行顺序:
从主函数中进入,执行语句 Construct ct = new Construct(9); ,进入到 Construct ** 类的构造函数当中,执行函数 ** Construct(int num) ** ,然后进入父类执行方法Pare()** ( 注意:进入函数 Construct(int num)当中后,虽然函数体中并没有执行父类构造方法的显示调用语句,但是系统会自动的隐式调用父类的构造方法 Pare(),) ,然后执行父类的初始化字段语句,然后执行方法 **Pare() **,的剩余语句,然后放回到方法 Construct(int num) 中,执行子类的初始化字段,然后执行函数的剩余语句,然后在返回到主函数中。

子类对象实例化的过程:

  1. 为子类对象分配内存空间,对成员变量进行默认的初始化
  2. 绑定子类构造方法,将new中的参数传递给构造方法的形式参数
  3. 调用this或super语句(二者必居其一,不能同时存在), 对从父类继承来的实例变量进行初始化。
  4. 按定义顺序执行实例变量初始化操作。
  5. 执行子类构造方法的剩余代码

2. 如何实现两个对象之间互发消息,请举例说明。

  1. 运用类的组合可以实现两个对象之间互发消息,即一个类的引用作为另一个类的数据成员。
class FighterPlane
{
   String name;
   int missileNum;   
   public FighterPlane(String _name,int _missileNum){
	    name = _name;
		missileNum = _missileNum;
   }
   public void fire(){
       if (missileNum>0){
          System.out.println("now fire a missile !");
		  missileNum -= 1; 
       }
       else{
          System.out.println("No missile left !");
       }
   }   
}

class A 
{
    FighterPlane fp;
    public A(FighterPlane fpp){
       this.fp = fpp; //A对象中拥有了FighterPlane对象的引用
    }
    public void invoke(){
       //A中对象发送消息给FighterPlane的对象
       System.out.println(fp.name); 
    }
}
public class  Run{
   public static void main(String[] args)
   {  
      FighterPlane ftp = new FighterPlane("su35",10);
	//产生A对象,并将ftp对象引用作为参数传入
      A  a   = new A(ftp); 
      //发送消息,产生调用关系
      a.invoke();
   }
}

  1. 内部类
 class Outer
{
 private String index="The String is in Outer Class";
	 public class Inner
	 {
		  String index="The String is in Inner Class";
		  void print()
		  {
		   String index="The String is in print Method";
		   System.out.println(index);
		   System.out.println(this.index);
		   System.out.println(Outer.this.index);
		  }
	 }
	 void print()
	 {
		  Inner inner = new Inner();
		  inner.print();
	 }
 
	 Inner getInner()//注意:必须使用此方法创建内部类实例对象
	 {
		 return new Inner();
	 }
}

public class TestOuterAndInner
{
	 public static void main(String[] args)
	 {
	  Outer outer = new Outer();	   //先产生外部类对象
	//Outer.Inner inner = outer.getInner();//内部类前没有public时的访问方法
	  Outer.Inner inner = outer.new Inner(); //利用对象的new方法
	  inner.print();
	 }
}
//输出结果:
/** 
The String is in print Method
The String is in Inner Class
The String is in Outer Class
*/

注意:

  • 内部类可以直接访问外部类中的所有属性,包括修饰符为private 的属性或方法
  • 内部类对象产生方法:
    • 如果内部类的修饰符为public:则采用本例的代码形式 Outer.Inter inner = outer.new Inner() 来产生内部类的对象
    • 如果内部类前的修饰付为private:则需要使用外部的类的特定接口方法来得到内部类的实例,例如:本例的getInner 方法
  • static 修饰内部类时,内部类就相当于外部类的一个静态属性,内部类中就可以有 static 属性或方法。 static 内部类不能在使用外部类的非static 的属性或方法,产生对象时也有自己的特点。例如:按照本例的代码:Outer.Inner i = new Outer.Inner();

3. 谈谈组合与继承的区别以及两者的使用场景(即什么时候宜用组合 ?什么时候宜用继承?)。

区别:

  • 组合:一个类(A)的引用作为另一个类(B)的数据成员,类B的对象可以通过其中类A的引用,来使用类中的属性和方法,但是类A并不具有类B中所拥有的属性和方法
  • 继承 :一个类(A)是另一个类(B)的子类,则称类A继承类B,其中类B有的方法,在类A中都有,并且类A(子类)可以覆写类B(父类)中的方法。

使用场景:

  • 组合:
    • 当某一个类要使用另一个类的某些方法时,但这两个类又没有相同的之处,这时使用组合会更比较方便
  • 继承:
    • 如果要重写现有类的方法,使用继承
    • 如果定义了许多类,而这些类中又有较多的公共的属性和方法,则考虑写将这些类的属性和方法抽象出来,来实现继承。

4.Java中的运行时多态的含义是什么?有什么作用?请举例说明。

多态: 多态是指一个程序中同名的不同方法共存的情况,Java中提供两种多态的机制:重载(overloading)与覆写(overriding)
运行时多态: 当同一个引用变量(父类引用)指向不同的子类实例,然后访问引用变量成员方法,方法会有不同的表现。即一个接口,多个方法

  • 父类的引用指向子类对象,调用的方法是子类的方法
class A {  
    void fun()  {  
        System.out.println(I am A);  
    }  
}  

//A的子类B
class B extends A  {  
    void fun()  {      
        System.out.println(I am B);  
    }  
}  

//A的子类C  
class C extends A  {  

    void fun()  {      
        System.out.println(I am C);  
    }  
}  

class Test  {  
    public static void main(String[] args)  {  
        //定义父类对象引用变量
        A a;
        //子类对象  
        B  b = new B();  
        C  c = new C();
        //父类对象引用变量 引用 子类对象  
        a=b;  
        a.fun();  //输出   I am B
        a=c;  
        a.fun();  // //输出   I am C
    }  
}

  • 通过接口指向不同的对象来实现不同的方法
//定义接口InterA  
interface InterA  {  
    void fun();  
}  

//实现接口InterA的类B  
class B implements InterA  {  
    public void fun()  {      
        System.out.println(This is B);  
    }  
}  

//实现接口InterA的类C  
class C implements InterA  {  
    public void fun()   {      
        System.out.println(This is C);  
    }  
}  

class Test  {  
    public static void main(String[] args)   {  
        //定义接口
        InterA  a;
        //接口类型变量引用实现接口的类的对象 
        a= new B();  
        a.fun();   
        a = new C();   
        a.fun();   
    }  
}

5.使用接口改写例6.8中的程序。’

//例6.8
public abstract class Shape {
   protected double x,y,z; //子类可见
   //抽象方法
   abstract public double getArea();
   abstract public double getPerimeter();

   //抽象类可以有构造方法,初始化从父类继承的实例变量
   public Shape(double x,double y,double z){
     this.x = x;
     this.y = y;
     this.z = z;
   }
}
public class Rect extends Shape {
  public Rect(double height, double width) { super(height,width, 0.0); }
  public double getArea(){
     return x * y;
  }
  public double getPerimeter(){
     return (x + y)*2;
  }
}
public class Circle extends Shape{
   public Circle(double radius){
       super(radius,0.0,0.0);
   }
   public double getArea(){ 
        return( x * x *Math.PI);
   }
   public double getPerimeter(){
        return(2*Math.PI* x);
   }
}
public class Triangle extends Shape {
 public Triangle(double lenX,double lenY,double lenZ){
       super(lenX, lenY, lenZ);
 }
  public double getArea(){
     double m= (x + y+ z)/2.0;
     return Math.sqrt(m*( m-x)*( m-y)*(m-z));
  }
  public double getPerimeter(){
      return x + y + z;
  }
}
public class TestShape {

   public static void main(String[] args) {
   Rect rect = new Rect(25,25);
   Triangle tri = new Triangle(5,5,8);
   Circle cir = new Circle(12.5);
   printArea(rect);
   printPerimeter(rect);
   printArea(tri);
   printPerimeter(tri);
   printArea(cir);
   printPerimeter(cir);
  }

   public static void printArea(Shape s){
       System.out.println(s.getClass()+":"+s.getArea());
  }
   public static void printPerimeter(Shape s){
       System.out.println(s.getClass()+":"+s.getPerimeter());
  }
}

使用接口改编

//例6.8
interface Shape {
double getArea();
double getPerimeter();
}
public class Rect implements Shape {
  private double x,y;
  public Rect(double height, double width) {
   this.x = height;
   this.y = width; 
   }
  public double getArea(){
     return x * y;
  }
  public double getPerimeter(){
     return (x + y)*2;
  }
}
public class Circle implements Shape{
 private double x;
   public Circle(double radius){
       this.x = radius;
   }
   public double getArea(){ 
        return( x * x *Math.PI);
   }
   public double getPerimeter(){
        return(2*Math.PI* x);
   }
}
public class Triangle implements Shape {
 private double x,y,z;
 public Triangle(double lenX,double lenY,double lenZ){
       this.x = lenX;
       this.y = lenY;
       this.z = lenZ;
 }
  public double getArea(){
     double m= (x + y+ z)/2.0;
     return Math.sqrt(m*( m-x)*( m-y)*(m-z));
  }
  public double getPerimeter(){
      return x + y + z;
  }
}
public class TestShape {

   public static void main(String[] args) {
   Rect rect = new Rect(25,25);
   Triangle tri = new Triangle(5,5,8);
   Circle cir = new Circle(12.5);
   printArea(rect);
   printPerimeter(rect);
   printArea(tri);
   printPerimeter(tri);
   printArea(cir);
   printPerimeter(cir);
  }

   public static void printArea(Shape s){
       System.out.println(s.getClass()+":"+s.getArea());
  }
   public static void printPerimeter(Shape s){
       System.out.println(s.getClass()+":"+s.getPerimeter());
  }
}
/** 
运行结果:
class com.runoob.test.Rect:625.0
class com.runoob.test.Rect:100.0
class com.runoob.test.Triangle:12.0
class com.runoob.test.Triangle:18.0
class com.runoob.test.Circle:490.8738521234052
class com.runoob.test.Circle:78.53981633974483
*/

接口注意事项attenion:

  • 接口定义用关键字interface,而不是用class,interface前的修饰符要么为public,要么为缺省
  • 在类中,用implements关键字来实现接口。一个类可以实现多个接口,在implements后用逗号隔开多个接口的名字。一个接口也可被多个类来实现。
  • 接口具有继承性,可通过extends关键字声明接口的父接口列表。
  • 接口定义的数据成员全是public final static(静态常量)即使没有修饰符。存储在该接口的静态存储区域内,使用接口名.字段或实现类.字段均可访问。
  • 接口中没有构造方法;所有的抽象方法都是public abstract 方法(与抽象类有所不同)。即使没有修饰符,其效果完全等效。注:方法前不能修饰为final。
  • 如果实现某接口的类不是abstract修饰的抽象类,则在类的定义部分必须实现接口的所有抽象方法,而且方法头部分应该与接口中的定义完全一致。
  • 如果实现接口的类是abstract类,则它可以不实现该接口的所有方法。但对于抽象类的任何一个非抽象的子类而言,接口中的所有抽象方法都必须实现。
  • 类在实现接口的抽象方法时,必须显式使用public修饰符,否则将被警告为缩小了接口中定义的方法的访问控制范围。

6.自定义一个类,覆写equals方法,以满足自身业务需求

覆写的要点:

  • 首先判断要比较的对象是否为null,若是直接返回false,若不比较则会发生空指针异常
  • 然后判断是否与自己相等(地址是否一样),若一样则直接返回true
  • 接下来判断两个类是否是同类,若不是返回false
  • 最后向下转型,比较两个类的内容是个相同
class Person{
    private String name;
    private int age;
    public Person(String name,int age){
        this.name=name;
        this.age=age;
    }
    public String toString(){
        return this.name+"今年"+this.age+"岁";
    }
    public boolean equals(Object obj){//Object类可接受任何类
        if(obj==null){//判断是否为空,若不判断则会出先空指针异常(NullPointerException)
            return false;
        }
        //判断是否在与自身比较(通过比较地址),若是则直接返回true
        if(this==obj){
            return true;
        }
        //判断是否是同类
        if(!(obj instanceof Person)){
            return false;
        }
        //到达此处时必定是同类但不同地址的对象在比较
        Person per=(Person)obj;//向下转型,比较属性值
        return this.name. == per.name && this.age==per.age;//判定属性内容是否相等
    }
}
class Student{}
public class Test{
    public static void main(String[] args) {
       Person per1=new Person("张三",18);
       Person per2=new Person("张三",18);
       Person per3=new Person("lisi",19);
       Person per4=null;
       Student stu=new Student();
       System.out.println(per1.equals(per1));//true
       System.out.println(per1.equals(stu));//false
       System.out.println(per1.equals(per3));//false
       System.out.println(per1.equals(per4));//false
    }
}

7. 举例说明运算符instanceof的使用场景。

instanceof 关键字用于比较引用类型
格式: a instanceof A 其中 a 为对象的引用,A为类。

  • 如果a 是 A的实例或 A 子类的实例,则返回true
  • 如果a 是 A 父类的实例,则放回false
  • 如果a 对象的类 和A 没有任何的关系,则编译不会通过
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 ( p2 instanceof Pare)
	   		{System.out.println("p2 instanceof Pare");}
	 	if ( p1 instanceof Pare1)
	   		{System.out.println("p1 instanceof Pare1");}
	 	if ( p2 instanceof Pare1)
	   		{System.out.println("p2 instanceof Pare1");}
	 	if ( p1 instanceof Pare2)
	   		{System.out.println("p1 instanceof Pare2");}
		 else
	   		{System.out.println("p1 not instanceof Pare2");}
	 	/*if (p instanceof Uncle) //去掉注释后,由于类Uncle 和类 Pare 无任何的关系,所以会出现第三中情况,编译不通过
	 		{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");}
   	  }
}
/**
运行结果:
 p instanceof Pare
 p1  instanceof Pare
 p2 instanceof Pare
 p1 instanceof Pare1
 p1 not instanceof Pare2
 null not instanceof String
*/

8.谈谈抽象类与接口的异同以及两者的使用场景。

抽象类:abstract 修饰的类车称为抽象类,用abstract 修饰的成员方法称为抽象方法

public abstract class AbstractCla {
   private int m;
   public abstract void a(); //抽象方法
   public abstract void b(); //抽象方法
   public void c(){
      a(); //非抽象方法可以调用抽象方法
   }
}
abstract class AbstractClb extends AbstractCla {
    public void b() {  } //仅实现抽象方法b()
}

class ConcreteCl extends AbstractClb {
    @Override
    public void a() {  } //实现抽象方法a()
}
  • 抽象类中可以有零个或多个抽象方法,也可以包含非抽象方法。只要有一个抽象方法,类前就必须有abstract 修饰
  • 抽象类不能创建对象,创建对象由具体子类来实现,但可以有声明,声明能引用所有具体子类的对象。
  • 对于抽象方法,在抽象类中只指定方法名及类型,而不写实现代码。抽象类必定要派生子类,若派生的子类是具体类,则具体子类中必须实现抽象类中定义的所有抽象方法(覆盖)
  • 在抽象类中,非抽象方法可以调用抽象方法
  • abstract 不能与private ,static、final 或 native 并列修饰同一个方法

接口:

  • 可以被引用调用的方法(public 方法或同包中的protected 方法 或 默认方法),相当于API 的概念
  • 同“类” 概念地位相等的专有概念 interface ,interface 是方法说明的集合
    interface 声明格式:
[public] interface 接口名 [extends 父接口列表] {
  //静态常量数据成员声明
  [punlic][static][final] 变量类型 变量名 = 常量值;
  // 抽象方法声明
  [public][abstract] 返回值类型 方法名(参数列表)[throws 异常列表];
  // 缺省方法,Java8引入,可被实现类继承使用
  [public] default 返回值类型 方法名(参数列表)[throws 异常列表] { 方法实现语句} ;
  //静态方法 Java 8 引入,通过接口直接使用
  [public] static 返回值类型 方法名(参数列表)[throws 异常列表] { 方法实现语句} ;
}
public interface InterfaceA {
   public static final int ON = 1; 
   public abstract void a();
}
public interface InterfaceB {
   public abstract void b();
}
//允许多继承
public interface InterfaceC extends InterfaceA, InterfaceB {
   public abstract void c();
}
public class InterfaceImp implements InterfaceC{
   @Override
   public void a() {
	System.out.println("call a()");
   }
   @Override
   public void b() {
        System.out.println("call b()");
   }
   @Override
   public void c() {
  	System.out.println("call c()");
   }
}
  • 共同点:二者都可以具有抽象方法,都不能实例化,但都可以有自己的声明,并能引用子类或实现类对象
  • 不同点:
特性接口抽象类
组合新的类可以组合对个接口只能继承单一抽象类
状态不能包含属性(除了静态属性,不支持对象状态 )可以包含属性,非抽象方法可能引用这些属性
默认方法和抽象方法不需要在子类中实现默认方法。默认方法可以引用其他接口的方法必须在子类中实现抽象方法
构造器没有构造器可以有构造器
可见性隐式public可以是protected 或 友元

抽象类在被继承时体现的是is - a 关系,接口在被实现时体现的是can-do关系
使用场景:

  • 抽象类:
    • 即想约束子类具有共同的行为(但不在乎其如何实现),又想拥有缺省的方法,又拥有实例变量
    • 如:模板方法设计模式,模板方法使得子类可以在不改变算法结构的情况下,重现定义算法中某些步骤的具体实现
  • 接口:
    • 约束多个实现类具有共同的行为,但不在乎每个实现类如何具体实现
    • 作为能够实现特定功能的标识存在,也可以是什么接口方法都没有的纯粹标识
    • 实现类需要具备很多不同的功能,但各个功能之间可能没有任何联系
    • 使用接口的引用调用用具体实现类中的实现的方法。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值