Java基础-面向对象第三大特性之多态(polymorphism)
作者:尹正杰
版权声明:原创作品,谢绝转载!否则将追究法律责任。
一.多态概述
多态是继封装,继承之后,面向对象的第三大特性,多态的前提是继承。
从现实事物经常会体现出多种形态,如学生,学生是人的一种,则一个具体的同学张三即是学生也是人。即出现两种形态。Java作为面向对象的语言,同样可以描述一个事物的多种形态。如Student类继承了Person类,一个Student的对象便既是Student,又是Person。
最终多态体现为父类引用变量可以指向子类对象。多态的前提是必须有子父类关系或者类实现接口关系,否则无法完成多态。在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法。
二.多态调用的三种形式
多态调用方法,方法必须运行子类的重写!
1>.多态的定义格式:就是父类的引用变量指向子类对象。
1 父类类型 变量名 = new 子类类型();
2 变量名.方法名();
2>.普通类多态定义的格式
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 class Father{ 8 public void show(){ 9 System.out.println("父类的show方法!"); 10 } 11 } 12 13 class Son extends Father{ 14 public void show(){ 15 System.out.println("子类重写父类的show方法!"); 16 } 17 } 18 19 public class MyInterfaceDemo{ 20 public static void main(String[] args){ 21 //Java中,对象的多态性,调用公式:“父类类型或者接口类型 变量 = new 子类对象();” 22 Father f = new Son(); 23 f.show(); 24 } 25 } 26 27 28 29 /* 30 以上代码执行结果如下: 31 子类重写父类的show方法! 32 */
3>.抽象类多态定义的格式
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 abstract class Father{ 8 public abstract void eat(); 9 } 10 11 class Son extends Father{ 12 public void eat(){ 13 System.out.println("儿子喜欢吃米饭!"); 14 } 15 } 16 17 public class MyInterfaceDemo{ 18 public static void main(String[] args){ 19 //抽象类Father,子类是Son 20 Father f = new Son(); 21 f.eat(); 22 } 23 } 24 25 26 27 /* 28 以上代码执行结果如下: 29 儿子喜欢吃米饭! 30 */
4>.接口多态定义格式
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 interface Father{ 8 public abstract void smoking(); 9 } 10 11 class Son implements Father{ 12 public void smoking(){ 13 System.out.println("儿子会抽烟!"); 14 } 15 } 16 17 public class MyInterfaceDemo{ 18 public static void main(String[] args){ 19 //接口Father,实现类Son 20 Father f = new Son(); 21 f.smoking(); 22 } 23 } 24 25 26 27 /* 28 以上代码执行结果如下: 29 儿子会抽烟! 30 */
三.多态中成员变量的特点
1>.成员变量(编译运行全看父类)
a>.编译的时候,参考父类中有没有这个变量,如果有,编译成功,没有则编译失败;
b>.运行的时候,运行的是父类中的变量值;
2>.成员方法(编译看父类,运行看子类。)
a>.编译的时候,参考父类中有没有这个方法,如果有,编译成功,没有则编译失败
b>.运行的时候,运行的是子类的重写方法;
3>.静态成员方法
没有多态性。
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 class Father{ 8 int age = 30; 9 public void show(){ 10 System.out.println("Father的方法"); 11 } 12 } 13 14 class Son extends Father{ 15 int age = 8; 16 public void show(){ 17 System.out.println("Son的方法"); 18 } 19 } 20 21 public class MyInterfaceDemo{ 22 public static void main(String[] args){ 23 Father f = new Son(); 24 System.out.println(f.age); 25 f.show(); 26 } 27 } 28 29 30 31 /* 32 以上代码执行结果如下: 33 30 34 Son的方法 35 */
四.instanceof关键字
我们了解多态之后,会发现一个对象的数据类型不一定会保持不变。当我们想要确定一个对象是否是某个类时,就会用到比较运算符,只不过它很特殊,不能赢大于小于或是等于号直接进行判断,而是要用到关键字,即instanceof,它只能用作比较引用数据类型,用来比较一个引用类型的变量,是不是这个类型的对象。
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 abstract class Person{ 8 public abstract void sleep(); 9 } 10 11 12 class Student extends Person{ 13 public void sleep(){ 14 System.out.println("学生在休息!"); 15 } 16 } 17 18 class Teacher extends Person{ 19 public void sleep(){ 20 System.out.println("老师在休息!"); 21 } 22 } 23 24 public class PersonDemo{ 25 public static void main(String[] args){ 26 27 Person p = new Student(); 28 29 p = new Teacher(); 30 31 boolean flag = p instanceof Student; 32 33 System.out.println("引用数据类型'p'是否为Student类型:>>> " + flag); 34 35 p.sleep(); 36 } 37 } 38 39 40 41 42 /* 43 以上代码执行结果如下: 44 引用数据类型'p'是否为Student类型:>>> false 45 老师在休息! 46 */
五.多态转型
1>.多态的向上转型
多态常见的就是自动类型提升,将取值范围小的,自动提升为取值范围大的。范围小的,看成是子类,范围大的看成父类。
优点:
这样做的好出就是可以调用子类父类的公共属性(方法)。
缺点:
无法调用子类特有的属性(方法)。
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 abstract class Person{ 8 public abstract void sleep(); 9 } 10 11 12 class Student extends Person{ 13 public void sleep(){ 14 System.out.println("学生在休息!"); 15 } 16 } 17 18 class Teacher extends Person{ 19 public void sleep(){ 20 System.out.println("老师在休息!"); 21 } 22 } 23 24 public class PersonDemo{ 25 public static void main(String[] args){ 26 27 //从子类对象往父类变量赋值(自动类型提升,向上转型) 28 Person p = new Student(); 29 30 } 31 }
2>.多态的向下转型
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 abstract class Person{ 8 public abstract void sleep(); 9 } 10 11 12 class Student extends Person{ 13 public void sleep(){ 14 System.out.println("学生在休息!"); 15 } 16 } 17 18 class Teacher extends Person{ 19 public void sleep(){ 20 System.out.println("老师在休息!"); 21 } 22 } 23 24 public class PersonDemo{ 25 public static void main(String[] args){ 26 27 //从子类对象往父类变量赋值(自动类型提升,向上转型) 28 Person p = new Student(); 29 30 31 //从父类类型转向子类类型(向下转型) 32 Student s = (Student)p; 33 34 } 35 }
3>.多态转型的案例
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 abstract class Animal{ 8 public abstract void eat(); 9 } 10 11 class Cat extends Animal{ 12 public void eat(){ 13 System.out.println("猫吃猫粮!"); 14 } 15 16 public void catchMouse(){ 17 System.out.println("猫抓老鼠!"); 18 } 19 } 20 21 class Dog extends Animal{ 22 public void eat(){ 23 System.out.println("狗吃狗粮!"); 24 } 25 26 public void run(){ 27 System.out.println("狗能跑!"); 28 } 29 } 30 31 public class AnimalDemo{ 32 public static void main(String[] args){ 33 //两个子类,使用两次多态调用 34 Animal a1 = new Cat(); 35 Animal a2 = new Dog(); 36 //a1,a2调用子类父类共有方法,运行走子类的重写方法 37 a1.eat(); 38 a2.eat(); 39 /*类型向下转型,强制转换,调用子类的特有,防止发生异常, 40 a1属于CatUI小,转成Cat类,a2属于Dog对象,转成Dog类,我们 41 可以用关键字instanceof判断。 42 */ 43 if(a1 instanceof Cat){ 44 Cat c = (Cat)a1; 45 c.catchMouse(); 46 } 47 48 if(a2 instanceof Dog){ 49 Dog d = (Dog)a2; 50 d.run(); 51 } 52 53 } 54 } 55 56 57 /* 58 以上代码执行结果如下: 59 猫吃猫粮! 60 狗吃狗粮! 61 猫抓老鼠! 62 狗能跑! 63 */
六.匿名对象
1>.匿名对象的概念
匿名对象是指创建对象时,只有创建对象的语句,却没有把对象地址值赋值给某个变量。
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 class Person{ 8 public void eat(){ 9 System.out.println("人是铁,饭是钢,一顿不吃饿得慌!"); 10 } 11 } 12 13 14 public class PersonDemo{ 15 public static void main(String[] args){ 16 17 //有名字的对象,引用类型变量,可以反复使用eat方法。 18 Person p = new Person(); 19 p.eat(); 20 21 //匿名对象,没有引用变量,只能使用一次,如果你再通过new调用eat方法的话实际上是又新生成了一个对象。 22 new Person().eat(); 23 new Person().eat(); 24 25 } 26 }
2>.匿名对象的特点
a>.创建匿名对象直接使用,没有变量名;
b>.匿名对象在没有指定其引用变量时,只能使用一次;
c>.匿名对象可以作为方法接受的参数,方法返回值使用;
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 class Person{ 8 public void eat(){ 9 System.out.println("人是铁,饭是钢,一顿不吃饿得慌!"); 10 } 11 } 12 public class PersonDemo{ 13 public static void main(String[] args){ 14 //有名字的对象,引用类型变量,可以反复使用eat方法。 15 Person p = new Person(); 16 p.eat(); 17 //匿名对象,没有引用变量,只能使用一次,如果你再通过new调用eat方法的话实际上是又新生成了一个对象。 18 new Person().eat(); 19 new Person().eat(); 20 method(p); 21 22 //匿名方法当实参 23 method(new Person()); 24 p = method(); 25 method(p); 26 } 27 //方法的返回值是Person类型,方法的return语句,返回的是这个类的对象。就可以用匿名对象来实现。 28 public static Person method(){ 29 return new Person(); 30 } 31 //调用方法method,传递Person类型对象 32 public static void method(Person p){ 33 p.eat(); 34 } 35 } 36 37 38 39 /* 40 以上代码执行结果如下: 41 人是铁,饭是钢,一顿不吃饿得慌! 42 人是铁,饭是钢,一顿不吃饿得慌! 43 人是铁,饭是钢,一顿不吃饿得慌! 44 人是铁,饭是钢,一顿不吃饿得慌! 45 人是铁,饭是钢,一顿不吃饿得慌! 46 人是铁,饭是钢,一顿不吃饿得慌! 47 */
七.内部类
1>.内部类概念
a>.什么是内部类
将类写在其它类的内部,可以写在其它类的成员位置和局部位置,这时写在其它类内部的类成为内部类,其它类也称为外部类。
b>.什么时候使用内部类
在描述事物时,若一个事物内部还包含其它可能包含的事物,比如在描述汽车时,汽车中还包含这发动机,这时发动机就可以使用内部类来描述。
c>.内部类分类
内部类分为成员内部类和局部内部类。我们定义内部类时,就是一个正常定义类的过程,同样包含各种修饰符,继承与实现关系等。在内部类中可以直接访问外部类的所有成员。
2>.成员内部类的调用格式
内部类可以使用外部类成员,包括私有变量。外部类要使用内部类的成员,必须建立内部类对象。
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 class Outer{ 8 private int a = 100; 9 class Inner{ 10 public void inner(){ 11 System.out.println("内部类方法inter " + a); 12 } 13 } 14 } 15 16 public class InnerClassDemo{ 17 public static void main(String[] args){ 18 //创建内部类对象in 19 Outer.Inner in = new Outer().new Inner(); 20 //调用内部类方法 21 in.inner(); 22 } 23 } 24 25 26 /* 27 以上代码执行结果如下: 28 内部类方法inter 100 29 */
3>.成员内部类的同名变量调用
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 class Outer{ 8 int a = 100; 9 class Inner{ 10 int a = 200; 11 public void inner(){ 12 int a = 300; 13 System.out.println("内部类局部变量(内部类成员方法)访问:>>> "+ a); 14 System.out.println("内部类成员变量访问:>>> "+ this.a); 15 System.out.println("外部类成员变量访问:>>> "+ Outer.this.a); 16 } 17 } 18 } 19 20 public class InnerClassDemo{ 21 public static void main(String[] args){ 22 //创建内部类对象in 23 Outer.Inner in = new Outer().new Inner(); 24 //调用内部类方法 25 in.inner(); 26 } 27 } 28 29 30 /* 31 以上代码执行结果如下: 32 内部类局部变量(内部类成员方法)访问:>>> 300 33 内部类成员变量访问:>>> 200 34 外部类成员变量访问:>>> 100 35 */
3>.局部内部类
局部内部类,定义在外部类方法中的局部位置。与访问方法中的局部变量相似,可通过调用方法进行访问。
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 class Outer{ 8 public void out(){ 9 class Inner{ 10 public void inner(){ 11 System.out.println("局部内部类方法!"); 12 } 13 } 14 //创建我们定义的Inner对象 15 Inner in = new Inner(); 16 //调用创建好的对象的方法,这样不管谁只要能调用out方法就会触发调用Inner类中的inner()方法啦! 17 in.inner(); 18 } 19 } 20 21 public class InnerClassDemo{ 22 public static void main(String[] args){ 23 new Outer().out(); 24 } 25 } 26 27 28 /* 29 以上代码执行结果如下: 30 局部内部类方法! 31 */
4>.匿名内部类
a>.匿名内部类概念
内部类是为了应对更为复杂的类间关系。查看源代码中会涉及到,而在日常业务中很难遇到,这里不做赘述。最长用到的内部类就是匿名内部类,它是局部内部类的一种。定义的匿名内部类有两个含义:第一,临时定义某一指定类型的子类;第二,定义后即刻创建刚刚定义的这个子类的对象。
b>.定义匿名内部类的作用与格式
匿名内部类是创建某个子类对象的快捷方式。
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 interface Smoking{ 8 public abstract void smoking(); 9 } 10 11 public class SmokingDemo{ 12 public static void main(String[] args){ 13 //这就是用匿名对象实现接口并调用接口中的方法,切记不要忘记调用smoking()了哟! 14 new Smoking(){ 15 public void smoking(){ 16 System.out.println("运维不喜欢吸烟!"); 17 } 18 }.smoking(); 19 } 20 } 21 22 /* 23 以上代码执行结果如下: 24 运维不喜欢吸烟! 25 */