第五章 类的继承和多态
5.1 Java中的继承
子类继承父类的属性和方法,并根据需要增加它为自己的新的属性和方法。由此而得到的类为子类,被继承的类为父类,也叫超类。Java不支持多继承(子类只能有一个父类)。
*5.1.1 Object类及其方法
Java中的所有类都默认继承Object类,Object是Java所有类的父类。
定义:public class Object
*5.1.2 对象中的克隆
Java克隆是Java语言的特性之一,在实际中应用比较少见。但有时用克隆方法会更方便、更有效率。
(1)克隆的限制
*被克隆的类必须自己实现Cloneable接口,以指示Object.clone()方法可以合法的对该类实例进行按字段赋值。Cloneable接口实际上是个标识接口,没有任何接口方法。
*实现Cloneable接口的类应该使用公共方法重写Object.clone(它是受保护的)。某个对象实现了此接口就克隆它是不可能的。
*在Java.lang.Object类中克隆方法是这么定义的。
protected Object clone()
throws CloneNotSupportedException
(2)浅层克隆和深层克隆
*浅层克隆:主要复制基本对象的值
*深层克隆:当类存在聚合关系是,克隆就必须考虑聚合对象的克隆。可以复制引用类型的字段。
示例:浅层克隆
public class CloneDemo {
public static void main(String[] args) throws CloneNotSupportedException {
Person p = new Person("任我行", 35);
Person p2 = (Person)p.clone();
System.out.println("克隆前:"+p.getName()+","+p.getAge());
System.out.println("克隆后:"+p2.getName()+","+p2.getAge());
if(p == p2){
System.out.println("p和p2的地址相等!");
} else{
System.out.println("p和p2的地址不相等!!");
}
}
}
/*简单类克隆实现
要实现克隆,必须实现Cloneable接口,这是一个标识接口,没哟接口方法
实现了Cloneable接口,以指示Object.clone()方法
可以合法地对该类实例进行按字段复制
按照惯例,实现此接口的类应该使用公共方法重写Object.clone(它是受保护的)
*/
class Person implements Cloneable{
private String name;
private int age;
public Person(String name, int age){
this.name=name;
this.age=age;
}
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.age=age;
}
@Override
protected Object clone() throws CloneNotSupportedException{
return super.clone();
}
}
示例:深层克隆
public class CloneDeepDemo {
public static void main(String[] args) throws CloneNotSupportedException{
Person per = new Person("令狐冲",20);
P p = new P(per);
P p2 =(P)p.clone();
System.out.println("克隆前:"+p.getPer().getName()+","+p.getPer().getAge());
System.out.println("克隆后:"+p2.getPer().getName()+","+p2.getPer().getAge());
if(p==p2)
System.out.println("p和p2的地址相等!");
else
System.out.println("p和p2的地址不相等!");
if(p.getPer()==p2.getPer())
System.out.println("p中的Person对象与p2中的Person对象相等!");
else
System.out.println("p中的Person对象与p2中的Person对象不相等!");
}
}
class P implements Cloneable{
Person per;
public P(Person per){
this.per = per;
}
public Person getPer(){
return per;
}
public void setPer(Person per){
this.per = per;
}
@Override
protected Object clone() throws CloneNotSupportedException{
P p = (P)super.clone();
p.per = (Person)this.per.clone();
return p;
}
}
5.1.3 Java的继承
Java中的继承使用关键字“extends”表示,
公式:class 子类 extennds 父类 {}
使用继承子类可以拥有父类中的非私有属性
示例:
定义一个Person类
public class Person {
public String name;
public String sex;
public int age;
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
}
public String getSex(){
return sex;
}
public void setSex(String sex){
this.sex=sex;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.age=age;
}
}
定义Student类继承Person类
public class Student extends Person {
private String deparetment;//系别
private String specialty;//专业
}
public class day06 {
public static void main(String[] args) {
Student s = new Student();
//子类共享父类中的非私有属性
String str=s.name;
//子类可以拥有父类中的非私有方法
s.setName("伍正云");
s.setAge(25);
}
}
*Java不支持不支持多继承,一个类只能继承一个类,即子类只能继承一个父类,但是一个父类可以被多个子类继承。
5.1.4 super关键字
在Java继承中,子类可以使用super富案件自调用父类的非私有属性和非私有方法,还可以调用父类的非私有构造方法。
(1)使用super关键字调用父类属性
public class Person {
public String name;
public String sex;
public int age;
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
}
public String getSex(){
return sex;
}
public void setSex(String sex){
this.sex=sex;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.age=age;
}
}
public class Student extends Person {
private String deparetment;//系别
private String specialty;//专业
String name1 = super.name; //调用父类的属性name
public void test(){
super.getAge(); //调用父类方法
}
}
*super关键字可以省略
public class Student extends Person {
private String deparetment;//系别
private String specialty;//专业
String name1 = name; //调用父类的属性name
public void test(){
getAge(); //调用父类方法
}
}
(2)使用super调用父类的构造方法
public class Person {
private String name;
private int age;
public Person(){
}
public Person(String name, int age){
this.name = name;
this.age = age;
}
}
class Student extends Person{
public Student(){
super("sky",26);
}
}
(3)子类和父类构造方法的执行顺序
*子类默认调用父类的无参构造方法,也就是说,子类的构造方法中会默认调用super(),并且在构造方法的第一行。
*如果子类只提供有参的构造方法,则必须在子类的构造刚发中调用父类的构造方法。
*执行父类的构造方法后,在调用本类中的构造方法。
public class Person {
private String name;
private int age;
public Person(){
System.out.println("父类无参构造方法");
}
public Person(String name, int age){
this.name = name;
this.age = age;
System.out.println("父类有参构造方法");
}
public static void main(String[] args){
Student stu = new Student();
}
}
class Student extends Person{
public Student(){
super("sky",26);
System.out.println("子类有参构造方法");
}
}
5.2 多态变化
5.2.1 Java中多态的实现
覆盖:子类重写父类中的方法
public class Father {
public void say(){
System.out.println("Father say()");
}
public static void main(String[] args) {
Son son = new Son();
son.say();
}
}
class Son extends Father{
public void say(){
System.out.println("Son say()");
}
}
5.2.2 类型检测——向上转型/向下转型
向上转型是指父类对象的引用指向子类对象。向下转型是指在向上转型的基础上再次指向子类对象。
(1)向上转型
语法:父类 对象 = new 子类()
public class Father {
public void say(){
System.out.println("Father say()");
}
public static void main(String[] args) {
//向上转型
Father son = new Son();
son.say();
}
}
class Son extends Father{
public void say(){
System.out.println("Son say()");
}
}
有时使用向上转型会丢失掉子类特有的方法
public class Father {
public void say(){
System.out.println("Father say()");
}
public static void main(String[] args) {
//向上转型
Father son = new Son();
son.sayMe();//报错
}
}
class Son extends Father{
public void say(){
System.out.println("Son say()");
}
public void sayMe(){
System.out.println("Son sayMe()");
}
}
(2)向下转型
语法: 父类 对象1 = new 子类()
子类 对象2 = (子类)对象1
public class Father {
public void say(){
System.out.println("Father say()");
}
public static void main(String[] args) {
//向上转型
Father son = new Son();
//向下转型
Son son2 = (Son)son;
son2.sayMe();//报错
}
}
class Son extends Father{
public void say(){
System.out.println("Son say()");
}
public void sayMe(){
System.out.println("Son sayMe()");
}
}
如果直接将父类强转成子类对象会报错
public class Father {
public void say(){
System.out.println("Father say()");
}
public static void main(String[] args) {
Father f = new Father();
Son son = (Son)f;
}
}
class Son extends Father{
public void say(){
System.out.println("Son say()");
}
public void sayMe(){
System.out.println("Son sayMe()");
}
}
5.2.3 动态绑定
*动态绑定
会基于对象实际的类型(只能在运行时得知)来选择所调用的方法
*静态绑定
在程序编译时就绑定的,在Java中的变量都是静态绑定的,只有private、static和final是静态绑定的。