面向对象与面向过程
在面向对象程序(OOP:Object Oriented Programming)设计办法呈现之前,软件界宽泛风行的是面向过程(POP:Process Oriented Programming)的设计形式。
面向过程的设计中,解决的对象多以变量模式存在,解决的过程之间也不存在束缚关系,剖析出解决问题所需的步骤,而后用函数把这些步骤一步一步实现,应用时一次调用即可。简略来说,面向过程以实现性能的函数开发为主。
面向对象的设计中,将形成问题的事务形象成不同的对象,建设的对象不是为了实现一个步骤,而是为了形容某个事物在整个解决问题的步骤中的行为。简略来说,面向对象要首先形象出类、属性及其办法,而后通过实例化类、执行办法来实现性能。
面向对象的三大个性
-
封装:暗藏对象的属性和实现细节,对外只提供拜访的接口,进步复用性和安全性;
面向对象与面向过程都具备封装性,然而面向过程是封装的是性能,而面向对象封装的是数据和性能。
-
继承:定义父类之后,子类能够从根底类进行继承,进步了代码的复用率,重要的一点:类只能单继承;
继承是从已有类失去继承信息创立新类的过程。提供继承信息的类被称为父类(超类 / 基类),失去继承信息的被称为子类(派生类)。
-
多态:父类或者接口定义的援用变量能够指向子类或具体实现类的实例对象,进步了程序的扩展性。
分为编译时多态(办法重载)和运行时多态(办法重写)。要实现多态须要做两件事:一是子类继承父类并重写父类中的办法,二是用父类型援用子类型对象,这样同样的援用调用同样的办法就会依据子类对象的不同而体现出不同的行为。
封装
封装是面向对象编程的核心思想,封装指的是将对象的属性和行为封装起来,这个将对象的属性和行为封装起来的载体是类。在面向对象中,形象进去的实物就是类,属于同一类的对象实例领有雷同的属性(数据)及办法(行为),Java 中,所有对象 / 类的独特父类都是 Object。
具体来说,生物的划分有:界、门、纲、目、科、属、种等,这里的每一个级别在程序中都能够形象成类。按人类的类别来看,咱们属于动物界 - 脊索动物门 - 脊椎动物亚门 - 哺乳纲 - 真兽亚纲 - 灵长目 - 类人猿亚目 - 人科 - 人亚科 - 人族 - 人属 - 智人种 - 早期智人亚种。具体的分类太业余,将其简单化一点:动物 - 脊椎动物 - 哺乳动物 - 灵长动物 - 人类。
所以人类能够形象成人(Human)这个类,而咱们每一个人,就是这个类外面的一个实例。
如果说咱们人的类命名成 Human,代码能够示意如下:
public class Human{}
封装须要用到 Java 中的润饰变量(拜访控制符):private、protected、default 和 public。这些润饰变量代表着不同的拜访权限,其中:
- private:示意公有,只有本人类能拜访
- default:示意没有修饰符润饰,只有同一个包的类能拜访
- protected:示意能够被同一个包的类以及其余包中的子类拜访
- public:示意能够被该项目标所有包中的所有类拜访
修饰符 | 本类 | 同包 | 子类 | 所有类 |
---|---|---|---|---|
private | √ | × | × | × |
default | √ | √ | × | × |
protected | √ | √ | √ | × |
public | √ | √ | √ | √ |
在类中,属性须要被暗藏起来,只提供接口来对其进行操作。属性设置为暗藏的益处有:
- 设置为公有后,只能通过 getter 办法来获取属性以及 setter 办法来设置属性,能够通过只提供 getter 办法而不提供 setter 办法来将属性设置为只读;
- 因为只有 getter/setter 办法能获取到 / 设置实例对象的属性,所以能够在 getter/setter 办法里解决好这个属性的数据,减少一些检查和转化,能够防止一些属性谬误的问题;
- 对于一些非凡的业务,甚至能够设置属性为 protected 等不便子类获取到父类的属性及办法,而又能够禁止其余的类获取这些属性和办法。
public class Human{
// 名字
private String name;
// 构造方法
Human(){}
// 设置名字
public void setName(String name){this.name = name;}
// 获取名字
public String getName(){return this.name;}
}
继承
继承是一种层次模型,层次结构的下层具备通用性,然而上层构造则具备特殊性。在继承的过程中子类(绝对上层)则能够从父类(绝对下层)继承一些属性和办法。提供继承信息的类被称为父类(超类 / 基类),失去继承信息的被称为子类(派生类)。
类除了能够继承这些办法和变量以外,同时还可能对这些属性和办法进行批改或者增加。从已有类失去继承信息创立新类的过程可能无效进步工作效率。简略来说,子类的对象领有父类的全副属性和行为,同时能够削减本人的所特有的属性和行为。
按下面人类的例子,人继承了灵长动物,灵长动物还有其余的子类,例如:猿、猴、猩猩等。灵长类都领有对生的拇指,所以人类继承灵长类的时候不须要重申这一点,然而人和其余灵长类的进食形式不同,所以能够对 eat 办法进行重写(override)。
灵长类:
public class Primates{
// 有对生的拇指
private static boolean hasOpposingThumbs = true;
public void eat(){// 用手抓着吃}
}
人类:
public class Human extends Primates{
@Override
public void eat(){// 用刀叉筷子等工具吃}
}
多态
同一个行为具备多个不同表现形式或状态的能力就是多态。
Java 种的多态分为两种:运行时多态(办法重写)和编译时多态(办法重载)。
这两种多态有一个共同点,就是实质上都是对象在有某种行为时,存在两个或以上的办法来实现这个行为。比如说灵长类的进食行为 eat(),身为人类能够用手抓着吃,也能够用刀叉筷子等工具吃,也能够用搅拌机将食物打碎了喝着“吃”上来。
运行时多态(重写)
运行时多态的前提条件是继承或实现,当子类继承父类时或一个类实现一个接口时,须要对其中的办法进行重写。这种多态通过动静绑定(dynamic binding)技术来实现,是指在执行期间判断所援用对象的理论类型,依据其理论的类型调用其相应的办法。也就是说,只有程序运行起来,你才晓得调用的是哪个子类的办法。
运行时多态的条件是:
- 继承: 在多态中必须存在有继承关系的子类和父类。
- 重写: 子类对父类中某些办法进行从新定义,在调用这些办法时就会调用子类的办法。
- 向上转型: 在多态中须要将子类的援用赋给父类对象,只有这样该援用才可能具备技能调用父类的办法和子类的办法。继承也能够替换为实现接口。
用灵长类与人类的例子来看,就是人类继承灵长类时,能够对进食办法 eat() 进行重写,重写之后,人类这个子类的对象进食时就有了其余的实现形式;如果人类不重写 eat() 办法,则人类的实例对象在进食时还是只能采取手抓的形式。
情景 1:
人类重写了 eat() 办法,则在向上转型时,如:Primates p = new Human(),p.eat() 执行的是 Human 类中的办法,即用刀叉筷子等工具吃。
public class Primates{
// 有对生的拇指
private static boolean hasOpposableThumbs = true;
public void eat(){// 用手抓着吃}
}
----------
public class Human extends Primates{
@Override
public void eat(){// 用刀叉筷子等工具吃}
}
情景 2:
人类未重写了 eat() 办法,则在向上转型时,如:Primates p = new Human(),p.eat() 执行的是 Primates 类中的办法,即还是用手抓着吃,因为未重写父类的办法,所以情景 2 未体现出对多态的使用。
public class Primates{
// 有对生的拇指
private static boolean hasOpposingThumbs = true;
public void eat(){// 用手抓着吃}
}
----------
public class Human extends Primates{}
编译时多态(重载)
在同一个类中,对于同一个办法 / 行为有多种实现形式,就被成为重载,也叫编译时多态。对比重写,重写是在程序运行的时候才晓得理论调用哪个类外面的办法,而重载则是在代码编译的时候就晓得调用的时哪个具体的形式。因为在同一个类中,同名办法只能通过不同的参数模式来辨别,所以不须要到运行的时候才晓得理论调用的办法。
重载须要留神:
- 位于同一个类中
- 函数的名字必须雷同
- 形参列表不同
重载办法时,必须要使得两个同名办法的形参列表不同,而不能通过返回值来辨别两个同名办法。其起因是,办法在调用时能够仅调用而不接管办法返回值。因而,如果办法的形参列表一样且办法名雷同,则编译器无奈判断到底调用的是哪个办法。
public class Human {public static void main(String[] args) {Human human = new Human();
// 接管返回值
int isFull = human.eat();
// 不接管返回值
human.eat();}
public int eat(){return 1;}
}
用下面人类的例子来实现 eat 这一行为的多态:如果没餐具,就用手抓着吃;有餐具,也能够用餐具吃;如果还有搅拌机,也能够将食物打碎喝下去,代码能够这样体现:
public class Human extends Primates{public static void main(String[] args) {Human human = new Human();
// 用手抓着吃,重写父类办法
human.eat();
// 用刀叉等工具吃,重载
human.eat(tableware);
// 用搅拌机搅拌后,用餐具盛着喝,重载
human.eat(tableware, blender, water);
}
// 参数为空,没有任何工具,只能用手抓着吃
@Override
public void eat(){// 用手抓着吃}
// 参数为餐具,能够用餐具吃
public void eat(Tableware tableware){// 用刀叉筷子等工具吃}
// 参数为餐具和搅拌机,能够用餐具喝
public void eat(Tableware tableware, Blender blender, Water water){// 用搅拌机搅拌后,用餐具盛着喝}
}
总结
面向对象是一种“万物皆对象”的编程思维,在现实生活中的任何物体都形象演绎成不同的类,而每一个个体都是一类事物的实例,也称为对象;
- 面向过程以实现性能的函数开发为主,而面向对象要首先形象出类、属性及其办法,而后通过实例化类、执行办法来实现性能;
- 面向对象及面向过程都具备封装性,然而面向过程是封装的是性能,而面向对象封装的是数据和性能;
- 面向对象具备继承性和多态性,而面向过程没有继承性和多态性,所以面向对象劣势很显著,次要在于缩小代码冗余,代码有条理;
- 面向对象有三大个性:封装、继承、多态。对具体实物地封装能够使得继承更好实现,而继承又是多态的一个根底;
- 子类领有父类对象所有的属性和办法(包含公有属性和公有办法),然而父类中的公有属性和办法子类是无法访问,只是领有。在一个子类被创立的时候,首先会在内存中创立一个父类对象,而后在父类对象内部放上子类独有的属性,两者合起来造成一个子类的对象;
- 子类能够领有本人属性和办法,也能够用本人的形式实现父类的办法。