面向对象2

多态

向上转型

例如:

Bird bird = new Bird("圆圆");
该代码也可以写成下面的形式:
Bird bird = new Bird("圆圆"); 
Animal bird2 = bird; 
// 或者写成下面的方式 
Animal bird2 = new Bird("圆圆");

此时 bird2 是一个父类 (Animal) 的引用, 指向一个子类 (Bird) 的实例. 这种写法称为 向上转型

向上转型发生的时机:
1.直接赋值
2.方法传参
3.方法返回

方法传参

public class Test {     
	public static void main(String[] args) {         
		Bird bird = new Bird("圆圆");         
		feed(bird);     
	} 
    public static void feed(Animal animal) {         
    	animal.eat("谷子");     
    } 
}  
// 执行结果 圆圆正在吃谷子 

方法返回

public class Test {     
	public static void main(String[] args) {         
		Animal animal = findMyAnimal();     
	}  
    public static Animal findMyAnimal() {         
    	Bird bird = new Bird("圆圆");         
    	return bird;     
    } 
} 

动态绑定

在 Java 中, 调用某个类的方法, 究竟执行了哪段代码 (是父类方法的代码还是子类方法的代码) , 要看究竟这个引 用指向的是父类对象还是子类对象. 这个过程是程序运行时决定的(而不是编译期),因此称为动态绑定

方法重写

子类实现父类的同名方法, 并且参数的类型和个数完全相同, 这种情况称为 覆写/重写/覆盖(Override).

关于重写的注意事项
1.重写和重载完全不一样. 不要混淆(思考一下, 重载的规则是啥?)
2. 普通方法可以重写, static 修饰的静态方法不能重写.
3. 重写中子类的方法的访问权限不能低于父类的方法访问权限.

小结(重写和重载的区别):
重写/覆盖/复写:
1.函数名相同 子类的访问修饰限定符一定要大于等于父类的访问修饰限定符
2.参数列表相同 父类的方法不能是私有的
3.返回值也要相同
4.静态的方法和private不能被重写
重载(Overload)
1.函数名相同
2.参数列表不同(个数、类型)
3.返回值不做要求

理解多态

代码示例: 打印多种形状

//类的实现
class Shape {     
	public void draw() {         
	// 啥都不用干     
	} 
}  
class Cycle extends Shape {     
	@Override     
	public void draw() {         
		System.out.println("○");     
	} 
}  
class Rect extends Shape {     
	@Override     
	public void draw() {         
		System.out.println("□"); 
	} 
}  
class Flower extends Shape {     
	@Override     
	public void draw() {         
		System.out.println("♣");     
	} 
} 

//类的调用
// Test.java 
public class Test {     
	public static void main(String[] args) {         
		Shape shape1 = new Flower();         
		Shape shape2 = new Cycle();         
		Shape shape3 = new Rect();         
		drawMap(shape1);         
		drawMap(shape2);         
		drawMap(shape3);     
}  
// 打印单个图形     
	public static void drawShape(Shape shape) {         
		shape.draw();     
	} 
} 

当类的调用者在编写 drawMap 这个方法的时候, 参数类型为 Shape (父类), 此时在该方法内部并不知道, 也不关注当 前的 shape 引用指向的是哪个类型(哪个子类)的实例. 此时 shape 这个引用调用 draw 方法可能会有多种不同的表现 (和 shape 对应的实例相关), 这种行为就称为 多态.

使用多态的好处

  1. 类调用者对类的使用成本进一步降低.
    封装是让类的调用者不需要知道类的实现细节.
    多态能让类的调用者连这个类的类型是什么都不必知道, 只需要知道这个对象具有某个方法即可.
    因此, 多态可以理解成是封装的更进一步, 让类调用者对类的使用成本进一步降低.
  2. 能够降低代码的 “圈复杂度”, 避免使用大量的 if - else
  3. 可扩展能力更强.
    如果要新增一种新的形状, 使用多态的方式代码改动成本也比较低.

向下转型

向上转型是子类对象转成父类对象, 向下转型就是父类对象转成子类对象. 相比于向上转型来说, 向下转型没那么常见, 但是也有一定的用途.
向下转型的前提条件是:父类已经引用了子类(向下转型后的类型)的对象

super 关键字

前面的代码中由于使用了重写机制, 调用到的是子类的方法. 如果需要在子类内部调用父类方法可以使用 super 关键字.
super 表示获取到父类实例的引用. 涉及到两种常见用法.

  1. 使用了 super 来调用父类的构造器
public Bird(String name) {     
	super(name); 
} 
  1. 使用 super 来调用父类的普通方法
public class Bird extends Animal {     
	public Bird(String name) {         
		super(name);     
	}  
    @Override     
    public void eat(String food) {         
    	// 修改代码, 让子调用父类的接口.          
    	super.eat(food);         
    	System.out.println("我是一只小鸟");         
    	System.out.println(this.name + "正在吃" + food);     
    } 
} 

在这个代码中, 如果在子类的 eat 方法中直接调用 eat (不加super), 那么此时就认为是调用子类自己的 eat (也就是递 归了). 而加上 super 关键字, 才是调用父类的方法.
super 和 this 的区别:
1.概念:
this:访问本类中的属性和方法;
super:由子类访问父类中的属性、方法
2.查找范围:
this:先查找本类,如果本类没有就调用父类
super:不查找本类而直接调用父类
3.this表示当前对象

抽象类

语法规则

在刚才的打印图形例子中, 我们发现, 父类 Shape 中的 draw 方法好像并没有什么实际工作, 主要的绘制图形都是由 Shape 的各种子类的 draw 方法来完成的. 像这种没有实际工作的方法, 我们可以把它设计成一个 抽象方法(abstract method), 包含抽象方法的类我们称为 抽象类(abstract class).

abstract class Shape {     
	abstract public void draw(); 
}

在 draw 方法前加上 abstract 关键字, 表示这是一个抽象方法.
同时抽象方法没有方法体(没有 { }, 不能执行具体代码). 对于包含抽象方法的类, 必须加上 abstract 关键字表示这是一个抽象类.
注意事项

  1. 抽象类不能直接实例化.
  2. 抽象方法不能是 private 的
  3. 抽象类中可以包含其他的非抽象方法, 也可以包含字段. 这个非抽象方法和普通方法的规则都是一样的, 可以被重写, 也可以被子类直接调用

抽象类的作用

1.抽象类存在的最大意义就是为了被继承.
2.抽象类本身不能被实例化, 要想使用, 只能创建该抽象类的子类. 然后让子类重写抽象类中的抽象方法.

接口

接口是抽象类的更进一步. 抽象类中还可以包含非抽象方法, 和字段. 而接口中包含的方法都是抽象方法, 字段只能包含 静态常量.

语法规则

interface IShape {     
	void draw(); 
}  
class Cycle implements IShape {     
	@Override     
	public void draw() {         
		System.out.println("○");     
	} 
}  
public class Test {     
	public static void main(String[] args) {         
		IShape shape = new Rect();         
		shape.draw();     
	} 
} 
  • 使用 interface 定义一个接口
  • 接口中的方法一定是抽象方法, 因此可以省略 abstract
  • 接口中的方法一定是 public, 因此可以省略 public
  • Cycle 使用 implements 继承接口. 此时表达的含义不再是 “扩展”, 而是 “实现”
  • 在调用的时候同样可以创建一个接口的引用, 对应到一个子类的实例.
  • 接口不能单独被实例化

接口中只能包含抽象方法. 对于字段来说, 接口中只能包含静态常量(final static).

interface IShape {     
	void draw();     
	public static final int num = 10; 
} 

其中的 public, static, final 的关键字都可以省略. 省略后的 num 仍然表示 public 的静态常量
注意:
1.创建接口的时候, 接口的命名一般以大写字母 I 开头.
2.接口的命名一般使用 “形容词” 词性的单词.
3.阿里编码规范中约定, 接口中的方法和属性不要加任何修饰符号, 保持代码的简洁性。

接口间的继承

接口可以继承一个接口, 达到复用的效果. 使用 extends 关键字.

interface IRunning {     
	void run(); 
}  
interface ISwimming {     
	void swim(); 
}  
// 两栖的动物, 既能跑, 也能游 
interface IAmphibious extends IRunning, ISwimming { 
 
} 
 
class Frog implements IAmphibious {  
	... 
} 

通过接口继承创建一个新的接口 IAmphibious 表示 “两栖的”. 此时实现接口创建的 Frog 类, 就继续要实现 run 方法, 也需要实现 swim 方法.
接口间的继承相当于把多个接口合并在一起。

总结

抽象类和接口的区别:
核心区别:抽象类中可以包含普通方法和普通字段, 这样的普通方法和字段可以被子类直接使用(不必重写), 而接口中不 能包含普通方法, 子类必须重写所有的抽象方法。
区别:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值