面向对象程序设计 (OOP)
封装(encapsulation) 亦称数据隐藏
是将数据和行为组合在一个包中,并对对象的使用者隐藏了数据的实现方法
关键:在于绝对不能让类中的方法直接访问其他类的实例域。
程序仅通过对象的方法与对象数据进行交互
实例域(instance field)
对象中的数据称为实例域
方法(method)
操纵数据的过程
当前状态(state)
对于每个特定的类实例(对象)都有一组特定的实例域值。这些值得集合就是对象的当前状态,无论何时,只要向对象发送一个信息,它的状态就有可能改变
对象的三个主要特征
对象的行为(behavior)
–可以对对象施加哪些操作,或可以对对象施加哪些方法
对象的状态(state)
–当施加哪些方法时,对象如何响应
对象标识(identity)
–如何辨别具有相同行为与状态的不同对象?
类之间的关系
依赖(uses-a) dependence
如果一个类的方法操纵另一个类的对象,我们就说一个类依赖于另一个类
聚合(has-a) aggregation
类A的对象包含类B的对象
继承(is-a)
如果类A扩展类B,类A不但包含类B继承的方法,还会拥有一些额外的功能
构造器(constructor)
是一张特殊的方法,用来构造并初始化对象
使用操作符 new
Date.toString()
该方法返回日期的字符串描述
String s = new Date().toString();
区别:
Date deadline;
定义了一个对象变量deadline, 它可以引用Date类型的对象
Date deadline = new Date();
表达式new Date() 构造了一个Date类型的对象,并且它的值是对新建对象的引用
更改器方法(mutator method)
对实例域做出修改的方法称为更改器方法
访问器方法 (accessor method)
仅访问实例域而不进行修改的方法称为访问器方法
get()
方法仅仅查看并返回对象的状态
set() 与 add()
方法却对对象的状态进行修改
#类的访问权限
方法可以访问所调用对象的私有数据
私有方法 关键字 private
对于私有方法,如果改用其他方法实现相应的操作,则不必保留原有的方法。如果数据的表达式发生了变化,这个犯法可能会变得难以实现,或者不再需要
只要方法是私有的,它不会被外部的其他类操作调用,可以将其删去
公有方法 关键字 public
如果方法是公有的,就不能将其删去,因为其他的代码很可能依赖它
final 实例域 将实例域定义为final
构建对象时必须初始化这样的域。
必须确保在每个构造器执行之后,这个域的值被设置,并且在后面的操作中,不能在对它进行修改
class Employee
{
private final String name; //将Employee 类中的name域声明为final, 因为在对象构建之后,这个值不会再被修改,即没有 setName() 方法
}
静态域 static
如果将域定义为static, 每个类中只有一个这样的域
class Employee
{
private static int nextId = 1; //静态域
private int id; //实例域
}
基本类型域 primitive
不可变(immutable)类的域
如果类中的每个方法都不会改变其对象,这种类就是不可变的类
静态常量
public class Math
{
public static final double PI = 3.14159265358979323846;
}
静态方法
是一种不能向对象实施操作的方法,没有隐式的参数,没有this参数的方法
静态方法不能操作对象,所以不能在静态方法中访问实例域
静态方法可以访问自身类中的静态域
main方法也是一个静态方法
eg. Math.pow(x,a)
计算幂
x
a
x^a
xa,在运算时,不使用任何Math对象
例3
一个静态域nextId 和一个静态方法 getNextId
将三个Employee对象写入数组,然后打印雇员信息。最后,打印出下一个可用员工标识码来展示静态方法
/**
* This program demonstrates static methods.
* @version 1.01 2004-02-19
* @author Cay Horstmann
*/
public class StaticTest
{
public static void main(String[] args)
{
// fill the staff array with three Employee objects
Employee[] staff = new Employee[3];
staff[0] = new Employee("Tom", 40000);
staff[1] = new Employee("Dick", 60000);
staff[2] = new Employee("Harry", 65000);
// print out information about all Employee objects
for (Employee e : staff)
{
e.setId();
System.out.println("name=" + e.getName() + ",id=" + e.getId() + ",salary="
+ e.getSalary());
}
int n = Employee.getNextId(); // calls static method
System.out.println("Next available id=" + n);
}
}
class Employee
{
private static int nextId = 1;
private String name;
private double salary;
private int id;
public Employee(String n, double s)
{
name = n;
salary = s;
id = 0;
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public int getId()
{
return id;
}
public void setId()
{
id = nextId; // set id to next available id
nextId++;
}
public static int getNextId()
{
return nextId; // returns static field
}
public static void main(String[] args) // unit test
{
Employee e = new Employee("Harry", 50000);
System.out.println(e.getName() + " " + e.getSalary());
}
}
方法参数
按值调用 (call by value)
表示方法按接收的是调用者提供的值
按引用调用 (call by reference)
表示方法接收的是调用者提供的变量地址
例4
import java.util.*;
/**
* This program tests the Employee class.
* @version 1.11 2004-02-19
* @author Cay Horstmann
*/
public class EmployeeTest
{
public static void main(String[] args)
{
// fill the staff array with three Employee objects
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);
// raise everyone's salary by 5%
for (Employee e : staff)
e.raiseSalary(5);
// print out information about all Employee objects
for (Employee e : staff)
System.out.println("name=" + e.getName() + ",salary=" + e.getSalary() + ",hireDay="
+ e.getHireDay());
}
}
class Employee
{
private String name;
private double salary;
private Date hireDay;
public Employee(String n, double s, int year, int month, int day)
{
name = n;
salary = s;
GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
// GregorianCalendar uses 0 for January
hireDay = calendar.getTime();
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public Date getHireDay()
{
return hireDay;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
}
重载 overloading
(比如GregorianCalendar构造器方法)有相同的名字、不同的参数,便产生了重载
GregorianCalendar today = new GregorianCalendar();
GregorianCalendar deadline = new GregorianCalendar(2099,Calender.DECEMBER,31);
重载解析 (overloading resolution)
编译器必须挑选出具体执行哪个方法,它通过用各个方法给出的参数类型与特定方法调用所使用的值类型进行匹配来挑选出相应的方法。如果编译器找不到匹配的参数,或者找出多个可能的匹配,就会产生编译时错误,这个过程称为重载解析
方法的签名 signature
要完整地描述一个方法,需要指出方法名以及参数类型
默认域初始化
如果在构造器中没有显式地给出域赋予初值
数值 默认为 0
布尔值 默认为 false
对象引用 默认为 null
例5
import java.util.*;
/**
* This program tests the Employee class.
* @version 1.11 2004-02-19
* @author Cay Horstmann
*/
public class EmployeeTest
{
public static void main(String[] args)
{
// fill the staff array with three Employee objects
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);
// raise everyone's salary by 5%
for (Employee e : staff)
e.raiseSalary(5);
// print out information about all Employee objects
for (Employee e : staff)
System.out.println("name=" + e.getName() + ",salary=" + e.getSalary() + ",hireDay="
+ e.getHireDay());
}
}
class Employee
{
private String name;
private double salary;
private Date hireDay;
public Employee(String n, double s, int year, int month, int day)
{
name = n;
salary = s;
GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
// GregorianCalendar uses 0 for January
hireDay = calendar.getTime();
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public Date getHireDay()
{
return hireDay;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
}
包
包是将类组织起来,借助于包可以方便地组织自己的代码,并将自己的代码与别人提供的代码库分开管理
例6
javac PackageTest.java //编译器会自动查找文件 `com/horstmann/corejava/Employee.java`并进行编译
PackageTest.java
import com.horstmann.corejava.*;
// the Employee class is defined in that package
import static java.lang.System.*;
/**
* This program demonstrates the use of packages.
* @version 1.11 2004-02-19
* @author Cay Horstmann
*/
public class PackageTest
{
public static void main(String[] args)
{
// because of the import statement, we don't have to use com.horstmann.corejava.Employee here
Employee harry = new Employee("Harry Hacker", 50000, 1989, 10, 1);
harry.raiseSalary(5);
// because of the static import statement, we don't have to use System.out here
out.println("name=" + harry.getName() + ",salary=" + harry.getSalary());
}
}
./com/horstmann/corejava/Employee.java
package com.horstmann.corejava;
// the classes in this file are part of this package
import java.util.*;
// import statements come after the package statement
/**
* @version 1.10 1999-12-18
* @author Cay Horstmann
*/
public class Employee
{
private String name;
private double salary;
private Date hireDay;
public Employee(String n, double s, int year, int month, int day)
{
name = n;
salary = s;
GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
// GregorianCalendar uses 0 for January
hireDay = calendar.getTime();
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public Date getHireDay()
{
return hireDay;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
}