类之间的关系
- 依赖(“uses-a”):如果一个类的方法操纵另一个类的对象,就说一个类依赖于另一个类。(应该尽可能地将相互依赖的类减至最少)
- 聚合("has-a"): 类A的对象包含类B的对象
- 继承("is-a"):是一种用于表示特殊与一般的关系
对象与对象变量
一个对象变量并没有实际包含一个对象,而仅仅引用一个对象。在java中,任何对象变量的值都是存储在另外一个地方的一个对象的引用。new 操作符返回值也是一个引用。
Date birthday = new Date();
Date deadline = birthday;
可以显示地将对象变量设置为null,表明这个对象变量目前没有引用任何对象。
局部变量不会自动地初始化为null,而必须通过new或将它们设置为null进行初始化。
用户自定义类
要想创建一个完整的程序,应该将若该类组合在一起,其中只有一个类有main方法。
在一个源文件中,只能有一个公共类,但可以有任意数目的非公共类。
- 构造器:构造器与其他的方法有一个重要的不同。构造器总是伴随着new操作符的执行被调用,而不能对一个已经存在的对象调用构造器来达到重新设置实例域的目的。
- 构造器与类同名
- 每个类可以有一个以上的构造器
- 构造器可以有0个、1个或多个参数
- 构造器没有返回值
- 构造器总是伴随着new操作一起调用
所有的java对象都是在堆中构造的。
- 隐式参数与显式参数
- 第一个参数为隐式(implicit)参数,是出现在方法名前的对象
- 第二个参数为显式(explicit)参数,显式参数是明显地列在方法声明中的
num.raiseSalary(10);
在JAVA中,所有的方法都必须在类的内部定义,但并不表示它们是"内联方法"。是否将整个方法设置为内联方法是java虚拟机的任务。即时编译器会监视调用那些简介、经常被调用、没有被重载以及可以优化的方法。
注意:不要编写返回引用可变对象的访问器方法,如果需要返回一个可变对象的引用,应该首先对它进行克隆(clone)。对象clone是指存放在另一个位置上的对象副本。
- 一个方法可以访问所属类的所有对象的私有数据
- 如下面调用,equal不仅可以访问harry的私有域,也可以访问boss的私有域
if(harry.equal(boss))
- final实例域:构建对象时必须初始化这样的域。也就是说必须确保在每一个构造器执行之后,这个域的值被设置,并且在后面的操作中,不能够再对它进行修改。
- final修饰符大都应用于基本类型域或不可变类的域(如果类中的每个方法都不会改变其对象,这种类就是不可变的类。例如:String类就是一个不可变的类)。对于可变的类,使用final修饰符可能会对读者造成混乱。如:仅仅意味着存储在hiredate变量中的对象引用在对象构造之后不能被改变,而并不意味着hiredate对象是一个常量。任何方法都可以对hiredate引用的对象调用setTime更改器。
- String类,因其方法不会修改对象,故对应的对象变量定义为final时,一、该变量不能被修改 二、调用String类的方法时,其对应的对象的引用也不会被修改,但是下面的例子里,对应的对象引用是可以被其对应的方法修改的。
private final Date hiredate;
- 静态域
- 静态域是属于类的,不属于单个对象,因此不能再构造函数中初始化,1. 类声明时直接初始化 2. 在静态块中初始化
- 静态变量使用的比较少,静态常量使用的比较多,如Math类中的PI
class Employee{
private static int nextId = 1;
private int id;
...
}
- 静态方法:静态方法是一种不能向对象实施操作的方法。可认为静态方法是没有this参数的方法。静态方法可以访问自身类中的静态域。在下面两种情况下使用静态方法:
- 一个方法不需要访问对象状态,其所需参数都是通过显式参数提供
- 一个方法只需要访问类的静态域
- 工厂方法
- main方法:在启动程序时还没有任何一个对象。静态的main方法将执行并创建程序所需要的对象。每一个类可以有一个main方法。这是一个常用语对类进行单元测试的技巧。
- 方法参数:java中,总是采用按值调用。java程序设计语言中方法参数的使用情况:
- 一个方法不能修改一个基本数据类型的参数(即数值型和布尔型)
- 一个方法可以改变一个对象参数的状态
- 一个方法不能让对象参数引用一个新的对象
对象构造
- 重载:如果多个方法有相同的名字、不同的参数,便产生了重载。java允许重载任何方法,因此要完整的描述一个方法需要指出方法名以及参数类型。这叫做方法的签名。返回类型不是方法签名的一部分。
- 默认域初始化:如果在构造器中没有显式地给域赋予初值,那么就会被自动地赋为默认值:数值为0、布尔值为false、对象引用为null。注:这是域与局部变量的主要不通电。必须明确地初始化方法中的局部变量。但是如果没有初始化类中的域,将会被初始化为默认值(0、false、null)、
- 无参构造函数:仅当类没有提供任何构造器的时候,系统才会提供一个默认的无参构造器,这个构造器将所有的实例域设置为默认值。如果类中提供了至少一个构造器,但是没有提供无参数的构造器,则在构造对象时如果没有提供参数就会被视为不合法。
- 显示域初始化:在类定义中,直接将一个值赋给任何域。在执行构造器之前,先执行赋值操作。当一个类的所有构造器都希望把相同的值赋予某个特定的实例域时,这种方式特别有用。
//直接将一个值赋给任何域
class Employee{
private String name = "";
...
}
//或调动方法对域进行初始化
class Employee{
private static int nextId;
private int id = assignId();
...
private static int assignId(){
int r = nextId;
nextId++;
return r;
}
...
}
- 调用另一个构造器: 如果构造器的第一个语句形如this(...),这个构造器将调用同一个类的另一个构造器。
public Employee(double s){
//calls Employee(String,double)
this("Employee #"+nextId,s);
nextId++;
}
- 初始化块:在一个类的声明中,可以包含多个代码块。只要构造类的对象,这些块就会被执行。
class Employee{
private static int nextId;
private int id;
private String name;
private double salary;
//object initialization block
{
id = nextId;
nextId++;
}
public Employee(String n,String s){
name = n;
salary = s;
}
public Employee(){
name = "";
salary = 0;
}
...
}
数据初始化步骤:
- 所有数据域被初始化为默认值(0、false或null)
- 按照在类声明中出现的次序,依次执行所有域初始化语句和初始化块
- 如果构造器第一行调用了第二个构造器,则执行第二个构造器主体
- 执行这个构造器主体
静态域不适合放在构造函数和非静态块中初始化,因为它被所有对象共享。静态域可以1. 直接赋值进行初始化 2. 如果初始化的代码比较复杂,那么可以适用静态的初始化块
//static initialization block
static
{
Random generator = new Random();
nextId = generator.nextInt(10000);
}
在类第一次加载的时候,会进行静态域的初始化。与实例域一样,除非将它们显式地设置成其它值,否则默认的初始值是0、false或null。
- finalize方法:可以为任何一个类添加finalize方法。finalize方法将在垃圾回收器清除对象之前调用。
包
Java允许使用包(package)将类组织起来。使用包的主要原因是确保类名的唯一性。从编译器的角度看,嵌套的包没有任何关系。如java.util包与java.util.jar包毫无关系。每一个都拥有独立的类集合。
一个类可以使用所属包中的所有类,以及其它包中的公有类。
import语句不仅可以导入类,还可以导入静态方法和静态域的功能。
要想将一个类放入包中,就必须将包的名字放在源文件的开头,包中定义类的代码之前。将包中的文件放到与完整的包名匹配的子目录中。编译器将类文件也放在相同的目录结构中。
标记为public的部分可以被任意的类使用;标记为private的部分只能被定义它们的类使用。如果没有指定public或private,这个部分(类、方法或变量)可以被同一个包中的所有方法访问。
类路径
类路径所列出的目录和归档文件是搜寻类的起始点。
javac编译器总是在当前的目录中查找文件,但java虚拟机尽在类路径中有"."目录的时候才查看当前目录。如果没有设置类路径,那也并不会产生什么问题,默认的类路径包含"."目录。然而如果设置了类路径却忘记了包含"."目录,则程序仍然可以通过编译,但不能运行。
设置类路径:最好采用-classpath(或-cp)选项指定类路径,java -classpth /home/user/classdir:.:/home/user/archives/archive.jar MyProg。利用-classpath选项设置类路径是首选的方法,也可以通过设置CLASSPATH环境变量完成这个操作。
一个源文件只能包含一个公有类,并且文件名必须与公有类匹配。