OCJP之继承与多态

错误之处,请修正


继承与多态,方法重写与重截  肯定是面向对象的重中之中,考题变化多端


1 多态

    发生的三个必要条件:

    1) 有继承关系

    2) 有方法重写 (非私有,非静态,非final方法重写)

    3) 父类的引用指向子类


2 原则

    1) 继承关系中, 属性, 私有方法, 静态方法不存在重写

    2) 方法重写:

          子类方法不能有更严格的权限

          父类方法如果有异常,子类不能抛出更大的异常,但可以不抛,如果不抛,应该缺省为throws RuntimeException及其子类; 父类方法没抛需要处理的异常,子类也不能抛,但子类可以抛RuntimeException及其子类;简单来说,可以理解成RuntimeException等同于没有抛异常,重写时可以不考虑

          子类方法的返值,不能大于父类;如果父类返值为void和基本类型,子类返值必须与父类完全一样;如果父类返值为引用类型object,子类可以返回object或者它的子类;

           换句话说,返值是支持variant的,但参数绝对不支持variant,如果说参数有父类,有子类,那很可能就变成了重载,请参考后面的代码;

          重写接口的方法,子类必须显式声明public权限控制符号

    3) 父类不能转成子类,否则运行时会抛ClassCastException

        编译时查类型,运行时找对象: 编译时,会产生一个类型Parent p,运行时,找对象Child c,如果编译时的对象和运行时的对象,就会抛ClassCastException


3 技巧

多态中,方法重写时,看实际对象,即=右边的部分;

多态中,对于没有重写的方法(static, final, private) 或者属性, 看引用, 即=左边的部分;

编译时看类型,运行时找对象....所以,如果将父类强转成子类Child c = (Child)Parent; 编译时不会报错,运行时,会报ClassCastException;


4 例如

关于成员变量、方法、重写的代码:

package com.ocjp.extend;

public class Upcast extends ParentWStringField {
	
//	String s;
	String className = "child class";
	Upcast(){
		s = "child";
	}
	
	void go(){
		System.out.println("child go()");
	}
	
	public static void main(String[] args) {
		ParentWStringField[] p = {new ParentWStringField(), new Upcast(),(ParentWStringField)new Upcast()};
	
		for(ParentWStringField tmp:p){
			//成员变量,私有方法,静态方法,不存在重写,也就是说不存在多态;
			//成员变量,私有方法,静态方法,当有转型发生时,运行结果只看等号左边的类型,左边是什么,运行就调用誰的;
			//普通方法,有多态多生,要看等号右边的;
			//本例中,比较有意思的是,它在构造方法中,给父类的成员变量赋值了,当转型发生时,不会再调用构造方法,所以输出时,按转型前输出;
			System.out.print(tmp.s + "    " +tmp.className +"  ");
			tmp.go();
		}
		
//		ParentWStringField c  = (ParentWStringField)new Upcast();
//		System.out.println(c.s);
	}
}

class ParentWStringField{
	String s;
	String className = "parent class";
	
	ParentWStringField(){
		s = "parent";
	}
	
	void go(){
		System.out.println("parent go()");
	}
}

输出:

parent    parent class  parent go()
child    parent class  child go()
child    parent class  child go()


分析:

tmp.s  打印父类的成员变量,由构造器来完成,转型后不会再次调用构造器;所以转型前是什么就打印什么

tmp.className  打印每个类自己的成员变量,转型后,等号左边是什么,就打印什么;本例中用FOR循环,全部强转成了父类;所以,全部打印父类的成员变量;

tmp.go()  有多态发生,运行时,看等号右边的,等号右边是什么实际内型,就调用实际类型的方法;


关于static方法:


在继承关系中,可以为当成成员变量来看。如果父类中有一个静态方法,

子类中有同样名字的,但是非静态的方法,报编译错误;

子类中有同样名字,也是static的方法,编译通过,但绝不是多态,可以看成成员变量。即运行时,看=左边是什么类型,是什么类型就调哪个类型的静态方法

模拟代码

 class Weed {
	 protected static String s = "";
	 void grow() { s += "grow "; }
	 //这里如果不加static,会报编译错误,因为static的方法不能被重写;
	 //这里如果加static,就等于类的成员变量,运行时,看等号左边的部分;
	 static void growFast() { s += "fast "; }
 }
 public class Thistle extends Weed {
	 void grow() { s += "t-grow "; }
	 static void growFast() { s+= "t-fast "; }
	 public static void main(String[] args) {
		Weed[] w = {new Weed(),new Thistle(),(Weed)new Thistle()};
		for(Weed tmp: w){
			tmp.growFast();
			System.out.println(tmp.s);
		}
	}
 }

Output:

fast
fast fast
fast fast fast


关于异常的代码:

import java.io.IOException;

interface Risky {
	String doStuff() throws Exception;
//	父类抛RuntimeException,子类可以不抛异常;
//	Risky doCrazy() throws NullPointerException;
	Risky doCrazy();
	void doInsane();
}

class Bungee implements Risky {
	public String doStuff() throws IOException {
		throw new IOException();
	}
	public Bungee doCrazy() { return new Bungee(); }
	
	//父类没有抛异常,但子类可以抛RuntimeException及其子类;
	public void doInsane() throws NullPointerException{
//	下面的写法也可以,	RuntimeException可以不处理;	
//	public void doInsane(){
		throw new NullPointerException();
	} }

public class ExceptionTest {
	public static void main(String[] args) {
		Bungee b = new Bungee();
		b.doInsane();
	}
}

关于variant 参数:

返值:  子类重写父类的方法后,返值可以为variant类型,即可以为子类;

参数: 参数改变成variant类型,即子类后,就变成了重载.....

作为初学者,这个混淆了我很久....希望下面的代码,能给其他初学者一个参考


package com.ocjp.extend;

 class MotorVehicle {
	 protected int doStuff(int x) { return x * 2; }
 }
 
 class Bicycle {
	 void go(MotorVehicle m) {
	 System.out.print(m.doStuff(21) + " ");
	 } 
 }
 
 public class Beemer extends MotorVehicle {
	 public static void main(String[] args) {
		 
		 System.out.print(new Beemer().doStuff(11) + " ");
//		 doStuff() is overloaded, not overrided, which doStuff() will be called is determined by its parameter;
		 
		 new Bicycle().go(new Beemer()); //Beemer.doStuff() will be called;
		 new Bicycle().go(new MotorVehicle()); //MotorVehicle.doStuff() will be called;
	 }
	 protected int doStuff(int x) { return x * 3; }
 }

Output:

33 63 42



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值