面向对象
组合
和继承类似,组合也是表达类之家关系的方式,也是能够达到代码冲用的效果
例如:
组合并没有涉及到特殊的语法(诸如
extends
关键字),仅仅是将一个类的实例作为另一个类的字段,这是我们设计类的一种常用的方式之一
组合表示is-a语义
在刚才的例子中我们可以理解成一个学校“包含”若干学生和教师
继承表示has-a语义
在上面的“动物和猫”的例子中,也可以理解成猫也“是”一种动物
多态
向上转型
在上篇文章当中我们写了如下的代码
Bird bird = new Bird("圆圆");
这个代码也可以写成这样子
Animal bird2 = new Bird("圆圆");
此时bird2
是一个父类(Animal)的引用,指向了一个子类(Bird)的实例,这种写法为向上转型
为啥叫“向上转型”?
在面向对象程序设计中,针对一些复杂的场景(很多类,很复杂的继承关系),程序员会画一种UML图来表示类之间的关系,此时父类通常画在子类的上方,我们就称之为“向上转型”,表示往父类的方向转,
注意:关于UML图的的规则我们不详细讨论,有兴趣的伙伴可以自己看看
向上转型发生的时机:
- 直接赋值
- 方法传参
- 方法返回
直接赋值的方式就是如上所示,另外两种方式和直接赋值没有本质区别
方法传参
public class Test { public static void feed(Animal animal){ animal.eat("谷子"); } public static void main(String[] args) { Bird bird = new Bird("圆圆"); feed(bird); } } //执行结果 //圆圆正在吃谷子
此时形参animal
的类型是Animal(基类),实际上对应到Bird
(父类)的实例
方法返回
public class Test { public static void main(String[] args) { Animal animal = findMyAnimal(); } public static Animal findMyAnimal() { Bird bird = new Bird("圆圆"); return bird; } }
此时findMyAnimal返回的是一个Animal类型的的引用,但是实际上对应到Bird的实例
动态绑定
当子类和父类中出现同名方法的时候,再去调用会出现什么情况?
class Animal { String name; public Animal(String name) { this.name = name; } public void eat(String food) { System.out.println("我是一只小动物"); System.out.println(this.name + "正在吃" + food); } } class Bird extends Animal { public Bird(String name) { super(name); } public void eat(String food) { System.out.println("我是一只小鸟"); System.out.println(this.name + "正在吃" + food); } } public class Main { public static void main(String[] args) { Animal animal = new Animal("圆圆"); animal.eat("谷子"); Animal animal2 = new Bird("扁扁"); animal2.eat("谷子"); } }
运行结果:
此时我们发现:
animal1
和animal2
虽然都是Animal
类型的引用,但是animal1
指向了Animal
类型的实例,animal2
指向了Bird
类型的实例- 针对
animal1
和animal2
分别调用了eat
方法,发现animal1.eat
实际调用了父类的方法,而animal2.eat
实际调用了子类的方法
方法重写
针对刚才的eat
方法来说
子类实现父类的同名方法,并且参数的类型和个数完全相同,这种情况成为覆写/重写/覆盖(Override)
关于重写的注意事项
1.重写和重载完全不一样,不要混淆
2.普通的方法也可以重写,static
修饰的静态方法不能重写
3.重写中子类的方法的访问权限不能低于父类的方法访问权限
4.重写的方法返回值类型不一定和父类的方法相同(但是建议写成相同,特殊情况除外)
有了这个注解能帮助我们进行一些合法性校验,例如不小心将名字拼错了(比如写成aet),那么此时编译器就会发现父类中没有aet方法,就会编译错误显示无法构成重写
小结:重载和重写的区别
体会动态绑定和方法重写
上面的动态绑定和方法重写使用的相同的代码示例
事实上,方法重写是Java语法层次上的规则,而动态绑定是方法重写这个语法规则的底层实现,两者本质上描述的是相同的事情,只是侧重点不同