对象与类
1 面向对象程序设计
**面向对象的程序(oop)**是由对象组成的, 每个对象包含对用户公开的特定功能部分和隐藏的实现部分。
传统的结构化程序设计通过**设计一系列的过程(即算法)**来求解问题。一旦确定了这些过程, 就要开始考虑存储数据的方式。先要确定如何操作数据, 然后再决定如何组织数据, 以便于数据操作。 而 OOP 却调换了这个次序, 将数据放在第1位,然后再考虑操作数据的算法
2 类
类的要素:
-
类( class) 是构造对象的模板或蓝图。
-
由类构造(construct) 对象的过程称为创建类的实例 (instance )
-
对象中的数据称为实例域( instance field )
-
操纵数据的过程称为方法( method ),对于每个特定的 类实例(对象)都有一组特定的实例域值
-
这些值的集合就是这个对象的当前状态( state )
类的三大特征:
-
封装:实现封装的关键在于绝对不能让类中的方法直接地访问其他类的实例域。程序仅通过对象的方法与对象数据进行交互,这意味着一个类可以全面地改变存储数据的方式,只要仍旧使用同样的方法操作数据, 其他对象就不会知道或介意所发生的变化
-
继承:在 Java 中, 所有的类都源自于一个“ 神通广大的超类”, 它就是 Object
2.1 对象
对象的三个主要特性:
- 对象的行为(behavior) —可以对对象施加哪些操作,或可以对对象施加哪些方法?
- 对象的状态(state ) —当施加那些方法时,对象如何响应?
- 对象标识(identity ) —如何辨别具有相同行为与状态的不同对象?
2.2 类之间的关系
在类之间, 最常见的关系有
- 依赖(“ uses-a”)
- 聚合(“ has-a”)
- 继承(“ is-a”)
如果一个类的方法操纵另一个 类的对象,我们就说一个类依赖于另一个类。例如,Order 类使用 Account 类是因为 Order 对象需要访问 Account 对象查看信用状态。应该尽可能地将相互依赖的类减至最少。
聚合(aggregation ), 即“ has-a ” 关系,意味着类 A 的对象包含类 B 的对象。例如, 一个 Order 对象包含一些 Item 对象
3 对象与对象变量
要想使用对象,就必须首先构造对象, 并指定其初始状态。然后,对象应用方法
3.1 使用构造器(constructor ) 构造新实例
构造器是一种特殊的方法, 用来构造并初始化对象
构造器的名字应该与类名相同。因此 Date 类的构造器名为 Date。要想构造一个 Date 对 象, 需要在构造器前面加上 new 操作符,如下所示:
new Date()
3.2 对象使用
- 可以将这个对象传递给一个方法:
System.out.println(new Date())
- 可以将一个方法应用于刚刚创建的对象
String s = new Date().toString()
- 将对象存放在一个变量中
Date birthday = new Date()
在对象与对象变量之间存在着一个重要的区别。例如
Date deadline; // deadline doesn't refer to any object
定义了一个对象变量 deadline, 它 可 以 引 用 Date 类型的对象。但是,一定要认识到: 变量 deadline 不是一个对象, 实际上也没有引用对象。此时,不能将任何 Date 方法应用于这个变 量上
3.3 注意
- 一个对象变量并没有实际包含一个对象,而仅仅引用一个对象
Date birthday = new Date()
Date deadline = birthday
- 可以显式地将对象变量设置为null, 表明这个对象变量目前没有引用任何对象
4 用户定义类
现在开始学习如何设计复杂应用程序所需要的各种主力类。通常, 这些类没有 main 方法, 却有自己的实例域和实例方法。 要想创建一个完整的程序, 应该将 若干类组合在一起, 其中只有一个类有 main 方法。
在 Java 中, 最简单的类定义形式为:
import java.time.*;
public class Employee {
private String name;
private double salary;
private LocalDate hireDay;
public Employee(String n, double s, int year, int mouth, int day) {
name = n;
salary = s;
hireDay = LocalDate.of(year, mouth, day);
}
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
public LocalDate getHireDay() {
return hireDay;
}
public void raiseSalary(double byPercent) {
salary = salary * (1 + byPercent/100);
}
}
public class EmployeeTest {
public static void main(String [] args) {
Employee [] staff = new Employee[3];
staff[0] = new Employee("Carl Cracker", 75000, 1987, 12, 15);
staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1);
staff[2] = new Employee("Tony Tester", 40000, 1990, 3, 15);
for (Employee e:staff)
e.raiseSalary(5);
for (Employee e:staff)
System.out.println("name = " + e.getName() + ",salary = " +
e.getSalary() + ",hireDay = " + e.getHireDay());
}
}
4.1 多个源文件使用
在上边的示例代码中,一个源文件中有两个类,但是在编写代码时可以将两个类分别放在单独源文件。
可以有两种调用方式:
- 使用通配符调用
javac Employee*.java
- 或者直接编译
javac EmployeeTest.java
。当 Java 编 译器发现 EmployeeTestjava 使用丫 Employee 类时会查找名为 Employee.class 的文件。如果没有找 到这个文件, 就会自动地搜索 Employeejava, 然后,对它进行编译
4.2 创建类
4.2.1 构造器
构造器与类同名。在构造 Employee 类的对象时, 构造器会运行,以便将实例域 初始化为所希望的状态
- 构造器与类同名。在构造 Employee 类的对象时, 构造器会运行,以便将实例域 初始化为所希望的状态。
- 构造器总是伴随着 new 操作符的执行被调用, 而不能对一个已经存在的对象调用构造器来达到重新设置实例域的目的。
- 每个类可以有一个以上的构造器
- 构造器可以有 0 个、1 个或多个参数
- 构造器没有返回值
4.2.2 隐式参数与显式参数
number007. raiseSalary(5);第一个参数称为隐式 ( implicit ) 参数, 是出现在方法名前的 Employee 类对象。
第二个参数位于方法名后面括号中的数值,这是一个显式 ( explicit) 参 数 .例如 double byPercent
如果需要的话,可以使用this替代隐式参数,可以用下列方式编写 raiseSalary 方法:
4.2.3 封装的优点
在设置实例域的时候,我们经常将域设置为私有域,创建公开的访问器和修改器来使用他。这样可以保证封装性和代码出错时的查找。
- 一 私有的数据域;
- 一 公有的域访问器方法;
- 一个公有的域更改器方法
4.2.4 基于类的访问权限
一个方法可以访问所属类的所有 对象的私有数据
4.2.5 final实例域
可以将实例域定义为 final。 构建对象时必须初始化这样的域。也就是说, 必须确保在每一个构造器执行之后,这个域的值被设置, 并且在后面的操作中, 不能够再对它进行修改。
final 修饰符大都应用于基本 (primitive ) 类型域,或不可变(immutable) 类的域
4.3 静态域与静态方法
4.3.1 静态域
如果将域定义为 static, 每个类中只有一个这样的域。而每一个对象对于所有的实例域 却都有自己的一份拷贝
所有属于这个类的对象具有同样的值
静态变量可以在对象之中修改
而静态常量不可修改,如private static final int x =
4.3.2 静态方法
静态方法是一种不能向对象施加操作的方法。例如, Math 类的 pow 方法就是一个静态方法。
在运算时, 不使用任何具体的对象,也就是没有隐式参数。
5 方法参数
将参数传递给方法(或函数)的方式:
- 按 值调用 (call by value) 表示方法接收的是调用者提供的值
- 按引用调用 ( call by reference) 表示方法接收的是调用者提供的变量地址
Java 程序设计语言总是采用按值调用。也就是说, 方法得到的是所有参数值的一个拷 贝,特别是,方法不能修改传递给它的任何参数变量的内容。
一个方法不可能修改一个基本数据类型的参数。而对象引用作为参数却可以完成
- 一个方法不能修改一个基本数据类型的参数(即数值型或布尔型)
- 一个方法可以改变一个对象参数的状态
- 一个方法不能让对象参数引用一个新的对象。
6 包
Java 允许使用包( package )将类组织起来。借助于包可以方便地组织自己的代码,并将 自己的代码与别人提供的代码库分开管理
使用包的主要原因是确保类名的唯一性。假如两个程序员不约而同地建立了 Employee 类。只要将这些类放置在不同的包中, 就不会产生冲突。
6.1 类的导入
一个类可以使用所属包中的所有类, 以及其他包中的公有类( public class)
我们可以采用两种方式访问另一个包中的公有类:
- 第一种方式是在每个类名之前添加完整的包名
- 使用 import 语句。一旦使用了 import 语句,在使用类时,就不必写出包的全名了
6.2 静态导入
import 语句不仅可以导人类,还增加了导人静态方法和静态域的功能
如果在源文件的顶部, 添加一条指令: import static java.lang.System.*;
就可以使用 System 类的静态方法和静态域,而不必加类名前缀
6.3 包作用域
标记为 public 的部分可以被任意的类使用
标记为 private 的部分只能被定义它们的类使用
- 使用 import 语句。一旦使用了 import 语句,在使用类时,就不必写出包的全名了
6.2 静态导入
import 语句不仅可以导人类,还增加了导人静态方法和静态域的功能
如果在源文件的顶部, 添加一条指令: import static java.lang.System.*;
就可以使用 System 类的静态方法和静态域,而不必加类名前缀
6.3 包作用域
标记为 public 的部分可以被任意的类使用
标记为 private 的部分只能被定义它们的类使用
如果没有指定 public 或 private , 这个部分(类、方法或变量)可以被同一个包中的所有方法访问