Java笔记复习---继承和多态

1.类继承、子类和父类的IsA关系

在这里插入图片描述
说明:任何类在设计时应考虑覆盖祖先类Object的以下函数:equals,clone,toString等
案例代码:java

public class GeometricObject { 
 
   //等价于public class GeometricObject extends Object
    private String color = "white";
    private boolean filled;
    private Date dateCreated; //java.util.Date是JDK定义的类,表示日期和时间
    public GeometricObject() { 
 
   dateCreated = new Date();}
    //返回当前时间
    public String getColor() { 
 
   return color; }
    public void setColor(String color) { 
 
   this.color = color;}
    public boolean isFilled() { 
 
   return filled; }//返回Boolean类型
    public void setFilled(boolean filled) { 
 
   this.filled = filled;}
    public Date getDateCreated() { 
 
  return dateCreated;}
    //这里不该该提供setDateCreated方法
    @Override //覆盖Object类的toString()方法(返回哈希值)
    public String toString( ) { 
 
   //还应考虑equals,clone
	return "created on " + dateCreated + "\n\tcolor: " + color
				+ " and filled: " + filled;
   }//toString方法应该返回一个描述当前对象的有意义的字符串
}

子类--------web

public class Circle extends GeometricObject { 
 
  
	private double radius; //新增属性
	public Circle() { 
 
   }   
	public Circle(double radius) { 
 
   this.radius = radius; } 
	public double getRadius() { 
 
   return radius; }
	public void setRadius (double radius) { 
 
   this.radius = radius; }

	public double getArea() { 
 
  
		return radius * radius * Math.PI;
	}
	public double getDiameter() { 
 
  
		return 2 * radius;
	}
	public double getPerimeter() { 
 
  
		return 2 * radius * Math.PI;
	}
       //还应考虑equals,clone,toString等函数
}
public class Rectangle extends GeometricObject { 
 
  
	private double width;
	private double height;

	public Rectangle() { 
 
   }
	public Rectangle(double width, double height) { 
 
  
		this.width = width;
		this.height = height;
	}

	public double getWidth() { 
 
   return width; }
	public void setWidth (double width) { 
 
   this.width = width;}
	public double getHeight() { 
 
   return height;}
	public void setHeight (double height) { 
 
   this.height = height;}
	public double getArea() { 
 
   return width * height;}
	public double getPerimeter() { 
 
  
		return 2 * (width + height);
	}
         //还应考虑equals,clone,toString等函数
}

实例初始化模块数组

  • 初始化块是Java类中能够出现的第四种成员(前三种包括属性、方法、构造函数),分为实例初始化块和静态初始化块。
  • 实例初始化模块(instance initialization block,IIB)是一个用大括号括住的语句块,直接嵌套于类体中,不在方法内。
    它的做用就像把它放在了类中每一个构造方法的最开始位置。用于初始化对象。实例初始化块先于构造函数执行
    做用:若是多个构造方法共享一段代码,而且每一个构造方法不会调用其余构造方法,那么能够把这段公共代码放在初始化模块中。即初始化模块能够简化构造方法
    一个类能够有多个初始化模块,模块按照在类中出现的顺序执行
    在这里插入图片描述
    实例初始化模块还能够捕获异常
    在这里插入图片描述

实例初始化模块最重要的做用是当咱们须要写一个内部匿名类时:匿名类不可能有构造函数,这时能够用实例初始化块来初始化数据成员
在这里插入图片描述安全

  • final变量建立就要初始化,除非上述状况
  • 实例初始化模块只有在建立类的实例时才会调用。//new
  • 定义并初始化类的实例变量等价于实例初始化块:private int id = 0;
  • 一个类能够有多个实例初始化块,对象被实例化时,模块按照在类中出现的顺序执行,构造函数最后运行。
public class Book{ 
 
  
  private int id = 0;		//执行次序:1
  public Book(int id){ 
 
  		//执行次序:4
    this.id = id		
  }
  { 
 
  
     //实例初始化块 //执行次序:2
  }
  { 
 
  
     //实例初始化块 //执行次序:3
  }
}


public class A{ 
 
  
   //类的属性和方法定义
   { 
 
  
     //实例初始化模块 
   }
   static { 
 
  
     //静态初始化模块
   }
   public static int i = 0;//直接定义静态变量至关于静态初始化块
}
public class Book{ 
 
  
  private static int id = 0;	//执行次序:1
  public Book(int id){ 
 
  
	 this.id = id 		    		
  }
  static { 
 
  
     //静态初始化块 //执行次序:2
  }
  static { 
 
  
     //静态初始化块 //执行次序:3
  }
}

初始化模块执行顺序app

  • 第一次使用类时装入类
    若是父类没装入则首先装入父类,这是个递归的过程,直到继承链上全部祖先类所有装入
    装入一个类时,类的静态数据成员和静态初始化模块按它们在类中出现的顺序执行
  • 实例化类的对象
    首先构造父类对象,这是个递归过程,直到继承链上全部祖先类的对象构造好
    构造一个类的对象时,按在类中出现的顺序执行实例数据成员的初始化及实例初始化模块
    执行构造函数函数体

实例:ide

public class InitDemo{ 
 
  
    InitDemo(){ 
 
  
        new M();
    }
    public static void main(String[] args){ 
 
  
	  System.out.println("(1) ");
	  new InitDemo();
    }
    { 
 
  
        System.out.println("(2) ");
    }
    static{ 
 
  
        System.out.println("(0) ");
    }
}
class N{ 
 
  
    N(){ 
 
   	System.out.println("(6) "); }
    { 
 
  
        System.out.println("(5) "); 
    }
    static { 
 
  
        System.out.println("(3) "); 
    }
}
class M extends N{ 
 
  
    M(){ 
 
   	System.out.println("(8) "); }
    { 
 
  
        System.out.println("(7) "); 
    }
    static { 
 
  
        System.out.println("(4) "); 
    }
}

2.SUPER关键字

  • 利用super能够显式调用父类的构造函数
    super(parametersopt)调用父类的的构造函数。
    必须是子类构造函数的第1条且仅1条语句(先构造父类)。
    若是子类构造函数中没有显式地调用父类的构造函数,那么将自动调用父类不带参数的构造函数。
    父类的构造函数在子类构造函数以前执行。
  • 访问父类的实例成员(super不能用于静态上下文,经过子类对象去调用
    super.data(若是父类属性在子类可访问)
    super.method(parameters)(若是父类方法在子类可访问)
    1.Circle代码里能够写getDataCreated()或super.getDataCreated()
    2.不能使用super.super.p()这样的super链
  • 若是子类中没有显式调用父类的构造函数,那么将自动调用父类不带参数的构造函数,由于编译器会偷偷在子类构造函数第一条语句前加加上super();
    在这里插入图片描述
  • 若是父类没有无参构造函数,那么子类构造函数里若调用父类无参构造函数就会编译出错
class Fruit { 
 
  
	public Fruit(String name) { 
 
  
		System.out.println("调用Fruit的构造函数");
	}
}
class Apple extends Fruit { 
 
   }
//编译器为Apple提供无参构造函数时出错
//子类Apple没定义任何构造函数,故编译为子类提供无参构造函数Apple();
//提供的Apple()会调用父类无参构造函数Fruit(),因下列缘由没法调用:
//父类定义了有参构造函数,因此编译没有为父类提供无参Fruit( )。

3.实例方法覆盖

子类从新定义了从父类中继承的实例方法,称为方法覆盖
注意事项svg

  1. 仅当父类方法在子类里是可访问的,该实例方法才能被子类覆盖,即父类私有实例方法不能被子类覆盖,父类实例私有方法自动视为final的。
  2. 静态方法不能被覆盖,若是静态方法在子类中从新定义,那么父类方法将被隐藏。
  3. 覆盖特性:一旦父类中的实例方法被子类覆盖,同时用父类型的引用变量引用了子类对象,这时不能经过这个父类型引用变量去访问被覆盖的父类方法(即这时被覆盖的父类方法不可再被发现)。由于实例方法具备多态性(晚期绑定)
    在子类类体函数中可使用super调用被覆盖的父类方法。
  4. 隐藏特性:指父类的变量(实例变量、静态变量)和静态方法在子类被从新定义,但因为类的变量和静态方法没有多态性,所以经过父类型引用变量访问的必定是父类变量、静态方法(即被隐藏的可再发现)。
  5. 方法覆盖的哲学涵义:子对象固然能够修改父类的行为(生物进化除了遗传,还有变异)

具体看代码:函数

class A{ 
 
  
    public void m() { 
 
  
        System.out.println("A's m");
    }

    public static void s() { 
 
  
        System.out.println("A's s");
    }
}
class B extends A{ 
 
  
    //覆盖父类实例方法
    public void m() { 
 
  
        System.out.println("B's m");
    }
    //隐藏父类静态方法
    public static void s() { 
 
  
        System.out.println("B's s");
    }
}
public class OverrideDemo { 
 
  

    public static void main(String[] args) { 
 
  
        A o = new B();  //父类型变量引用子类对象
        o.m();  //因为父类实例方法m被子类覆盖,o运行时指向B类对象,因为多态性,执行的是B的m
        o.s();    //因为s是静态方法,没有多态性,编译器编译时对象o的声明类型是A,因此执行的是A的s
    }
}

//
public class OverrideDemo { 
 
  

    public static void main(String[] args) { 
 
  
         //静态方法隐藏
         B o = new B();
         o.s(); //调用B的s,将父类A的s隐藏,即经过B类型的引用变量o是不可能调用A的s
        ((A)o).s(); //经过强制类型转换,能够调用A的s,能够找回。也能够经过类名调用来找回:A.s( );
     }		//这就是第29页PPT讲的:被隐藏的变量和静态方法能够再发现
}

在这里插入图片描述

4.OBJECT类中的方法

  • java.lang.Object类是全部类的祖先类。若是一个类在声明时没有指定父类,那么这个类的父类是Object类。
    1. 它提供方法如toString、equals、getClass、clone、finalize。前3个为公有,后2个为保护。getClass为final(用于泛型和反射机制,禁止覆盖)。
    2. equals方法:用于测试两个对象是否相等。Object类的默认实现是比较两个对象引用是否引用同一个对象。
    3. toString方法:返回表明这个对象的字符串。Object类的默认实现是返回由类名、@和hashCode组成。
    4. Object的toString方法提供的信息不是颇有用。所以一般子类应该覆盖该方法,提供更有意义的信息
Circle circle = new Circle();
   circle.toString();//Circle@15037e5,若是Circle没有覆盖toString

equals方法测试

  • equals用于判断一个对象同另外一个对象的全部成员内容是否相等。覆盖时应考虑:
    1. 对基本类型数值成员。直接使用判断便可。
    2. 对引用类型变量成员。则须要对这些变量成员调用equals判断,不能用
  • 覆盖equals函数,最好同时覆盖hashCode()方法,该方法返回对象的hashCode值。
    须要对比的时候,首先用hashCode去对比,若是hashCode不同,则表示这两个对象确定不相等(也就是没必要再用equals()去再对比了),若是hashCode相同,此时再用equals()比,若是equals()也相同,则表示这两个对象是真的相同了,这样既能大大提升了效率也保证了对比的绝对正确性!
  • 覆盖equals函数,首先用instanceof检查参数的类型是否和当前对象的类型同样。
  • 例如,在Circle类中覆盖:
public boolean equals(Object o){ 
 
  
        if(o instanceof Circle)	//应先检查另外一对象o的类型 
            return  radius==((Circle)o).radius;//注意强制类型转换
        return false;
    }

clone方法ui

  • 要实现一个类的clone方法,首先这个类须要实现Cloneable接口,不然会抛出CloneNotSupportedException异常
  • Cloneable接口其实就是一个标记接口,里面没有定义任何接口方法,只是用来标记一个类是否支持克隆:没有实现该接口的类不能克隆
  • 还要公有覆盖clone方法,即Object类里clone方法是保护的,子类覆盖这个方法时应该提高为public
  • 方法里应实现深拷贝clone,Object的clone实现是浅拷贝。
  • 克隆的深度:要克隆的对象可能包含基本类型数值成员或引用类型变量成员,对于基本类型数值成员使用=赋值便可,对于引用类型成员则须要进一步嵌套调用该成员的克隆方法进行赋值。
    code
//首先必须实现Cloneable接口
class A implements Cloneable{ 
 
  //实现 A能够克隆
    public static final int SIZE = 10;
    private int[] values = new int[SIZE]; //A的values成员是数组

    public int[] getValues(){ 
 
  
        return values;
    }
    //覆盖clone方法,提高为public,只是调用Object的的clone,
    //不修改行为
    @Override
    public Object clone() throws CloneNotSupportedException { 
 
  
        return super.clone();//调用Object的clone
    }
}
//Clone方法不带参数,返回Object,同时可能会抛出CloneNotSupportedException异常
public class CloneTest { 
 
  
    public static void main(String[] args) throws CloneNotSupportedException { 
 
  
        A o1 = new A();
        A o2 = (A)(o1.clone()); //clone返回Object,所以要强制类型转换
        System.out.println(o1 == o2); //false,说明clone返回的是新的引用
        System.out.println(o1.getValues() == o2.getValues()); //true 但为浅拷贝克隆
    }				//由于o1和o2内部values引用指向了内存里同一个数组 //说明A的clone方法里,所调用的super.clone()是浅拷贝
}
class A implements Cloneable{ 
 
  
    public static final int SIZE = 10;
    private int[] values = new int[SIZE];
    public int[] getValues(){ 
 
   return values; }
    public void setValues(int[] newValues) { 
 
  
	this.values = newValues;
    }
    //覆盖equals方法,比较二个A类型对象内容是否同样
    public boolean equals(Object obj) { 
 
  
        if(obj instanceof A){ 
 
  
            A o = (A)obj;
            return java.util.Arrays.equals(this.getValues(),o.getValues());
        }
        else return false;
    }
    //覆盖clone方法,提高为public,从新实现为深拷贝
    public Object clone() throws CloneNotSupportedException { 
 
  
        A newObj = new A(); //new一个新对象
	  newObj.values = this.values.clone(); //数组的clone是深拷贝,若是去掉clone,则是浅拷贝
	    return newObj;
    }
}

tostring方法

class A implements Cloneable{ 
 
  
    //覆盖toString方法
    public String toString() { 
 
  
        StringBuffer buf = new StringBuffer();
        for(int v : values){ 
 
  
            buf.append(v + " ");
        }
        return buf.toString().trim(); //去掉最后多余的空格
    }
}
public class CloneTest { 
 
  
    public static void main(String[] args) throws CloneNotSupportedException { 
 
  
        A o1 = new A();
        o1.setValues(new int[]{ 
 
  1,2,3,4,5,6,7,8,9,10});
        A o2 = (A)(o1.clone());
        System.out.println(o1 == o2); //false
        System.out.println(o1.getValues() == o2.getValues()); //false 说明不是浅拷贝克隆
        System.out.println(o1.equals(o2)); //true,二个对象的内容相等, 说明是深拷贝克隆 
        System.out.println(o2.toString()); //显示 1 2 3 4 5 6 7 8 9 10
    }
}
//
class B implements Cloneable{ 
 
  
    A a;    //引用类型成员
    int i;  //值类型
	/*若是B类里包含了A类型成员,只要A实现了深拷贝克隆,则B能够很方便地实现深拷贝克隆。如C类里包含了B类型成员,D类型里包含了C类型成员…,以此类推,只要每一个类型都实现了深拷贝克隆,那么最外层的包装类能够很是方便的实现深拷贝克隆。这就是第37页PPT里讲到的克隆的深度问题*/

    @Override
    public Object clone() throws CloneNotSupportedException { 
 
  
        B newObj = new B();
        newObj.i = this.i;   //值类型成员直接=赋值
        newObj.a = (A)(this.a.clone()); //引用类型的成员不能直接赋值,必须调用clone方法,深拷贝
        return newObj;
    }
}

5.多态性、动态绑定和对象的强制类型转换

多态:经过引用变量调用实例函数时,根据所引用的实际对象的类型,执行该类型的相应实例方法,从而表现出不一样的行为称为多态。经过继承时覆盖父类的实例方法实现多态。多态实现的原理:在运行时根据引用变量指向对象的实际类型,从新计算调用方法的入口地址(晚期绑定)。

class Person{ 
 
    void Greeting( ){ 
 
   System.out.println(“Best wish from a person!"); } }
class Employee extends Person
	      { 
 
  	void Greeting( ){ 
 
   System.out.println(“Best wish from a employee!");} }
class Manager extends Employee{ 
 
   
		void Greeting( ){ 
 
   System.out.println(“Best wish from a manager!");} }

public class GreetingTest1{ 
 
  
    public static void main(String[] args){ 
 
  
       //父类引用变量能够引用本类和子类对象,p1,p2,p3的声明类型都是Person(父类型),p2,p3执行子类对象
       Person p1= new Person( ),p2= new Employee( ),p3= new Manager( );      
	p1.Greeting( ); //调用Person的Greeting() ,因为实际指向对象类型是Person
 	p2.Greeting( ); //调用Employee的Greeting() ,因为实际指向对象类型是Employee
 	p3.Greeting( ); //调用Manager的Greeting() ,因为实际指向对象类型是Manager
	
    }
}

动态绑定的实现

  • 多态条件:父类变量可引用本类和子类对象,子类对象isA父类对象
  • 当调用实例方法时,由Java虚拟机动态地决定所调用的方法,称为动态绑定(dynamic binding)或者晚期绑定或者延迟绑定(lazy binding)或者多态。
    假定对象o是类C1的实例,C1是C2的子类,C2是C3的子类,…,Cn-1是Cn的子类。也就是说,Cn是最通常的类,C1是最具体的类。在Java中,Cn是Object类。若是调用继承链里子类型C1对象o的方法p,Java虚拟机按照C一、C二、…、Cn的顺序依次查找方法p的实现。一旦找到一个实现,将中止查找,并执行找到的第一个实现(覆盖的实例函数)。
    在这里插入图片描述
    查找方法p的顺序:看C1是否覆盖p,若是已覆盖,调用C1的p;若是C1没有覆盖p,则查看C2是否覆盖,以此类推
    从C1开始顺着继承链往父类查找,直到找到第一个p的实现,并调用这个p的实现

类型转换

  • 从子类到父类的转换是合法的,称为隐式转换。
Person p=new Manager();//将子类对象转换为父类对象
  • 从父类到子类必须显式(强制)转换。
Manager m = p; //编译错 ,p是Person父类型,Person不必定是Manager
   Manager m = (Manager)p;//ok,但转换前没有检查 
  • 从父类到子类转换必须显式转换,转换前应进行检查更安全。
Manager m = null;
   if(p instanceof Manager) m= (Manager)p; //安全:转换前检查 

在这里插入图片描述
instanceof操做符
能够用instanceof操做符判断一个引用指向的对象是不是一个类的实例。表达式返回boolean值。
语法
referenceVariable instanceof TypeName
因此上面的例子安全的写法为:
Person p = new Manager()
if(p instanceof Manager)
Manager m = (Manager)p;
意思是若是p指向的对象真的是Manager实例,再强制转换类型

  • 重载发生在编译时(Compile time),编译时编译器根据实参比对重载方法的形参找到最合适的方法。
  • 多态发生在运行(Run time)时,运行时JVM根据变量所引用的对象的真正类型来找到最合适的实例方法。
  • 有的书上把重载叫作“编译时多态”,或者叫“早期绑定”(早期指编译时)。
  • 多态是晚期绑定(晚期指运行时)
  • 绑定是指找到函数的入口地址的过程。
//警告: 对象访问运算符(.)优先于类型转换运算符。使用括号保证在(.)运算符以前转换
  ((Circle)object).getArea() //OK
  (Circle)object.getArea(); //错误

6.访问控制符和修饰符FINAL

在这里插入图片描述

  • final能够修饰变量、方法、类
  • final修饰变量
    • final成员变量:常量,数据初始化后不能再修改。
    • final局部变量:常量,数据初始化后不能再修改。
  • final修饰方法(实例方法和静态静态):最终方法,实例方法不能被子类覆盖,静态方法不能被隐藏
    • Object类的getClass( )
  • final类:最终类,不能派生子类。
    • String, StringBuffer
    • Math