Skip to content

面向对象概述

两种程序设计方法

结构化程序设计,典型代表Pascal,C

面向对象程序设计( Object Oriented Programming,OOP ),典型代表Java, C++, Python, C#

结构化程序设计

  • 模块分解与功能抽象和自顶向下、分而治之的方法。支持子程序、全局变量、丰富的控制结构。
  • 是面向过程的。
  • 不能很好的适应需求变化,在开发大型项目时,力不从心。

面向对象程序设计

  • 以接近人类思维的方法描述软件对象,降低了软件开发难度,提高了开发速度。
  • 软件具有易维护、可重用以及可扩展性。吸收了结构化程序设计思想精华,提出一些新的概念。
  • OOP成为当今最流行的软件开发技术。

面向对象基本概念

对象(Object)

现实世界中,一个人、一辆汽车、一台电视机、一所学校,一次会议、一场足球比赛、一个账户等

对象的定义

是其自身所具有的状态特征及可以对这些状态施加的操作结合在一起所构成的实体。

对象的两方面特征

静态特征: 描述对象的状态。

动态特征: 描述对象的行为。

举例:一辆汽车。状态描述:生产厂家、颜色、出厂年份、价格等;行为描述:启动、加速、转弯和停止等

类(Class)

最重要的概念。如人类、鱼类、交通工具类等。

类定义

具有相似特征和行为的对象的集合。

  • 人类具有的区别于其他动物的特征有直立行走、使用工具、使用语言交流、能工作等
  • 所有的事物都可以归到某类中。例如,汽车属于交通工具类,手机属于通信工具类。

实例(instance)或对象

某个类的一个具体的对象称为该类的一个实例。例如,我的汽车是汽车类的实例,张三是学生类的实例。

消息(message)

  • 对象与对象之间不是孤立的,它们之间存在着某种联系,这种联系是通过消息传递的。
  • 例如,开汽车就是人向汽车传递消息。

消息三要素

  1. 哪个对象发出的消息?接收者
  2. 给哪个对象发送的消息?发送者
  3. 发送了什么样的消息?消息内容(方法名+参数列表)
Java
1
 receiver.Method(params)

封装性

  • 对象是状态(属性)和行为(操作或方法)封装体。例如,汽车就是一个封装体,电视机是一个封装体。
  • 实现信息隐藏,通过接口与外界通信。

继承性

  • 一个对象获得另一个对象的属性的过程。继承性实现了类之间的是一种(IS-A)关系。 汽车是一种(is a)交通工具。

多态性

接口的多种不同的实现方式即为多态。

面向对象的优势

易维护

  • OOP方法很容易实现程序模块化,减少了维护的开销
  • 在OOP中,类就是一个模块,并且可以继承。可以实现“高内聚,低耦合”。

可扩展

  • 功能可以被扩展或增强。
  • 通过继承来实现。

可重用

  • 之前写好的代码可以被代码的创建者或其他人重用。
  • 可使用语言本身提供类库或API(应用编程接口)。
  • 问题的解决方案也可重用,这些方案称为设计模式。

面向对象的应用

面向对象分析:OOA,Object Oriented Analysis

面向对象设计:OOD,Object Oriented Design

面向对象编程:OOP,Object Oriented Programming

为对象定义类

如何定义一个类

一个类的定义包括两个部分,分别是:

  1. 类的声明
  2. 类体的定义

类声明的一般格式为:

Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
/* 基本格式 */
public class ClassName {
    /* 成员变量声明 */
    /* 构造方法定义 */
    /* 成员方法定义 */
}

[public] class ClassName [extends SuperClass] [implements InterfaceName or AbstractClass]{
    // 1.成员变量声明    :定义状态
    // 2.构造方法的定义
    // 3.成员方法的定义  :定义行为
  }

用类创建对象(实例)

用构造方法创建对象

Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
/* 定义Employee类 */
public class Employee {
    private String name;
    private int age;
    private double salary;

    /* 构造方法 */
    public Employee(){}
}


/* 用类创建对象 */
Employee employee;
employee = new Employee();  // 调用构造方法
employee.name = "李明";
employee.age = 28;
employee.salary = 5000.00;

用另一个对象创建对象

Java
1
2
3
4
Employee  employee2;
employee2 = employee;   //这是引用赋值   
employee2.name = "刘晨";
System.out.println(employee.name);

理解堆和栈

  • JVM将内存空间在逻辑上分为栈(stack)与堆(heap)两种结构。
  • 被调用**方法参数**和方法中的**局部变量**都存储在内存**栈**中,当程序使用**new**运算符创建对象时,JVM将在**堆**中分配内存。

用UML图表示类

UML,Unified Modeling Language,统一建模语言

  • “+”为public成员
  • “-”为private成员
  • “#”为protected成员
  • 不加前缀的成员具有默认访问级别。

img

方法设计

一个方法的定义包括两个部分,分别是:

  1. 方法的声明
  2. 方法体的定义

方法声明的一般格式为

Java
1
2
3
4
5
6
7
8
[public] returnType  methodName ([paramList]) {
    //方法体
 }

/* 方法签名 = 方法名+形参列表 */
[访问修饰符] 返回值类型 方法名([形参列表]){
    //方法体
}

访问方法(getter)

  • 能够返回成员变量值的方法称为访问方法(accessor method)
  • 访问方法名一般为getXxx(),因此,也称getter方法。如,String getName()

修改方法(setter)

  • 能够修改成员变量值的方法称为修改方法(mutator method)
  • 修改方法名一般为setXxx(),因此,也称setter方法。如,void setName(String name)

方法调用

实例方法(成员方法)

创建一个对象,然后通过对象引用调用。

静态方法(类方法)

通常使用类名调用。

方法调用主要应用场景

  1. 用对象引用调用类的实例方法
  2. 用类名直接调用static方法
  3. 类中的方法调用本类中的其他方法(注意实例方法与类方法相互调用)

方法重载

同名不同参。

  • Java允许在一个类中定义多个同名的方法,这称为方法重载(overloading)
  • 实现方法重载,要求同名的方法,要么参数个数不同,要么参数类型不同
  • 仅返回值不同不能区分重载的方法

构造方法(构造器)

构造方法也叫构造器(Constructor) 。Java每个类都有构造方法。

构造方法与普通方法的区别是

  • 构造方法的名称必须与类名相同。
  • 构造方法不能有返回值,也不能返回void。
  • 必须在创建对象时用new运算符调用。

构造方法定义格式:

Java
1
2
3
 [public] ClassName([paramList]) { //这里不能返回任何值
     // 方法体
 }

构造方法分类:

  1. 无参数构造方法(no-args constructor)
  2. 带参数构造方法
  3. 默认构造方法(default constructor)
  4. 构造方法重载

构造方法主要作用:创建对象并初始化对象的成员变量。类的成员变量,若声明时没有明确赋初值,则可用构造方法初始化。

对象初始化

Text Only
1
Employee employee = new Employee();

成员属性默认值:

  • 整型数据的默认值是0
  • 浮点型数默认值是0.0
  • 字符型是'\u0000'
  • 布尔型是false
  • 引用类型是null

this关键字

表示对象本身。

应用场景:

  1. 解决局部变量与成员变量同名的问题
  2. 解决方法(构造方法)参数与成员变量同名的问题
  3. 用来调用该类的另一个构造方法

方法参数的传递

  • 调用带参数的方法,需要向它传递参数。
  • 定义方法指定的参数称为形式参数,调用方法指定的参数称为实际参数。

Java语言中,方法的参数是按值传递(pass by value)的,即在调用方法时将实际参数的值的一个拷贝传递给方法中的形式参数,方法调用结束后实际参数的值并不改变。

  • 当参数是**基本类型**时,将实际参数**值**的一个**拷贝**传递给方法,方法调用结束后,对原来的值没有影响。
  • 当参数是**引用类型**时,实际传递的是**引用值**,因此在方法的内部有可能改变原来的对象。

小试牛刀:

静态变量和静态方法

静态变量

  • 用static修饰,也叫类变量
  • 所有实例共享一块内存空间
  • 通常使用类名访问

实例变量:

  • 未用static修饰。
  • 每个实例具有自己的内存空间。
  • 必须使用实例名访问。

类常量:

  • 通常,static与final一起使用来定义类常量。例如,Java类库中的Math类中就定义了两个类常量
  • 可以通过类名直接使用这些常量

静态方法

  • 用static修饰,也叫类方法。
  • 只能访问静态变量。
  • 通常使用类名访问

实例方法:

  • 未用static修饰
  • 可访问实例变量和静态变量
  • 必须使用实例名调用

相互调用关系:

img

单例模式

定义:类只能有一个实例对象。

设计:private构造器+static 创建方法

递归

  • 递归(recursio就是把问题逐渐简单化,最后实现问题n)是解决复杂问题的一种常见方法。其基本思想的求解。
  • Java语言支持方法的递归调用。所谓方法的递归调用就是方法自己调用自己。

趁热打铁1: 求正整数n的阶乘n!,就可以通过递归实现。n!可按递归定义如下:

​ n! = n ×(n-1)!; n > 0

​ 0! = 1;

Java
1
2
3
4
5
6
7
//设计算n!的方法为factor(n),则该算法的简单描述如下:
public int factor(int n){
    if(n == 0)
        return 1 ;
    else
        return factor(n-1) * n;
}

对象初始化和清除

实例变量初始化

实例变量初始化方式:

  1. 声明时初始化
  2. 使用初始化块
  3. 使用构造方法初始化

实例变量默认值:在类的定义中如果没有为变量赋初值,则编译器为每个成员变量指定一个**默认值**

  • 引用类型的变量,默认值为null
  • 整型默认值是0,浮点型默认值是0.0,布尔型默认值是false,字符型是空字符('\u0000'
Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public class Student {
    int id;
    String name;
    double marks;
    boolean pass;

    // int id = 10001;
    // String name = "Tom";
    // double marks = 90.0;
    // boolean pass = true;
}
Student stu = new Student();

初始化次序

如果在类中既为实例变量指定了初值,又有初始化块,还在构造方法中初始化了变量,那么它们执行的顺序如何,最后变量的值是多少?

  1. 首先使用**默认值或指定的初值**初始化
  2. 接下来执行**初始化块**
  3. 最后再执行**构造方法**

静态变量初始化

静态变量初始化方式:

  1. 声明时初始化
  2. 使用静态初始化块
  3. 使用构造方法初始化

垃圾回收

垃圾回收:当一个对象不再被引用时,Java运行系统就在后台自动运行一个线程,销毁该对象并释放其所占的内存空间,这个过程称为垃圾回收(Garbage Collection,GC)

垃圾回收器:后台运行的线程称为垃圾回收器(garbage collector)

Java
1
2
3
4
Employee emp1 = new Employee();
Employee emp2 = new Employee();
emp2 = emp1;
emp2 = null;

强制执行垃圾回收器:

  • 直接调用System类的gc()方法: System.gc();
  • 通过Runtime类的gc()实例方法:Runtime rt = Runtime.getRuntime(); rt.gc();

包与类的导入

包与package语句

Java语言使用包(package)来组织类库。包实际是一组相关类或接口的集合。

包主要有下面几个作用:

  • 功能相关的类和接口放到一个包中
  • 实现命名管理机制,同名的类可以放在不同包中
  • 实现对类的访问控制

定义一个类属于某个包,使用package语句

包名一般用域名的反转,减少冲突。

创建包有两种方法:

  • IDE自动创建
  • 带 –d 选项的编译命令

类的完全限定名(fully qualified name):com.xxx.Employee

类导入

  • import 语句
  • import static 语句

Java编译单元

  • 一个源程序文件通常称为一个编译单元(compile unit)。每个编译单元可以包含一个package语句、多个import语句以及类、接口和枚举定义。
  • 注意,一个编译单元中最多只能定义一个public类(或接口、枚举等),并且源文件的主文件名与该类的类名相同。

小试牛刀:

  1. 定义一个名为Person的类,其中含有一个String类型的成员变量name和一个int类型的成员变量age。分别为这两个变量定义访问方法和修改方法,另外再为该类定义一个名为speak的方法,在其中输出其name和age的值。画出该类的UML图。编写程序,使用上面定义的Person类,实现数据的访问、修改。
  2. 定义一个名为Circle的类表圆,其中含有double型的成员变量centerX、centerY表示圆心坐标,radius表示圆的半径。定义求圆面积的方法getArea()方法和求圆周长的方法getPerimeter()。为半径radius定义访问方法和修改方法。定义一个带参数构造方法,通过给出圆的半径创建圆对象。定义默认构造方法,在该方法中调用有参数构造方法,将圆的半径设置为1.0。画出该类的UML图。编写程序测试这个圆类的所有方法。
  3. 定义一个Triangle类表示三角形,其中包括三个double型变量a、b、c表示三条边长。为该类定义两个构造方法:默认构造方法设置三角形的三条边长都为0.0;带三个参数的构造方法通过传递3个参数创建三角形对象。定义求三角形面积的方法area(),面积计算公式为:area=Math.sqrt(s*(s-a)(s-b)(s-c)),其中s=a+b+c)/2。编写另一个程序测试这个三角形类的所有方法。
  4. 设计一个名为Stock的类表示股票,该类包括:

  5. 一个名为symbol的字符串数据域表示股票代码。

  6. 一个名为name的字符串数据域表示股票名称。
  7. 一个名为previousPrice的的double型数据域,它存储股票的前一日收盘价。
  8. 一个名为currentPrice的double型数据域,它存储股票的当前价格。
  9. 创建一个给定特定代码和名称的股票的构造方法。
  10. 一个名为getChangePercent()方法,返回从前一日价格到当前价格变化的百分比。

画出该类的UML图并实现这个类。

编写一个测试程序,创建一个Stock对象,它的股票代码是“600000”,股票名称是“浦发银行”,前一日收盘价是25.50,当前的最新价是28.6,显示市值变化的百分比。。