赞
踩
hello,上文带大家学习了java中类的继承,我们可以创建一个父类,将类中的共性抽取出来,通过子类继承的方式来实现代码的复用。今天带大家学习不同类之间的另外几种关系,即多态抽象类和接口。
多态,从字面意思去形象的理解可以解释为:针对不同的对象执行某一行为时,不同的对象会有不同的状态。
比如猫和狗都是动物,他们都有进食这个行为但是当我们调用狗这个对象时,吃的是狗粮,而调用猫时,则会选择吃猫食。
- class Animal{
- String name;
- int age;
- public Animal(String name,int age){
- this.name=name;
- this.age=age;
- }
- public void eat(){
- System.out.println("正在吃饭");
- }
-
- }
- class Cat extends Animal{
- public Cat(String name, int age) {
- super(name, age);//调用父类的构造方法
- }
-
- @Override
- public void eat() {
- System.out.println(this.name+"正在吃猫食");;
- }
- }
- class Dog extends Animal{
- public Dog(String name, int age) {
- super(name, age);
- }
-
- @Override
- public void eat() {
- System.out.println(this.name+"正在吃狗粮");
- }
- }
- public class Test {
- public static void eat(Animal a){
- a.eat();//这里放的是父类具体引用的是谁看传的对象
- }
-
- public static void main(String[] args) {
- Cat cat=new Cat("宝宝",3);
- Dog dog=new Dog("旺财",3);
- eat(dog);//传的狗
- eat(cat);//传的猫,这里其实就是动态绑定
-
- }
- }
这里还是解释一下,我们通过子类方法对父类的重写,后面又在Test类里创建eat()这里传的是Animal类,由于Animal是Dog和Cat的父类,所以后面我们在调用eat()时可以传Dog和Cat形,即小范围包含于大范围。这就是多态的含义。
重写:又称覆盖,子类可以对父类中的非静态,非private,非final,非构造方法的其他方法进行重新定义,注意:重写的方法返回值不能改变,方法的参数不能改变,方法名也不能改变。即方法的外壳,不变只对里面进行重新编写。我们可以根据子类的需要对父类中的方法进行重新定义。通常重写的方法会有一个标签 @override,上面的程序里有提及。
这里可以对比记忆一下我们学过的重载:
注意:重写是对于子类和父类中研究的,即不同类之间,而重载是在一个类中实现了多个参数不同的同名的方法。
动态绑定:即不能立马确定方法的行为,编译时不能确定,等到运行时才能世道方法到底调用的是哪个方法的类。
就是将子类引用给父类来使用,将小范围赋值给大范围,但本质上仍然为子类对象。
所以运行的结果为宝宝吃猫食。
当然你也可以使用Dog类来进行向下转型,因为Animal也包含Dog。
- 直接赋值
- 方法的返回值
- 方法的参数
直接赋值:
Animal animal=new Cat("宝宝",10);
当做返回值:
向上转型的优点:代码更加灵活。
向上转型的缺点:无法调用子类中的特有方法。
那如果我们就是要调用子类中的特有方法怎么办呢?这就需要使用向下转型了。
向下转型:
我们可以使用关键字instanceof来对向下转型的对象做一层检验,保障了代码的安全性。
- if(animal instanceof Cat){
- cat=(Cat)animal;
- cat.mew();
这里只有animal引用了Cat类的对象才执行if语句。
那么多态到底有什么优点呢,这里给大家总结一下,多态可以降低代码的圈复杂度,可以避免使用过多的if-else语句,其次,可拓展能力强需要新增一个类时,直接继承就好,需要注意构造方法么有多态性。
- class B {
- public B() {
- // do nothing
- func();
- }
- public void func() {
- System.out.println("B.func()");
- }
- }
- class D extends B {
- private int num = 1;
- @Override
- public void func() {
- System.out.println("D.func() " + num);
- }
- }
- public class Test {
- public static void main(String[] args) {
- D d = new D();
- }
- }
- // 执行结果
- D.func() 0
这里给大家一段有意思的代码,运行结果你猜对了吗,原因是new() D之后,第一步应该是调用父类的构造方法,即func(),但是构造方法在子类中被重写,所以调用的是子类中的func(),此时子类中的成员变量还没有赋值所以num仍然为0,所以运行结果为D.func() 0
其实,我们在最上面的代码中发现了一个问题,很多类由于不能将一个事物准确描述出来,针对不同的事物的行为不同,执行的方法也应该不同,比如Animal中的eat()并不能准确描述狗吃的狗粮,猫吃的猫粮,我们在狗类和猫类中还是要重写这个eat()方法,类似与Animal的这些类可以理解为抽象类。
抽象类,修饰限定符为public abstract,抽象类中的抽象方法(修饰符也是public abstract)类似与eat(),是不能有具体的定义的,原因很好理解,即使你写了,每个子类还得重写这个方法,我们只声明这个方法即可。注意:有抽象方法的类一定是抽象类,抽象类不一定含有抽象方法,抽象类也是类可以定义普通的成员变量,方法甚至是构造方法。(非常重要!!!!)
上代码理解一下:
- //抽象类
- public Abstract Animal{
-
- public abstract void eat();//抽象方法
- public abstract void look();//抽象方法
- }
- 抽象类也不能实例化对象,因为抽像类无法完整描述一个事物,他只能被子类继承,然后子类必须将抽象类当中的抽象方法进行重写,否则编译报错,
- 抽象方法不能是 private 的,因为子类还要重写抽象方法的。
- 抽象方法不能被final和static修饰,因为抽象方法要被子类重写 。
说到接口,我们第一想到的应该是USB接口插座...,这些当然都算接口,那么让我来说接口的特性的话,我觉得接口首先是一个封装好了的东西,并且它有着自己的功能,如果某个东西有了接口,那么他也应该具有接口的特性。
在java中接口是多个类的公共规范,是一种引用数据类型,接口的定义格式与定义类的格式基本相同,将class关键字换成 interface 关键字,就定义了一个接口。
- interface A{
- public abstract void method1(); // public abstract 是固定搭配,可以不写
- public void method2();
- abstract void method3()
- }
接口不能直接使用,必须要有一个"实现类"来"实现"该接口,实现接口中的所有抽象方法。 这也比较好理解,毕竟接口只是实现 了某个特定的功能,并不能描述一个具体的对象。我们使用关键字implement将类和接口连接起来。
- public class Animal implements IRunning{
- // ...
- }
这里给大家举一个电脑的例子:
- // USB接口
- public interface USB {
- void openDevice();
- void closeDevice();
- }
- // 鼠标类,实现USB接口
- public class Mouse implements USB {
- @Override
- public void openDevice() {
- System.out.println("打开鼠标");
- }
- @Override
- public void closeDevice() {
- System.out.println("关闭鼠标");
- }
- public void click(){
- System.out.println("鼠标点击");
- }
- }
- // 键盘类,实现USB接口
- public class KeyBoard implements USB {
- @Override
- public void openDevice() {
- System.out.println("打开键盘");
- }
- @Override
- public void closeDevice() {
- System.out.println("关闭键盘");
- }
- public void inPut(){
- System.out.println("键盘输入");
- }
- }
- // 笔记本类:使用USB设备
- public class Computer {
- public void powerOn(){
- System.out.println("打开笔记本电脑");
- }
- public void powerOff(){
- System.out.println("关闭笔记本电脑");
- }
- public void useDevice(USB usb){
- usb.openDevice();
- if(usb instanceof Mouse){
- Mouse mouse = (Mouse)usb;
- mouse.click();
- }else if(usb instanceof KeyBoard){
- KeyBoard keyBoard = (KeyBoard)usb;
- keyBoard.inPut();
- }
- usb.closeDevice();
- }
- }
- // 测试类:
- public class TestUSB {
- public static void main(String[] args) {
- Computer computer = new Computer();
- computer.powerOn();
- // 使用鼠标设备
- computer.useDevice(new Mouse());
- // 使用键盘设备
- computer.useDevice(new KeyBoard());
- computer.powerOff();
- }
- }
接口虽然是一种引用类型但是,他不能new(),必需通过引用类来重写接口中的所有方法,并且每个方法都默认为public abstract,也可以使用static和default修饰但是必须立即定义该方法,注意重写方法时一定要用public修饰。
- public interface USB {
- void openDevice(); // 默认是public的
- void closeDevice(); // 默认是public的
- }
- public class Mouse implements USB {
- @Override
- void openDevice() {
- System.out.println("打开鼠标");
- }
- // ...
- }
- // 编译报错,重写USB中openDevice方法时,不能使用默认修饰符
- // 正在尝试分配更低的访问权限; 以前为public
而且一个类是可以实现多个接口的,这也间接解决了java的多继承问题。
- class Animal {
- protected String name;
- public Animal(String name) {
- this.name = name;
- }
- }
- interface IFlying {
- void fly();
- }
- interface IRunning {
- void run();
- }
- interface ISwimming {
- void swim();
- }
- class Cat extends Animal implements IRunning {
- public Cat(String name) {
- super(name);
- }
- @Override
- public void run() {
- System.out.println(this.name + "正在用四条腿跑");
- }
- }
- class Fish extends Animal implements ISwimming {
- public Fish(String name) {
- super(name);
- }
- @Override
- public void swim() {
- System.out.println(this.name + "正在用尾巴游泳");
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。