当子类继承父类之后,发现继承过来的方法无法满足子类的业务需求,这个时候子类有权利对继承过来的方法进行重新编写(即方法覆盖,又称为方法重写,英语单词:Override、Overwrite)。
public class OverrideTest01 { public static void main(String[] args) { //创建鸟对象 Bird bird=new Bird(); bird.move(); //c创建猫对象 Cat cat=new Cat(); cat.move(); } } ? class Animal{ public void move(){ System.out.println("动物正在移动!"); } public void sing(int i){ System.out.println("Animal sing........."); } } ? class Bird extends Animal{ //没有进行方法重时,输出动物正在移动,我们需要的是鸟儿在飞翔,父类继承过来的方法不符合子类的业务需求,这个时候我们就需要进行方法重写了。 //以下代码即方法重写,就是将父类中的方法拿过来,然后更改方法体中的内容,用来满足子类的业务需求。 public void move(){ System.out.println("鸟儿在飞翔!"); } //父类sing(int i)和子类sing()没有构成方法覆盖,因为参数列表不同,构成了方法重载 public void sing(){ System.out.println("brid sing..........."); } ? } class Cat extends Animal{ //方法重写 public void move(){ System.out.println("猫在走猫步!"); } }
两个类之间必须要有继承关系
重写之后的方法和之前的方法具有:相同的返回值类型、相同的方法名、相同的参数列表
子类重写的方法访问权限不能比父类更低,但是可以比父类只能更高。
class Animal{ public void move{ } } class Bird extends Animal{ //这样是不可行的 protected void move{ } } --------------------------------------------------- class Animal{ protected void move{ } } class Bird extends Animal{ //这样是可行的 public void move{ } }
重写之后的方法不能比父类的方法抛出更多的异常,只能更少
注意1:方法覆盖只是针对于方法,和属性无关。
注意2:私有方法无法覆盖。
注意3:构造方法不能被继承,所以构造方法也不能被覆盖。
注意4:方法覆盖只是针对于“实例方法”,“静态方法”覆盖没有意义。
public class OverrideTest02 { public static void main(String[] args) { ChinesePeople cp=new ChinesePeople(); cp.setName("张三"); cp.say(); AmericaPeople ap=new AmericaPeople(); ap.setName("jack"); ap.say(); } } ? class People{ private String name; ? public void say(){ System.out.println(this.getName()+"正在说话!"); } ? public String getName() { return name; } ? public void setName(String name) { this.name = name; } } ? class ChinesePeople extends People{ public void say(){ System.out.println(this.getName()+"正在说汉语!"); } } ? class AmericaPeople extends People{ public void say(){ System.out.println(this.getName()+" speak English!"); } }
将“Java对象”转换成“字符串形式”。
当我们在调用类时候想将类的对象进行字符串转换时不满足,Object类中默认的toString()时,可在在所需类中对toString()方法进行重写。
public class OverrideTest03 { public static void main(String[] args) { Time time=new Time(1999,2,3); System.out.println(time.toString()); ? } } ? class Time{ //属性 private int year; private int month; private int day; ? //构造方法 public Time(){ ? } ? public Time(int year, int month, int day) { this.year = year; this.month = month; this.day = day; } ? //setter and getter 方法 ? public int getYear() { return year; } ? public void setYear(int year) { this.year = year; } ? public int getMonth() { return month; } ? public void setMonth(int month) { this.month = month; } ? public int getDay() { return day; } ? public void setDay(int day) { this.day = day; } //没重写前调用toString()方法,输出样式:Time@1b6d3586 //重写toString(),想要它以这种方式输出Time{year=1999, month=2, day=3} @Override public String toString() { return "Time{" +"year=" + year +", month=" + month +", day=" + day +‘}‘; } }
/* 为了方便所以写在一个类,一般最好分开写 */ public class AnimalTest { public static void main(String[] args) { Animal a=new Animal(); Cat c=new Cat(); Bird b=new Bird(); ? //父类型的引用可以指向子类型的对象 Animal aCat=new Cat(); aCat.move();//猫在走猫步! Animal aBird=new Bird(); aBird.move();//鸟儿在飞翔! } } class Animal { public void move(){ System.out.println("动物正在移动!"); } } ? class Bird extends Animal { public void move(){ System.out.println("鸟儿在飞翔!"); } } ? class Cat extends Animal { public void move(){ System.out.println("猫在走猫步!"); } //猫除了move之外,应该有自己的特定行为,比如抓老鼠。 //子类中特有的方法 public void catchMouse(){ System.out.println("猫正在抓老鼠!"); } }
子------->父,
Animal acat=new Cat(); Animal abird=new Bird();
父------->子
Cat cat=(Cat)aCat;
Java中允许向上转型也允许向下转型。 !!!!!不过无论是向上转型还是向下转型两种之间都必须有继承关系,没有继承关系编译器报错。
多种形态,多种状态。
分析:aCat.move() ; java程序分为编译阶段和运行阶段。 先来分析编译阶段: 对于编译器来说,编译器只知道a2的类型是Animal,所以编译器在检查语法的时候,会去Animal.class字节码文件中找move()方法,找到了,绑定上move()方法,编译通过,静态绑定成功。(编译阶段属于静态绑定。)再来分析运行阶段: 运行阶段的时候,实际上在堆内存中创建的java对象是Cat对象,所以move的时候,真正参与move的对象是一只猫,所以运行阶段会动态执行cat对象的move ()方法。这个过程属于运行阶段绑定。(运行阶段绑定属于动态绑定。)
多态表示多种形态: 编译的时候一种形态。运行的时候另一种形态。
多态指的是
父类型的引用可以指向子类型的对象
包括编译阶段和运行阶段。
编译阶段:静态绑定父类的方法;
运行阶段:动态绑定子类型对象的方法。
多种形态
Animal aCat=new Cat(); /* 分析下面这段代码可以编译和运行吗 分析程序一定要分析编译阶段的静态绑定和运行阶段的动态绑定。 只有编译阶段通过了才能运行 答:不能编译通过,因为在编译阶段编译器只知道aCat的类型是Animal,去Animla.class文件中去赵catMouse()方法, 结果找不多,静态绑定失败,编译进行了报错,无法运行。 */ aCat.catchMouse();
什么时候必须“向下转型”
当你需要访问的是子类对象中“特有”的方法,这时候需要“向下转型”。
//假设这个时候我就想要调用catchMouse()方法,,那么这个时候就需要用到“向下转型”了 (强制类型转换) Cat cat=(Cat)aCat; cat.catchMouse();//猫正在抓老鼠! ? Bird bird=(Bird)aBird;
向下转型会有风险
//分析以下程序是否会报错,编译报错还是运行报错? ? Cat cat1=(Cat)aBird; cat1.catchMouse(); ? /* 编译器检测到cat1这个引用是Animal类型,而Animal和Cat之间具有继承关系,所以可以向下转型,编译不报错。 当进入运行的时候,堆内存中创建的是Bird对象,在实际下运行时,将Brid对象转换成Cat对象是不可行的,因为Bird与Cat 之间没有继承关系,此时运行会报错。 运行时出现异常,这个异常和空指针异常一样重要,也非常经典。 java.lang.ClassCastException:类型转换异常。 com.liu.duotai.Bird cannot be cast to com.liu.duotai.Cat(Bird不能转换成Cat) */
/* 怎么避免ClassCastException异常的发生? 新的内容,运算符: instanceof 1.instanceof可以在运行阶段动态判断引用指向的对象和类型。 2.instanceof的语法: (引用 instanceof 类型) 3.instanceof运算符的运算结果只能时True或False 4.c是一个引用,c变量保存了一个内存地址指向了堆中的对象 假设(c instanceof Cat)为True表示: c引用指向的堆内存中的Java对象是一个Cat 假设(c instanceof Cat)为False表示: c引用指向的堆内存中的Java对象不是一个Cat */ if (aBird instanceof Cat){//如果aBird是一个Cat,再进行强制类型转换 Cat cat1=(Cat)aBird; cat1.catchMouse(); }
!!!程序员要养成一个好习惯: 任何时候,任何地点,对类型进行向下转型时,一定要使用instanceof运算符进行判断。(Java规范中要求的。)
public class AnimalTest02 { /* 当我们的肉眼可以看到底层到底是new Cat()还是 new Brid()时候 为什么还要使用instanceof进行判断呢? 原因是有时候我们肉眼是看不到的 */ public static void main(String[] args) { Animal a1=new Cat(); Animal a2=new Bird(); if (a1 instanceof Cat){ Cat c=(Cat) a1; c.catchMouse(); }else if (a1 instanceof Bird){ Bird b=(Bird) a1; b.sing(); } if (a2 instanceof Cat){ Cat c=(Cat) a2; c.catchMouse(); }else if (a2 instanceof Bird){ Bird b=(Bird) a2; b.sing(); } } }
软件在扩展过程中修改的越少越好,修改的越多,你的系统稳定性越差,未知风险越多。这里就涉及到一个软件的开发原则: 软件开发原则有七大原则(不属于Java,这个开发原则属于整个软件业):其中有一条最基本的原则:ocp(开闭原则)。 什么是开闭原则? 对扩展开放(你可以额外增加),对修改关闭(最好很少修改现有程序)。在软件的扩展过程中,修改越少越好。
项目的开发不仅为了实现客户的需求,同时也需要考虑软件的扩展性。在编程的时候最好面向父类型编程,面向更加抽象进行编程,不建议面向具体进行编程。因为面向具体编程会让软件扩展性很差。
测试类
/* 编写程序模拟…主人喂养宠物"的场景:提示1:R 主人类:Master宠物类:Pet 宠物类子类:Dog、cat、 Yingwu提示2: 主人应该有喂养的方法:feed ()宠物应该有吃的方法:eat() 只要主人喂宠物,裾物就吃。 要求:主人类中只提供一个喂养方法feed(),要求达到可以喂养各种类型的宠物。 编写测试程序: 创建主人对象 创建各-种龙物对象 调用主人的喂养方法feed(),喂养不同的宠物,观察执行结果。 通过该案例,理解多态在开发中的作用。 重要提示:feed方法是否需要一个参数,参数选什么类型!! ! ? */ public class Test { public static void main(String[] args) { Master master=new Master(); master.feed(new Dog()); master.feed(new Cat()); master.feed(new YingWu()); ? } }
主人类
public class Master { /* 假设主人起初的时候只是喜欢养宠物狗狗1/喂养宠物狗狗 public void feed (Dog d){ d.eat() ; } 由于新的需求产生,导致我们"不得不"去修改Master这个类的代码 public void feed (cat ){ c.eat() ; } ? 能不能让Master主人这个类以后不再修改了。 即使主人又喜欢养其它宠物了,Master也不需要修改。 这个时候就需要使用:多态机制。 最好不要写具体的宠物类型,这样会影响程序的扩展性 */ //这个时候就可以把feed()方法中的参数列表换成Pet这个父类型, //喂养的方法 public void feed(Pet pet){ ? pet.eat(); } }
宠物类
public class Pet { public void eat(){ //这个方法可以不用给具体实现 System.out.println("宠物正在吃东西"); } ? }
狗类
public class Dog extends Pet{ public void eat(){ System.out.println("狗正在吃狗粮"); } }
猫类
public class Cat extends Pet{ public void eat(){ System.out.println("猫正在吃猫粮"); } }
鹦鹉类
public class YingWu extends Pet{ public void eat(){ System.out.println("鹦鹉正在吃豆子"); } }
降低程序的耦合度,提高程序的扩展力。 ? public class Master{ ? public void feed (Dog d){} ? public void feed (Cat c ){ } ? 以上的代码中表示:Master和Dog以及cat的关系很紧密(耦合度高)。导致扩展力很差。
public class Master{ ? public void feed ( Pet pet) { }} ? 以上的代表中表示:Master和Dog以及cat的关系就脱离了,Master关注的是Pet类这样Master和Dog以及cat的耦合度就降低了,提高 了软件的扩展性。
面向对象的三大特征: 封装、继承、多态真的是一环扣一坏。 有了封装,有了这种整体的概念之后。对象和对象之间产生了继承。 有了继承之后,才有了方法的覆盖和多态。
OCP(对扩展开放,对修改关闭),目的是:降低程序耦合度,提高程序扩展力。 ? 面向抽像编程,不建议面向具体编程
//经过测试,你记住就行。 // 私有方法不能覆盖。 public class OverrideTest05{ ? //私有方法,只能在本类中访问,不能在别的类中访问。 private void dosome () { System.out.println ( "OverrideTest06‘s private method dosome execute! "); } //入口 public static void main (String [] args){ //多态 OverrideTest05 ot = new T(); ot.dosome( ); //overrideTest06‘s private method dosome execute! } //子类 ? } class T extends OverrideTest05 { //尝试重写父类中的dosome ()方法 // 访问权限不能更低,可以更高。 public void dosome() { System.out.println("T‘s public dosome method execute! "); } }
/* 方法覆盖只是针对于“实例方法”,“静态方法”覆盖没有意义。 1、方法覆盖需要和多态机制联合起来使用才有意义。 Animal a =new Cat() ; a . move() ; 要的是什么效果? 编译的时候move()方法是Animal的。 运行的时候自动调用到子类重写move()方法上。 假设没有多态机制,只有方法覆盖机制,你觉得有意义吗? 没有多态机制的话,方法覆盖可有可无。 没有多态机制,方法覆盖也可以没有,如果父类的方法无法满足子类业务需求的时候,子类完全可以定义一个全新的方法。 2、静态方法存在方法覆盖吗? 多态自然就和对象有关系了.而静态方法的执行不需要对象。 所以,一般情况下,我们会说静态方法"不存在"方法覆盖。 ? ? */ public class OverrideTest04 { public static void main(String[] args) { //静态方法可以使用“引用.”来调用吗? //答:可以的,虽然使用“引用.”来调用,但和对象是没有关系的。 Animals a=new Cats();//多态 //静态对象和多态无关 //实际运行还是Animals.doSome() a.dosome();//Animals 的dosome折行了 ? } } ? class Animals{ //父类中的静态方法 public static void dosome(){ System.out.println("Animals 的dosome折行了"); } } ? class Cats extends Animals{ //尝试在子类中对父类的静态方法进行重写 public static void dosome() { System.out.println("Cat 执行了"); } }
原文:https://www.cnblogs.com/shiwangmmp/p/14828231.html