面向对象(一)


面向对象程序设计简称 OOP

面向对象的程序是由对象组成的,每个对象包含对用户公开的特定功能部分和隐藏的实现部分。程序中的很多对象来自标准库,还有一些是自定义的。究竟是自己构造对象,还是从外界购买对象完全取决于开发项目的预算时间。在 OOP 中,不必管新对象的具体实现,只要能够满足用户的需求即可。

—— 摘自《Core Java》

什么是对象?
对象:现实世界中真实存在的个体称为对象。Java 中一切皆对象!

  • 类(class)是构造对象的模板或蓝图。由类构造(construct)对象的过程称为创建类的实例(instance)
  • 对象中的数据称为实例域(instance field,有时称为成员变量),操纵数据的过程称为方法(method)
  • 每个特定的类实例(对象)都有一组特定的实例域值,这些值的集合就是这个对象的当前状态(state)

OOP 让用户自定义 Java 类的时候更加容易:可以通过扩展一个类来建立另外一个新的类(继承),在 Java 中,所有的类都源自于一个“神通广大的超类”,它就是 Object。

封装(encapsulation,有时称为数据隐藏):

封装是与对象有关的一个重要概念。

实现封装的关键在于绝对不能让类中的方法直接的访问其他类的实例域。程序仅通过对象的方法与对象数据进行交互。
封装给对象赋予了“黑盒”特征,这事提高重用性和可靠性的关键。

一个好的封装需要下面三个内容:

  • 一个私有的数据域;
  • 一个共有的域访问器方法;
  • 一个共有的域更改器方法;

这样做要比提供一个简单的共有数据域复杂些,但是却有更大的好处:

  • 可以改变内部实现,出了该类的方法之外,不会影响其他代码
    例如:

    private String firstName;
    private String lastName;

    那么可以写一个 getName 方法获得全名:

    public String getName() {
        return firstName + " " + lastName;
    }

    对于这点改变,程序的其他部分完全不可见。

  • 更改器方法可以执行错误检查,然而直接对实例域进行赋值将不会进行这些处理。

    当然这些错误检查及如何处理是你自己写的~人家怎么知道你想怎么检查错误以及如何处理呢。

警告:
不要编写返回 引用可变对象 的访问器方法。如果确实需要返回一个 可变对象 的引用,应该使用拷贝。


## 对象 ## 要想使用 OOP,一定要清楚对象的三个主要特征:
  • 对象的行为(behavior)——可以对对象施加哪些操作,或者可以对对象施加哪些方法?(方法)
  • 对象的状态(state)——当施加那些方法时,对象如何响应?(变量)
  • 对象标识(identity)——如何辨别具有相同行为与状态的不同对象?

对象状态的改变必须通过调用方法实现(如果不经过方法调用就可以改变对象状态,只能说明封装性遭到了破坏)
作为一个类的实例,每个对象的标识永远是不同的,状态常常也存在着差异。例如在一个订单系统中,任何两个订单都存在这不同之处,即使所订购的货物完全相同也是如此。


### 程序设计时,把什么创建成类?### 设计一个程序时,首先是从设计类开始的,然后再往每个类中添加方法。 但是,应当把什么当做类来创建呢? **识别类的简单规则就是在分析问题的过程中寻找名词,而类的方法对应着动词。** 例如,飞机,子弹...这些名词很可能成为类 Plane、Bullet 等。 接下来找动词:飞机发射子弹,飞机移动等。对于每一个动词如“发射”、“移动”等,都是该对象完成相应的动作。
## 类之间的关系 ## - **依赖(使用一个类)** - **聚合(包含一个类)** - **继承(就是这个类)**

依赖:

依赖(dependence)是一种最明显的、最常见的关系。如果一个类 A 的方法操纵另一个类 B 的对象,就是类 A 依赖于类 B。

应该尽可能的将互相依赖的类减至最少,如果类 A 不知道类 B 的存在,它就不会关心 B 的任何改变(这意味着 B 的改变不会导致 A 产生任何 bug)。

聚合:

聚合(aggregation)是一种具体且易于理解的关系。即:一个 A 对象(如订单)包含一些 B 对象(如商品)。

继承:

继承(inheritance)是一种用于表示特属于一般关系的。

  • 若类 A 继承于类 B,则称类 A 为类 B 的子类,类 B 为类 A 的父类。
  • 子类继承父类后,拥有父类所有的属性及方法,同时也拥有自己特有的属性及方法
  • 作用:提高代码的复用性,避免代码重复
  • Java 是单一继承的,即一个子类只能继承一个父类。

语法:

子类 extends 父类

## 对象与对象变量 ## 要想使用对象,就必须先构造对象,并指定其初始状态,然后对对象应用方法。 在 Java 中,使用**构造器(constructor)**构造新实例。

构造器:

构造器是描述对象的创建过程,每一个类都有构造器:

  • 构造器与类同名
  • 构造器没有返回值,也不能有 void
  • 通常构造器是来给类的属性赋初始值的
  • 构造器总是伴随着 new 操作符一起调用
  • 每个类可以有一个以上的构造器(即可以重载
  • 构造器可以有 0 个、1 个或多个参数

如果编写一个类时没有编写构造器,那么系统会提供一个无参数构造器。这个构造器将所有的实例域设置为默认值。仅当类没有提供任何构造器的时候,系统才会提供一个默认的构造器

如何创建对象实例(语法):

new 对象名()//创建名为“对象名”的实例对象,但是这个对象没有引用,后期若想操作这个对象,则应声明对象:
对象名 变量名 = new 对象名();//创建名为"对象名"的实例对象,同时将该对象赋值给引用类型变量“变量名”

构造器与其他的方法有一个重要的不同:构造器总是伴随着 new 操作符的执行被调用,而不能对一个已经存在的对象调用构造器来达到重新设置实例域的目的:

Bee b = new Bee(1,2);
b.Bee(3,5);//这是错误的写法

一个对象变量并没有实际包含一个对象,而仅仅引用一个对象:

Bee bee = b;

此时变量 b 和变量 bee 引用的是同一个对象


### 无参构造器 很多类都包含一个无参构造器函数,**对象由无参构造器创建对象时,其状态会设置为默认值。**

null 与 NullPointException:

null:可以显示地将对象变量设置为 null,表明这个对象变量目前没有引用任何对象

Bee b;//声明一个 Bee 对象 b 但是没有进行初始化则默认为 null
b = null;//显示将对象赋值为 null

NullPointException:运行错误,空指针异常,若调用值为 null 的对象的方法或者属性,则会报此异常。


### 构造器与继承 ### - **构造器不能继承** - **Java 规定构造子类之前必须先构造父类,即 super(...):**
super(参数与父类对应)

## 自定义类 **类的定义格式为:**
class 类名 {
    实例域 1//实例域也叫成员变量
    实例域 2
    ...
    {
        初始化块//可以是静态的
    }
    //构造器 1
    public 类名(参数 1 类型 参数 1, 参数 2 类型 参数 2, ...) {//参数数量大于等于 0 个
        ...
    }
    //构造器 2
    public 类名(参数 1 类型 参数 1) {//多个构造器遵循重载规则。
        ...
    }
    ...//各种构造器,只要遵循重载规则,随便你搞几个...
    方法 1() {
        ...
    }
    方法 2(...) {
        ...
    }
    ...//各种方法
}

### 实例域: - 实例域即为对象中的数据,也叫成员变量或类的属性。 - 实例域是定义在类里面,方法及构造器之外的变量。
### 局部变量: - 定义在构造器或方法内的变量(包括参数)为局部变量,局部变量在出了该语句块({}为块)后则不能再访问该局部变量

注意:

不要在构造器中定义与实例域重名的局部变量。例如下面的构造器将无法设置 x 和 y 的值:

public class Bee() {//类
    int x;//实例域(成员变量)
    int y;//实例域(成员变量)
    public Bee(int a, int b) {//构造器
        int x = a;//不会报错,但是这个 x 是局部变量,不是上面的成员变量 x
        int y = b;//不会报错,但是这个 y 是局部变量,不是上面的成员变量 y
    }
}

Java 中有一个就近原则:相同变量名的不同变量,哪个的声明近,这个变量就是谁
再解释仔细一点就是:构造器或方法中的变量名(局部变量)若与成员变量名相同,则该构造器或方法中的这个变量不是成员变量。
但是可以通过 this 关键字来调用当前类的成员变量。


## 隐式参数与显示参数 方法用于操作对象以及存取它们的实例域。例如 Bee 类的方法 setLocation():
public void setLocation(int x, int y) {//设置坐标
    this.x = x;
    this.y = y;
}

一个 Bee 类型的对象 b 调用该方法,其坐标会被设置为新的值:
b.setLocation(1,2);
它的结果是将 b 的 x 和 y 值设置为了 1 和 2.
而这个 setLocation 方法实际上是有三个参数:一个隐式参数和两个显式参数

  1. b:隐式参数(implicit)是出现在方法名之前的 Bee 类对象。
  2. x 和 y:显式参数(explicit)是位于方法名后面的括号内的各种类型的值。

除此之外,在每一个方法中,关键字 this 也表示隐式参数:

  • this 指的是当前类,在哪个类中使用 this,则 this 就是哪个类。
  • 访问成员变量或方法及构造器的时候默认使用 this

文章作者: CrazyBunQnQ
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC 4.0 许可协议。转载请注明来源 CrazyBunQnQ !
 上一篇
方法及修饰符 方法及修饰符
Java 中定义一个方法的格式大概如下: 访问权限修饰符 修饰符 返回类型 方法名(参数类型 参数名, ....) {//参数个数 >= 0 方法体 }
2017-03-01
下一篇 
流程控制 流程控制
块作用域学习流程控制之前,先来了解以下块作用域:块(即符合语句)是指由一对花括号括起来的若干条简单的 Java 语句。块确定了变量的作用域。一个块可以嵌套在另一个块中。但是不能再嵌套的两个块中声明同名的变量: public static v
2017-02-23
  目录