面向对象的特点
封装
什么是封装?
封装表面意思就是封锁和包装;把信息进行隐藏起来;是指利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体,数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系。
好处:
调用封装的对象时,只需通过接口来进行操作,无需知道对象内部的细节,最重要的是可以起到保护数据的作用。
另外,封装可以隐藏内部实现细节,站在对象外部是看不到内部复杂结构的,对外只提供了简单的安全的操作入口。
如何封装?
在Java语言中可以使用private修饰符,private修饰的数据表示私有的,私有的数据只能在本类中访问。当外界需要进入访问的时候,这时需要对外提供公共的访问入口,让外部程序统一通过这个入口处设立关卡,进行安全控制.
对外公开的入口:一般情况下访问对象的某个属性,就两种情况,读取(get)和修改(set);所以,对外访问入口应该有两个,get方法和set方法.
- 对需要封装的对象进行封装,使用private修饰符进行私有化;
- 使用get方法和set方法作为外公开入口,访问对象;
继承
什么是继承?
继承是面向对象的三大特征之一,封装居首位,封装之后形成独立体,独立体之间存在继承关系.继承在现实生活中处处可见的,如:父与子的关系.
继承时子类会继承父类的特征和行为,使子类对象具有父类的属性,子类继承父类的方法使子类拥有父类相同的方法.
为什么要使用继承机制?
在不同类中可能会有共同的特征和动作,把这些动作和行为写成一个类中,从而可以构成一个通用类,再扩展多个特定类中,这些特定类继承通用类的方法.
继承使Java中实现重用的重要手段,避免重复,易于维护.
public class animal {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
void move(){
System.out.println(name+"移动");
}
}
public class fish extends animal {
private String color;//颜色
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public static void main(String[] args) {
//鱼与动物类的继承关系
fish fish = new fish();
fish.setName("鲤鱼");
fish.setColor("红色");
fish.move();
}
}
继承的作用中除了可以让代码复用外,还有非常重要的两个作用,这是在继承的基础之上衍生出的,方法的覆盖和多态机制.
继承的相关性(特性):
- B类继承A类,则称A类为超类(superclass),父类,基类;B类则称为子类(subclass),派生类,扩展类.
- Java只支持单继承,Java不允许多继承;c++支持多继承.
- Java不支持多继承,但可以产生间接继承的后果.
- Java规定,子类继承父类,除构造方法和被private修饰的数据不能继承外,剩下都可以继承.
- Java中的类没有显示的任何继承类,则默认继承Object类,Object类是Java语言提供的根类,也就是说,一个对象与生俱来的就有Object类型中的所有的特征.
继承也是有些缺点,例如,父类与子类的切合度非常高,父类的改变会影响子类.
覆盖
什么是方法覆盖?
在介绍覆盖之前先回顾一下重载;重载是在同一个类当中,如果功能相似,尽可能将方法名定义相同,这样代码会美观的.
重载的条件:只要在同一个类当中,方法名相同,参数列表不同(类型,个数,顺序),即构成方法重载.
覆盖:子类重写父类的方法.
如何实现方法覆盖呢?
只有当从父类继承过来的方法无法满足当前子类业务需求的时候,需要把父类中继承过来的方法进行覆盖.或是,父类中继承的方法已经不够用了,子类有必要将方法重写.所以,方法覆盖又称方法重写.
方法覆盖的条件和注意实现?
- 具备的条件:
- 方法覆盖发生在具有继承关系的父子类之间;
- 覆盖之后与原方法具有相同的返回值类型,相同的方法名,相同的形式和参数 列表;
- 注意事项:
- 由于覆盖之后的方法与原方法一样,建议是复制粘贴,不建议手写;
- 私有方法不能被继承,所以不能被覆盖;
- 构造方法不能被继承,所以不能覆盖;
- 覆盖之后的方法不能比原方法拥有更低的访问权限,可以更高;
- 覆盖之后不能比原方法抛出更多的异常,只能一样或更少.
- 静态方法(被 static 修饰的成员方法)不存在覆盖.
多态
什么是多态?
多态(Polymorphism)属于面向对象三大特征之一,它的前提是封装形成独立体,独立体之间存在继承关系,从而产生多态机制.
多态是同一个行为具有多个不同表现形式或形态的能力.多态就是"同一个行为"发生在"不同对象上"会产生不同的效果.
在Java中有两种语法,一种是向上转型,一种是向下转型.
- 向上转型---->子类类型转换为父类类型,简称:自动类型转换
- 向下转型---->父类类型转换为子类类型,简称:强制类型转换.
不管是向下转型还是向上转型,他们之间必须要有继承关系,没有继承关系,转型的时候编译器报错.
public class Test02 {
public static void main(String[] args) {
Animal a1 = new Cat()
a1.move();
Animal a2 = new Bird();
a2.move();
}
}
Animal a1 = new Cat()就是一个向上转型的,自动转型的,本质上Cat就是一个Animal.
public class Test04 {
public static void main(String[] args) {
//向上转型
Animal a = new Cat();
//向下转型:为了调用子类对象特有的方法
Cat c = (Cat)a;
c.catchMouse();
}
}
Animal a = new Cat()与 Cat c = (Cat)a;这就属于向下转型,强制类转型.
以上可知:
只有在访问子类中特有的数据时候,需要先进行向下转型.
向下转型存在一定风险,如下:
public class Test05 {
public static void main(String[] args) {
Animal a = new Bird();
Cat c = (Cat)a;
}
}
本来new的对象是一只小鸟,向上转型表达的是它是动物,但向下转型也应该是一只小鸟,但变成了一只猫了.而这个语法上没有错误,编译器通过了,但会报异常,ClassCastException,翻译为类型转换异常.
这个原因是小鸟与猫之间没有继承关系的,转型会出现错误.
instanceof运算符
instanceof运算符的运算结果是布尔类型,可能是true,也可能是false,本质上是一种假设的判断.可以使用instanceof进行判断.
这样写就不会发生异常了:
public class Test05 {
public static void main(String[] args) {
Animal a = new Bird();
if(a instanceof Cat){
Cat c = (Cat)a;
c.catchMouse();
}
}
}
多态存在的三个必要条件分别是:
- 继承
- 方法覆盖
- 父类引用指向子类对象
多态显然离不开覆盖机制,多态是因为在编译阶段绑定父类当中的方法,程序运行阶段自动调用子类对象上的方法.如果子类对象上的方法没有进行重写,创建多态就没有意义了.只有方法重写之后,运行时调用子类对象上的方法产生不同的效果,多态形成了.
方法覆盖与多态机制是捆绑的形式.
多态在开发中的作用
在软件开发过程中,有这样的一个开发原则:开闭原则。开闭原则(OCP)是面向对象设计中“可复用设计”的基石,是面向对象设计中最重要的原则之一。它的原文是这样: “Software entities should be open for extension,but closed for modification”。
开闭原则中“开”,是指对于组件功能的扩展是开放的,是允许对其进行功能扩展的;开闭原则中“闭”,是指对于原有代码的修改是封闭的,即修改原有的代码对外部的使用是透明的。
如下例子:
//宠物狗
public class Dog {
String name;
public Dog(String name){
this.name = name;
}
//吃的行为
public void eat(){
System.out.println(this.name + "在啃肉骨头!");
}
}
//主人
public class Master {
//喂养行为
public void feed(Dog dog){
//主人喂养宠物,宠物就吃
System.out.println("主人开始喂食儿");
dog.eat();
System.out.println("主人喂食儿完毕");
}
}
public class Test {
public static void main(String[] args) {
//创建狗对象
Dog dog = new Dog("二哈");
//创建主人对象
Master master = new Master();
//喂养
master.feed(dog);
}
}
如果在此基础之上,要增加一个Cat类,来表示宠物猫呢?
//宠物猫
public class Cat {
String name;
public Cat(String name){
this.name = name;
}
//吃的行为
public void eat(){
System.out.println(this.name + "在吃鱼!");
}
}
public class Master {
//喂养行为
public void feed(Dog dog){
//主人喂养宠物,宠物就吃
System.out.println("主人开始喂食儿");
dog.eat();
System.out.println("主人喂食儿完毕");
}
//喂养行为
public void feed(Cat cat){
//主人喂养宠物,宠物就吃
System.out.println("主人开始喂食儿");
cat.eat();
System.out.println("主人喂食儿完毕");
}
}
而这样做的话,会违背了OCP原则;而多态就可以解决:
//宠物类
public class Pet {
String name;
//吃的行为
public void eat(){
}
}
public class Cat extends Pet{
public Cat(String name){
this.name = name;
}
//吃的行为
public void eat(){
System.out.println(this.name + "在吃鱼!");
}
}
//宠物狗
public class Dog extends Pet{
public Dog(String name){
this.name = name;
}
//吃的行为
public void eat(){
System.out.println(this.name + "在啃肉骨头!");
}
}
//主人
public class Master {
//喂养行为
public void feed(Pet pet){
//主人喂养宠物,宠物就吃
System.out.println("主人开始喂食儿");
pet.eat();
System.out.println("主人喂食儿完毕");
}
}
public class Test {
public static void main(String[] args) {
//创建狗对象
Dog dog = new Dog("二哈");
//创建主人对象
Master master = new Master();
//喂养
master.feed(dog);
//创建猫对象
Cat cat = new Cat("汤姆");
//喂养
master.feed(cat);
}
}
显然Master类和具体的Dog,Cat类解耦合了,依赖性弱了,这就是通常说的面向对象编程,尽量不要面向具体编程,面向抽象编程会让你的代码耦合度降低,扩展能力增强,从而符合 OCP 的开发原则。
总结:
通过以上内容的学习,我们可以看到多态在开发中联合方法覆盖一起使用,可以降低程序 的耦合度,提高程序的扩展力。在开发中尽可能面向抽象编程,不要面向具体编程。
所谓多态就是同一个行为作用到不同的对象上,最终的表现结果是不同的,主要的要求就是对象是可以进行灵活切换的,灵活切换的前提就是解耦合,解耦合依赖多态机制。