面向对象的概念
面向对象编程(Object-Oriented Programming OOP
)是一种编程思维方式和编码架构。
抽象机制
所有编程语言都提供抽象机制。我们将问题解决方案的表示称作“对象”(Object)。
一种对对象
简洁地描述:一个对象具有自己的状态(属性),行为(方法)和标识(唯一内存地址)。
接口
这里的接口表述的应该是,我们通常意义上的方法。
Each object accepts only certain requests, defined by its interface The type determines the interface.
每个对象只接收特定的请求,这些请求通过接口定义。
在面向对象程序设计中,我们要做的是新建各种数据“类型”(Type),但几乎所有面向对象语言都采用了 class
关键字。所以class
=type
。
服务提供
在开发或理解程序设计时,可以将对象看成是“服务提供者”。
在良好的面向对象设计中,每个对象功能单一且高效(高内聚)。
封装
某些被封装的类,只需要公开必要的内容,并隐藏内部实现的细节。这样可以避免该类被错误地使用、更改,并且可以在不影响外部使用的情况下,完善更新。
Java中有三个显示关键字和一个隐式关键字,来设置类的访问权限。
public
任何人都可以访问private
只能在本类内部访问protected
类似private
,但子类可以访问。default
不指定访问权限时的默认权限,同包下可以访问。
复用/继承
一般有组合和继承,两种方式。
创建类时,首先要考虑“组合”,因为它更简单灵活,而且设计更加清晰。
多态
把子类当成其基类来处理,叫做“向上转型”(upcasting
)
List list1 = new ArrayList<>(); 此时变量list就指向ArrayList的实例。
List list2 = new LinkedList<>() 这时变量就指向LinkedList的实例。
list1.add();
list2.add();
在调用add()
方法时,如何保证调用的是正确的方法?
非 OOP
语言的函数调用,会引起早期绑定。编译器生成对特定函数名的调用,该调用会被解析为将执行的代码的绝对地址。
oop
语言使用后期绑定的概念。Java 使用一个特殊的代码位来代替绝对调用。这段代码,使用对象中存储的信息,来计算方法主体的地址。
单继承结构
Java 中,顶级父类是Object
。所有的类最终都属于同一基类。
集合
集合(Collection
)是Java的基础包。
“集合”这种类型的对象可以存储任意类型、数量的其他对象。根据需要自动扩容,我们不用关心过程是如何实现的。
Java 5 之前,集合元素的类型都是Object
;之后引入泛型,减少集合元素“向下转型”的开销和可能出现的错误。
强制类型转换,将其转为更具体的类型,称为“向下转型”。
对象创建与生命周期
对象创建方式:
-
栈(Stack,有时称为自动变量或作用域变量)或静态存储区域(static storage area)
由程序员自己控制生命周期
-
在堆内存(Heap)中动态地创建对象
在栈内存开辟和释放空间通常是一条将栈指针向下移动和一条将栈指针向上移动的汇编指令。
开辟堆内存空间的时间取决于内存机制的设计。
Java 使用动态内存分配。
Java 内存管理建立在垃圾收集器上,它自动发现对象不再被使用,并释放内存。
异常处理
“异常”(Exception)是一个从出错点“抛出”(thrown)后,能被特定类型的异常处理程序捕获(catch)的一个对象。
这里也诠释了''万物皆对象'',连"异常"都是个对象。
万物皆对象
Objects Everywhere
翻译成 万物皆对象,还挺哲学的。
对象操控
Java 中操作对象,是通过对象的"引用"。但是有一个“引用”并不意味着你必然有一个与之关联的“对象”。
例如电视和遥控器。我们通过遥控器来操作电视, 遥控器可以看作电视的引用。但是也有可能,只有遥控器,没有电视。
对象创建
new
关键字代表:创建一个新的对象实例。
数据存储
程序在运行时,有5个不同的地方可以存储数据:
-
寄存器(
Registers
)最快的存储区域,没有直接的控制权。
-
栈内存(
Stack
)常规内存 RAM(随机访问存储器,Random Access Memory)区域中,可通过栈指针获得处理器的直接支持。
虽然在栈内存上存在一些 Java 数据(如对象引用),但 Java 对象本身的数据却是保存在堆内存的。
-
堆内存(
Heap
)通用的内存池(也在 RAM 区域),所有 Java 对象都存在于其中。
创建一个对象时,只需用
new
命令实例化对象即可,当执行代码时,会自动在堆中进行内存分配。 -
常量存储(
Constant storage
)常量值通常直接放在程序代码中。
-
非 RAM 存储(Non-RAM storage)
数据完全存在于程序之外,在程序未运行以及脱离程序控制后依然存在。比如数据库中之类的。
基本类型的存储
基本数据类型,保存在栈中。因为基本数据类型小而简单,创建到栈中更高效。
基本类型 | 大小 | 最小值 | 最大值 | 包装类型 |
---|---|---|---|---|
boolean | — | — | — | Boolean |
char | 16 bits | Unicode 0 | Unicode 2^16 -1 | Character |
byte | 8 bits | -128 | +127 | Byte |
short | 16 bits | - 2^15 | + 2^15 -1 | Short |
int | 32 bits | - 2^31 | + 2^31 -1 | Integer |
long | 64 bits | - 2^63 | + 2^63 -1 | Long |
float | 32 bits | IEEE754 | IEEE754 | Float |
double | 64 bits | IEEE754 | IEEE754 | Double |
void | — | — | — | Void |
高精度数值
BigInteger
支持任意精度的整数。可用于精确表示任意大小的整数值,同时在运算过程中不会丢失精度。
BigDecimal
支持任意精度的定点数字。例如,可用它进行精确的货币计算。
数组的存储
在 Java 中,数组使用前需要被初始化,并且不能访问数组长度以外的数据。
如果试图访问数组长度以外的数据,会抛出"索引越界异常"。
对象清理
Java 的垃圾收集器,会检查所有 new
出来的对象,并判断哪些不再可达,继而释放那些被占用的内存,供其他新的对象使用。
类的创建
字段/方法
类中可以包含,两种类型的元素:方法(method)和字段(field)。
基本类型默认值
只有类的成员变量是基本类型,在类初始化的时候,才会被赋予初始值。局部变量并不会赋予默认值。
基本类型 | 初始值 |
---|---|
boolean | false |
char | \u0000 (null) |
byte | (byte) 0 |
short | (short) 0 |
int | 0 |
long | 0L |
float | 0.0f |
double | 0.0d |
方法
方法的基本组成部分包括名称、参数、返回类型、方法体。格式如:
[返回类型] [方法名](/*参数列表*/){
// 方法体
}
参数列表
参数列表必须指定每个对象的类型和名称。
方法名和参数列表统称为方法签名(signature of the method)。签名作为方法的唯一标识。
返回类型
方法可以返回任何类型的数据。void
来表明这是一个无返回值的方法。
当返回类型为 void 时, return 关键字仅用于退出方法,因此在方法结束处的 return 可被省略。我们可以随时从方法中返回,但若方法返回类型为非 void
,则编译器会强制我们返回相应类型的值。
程序编写
命名
Java 创建者希望我们反向使用自己的网络域名,来命令我们的项目包,例如com.baidu.[otherName]
使用其他组件(包)
通过使用 import 关键字来导入需要使用的类库。例如:
import java.util.ArrayList;
//或
import java.util.*;
static 关键字
使用 static 关键字表示,该字段/方法是静态的。
静态数据和方法只是属于类,而不是类的某个特定对象而存在的。即使创建多个对象,静态变量只占一份内存空间。
推荐使用类名直接引用静态变量。