向Java程序员的目标前进!
为了加深多态的成员特点的理解,我们将以伪代码的形式介绍。
故事背景:孔子是一名专门讲解论语的老师。而孔子的父亲是刚刚下岗的Java程序员,由于当时JavaSE非常火,很多人都需要学习,孔子的父亲就开始当老师讲JavaSE。有一天,孔子的爹被请去讲课了,孔子一个人在家。这时,还有一些人慕名前来找孔子爹学习JavaSE,孔子开始装他爹的模样,带上装备,黏上胡子,样子很像!1
伪代码:
class 孔子爹{ int age = 40; //teach功能 public void teach(){ System.out.println("讲解JavaSE"); } } class 孔子 extends 孔子爹{ int age = 20; @Override public void teach(){ System.out.println("讲解论语"); } //特有功能 public void playGame(){ System.out.println("会写书"); } }
对以上伪代码在测试类中进行测试:
public class Test { public static void main(String[] args) { 孔子爹 k1 = new 孔子() ;//向上转型 System.out.println(k1.age);//编译看左,运行看左。所以这里输出结果是40 k1.teach();//编译看左,运行看右。输出结果是“讲解论语”,一讲课就暴露了。 k1.playGame();//这句会报错,因为这是子类的特有功能,访问不了,为了节省内存空间,推荐使用向下转型。将父类的引用强制转换为子类的引用 孔子 k2 = (孔子) k1; k2.playGame();//这样写不会报错 } }
/** * 需求:设计一个台灯类(Lamp)其中台灯有灯泡类(Buble)这个属性, * 还有开灯(on)这个方法。设计一个灯泡类(Buble), * 灯泡类有发亮的方法,其中有红灯泡类(RedBuble)和绿灯泡类(GreenBuble) * 他们都继承灯泡类(Buble)一个发亮的方法,请设计出一段代码可以使台灯开启灯泡发亮 * 红灯泡发红光,绿灯泡发绿光!(多态) * */ //灯泡类 class Buble { //定义一个发亮的方法 public void shine(){ System.out.println("灯泡可以发亮了...") ; } } //绿灯泡 class GreenBuble extends Buble { @Override public void shine() { System.out.println("灯泡可以发绿光了..."); } } //台灯类 class Lamp { //台灯有灯泡类(Buble)这个属性 private Buble buble ;//灯泡类 //灯(on)这个方法,开灯---灯泡要"发亮" public void on(Buble buble){ buble.shine() ;//调用灯泡的发亮的方法 } } //红灯泡 class RedBuble extends Buble { @Override public void shine() { System.out.println("灯泡可以发红光了..."); } } //测试类 public class Test1 { public static void main(String[] args) { //创建台灯类对象 Lamp lamp = new Lamp() ; Buble buble = new RedBuble() ; //调用开灯方法 lamp.on(buble) ; System.out.println("--------------------------------"); Buble buble2 = new GreenBuble() ; lamp.on(buble2); System.out.println("--------------------------------"); //匿名对象:子类匿名对象 lamp.on(new RedBuble()); lamp.on(new GreenBuble()); } }
/** * 需求:定义一个动物类,里面有一个方法voice() * 定义一个类Cat,实现voice方法 * 然后增加一种新的动物类型:Pig(猪),实现voice()方法。 * 定义一个Dog类,实现voice方法 * 定义一个Store(宠物店)类的getInstance方法: * 如果传入的参数是字符串dog,则返回一个Dog对象; * 如果传入pig,则返回一个Pig对象;否则,返回一个Cat对象。 * */ //动物类 class Animal { public void voice(){ System.out.println("动物都需要发出声音..."); } } //猫类 class Cat extends Animal { @Override public void voice() { System.out.println("猫发出喵喵叫的声音..."); } } //狗类 class Dog extends Animal { @Override public void voice() { System.out.println("狗发出汪汪叫的声音"); } } //猪类 class Pig extends Animal { @Override public void voice() { System.out.println("猪发出哼哼叫的声音..."); } } //宠物店类 class Store { private Store(){} //构造方法私有化,外界不能new,然后功能加入static public static Animal getInsance(String type){ if(type.equals("dog")){ return new Dog() ; }else if(type.equals("pig")){ return new Pig() ; }else{ return new Cat() ; } } } //测试类 public class Test2 { public static void main(String[] args) { Animal a = Store.getInsance("pig");//new Pig() ; a.voice(); a = Store.getInsance("cat") ; //new Cat() ; a.voice(); a = Store.getInsance("dog") ; //new Dog() ; a.voice(); } }
回顾之前的猫狗案例,它们都有父类动物类。动物类是一个很抽象的事物,在定义时,我们只知道动物都会吃和睡。但是我们说一个动物,说的都是最具体的动物。只有说到具体的动物,我们才知道它吃的东西和其它的生活习性。
我们在开发中,应该将这种具有概括性的事物抽象化。并且,它们的吃或者睡的行为也不应该再给出具体的体现,应该只是声明出来(定义为没有方法提的方法),让具体的事物(子类)进行重写!
和之前学习定义成员方法是一样的,只是没有方法体和{}
而已
格式:
权限修饰符 abstract 返回值类型 方法名(参数列表);
abstract class 类名{}
一旦定义了抽象类和抽象方法,再定义子类时必须重写所有父类的抽象方法!
本质:强制子类必须做的事情!
抽象类不能实例化!(不能创建对象)
通过抽象类多态,父类引用指向子类对象。前提是要有最具体的子类,比如:
Fu fu = new Zi() ;
这里的Fu就是一个抽象类类型。
问题:如果一个类中没有抽象方法,那么把这个类定义为抽象类的意义何在?
解答:这是属于设计层面的问题。定义抽象类,目的是不想直接让这个类创建对象,因为最终还是有具体的子类存在的。
比如jdk提供的日历类Calendar,它是一个抽象类,不能创建对象。但是Calendar提供了静态方法,返回值就是它自己。所以调用其它成员方法的本质就是在创建具体的子类对象。
/** * 需求:设计一个台灯类(Lamp)其中台灯有灯泡类(Buble)这个属性, * 还有开灯(on)这个方法。设计一个灯泡类(Buble), * 灯泡类有发亮的方法,其中有红灯泡类(RedBuble)和绿灯泡类(GreenBuble) * 他们都继承灯泡类(Buble)一个发亮的方法,请设计出一段代码可以使台灯开启灯泡发亮 * 红灯泡发红光,绿灯泡发绿光! * */ //灯泡类 abstract class Buble { public abstract void shine() ; } //绿灯泡类 class GreenBuble extends Buble { @Override public void shine() { System.out.println("灯泡可以发绿光了"); } } //台灯类 class Lamp { private Buble buble ; public void on(Buble buble){ buble.shine() ; } } //红灯泡类 class RedBuble extends Buble { @Override public void shine() { System.out.println("灯泡可以发红光了"); } } //测试类 public class Test { public static void main(String[] args) { //创建台灯类对象 Lamp lamp = new Lamp() ; Buble buble = new RedBuble() ;//抽象类多态 //调用开灯方法 lamp.on(buble) ; System.out.println("--------------------------------"); Buble buble2 = new GreenBuble() ; lamp.on(buble2); System.out.println("--------------------------------"); //匿名对象:子类匿名对象 lamp.on(new RedBuble()); lamp.on(new GreenBuble()); } }
abstract的应用场景:修饰类或成员方法
修饰成员方法时注意:
abstract不能和private关键字使用。因为被private修饰的成员只能在当前类访问,而加入abstract后需要让子类实现这个方法。这已经超出了当前访问的范围,所以这是冲突的。
abstract不能和final关键字使用。因为被final修饰的成员方法是不能被重写的,而abstract修饰的抽象方法需要被子类强制重写的,所以这是冲突的。
abstract不能和static关键字使用。因为被static修饰的方法是跟类相关的,修饰后会随着类的加载而加载。而抽象方法是需要被子类重写的,最终需要使用对象来实现抽象类多态。abstract和static一块使用抽象方法在父类中又没有方法体,加载进入内存就没有意义了。所以这是冲突的。
//定义一个抽象方法 //private abstract void show() ;//非法格式 需要子类实现show方法,而private修饰的方法只能在当前访问 //public final abstract void show() ;//非法格式 //public static abstract void show() ; //非法格式 //标准的格式 public abstract 返回值类型 方法名(参数列表);//参数列表可能空参/有参(基本类型/引用类型)
/** * 需求:定义一个动物类,里面有一个方法voice(), * 定义一个类Cat,实现voice方法 * 然后增加一种新的动物类型:Pig(猪),实现voice()方法。 * 定义一个Dog类,实现voice方法 * 定义一个Store(宠物店)类的getInstance方法: * 如果传入的参数是字符串dog,则返回一个Dog对象; * 如果传入pig,则返回一个Pig对象;否则,返回一个Cat对象。 */ //动物类 abstract class Animal { //这个动物类----抽象类 //优化 //动物都需要发声,将voice方法应该仅仅声明即可,需要让子类实现发声的方法 public abstract void voice() ; } //猫类 class Cat extends Animal { @Override public void voice() { System.out.println("猫发出喵喵叫的声音..."); } } //狗类 class Dog extends Animal { @Override public void voice() { System.out.println("狗发出汪汪叫的声音"); } } //猪类 class Pig extends Animal { @Override public void voice() { System.out.println("猪发出哼哼叫的声音..."); } } //宠物店 class Store { private Store(){} //构造方法私有化,外界不能new,然后功能加入static public static Animal getInsance(String type){ if(type.equals("dog")){ return new Dog() ; }else if(type.equals("pig")){ return new Pig() ; }else{ return new Cat() ; } } } //测试类 public class Test { public static void main(String[] args) { Animal a = Store.getInsance("pig");//new Pig() ; a.voice(); a = Store.getInsance("cat") ; //new Cat() ; a.voice(); a = Store.getInsance("dog") ; //new Dog() ; a.voice(); } }
/** * 需求:用抽象类实现“猫狗”案例 * 定义一个动物类(Animal),属性有姓名、年龄、颜色,行为有吃和睡 * 再定义猫类(Cat)和狗类(Dog),都继承自动物类 * 猫和狗吃的不一样,重写它们的方法 * 分别定义方法 猫有特有功能:玩毛线;狗有特有功能:看门 */ //动物类 abstract class Animal { //抽象类 //姓名,年龄,颜色 private String name ; private int age ; private String color ; //无参构造方法 public Animal() { } //有参构造方法 public Animal(String name, int age, String color) { this.name = name; this.age = age; this.color = color; } //公共的访问方法 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; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } //动物的吃和睡觉,应该给出声明即可---抽象方法 public abstract String eat() ; public abstract void sleep() ; } //猫类 class Cat extends Animal { //构造方法 public Cat() { } public Cat(String name, int age, String color) { super(name, age, color); } @Override public String eat() { return "猫吃鱼..."; } @Override public void sleep() { System.out.println("猫舔着爪子睡觉..."); } //特有功能 public void playGame() { System.out.println("猫会玩毛线..."); } } //狗类 class Dog extends Animal { //构造方法 public Dog() { } public Dog(String name, int age, String color) { super(name, age, color); } @Override public String eat() { return "狗吃骨头"; } @Override public void sleep() { System.out.println("狗趴着睡觉..."); } //特有功能 public void catchRabit(){ System.out.println(this.getName()+"会抓兔子"); } } public class Test { public static void main(String[] args) { //测试类 //方式1:无参构造+setXXX()/getXXX() //多态测试 Animal a = new Dog() ;//抽象类多态 String result = a.eat(); a.setName("黑子") ; a.setAge(3) ; a.setColor("棕色") ; System.out.println(a.getName()+"---"+a.getAge()+"---"+a.getColor()); System.out.println(result); a.sleep(); ; //特有功能 //向下转型 Dog d = (Dog) a; d.catchRabit() ; System.out.println("--------------------------------------") ; //方式2:有参构造方法+getXXX() //猫类测试 Animal a3 = new Cat("橘猫", 3, "花色"); System.out.println(a3.getName()+"---"+a3.getAge()+"---" +a3.getColor()); String str2 = a3.eat(); System.out.println(str2); a3.sleep(); Cat cc = (Cat) a3; cc.playGame(); } }
继续回到猫狗的例子,猫和狗属于动物的一种,但是它能够具有跳高、钻火圈、做数学题这些它们本身不具备的功能。这是经过后天学习和驯养员培养出来,才具备了这些额外的功能。通过这个例子我们可以总结出来,一个事物如果实现了额外的功能,那么它也就具备了这个功能。
回到Java中,接口的定义其实与上面的总结十分相似。接口是一种规范,如果类能够实现接口中额外的功能,那么就说明当前这个类具备这个功能。从宏观角度来说,接口能让事物实现额外的功能。接口是比抽象类还抽象的一种类型。
public interface 接口名{}
接口名和类名的命名规范是一致的,都遵循"大驼峰命名法"
class 类名 implements 接口名{}
接口中的方法只能是抽象方法,不能有方法体
接口不能实例化(不能创建对象)
接口的实例化是通过接口多态实现的
/** * 在AnimalArain接口中定义两个功能: * jump():跳高 * compute():计算 * 最后编写测试类测试 */ interface Jump{ // public void jump(){ //接口的方法只能是抽象方法 // // } public abstract void jump() ; } interface ComplateCal{//做计算的接口 public abstract void cal() ; } class Dog{ public void lookDoor(){ System.out.println("狗可以看门"); } } //跳高高它是狗,然后局部额外的功能,跳高 class JumpDog extends Dog implements Jump,ComplateCal{ //继承一个类的同时,可以实现多个接口 @Override public void jump() { System.out.println("狗可以跳高了..."); } @Override public void cal() { System.out.println("狗可以做计算了..."); } } //测试类 public class InterfaceDemo { public static void main(String[] args) { //创建接口对象 // Jump jump = new Jump () ;//接口不能实例化, //如何实例化呢:接口多态---提供接口的子实现类 Jump jump = new JumpDog() ;//接口类型---->子实现类对象 (接口类型) jump.jump() ; //向下转型 JumpDog jumpDog = (JumpDog) jump; jumpDog.lookDoor() ; jumpDog.cal(); ComplateCal cc = new JumpDog() ;//接口多态 cc.cal() ; } }
public static final
,可以省略public abstract
,可以省略类与类:继承关系,只能单继承,不支持多继承,但是可以多层继承
类与接口:实现关系,可以单(接口)实现,也可以多(接口)实现。还可以在继承一个类的同时实现多个接口
class 子实现类名 extends 父类名 implements 接口1,接口2,...{}
接口与接口:继承关系,不仅可以单继承,还可以多继承,也可以多层继承
成员的区别
接口的成员:
实际开发中,如果要自定义一个常量,就先定义一个接口,里面再写常量即可!
抽象类的成员
关系的区别
类与类的关系:继承关系
类可能是抽象类或具体类。继承关系只支持单继承,不支持多继承,可以多层继承
类与接口的关系:实现关系
一个类继承另一个类的同时可以实现多个接口
接口与接口的关系:继承关系
可以单继承,可以多继承,也可以多层继承
设计理念的区别
抽象类最终肯定有具体的子类,是继承关系,体现的是一种“is a”的关系,可以用抽象类多态描述
接口描述的是事物本身不具备的功能,是通过后台学习培养出来的额外的拓展功能。
接口的核心思想体现的是“like a”的关系
分析:
实现:
//篮球教练类 class BasketballCoach extends Coach{ public BasketballCoach() { } public BasketballCoach(String name, int age, String gender) { super(name, age, gender); } @Override public void teach() { System.out.println("篮球教练教运动员怎么运球"); } } //篮球运动员类 class BasketBallPlayer extends Player { public BasketBallPlayer() { } public BasketBallPlayer(String name, int age, String gender) { super(name, age, gender); } @Override public void study() { System.out.println("篮球运动员学习如何运球和投篮"); } } //教练类 abstract class Coach extends Person{ public Coach() { } public Coach(String name, int age, String gender) { super(name, age, gender); } @Override public void eat() { System.out.println("教练吃的是快餐"); } public abstract void teach(); } //人类 public abstract class Person { //抽象类 private String name ; //姓名 private int age ; //年龄 private String gender ; //性别 //无参 public Person() { } //有参构造 public Person(String name, int age, String gender) { this.name = name; this.age = age; this.gender = gender; } //公共访问方法 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; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } //吃的东西不一样,所以让子类实现,仅仅给出声明即可 public abstract void eat() ; } //乒乓球教练类 class PingPangCoach extends Coach implements SpeakEnglish{ public PingPangCoach() { } public PingPangCoach(String name, int age, String gender) { super(name, age, gender); } @Override public void teach() { System.out.println("乒乓球教练教运动员怎么打球"); } @Override public void speak() { System.out.println("乒乓球教练会说英语!"); } } //乒乓球运动员类 class PingPangPlayer extends Player implements SpeakEnglish { public PingPangPlayer() { } public PingPangPlayer(String name, int age, String gender) { super(name, age, gender); } @Override public void study() { System.out.println("乒乓球运动员学习如何发球和接球"); } @Override public void speak() { System.out.println("乒乓球运动员会说英语!"); } } //运动员类 abstract class Player extends Person { //抽象类 //构造方法 public Player() { } public Player(String name, int age, String gender) { super(name, age, gender); } @Override public void eat() { System.out.println("运动员吃的是营养餐"); } //学习的的内容不一样,只有见到具体的运动员才能知道学的是什么 //学习的功能---给出声明即可 public abstract void study() ; } //说英语的接口 interface SpeakEnglish { //说英语 public abstract void speak() ; } //测试类 public class Test { public static void main(String[] args) { SpeakEnglish se = new PingPangPlayer() ; se.speak(); //会说英语 PingPangPlayer pingPangPlayer = (PingPangPlayer)se; pingPangPlayer.setName("马龙") ; pingPangPlayer.setAge(30) ; pingPangPlayer.setGender("男"); System.out.println(pingPangPlayer.getName()+"---"+pingPangPlayer.getAge()+"---"+pingPangPlayer.getGender()) ; pingPangPlayer.eat() ; pingPangPlayer.study(); System.out.println("--------------------------------------") ; Coach pc = new PingPangCoach("刘国梁",40,"男"); PingPangCoach pingPangCoach = (PingPangCoach) pc; pingPangCoach.speak(); System.out.println(pc.getName()+"---"+pc.getAge()+"---"+pc.getGender()); pc.eat(); pc.teach(); } }
博客难免会产生一些错误。如果写的有什么问题,欢迎大家批评指正。
故事是完全虚构的。 ↩︎