对象与类
关于oop的概念,网上已有很多介绍。
对象的三个主要特性:
对象的行为(behavior)——可以对对象施加哪些操作,或可以对对象施加哪些方法?
对象的状态(state)——当施加那些方法时,对象如何响应?
对象的标识(identity)——如何区分具有相同行为与状态的不同对象?
类之间最常见的关系有依赖(“uses-a”)、聚合(“has-a”)、继承(“is-a”)。依赖是一种最明显、最常见的关系。
对象与对象变量,要想使用变量,就必须首先构造对象,并指定其初始状态,然后对对象施加方法。使用构造器(constructor)构造新实例,这是一种特殊的方法,用来构造并初始化对象。构造器的名字应该与类名相同。例如,要想构造一个Date对象,需要在构造器前面加上new操作符,如new Date();这个表达式构造了一个新对象。这个对象初始化为当前日期和时间。如果需要的话,也可以将这个对象传递给一个方法:System.out.println(new Date());也可以将一个方法应用于刚刚构造的对象上。Date类中有一个toString方法。这个方法将返回日期的字符串描述:String s = new Daate().toString();通常,希望构造的对象可以被多次使用,因此需要将对象放在一个变量中:Date birthday = new Date();
在对象与对象变量之间存在着一个重要的区别。例如:Date deadling;//这不是一个对象。定义了一个对象变量deadline,它可以引用Date类型的对象,但是它本身不是一个对象,也没有引用对象,此时不能将任何Date方法应用于这个变量上。语句s = deadline.toString();将会编译错误。必须首先初始化变量deadline。当然可以用新构造的对象初始化这个变量:deadline = new Date();或者引用一个已存在的对象:deadline = birthday;这样这两个变量将引用同一个对象。一定要认识到,一个对象变量并没有实际包含一个对象,而仅仅引用一个对象。在java中,任何对象变量的值都是对存储在另外一处的一个对象的引用。new操作符的返回值也是一个引用。
更改器方法和访问器方法
通常的习惯是在访问器方法名前加上前缀get,在更改器方法前面加上前缀set。
用户自定义类
在java中,最简单的类定义形式为:
Class ClassName
{
constructor1
constructor2
...
method1
method2
...
field1
field2
...
}
例子:
import java.util.*;
public class EmployeeTest
{
public static void main(String[] args)
{
//file the staff array with three Employee objects
Employee[] staff = new Employee[3];
staff[0] = new Employee("Carl Cracker",7500,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
{
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;
}
private String name;
private double salary;
private Date hireDay;
}
在这个例子中,一个源文件包含了两个类。编译的时候,可以使用通配符调用java编译器:javac Employee*.java 或者 仅键入:javac EmployeeTest.java。编译之后,将会创建两个类文件EmployeeTest.class和Employee.class。在一个源文件中,只能有一个公有类,但是可以有任意数目个非公有类。
通过这个例子,我们发现,Employee类中有一个构造器和4个方法:
public Employee(String n,double s,int year,int month,int day);
public String getName();
public double getSalary();
public Date getHireDay();
public void raiseSalary(double byPercent);
三个实例域:
private String name;
private double salary;
private Date hireDay;
这个类的所有方法都标志为public。意味着,任何类的任何方法都可以调用这个方法。域中采用关键字private确保只有Employee类自身的方法能够访问,其它类不能够读写。
注意:
构造器与类同名;
每个类可以有一个以上的构造器;
构造器可以有0个、1个或者1个以上的参数;
构造器没有返回值;
构造器总是伴随着new操作符一同使用。
隐式参数与显式参数:在这个例子中的raiseSalary方法有两个参数,第一个参数被称为隐式参数,是出现在方法名前的Employee类型对象,第二个参数是方法名后面括号中的数值,这是一个显式参数。关键字this表示隐式参数。
封装的优点:
有时候,需要获取或设置实例域的值,应该提供三项内容:一个私有的数据域;一个公有的域访问器方法;一个公有的域更改器方法。这个做虽复杂些,但是却有明显的好处:可以改变内部实现,除了该类的方法之外,不会影响其他代码;更改器方法可以执行错误检查,然而直接对域进行赋值将不会做这些处理。例如setSalary方法可能会检查薪金是否小于0。
final修改符大都应用于基本数据类型域,或者不可变类型的域,如果类中的每个方法都不会改变其对象,这种类就是不可变的类。String类就是一个不可变的类。
静态域与静态方法:
如果将域定义为static,那么每个类中只有一个这样的域。
静态方法是不能向对象实施操作的方法。例如,Math类的pow方法就是一个静态方法。表达式Math.pow(x,a)计算幂xa。它在运算的时候,不使用任何Math对象。换句话说,没有隐式的参数。可以认为静态方法是没有this参数的方法。因为静态方法不能操作对象,所以不能在静态方法中访问实例域。但是,静态方法可以访问自身类中的静态域。
在下面两种情况下使用静态方法:
当一个方法不需要访问对象状态,其所需要参数都是通过显式参数提供的。
当一个方法只需要访问类的静态域。