对面向对象知识点做一总结,并分享给大家,学习过程其实很轻松,主要的是注重思想方面的学习。
目录
1、面向对象和面向过程思想的区别
面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了;面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。
可以拿生活中的实例来理解面向过程与面向对象,例如五子棋,面向过程的设计思路就是首先分析问题的步骤 :1、开始游戏,2、黑子先走,3、绘制画面,4、判断输赢,5、轮到白子,6、绘制画面,7、判断输赢,8、返回步骤2,9、输出最后结果。 把上面每个步骤用不同的方法来实现。
如果是面向对象的设计思想来解决问题。面向对象的设计则是从另外的思路来解决问题。整个五子棋可以分为 1、黑白双方,这两方的行为是一模一样的,2、棋盘系统,负责绘制画面,3、规则系统,负责判定诸如犯规、输赢等。第一类对象(玩家对象)负责接受用户输入,并告知第二类对象(棋盘对象)棋子布局的变化,棋盘对象接收到了棋子的变化就要负责在屏幕上面显示出这种变化,同时利用第三类对象(规则系统)来对棋局进行判定。
可以明显地看出,面向对象是以功能来划分问题,而不是步骤。同样是绘制棋局,这样的行为在面向过程的设计中分散在了多个步骤中,很可能出现不同的绘制版本,因为通常设计人员会考虑到实际情况进行各种各样的简化。而面向对象的设计中,绘图只可能在棋盘对象中出现,从而保证了绘图的统一。
这个案例是在网上借鉴的,这个案例形象的表达了他们之间的区别,面向对象可以总结成两句话,面向对象就是高度实物的抽象化(可以对象现实化这样会好理解一点)、面向过程就是自顶向下的编程。
2、对象的创建分析
- 通过 new 关键字来创建对象
- 使用 new 关键字创建的时候,除了分配空间之外,还会给 创建好的对象 进行默认的初始化 以及对类中构造器的调用
创建对象的步骤
- 创建项目一个普通项目(这里就不再过多说了)
- 在项目的 src 目录中创建一个 Person 类
- 给 Person 类创建一个 print() 方法,让它输出Hello World,意思是:我们在创建对象的时候可以通过创建的对象来调用此方法
- 创建一个 TestPerson 类,用来测试创建出的对象
- 通过 new 关键字创建 Person 这个对象
- 创建出 Person 对象后,运用 (.) 去操作对象里面的 print() 方法
Person 类
public class Person {
public void print() {
System.out.println("Hello world");
}
}
TestPerson类
public class TestPerson {
public static void main(String[] args) {
Person person = new Person();
person.print();
}
}
此时的 main() 方法打印的是Hello world,我们就成功创建出了一个对象。
对我们创建的对象进行分析
3、构造器
java中的构造方法是一种特殊的方法,用于初始化对象。java构造函数在对象创建时被调用。它构造值,即提供对象的数据。
创建java构造函数的规则
构造函数基本上定义了两个规则。分别如下:
- 1、必须和类的名字相同
- 2、必须没有返回类型,也不能写void
Java构造函数类型
有两种类型的构造函数:
- 默认构造函数(无参数构造函数)
- 有参构造函数
1、默认构造函数
没有参数的构造函数称为默认构造函数。默认构造函数的语法如下:
< class_name>(){}
默认构造函数的示例:
在这个例子中,在Bike类中创建了无参数(no-arg)构造函数。它将在对象创建时被调用。
class Bike{
Bike(){
System.out.println("Bike is created");
}
public static void main(String args[]) {
Bike b = new Bike();
}
默认构造函数的目的是根据类型为对象提供默认值,如:基本数据类型为 0,引用数据类型为 null。
显示默认值的默认构造函数示例
public class Bike {
int a;
String name;
void print(){
System.out.println(a + "\n" + name);
}
public static void main(String[] args) {
Bike bike = new Bike();
bike.print();
}
}
在上面代码中我们并没有为其提供构造函数,但是我们在输出是会发现其是有值的,这就是在上面的类中,代码中并没有创建任何构造函数,但编译器自动提供了一个默认构造函数。默认构造函数分别为字段:a 和 name 分别提供了 0 和 null 值。
2、有参构造函数
参数化构造函数用于为不同对象提供不同初始化的值。
示例:
在构造函数中我们可以添加任意多少的参数。
class Student4 {
int id;
String name;
Student4(int i, String n) {
id = i;
name = n;
}
void display() {
System.out.println(id + " " + name);
}
public static void main(String args[]) {
Student4 s1 = new Student4(111, "Karan");
Student4 s2 = new Student4(222, "Aryan");
s1.display();
s2.display();
}
}
Java构造函数重载
构造方法重载是Java中的一种技术,一个类可以有任何数量的,参数列表不同的构造函数。编译器通过构造函数参数列表中的参数数量及其类型来区分这些构造函数。
示例:
class Student5 {
int id;
String name;
int age;
Student5(int i, String n) {
id = i;
name = n;
}
Student5(int i, String n, int a) { //通过参数的不同告知编译器此处用到了构造函数的重载
id = i;
name = n;
age = a;
}
void display() {
System.out.println(id + " " + name + " " + age);
}
public static void main(String args[]) {
Student5 s1 = new Student5(111, "Karan");
Student5 s2 = new Student5(222, "Aryan", 25);
s1.display();
s2.display();
}
}
Java构造器和方法之间的区别
4、面向对象的三大特征和五大基本原则
面向的三大特征
1. 封装
封装就是隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别,将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作数据的源代码进行有机的结合,形成“类”,其中数据和函数都是类的成员。
封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,以特定的访问权限来使用类的成员。
面相对象的编程思想不就是使用程序处理事情时以对象为中心去分析吗,与面向过程不同,面向过程关心处理的逻辑、流程等问题,而不关心事件主体。而面向对象即面向主体,所以我们在解决问题时应该先进行对象的封装(对象是封装类的实例,比如张三是人,人是一个封装类,张三只是对象中的一个实例、一个对象)。比如我们日常生活中的小兔子、小绵羊都可以封装为一个类。
比如兔子的属性有两只耳朵、四只腿、一双眼睛、三瓣嘴等;行为(功能)有跑、跳、吃素等。
2. 继承
继承是面向对象的基本特征之一,继承机制允许创建分等级层次的类。继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。类似下面这个图:
我们在上面已经封装了兔子这个类,其他动物也一样可以进行封装。在封装过程中我们发现兔子、绵羊这两个类具有相似的功能或特性如吃草,所以我们可以抽取共有特征和方法形成高一层的类,如这里的食草动物、食肉动物。继承之间是子父类的关系。继承机制可以很好的描述一个类的生态,也提高了代码复用率,在Java中的Object类是所有类的超类,常称作上帝类。
3. 多态
多态同一个行为具有多个不同表现形式或形态的能力。是指一个类实例(对象)的相同方法在不同情形有不同表现形式。多态机制使具有不同内部结构的对象可以共享相同的外部接口。这意味着,虽然针对不同对象的具体操作不同,但通过一个公共的类,它们(那些操作)可以通过相同的方式予以调用。
多态的优点:
- 消除类型之间的耦合关系
- 可替换性
- 可扩充性
- 接口性
- 灵活性
- 简化性
多态存在的三个必要条件:
- 继承
- 重写(子类继承父类后对父类方法进行重新定义)
- 父类引用指向子类对象
简言之,多态其实是在继承的基础上的。比如说今天我们要去动物园参观动物,那么你说我们去参观兔子、参观绵羊、参观狮子、参观豹子都是对的,但你不能说我们去参观汽车。在这个例子中,子类具有多态性:除了使用自己的身份,还能充当父类。
五大基本原则
1、单一职责原则(SRP)
一个类应该有且仅有一个去改变它的理由,这意味着一个类应该只有一项工作。
比如在职员类里,将工程师、销售人员、销售经理这些情况都放在职员类里考虑,其结果将会非常混乱,在这个假设下,职员类里的每个方法都要if else判断是哪种情况,从类结构上来说将会十分臃肿。
2、开放封闭原则(OCP)
对象或实体应该对扩展开放,对修改封闭。
更改封闭即是在我们对模块进行扩展时,勿需对源有程序代码和DLL进行修改或重新编译文件!这个原则对我们在设计类的时候很有帮助,坚持这个原则就必须尽量考虑接口封装,抽象机制和多态技术!
3、里氏替换原则(LSP)
在对象 x 为类型 T 时 q(x) 成立,那么当 S 是 T 的子类时,对象 y 为类型 S 时 q(y) 也应成立。(即对父类的调用同样适用于子类)
4、依赖倒置原则(DIP)
高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。具体实现应该依赖于抽象,而不是抽象依赖于实现。
可以这样理解,上面我举例子的时候先说了兔子和绵羊,然后才推出食草动物。但如果我们继续认识了牛、马等食草动物,我们会发现我们需要不断调整食草动物的描述,这样程序会变得僵化,所以我们不应该让子类依赖于实体,不应该让父类模块依赖于子类模块。所以我们需要将食草动物设计为抽象类,即抽象类或接口。这样下层只需要实现相应的细节而不会影响父类。
5、接口隔离原则(ISP)
不应强迫客户端实现一个它用不上的接口,或是说客户端不应该被迫依赖它们不使用的方法,使用多个专门的接口比使用单个接口要好的多!
比如,为了减少接口的定义,将许多类似的方法都放在一个接口中,最后会发现,维护和实现接口的时候花了太多精力,而接口所定义的操作相当于对客户端的一种承诺,这种承诺当然是越少越好,越精练越好,过多的承诺带来的就是你的大量精力和时间去维护!