概念
面向对象的程序由对象组成,每个对象包含对用户公开的特定功能部分和隐藏的实现部分。
一般来说,算法加上数据结构组成程序,在以往的面向过程的程序设计中,算法是第一位的,数据结构是第二位的,整体实现注重过程实现。而面向对象程序设计在思维上完全不同,他调换了这个次序,将数据放在第一位,然后再考虑操作数据的算法。
注意
1. 数据和其行为的打包封装。
2. 程序的接口和实现的解耦。
这样做的好处就是提高软件的重用性、灵活性和扩展性。
举例
比如:从饮水机接水喝
以往的面向过程思想是:
1. 我拿着我的杯子放到饮水机出水口。
2. 按下饮水机开关。
3. 开关带动饮水机阀门,如果桶里有水就会流到杯子里。
4. 杯子接满水,关闭开关。
如果换成其他人,那么又得重新设计一套这样的行为。
面向对象的思想是:
我和饮水机都是对象。
1. 我这个对象将杯子递给饮水机,并让饮水机帮我接水
2. 水接满后饮水机将杯子还给我
中间的开关,桶里有没有水的判断,将水接杯子里就不是我关心的问题了,这些行为全部交给饮水机这个对象实现,如果换成其他人,同样可以将他们的杯子递给饮水机来实现这一动作,这里饮水机的重用性就体现出来了。而如果饮水机升级了,有了加热净水功能,也很方便,只需要添加饮水机对象所具有的行为即可,也就是提高了灵活性。以后出现了带有空气净化功能甚至可扩展插件的高级饮水机,可以从饮水机继承创建新的对象,也可以向上抽取定义接口等,也就是提高了扩展性。
这里我们将视角从动作的执行者转换为领导者,将软件设计的重心从算法转移到数据上来,将复杂的问题简单化,就是面向对象的思维。
面向对象的特征
封装
继承
多态
封装使得面向对象程序具有更佳的复用性
继承使得面向对象程序具有更佳的扩展性
多态使得面向对象程序具有更佳的扩展性和灵活性
类和对象
概述
类是构造对象的模板或蓝图,由类构造对象的过程称为创建类的实例。类中描述了事物的属性和行为,其属性对应于类的实例域(或称之为成员变量),行为对应于类的方法(或称之为成员函数)。
Java中,所有的类都源自于“上帝”Object类。
使用面向对象思想设计程序时,先查找具有所需功能的对象来用,如果没有就自己创建一个具有所需功能的对象,提高软件复用性。
当使用构造器创建对象时,对象变量和对象是不同的,例如:
Date birthday = new Date();
这里birthday是个对象变量,指向一个Date对象的引用,这时可以通过对象变量birthday调用该对象的方法。但是如果对象变量没有指向任何对象的引用,比如: Date birthday = null;则必须先初始化这个变量才可以调用方法: birthday = new Date();。
实例域和局部变量的区别
实例域:
1. 实例域定义在类中,在整个类中都可以被访问。
2. 实例域随着对象的建立而建立,存在于对象所在的堆内存中。
3. 实例域有默认初始化值。
局部变量:
1. 局部变量只定义在局部范围内,如:函数内,语句内等。
2. 局部变量存在于栈内存中。
3. 作用的范围结束,变量空间会自动释放。
4. 局部变量没有默认初始化值。
匿名对象
匿名对象是一般对象的简化形式,一般用于:
1. 当对对象方法仅进行一次调用时使用。
2. 匿名对象可以作为实际参数进行传递。
封装
封装就是隐藏对象的属性和实现细节,仅对外提供公共访问方式。
实现封装的关键在于:
1. 绝对不能让类中的方法直接访问其他类的实例域(使用 private 关键字修饰实例域)。
2. 程序仅能通过对对象的方法与对象数据进行交互。
这是提高复用性和可靠性的关键。
其好处是显而易见的:
1. 将变化隔离。
2. 便于使用。
3. 提高重用性。
4. 提高安全性。
示例
public class Person {
// 实例域用private修饰,隐藏属性
private String name;
private int age;
// 通过一系列set和get方法,实现和实例域数据的交互
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
内存模型
堆区
1. 存储的全部是对象,每个对象都包含一个与之对应的class的信息(class的目的是得到操作指令)。
2. JVM只有一个堆区(heap),且被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身。
栈区
1. 每个线程包含一个栈区,栈中只保存基础数据类型的变量和自定义对象的引用(不是对象本身),对象都存放在堆区中。
2. 每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。
3. 栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
方法区
1.又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
2.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量,常量池等。
示例
public class Memory {
public static void main(String[] args) {
Demo d = new Demo();
d.run();
}
}
class Demo {
void run() {
System.out.println("Hello Java");
}
}
- 首先启动JVM,读取 Memory.class 文件,将Memory类的类信息(包括实例域数据类型,静态变量,方法等)存放到运行时数据区的方法区中,称之为类的加载。
执行main()方法: Demo d = new Demo(); - 在方法区中查找 Demo 类信息,没找到,读取 Demo.class ,将Demo类的类信息存放到运行时数据区的方法区中。
- 在堆区为一个新的 Demo 实例分配内存,这个 Demo 实例持有指向方法区的 Demo 类类信息的引用。
- 创建变量d,将其添加入main线程的栈区,并将其指向上一步堆区中创建的 Demo 实例地址。也就是说,变量d持有指向Demo实例的引用
d.run(); - 执行run();方法时,JVM根据局部变量d持有的引用,找到堆区的Demo实例,再根据Demo实例持有的引用,找到方法区中Demo类的类信息,从而获得run()方法的字节码,接着执行该方法包含的指令。