对象与类
4.1 面向对象程序设计概述
略
4.2 使用预定义类
主要是用日期相关的类举例,具体看代码。
4.2.1 对象与对象变量
要想使用对象,就必须首先构造对象,并指定其初始状态。构造新对象需要在构造器前面加上new操作符,以Date类为例。
new Date();
在Java中,任何对象变量的值都是对存储在另外一个地方的一个对象的引用。new操作符的返回值也是一个引用。Java类库中的GregorianCaledar类
GregorianCalendar有几个很有用的构造器:
1.new GregorianCalendar(); 用于表示对象构造是的日期和时间。2.new GregorianCalendar(1999,11,31); 提供年,月,日构造一个表示某个特定日期的日历对象。但是月份是从0开始计数的,所以11表示十二月,为了避免混淆,可以使用常量,如:Calendar.DECEMBER。
3.new GregorianCalendar(1999,Calendar.DECEMBER,31,23,59,59); 后面三个参数表示时间,即:23:59:59。
更改器方法与访问器方法
主要是get与set方法,仍然以GregorianCalendar为例。
get方法。可以获取某个时间点的年月日等信息。这需要借助Calendar类中定义的一些常量。如:Calendar.MONTH 或 Calendar.DAY_OF_WEEK:
GregorianCalendar now = new GregorianCalendar();
int month = now.get(Calendar.MONTH);
int weekday = now.get(Calendar.DAY_OF_WEEK);
更多的请参考API文档
set方法。调用set方法,可以改变对象的状态。
deadline.set(Calendar.YEAR,2001);
deadline.set(Calendar.MONTH,Calendar.APRIL);
deadline.set(Calendar.DAY_OF_MONTH,15);
deadline.set(2001,Calendar.APRIL,15); //直接设置年月日
deadline.add(Calendar.MONTH,2); //deadline后移2个月
4.3 用户自定义类
class ClassName
{
field1
field2
...
constructor1
constructor2
...
method1
method2
}
以上是Java中,最简单的类的定义形式。
4.3.1 Employee 类
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 d = new GregorianCalendar(year,month-1,day);
hireDay = d.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;
}
}
4.3.2 多个源文件的使用
Employee.java 和 EmployeeTest.java 可以分开。
4.3.3 剖析Employee类
这个类包含一个构造器和4个方法。所有的方法被标记为public,共有4中访问级别。
4.3.4 从构造器开始
构造器与类同名
每个类可以有一个以上的构造器
构造器可以有0个,1个或多个参数
构造器没有返回值
构造器总是伴随着new操作符一起调用
4.3.5 隐式参数与显式参数
类中的非静态方法会包含一个隐式参数,即this,关键字this指向对象本身。显式参数则是明显地列于方法声明中的。
4.3.6 封装的优点
私有的数据域
公有的域访问器方法
公有的域更改器方法
注意不要编写返回引用可变对象的访问器方法。这样会破坏封装性。
4.3.7 基于类的访问权限
一个方法(类方法)可以访问所属类的所有对象的私有数据。
4.3.8 私有方法
略
4.3.9 final实例域
可以将实例域定义为finall。构建对象时必须初始化这样的域。
final修饰符大都应用于基本类型域,或不可变类的域。
4.4 静态域与静态方法
4.4.1 静态域
如果将域定义为static,每个类中只有一个这样的域,而每个对象对于所有的实例域却都有一份自己的拷贝。例如
class Employee
{
private static int nextID = 1;
private int id;
...
}
每一个雇员对象都有一个自己的id域,但是所有的对象共享一个nextID域。
4.4.2 静态常量
public static final, 类中静态常量的声明。
4.4.3 静态方法
静态方法是一种不能向对象实施操作的方法。也就意味着静态方法不能访问类中非静态域的任何其他实例域。在下面两种情况下使用静态方法。
一个方法不需要访问对象状态,其所需参数都是通过显式参数提供。(例如:Math.pow)
一个方法只需要访问类的静态域(例如:Employee.getNextId).
4.4.4 工厂方法
以NumberFormat举例,它使用工厂方法产生不同风格的格式对象。这是静态方法的用途之一。
无法命名构造器。这里希望将得到的货币实例和百分比实例采用不同的名字。
当使用构造其实,无法改变所构造的对象的类型。而Factory方法将返回一个DecimalFormat类对象,这是NumberFormat的子类。
4.4.5 main方法
main方法不对任何对象进行操作。
4.5 方法参数
方法参数有按值调用(call by value) 和 按引用调用(call by reference) 两种。Java中总是采用按值调用。也即方法得到的是所有参数值的一个拷贝。这里主要看看基本类型和引用类型作为参数传递的不同之处。
基本类型
public static void tripleValue(double x)
{
x = 3 * x;
}
double percent = 10;
triplevalue(percent);
由上图可以看到,x先被初始化为percent的一个拷贝,x被乘以3之后等于30。但是percent仍然是10。方法调用完之后,x的生命周期就结束了,percent并没有被改变。引用类型的传递
public static void tripleSalary(Employee x)
{
x.raiseSalary(200);
}
harry = new Employee(...);
tripleSalary(harry);
x被初始化为harry的一个拷贝,x也是一个对象的引用,与harry指向同一个对象
rasieSalary方法应用于这个对象引用。因此harry的薪金也提高了2倍。
方法调用结束后,x的声明周期结束,harry仍然使用,harry的值已经改变。总结:
一个方法不能修改一个基本数据类型的参数
一个方法可以修改对象参数的状态
一个方法不能让一个对象参数引用一个新的对象。
4.6 对象构造
4.6.1 重载
函数名相同,参数列表不同
4.6.2 默认域初始化
如果在构造器中没有显示地给域赋予初值,那么就会被自动地赋为默认值。这是域和局部变量的主要不同点。必须明确地初始化方法中的局部变量。但是如果没有初始化类中的域,将会被初始化为默认值
4.6.3 无参数的构造器
如果在编写一个类时没有编写构造器,那么系统就会提供一个无参数构造器。一定要记住,仅当类没有提供任何构造器的时候,系统才会提供一个默认构造器。
4.6.4 显式域初始化
可以在类的定义中,直接将一个值赋给任何域
class Employee
{
private String name = "";
}
4.6.5 参数名
public Employee(String name, double salary)
{
this.name = name;
this.salary = salary
}
4.6.6 调用另一个构造器
如果构造器的第一个语句形如this(…),这个构造器将调用同一个类的另一个构造器。
public Employee(double s)
{
this("Employee #" + nextId, s);
nextId++;
}
这种方式可以对公共的构造器代码部分只编写一次即可。
4.6.7 初始化块
在一个类的声明中可以包含多个代码块,只要构造类的对象,这些块就会被执行。
class Employee
{
private static int nextId;
private int id;
private String name;
private double salary;
//对象初始化块
{
id = nextId;
nextId++;
}
public Employee(String n, double s)
{
name = n;
salary = s;
}
public Employee()
{
name = "";
salary = 0;
}
...
}
调用构造器的具体处理步骤:
1)所有数据域初始化为默认值
2)按照在类声明中出现的次序,依次执行所有域初始化语句和初始化块。
3)如果构造器第一行调用了第二个构造器,则执行第二个构造器主体。
4)执行这个构造器主体。还可以提供一个静态初始化块来初始化类的静态域。
4.6.8 对象析构与finalize方法
不常用
4.7 包
Java允许使用包将类组织起来。从编译器的角度来看,嵌套的包之间没有任何关系。每一个都有独立的类集合
4.7.1 类的导入
一个类可以使用所属包中的所有类,以及其他包中的公有类。可以有两种方式访问另一个包中的公有类。第一种是在类名之前添加完整的包名。第二种是用import语句。注意,import只能导入类,不能导入包。不能import java.*; 导入以java为前缀的所有的包。
如果两个包中有类名冲突,在类名前加上完整的包名。
4.7.2 静态导入
import static java.lang.System.*;
加上以上语句,就可以使用System类的静态方法和静态域,而不必加类名前缀
out.println(“Goodbye, World!”);
exit(0);
4.7.3 将类放入包中
package语句。另外,编译器对文件进行操作,而Java解释器加载类带有(.分隔符)。
4.7.4 包作用域
public修饰的部分可以被任意的类使用;标记为private的部分职能被定义它们的类使用。如果没有指定任何修饰符,这个部分可以被同一个包中的所有方法访问。
4.8 类路径
为了使类能够被多个程序共享,需要做到下面几点
1)把类放到一个目录中,例如/home/user/classdir。需要注意,这个目录是包树状结构的基目录。
2)将JAR文件放在一个目录中,例如:/home/user/archives
3)设置类路径(classpath)。类路径是所有包含类文件的路径的集合。要想import语句能够正常引用其他包中的类,则其他包也必须要位于classpath下。虚拟机会根据设置的classpath的顺序来查找所需的类。
4.9 文档注释
略
4.10 类设计技巧
略