首页 > 代码库 > 4.3 用户自定义类

4.3 用户自定义类

 

4.3.1 Employee类

package class_;

import java.util.Date;
import java.util.GregorianCalendar;

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("Harray Hacler", 70000, 1988, 3, 12);
		staff[2] = new Employee("Tony Taler", 80000, 1982, 3, 15);
		
		for(Employee e: staff)
		{
			e.raiseSalary(10);
			System.out.println("name = " + e.getName() + ", salary = " + e.getSalary() + 
							   ", hireDay = "+ e.getHireDay()
			);
		}	
	}
	
	public static class Employee
	{
		private String name;
		private double salary;
		private Date hireDay;
		
		//构造器要与类同名
		//构造器没有返回值
		//构造器总是伴随着new操作一起调用
		public Employee(String n, double s, int year, int month, int day)
		{
			name = n;
			salary = s;
			GregorianCalendar calender = new GregorianCalendar(year, month - 1, day);
			// 0 表示的是1月,所以在这里使用的是month - 1
			hireDay = calender.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/10;
			salary += raise;
		}
	}
	


}

  

4.3.3 剖析Employee类

  类中包含:

    三个域(field):name、salary和hireDay

    一个构造器(constructor):Employee

    四个方法(method):getName、getSalary、getHireDay、raiseSalary

  类中的方法都是public,意味着任何类的任何方法都可以调用这些方法;

  类中的域都是privtate,确保只有Employee类自身的方法才能够访问这些域,而其他方法不能访问这些域,实现了封装性的目的。

 

4.3.4 从构造器开始

public Employee(String n, double s, int year, int month, int day)
		{
			name = n;
			salary = s;
			GregorianCalendar calender = new GregorianCalendar(year, month - 1, day);
			// 0 表示的是1月,所以在这里使用的是month - 1
			hireDay = calender.getTime();
		}

  构造器与类同名,在构造Employee类对象的时候,构造器运行,从而将实例域初始化。

  例如: 

new Employee("motivated", 2000, 2017, 2, 1)

  会将域设置为:

name = "motivated_Dou";
salary = 2000;
hireDay = February 1, 2017;

  构造器总是伴随着new操作符的执行被调用,不能对于一个已经存在的对象调用构造器达到重新设置实例域的目的。

 

4.3.5 隐式参数和显式参数

  方法用于操作对象以及存取他们的实例域

public void raiseSalary(double byPercent)
		{
			double raise = salary * byPercent/10;
			salary += raise;
		}

  该方法将会调用方法的对象的salary实例域设置为新值。例如: 

kobe.raiseSalary(5);

  这个结果是将kobe.salary域的值增加5%,可以分解为:

double raise = kobe.salary*5/100;
this.salary = salary + raise;

  所以可以看出,raiseSalary方法有两个参数:

    第一个参数是隐式(implicit)函数,是Employee对象kobe;

    第二个参数是位于方法名后面括号中的数值5,这是一个显示(explicit)函数。

  显式参数会出现在方法声明中,例如public void raiseSalary(double byPercent)中的double byPercent参数就是一个显示参数,而隐式参数不会出现在方法声明中。

  在每一个方法中,关键字this表示隐式参数,所以上面的raiseSalary方法也可以写成下面的形式:

public void raiseSalary(double byPercent)
		{
			double raise = this.salary * byPercent/10;
			this.salary += raise;
		}

  这样声明函数的方式,可以讲实例域与局部变量区分开。

 

4.3.6 封装的优点

    public String getName()
		{
			return name;
		}
		
    public double getSalary()
		{
			return salary;
		}
		
    public Date getHireDay()
		{
			return hireDay;
		}

  这些都是典型的访问器方法,由于它们只返回实例域值,因此又称为域访问器

    name被设置为只读域,一旦在构造器中设置完毕,其他方法不能修改,可以防止被外界的破坏。    

    虽然salary不是只读域,但是它只能通过raiseSalary方法来修改,一旦这个域值出现错误,只需要使用raiseSalary就可以对其值进行纠正,如果将salary设置为public,则很多方法都能对其进行修改,这会引起很大的混乱。

  在有些情况下,需要获得或设置实例域的值,在这个时候,需要提供以下三个内容:

    1、一个私有的(private)数据域;

    2、一个公有的域访问器方法;

    3、一个公有的域更改器方法。

 

4.3.7 基于类的访问权限

  方法可以访问所调用对象的私有数据。一个方法可以访问所属类的所有对象的私有数据。  

  

class Employee
{
        ...  
        ...
        public boolean equals(Employee other)
        {
             return name.equals(other.name);        
        }
}    

  在调用这个方法的时候,举例说if(harray.equals(boss))...

  这个方法访问harry的私有域,这是正常的,但是它也会访问boss的私有域,这种操作是合法的,其原因是boss是Employee类对象,而Employee类的方法可以访问Employee类的任何一个对象的私有域。

  

4.3.9 final实例域

  可以将实例域定义为fina。在构建对象的时候必须初始化这样的域,也就是说,必须确保在每一个构造器执行之后,这个域的值被设置,并且在后面的操作中,不能够再对它进行修改。

  在本章的例子中,如果将Employee类中的name域声明为final,则在构造对象之后,这个值不会再被修改,即没有setName方法。

class Employee
{
    private final String name;
    ...      
}

  final修饰符大都应用于基本类型域,或不可变类的域(如果类中的每个方法都不会改变其对象,这种类就是不可变的类)。

  对于可变的类,如果使用final修饰符,就造成混乱。

  private final Date hiredate;

  仅仅意味着存储在hiredate变量中的对象引用在对象构造之后不能被改变,而并不意味着hiredate对象是一个常量。任何方法都可以对hiredate引用的对象调用setTime更改器。

 

4.4 静态域与静态方法

4.4.1 静态域

  如果将域定义为static,每个类中只有一个这样的域,而每一个对象对于所有的实例域却都有自己的一份拷贝。

class Employee
{
      private static int nextId = 1;
      private int id;
      ...  
}

  每个雇员对象都有自己的id域,但所有的类的实例将共享一个nextId域,举例说如果有1000个Employee对象,则有1000个实例域id,但是只有一个静态域nextId。即便没有Employee对象,静态域nextId仍然是存在的。这个静态域nextId属于类,但不属于独立的对象。

 

4.4.2 静态常量

  静态常量的使用比较常见,如:

public class Math
{
    ...
    public static final double PI = 3.1415926;
    ...    
}

  在程序中,可以采用Math.PI的形式获得这个常量。

  如果关键字static被省略,PI就变成了Math类的一个实例域。需要通过Math类的对象访问PI,并且每一个Math对象都有它自己的一份PI拷贝。

4.4.3 静态方法

  静态方法是一种不能向对象实施操作的方法。例如,Math类的pow方法就是一个静态方法。

  Math.pow(x, a)

  用来计算幂函数,在运算的时候,不适用任何Math对象,即没有隐式的参数。

  可以认为静态方法是没有this参数的方法(在非静态方法中,this参数表示这个方法的隐式参数)

  因为静态方法不能操作对象,所以不能在静态方法中访问实例域,但是静态方法可以访问自身的静态域。

  

package class_;

public class StaticTest_4_3 {
	public static void main(String args[])
	{
		Employee[] staff = new Employee[3];
		staff[0] = new Employee("Tom", 40000);
		staff[1] = new Employee("Kobe", 50000);
		staff[2] = new Employee("James", 50000);
	}
}

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;
		nextId++;
	}
	
	public static int getNextId()
	{
		return nextId;
	}	
}

  

 

4.3 用户自定义类