面向对象,是对现实世界的模拟,下图简单模拟了一个动物世界。
面向对象的三个基本特征之一继承,这里Primat继承了Animal,Person继承了Primat,继承很简单,看以下代码实现:
1 | //动物这个类比较抽象,没有具体的属性,写成接口比较合理 |
1 | /** |
1 | /** |
1 | /** |
1 | public static void main(String[] args) { |
打印结果如下:
1 | 小猴1吃起了香蕉 |
在代码中,不管是动物,鸟类,人类,猴子,我们都可以抽象成类,类是对象的模板,通过new关键字,可以创建一个个对象。看上面代码中的animal1和animal2,虽然都是同一个形态(Animal),由于指向的是子类对象,当调用同一个eat()方法,运行时会智能匹配到子类的实现,最后得到的结果也不一样,这种形为,我们称之为多态。
多态要满足三个条件。
- 要有继承;(Person继承了Animal,Monkey也继承了Animal)
- 要有重写;(都重写了父类的eat方法)
- 父类引用指向子类对象。如上面的代码:
1 | Animal animal1 = new Monkey("小猴1"); |
person4这个对象能访问到name、sex属性,eat()、printAge()方法,但无法访问age属性,isLady()方法。是因为我们在该属性和方法前面加了private关键字。隐藏了不想对客户端暴露的age属性和isLady()方法(这里的客户端是main方法),但是我们对客户端提供了一个printAge方法来打印年龄,但在打印年龄前,我们对年龄做了一系列处理(不打印女士年龄)。
对于这种隐藏对象属性和实现细节,仅对外公开指定方法来控制程序中属性的访问和修改,我们称之为封装。(这儿我们没有对age提供set方法,提供了一个printAge()方法供外部访问)。
假如有这样一个需求,当Person对象的名字、年龄、性别都一致,就当成同一人处理,在Java中的==与equals可知,我们不会用==或equals直接去做比较,以下是结果:
1 | public static void main(String[] args) { |
1 | false |
当用==和equals失败后,开发者可能会写下以下代码去做判断:
1 | //如果名字相同,年龄相同,性别相同 |
由于age属性是私有的,又没有提供getAge()方法,获取不到age属性,可能会在Person这个类里提供一个getAge()方法或把age的private关键字去掉,这样虽然也能完成逻辑,但会导致后续使用该类的人再也不调printAge去打印age了,而是直接访问age属性或getAge()方法去打印,女士的age也就暴露了出去。很显然,这种做法打破了我们之前对age的封装,不建议这么做。
比较两个对象是否相等,不应该由客户端来决定,而是由对象本身来决定。这也是面向对象的技巧之一。
在Person里扩展如下方法:
1 | /** |
1 | public static void main(String[] args) { |
1 | true |
事实上这样比较还是有问题的,在Java中的==与equals中做了更进一步的说明。
参考资料:
清浅池塘 面向对象