1. 类定义形式
java简单类定义形式,如下:
class ClassName {
field1
field2
...
constructor1
constructor2
...
method1
method2
}
在一个源文件中,只能有一个公有类(public修饰),但可以有任意数目的非公有类。
雇员类示例:
import java.time.*;
/**
* This program tests the Employee class.
* @version 1.12 2015-05-08
* @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 LocalDate hireDay;
public Employee(String n, double s, int year, int month, int day)
{
name = n;
salary = s;
hireDay = LocalDate.of(year, month, day);
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public LocalDate getHireDay()
{
return hireDay;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
}
2. 实例域
private String name;
private double salary;
private LocalDate hireDay;
可以用public标记实例域,但这是一种极为不提倡的做法。public数据域允许程序中的任何方法对其进行读取和修改。这就完全破坏了封装。任何类的任何方法都可以修改public域,从我们的经验来看,某些代码将使用这种存取权限,而这并不是我们所希望的,因此,这里强烈建议将实例域标记为private。
3. 构造器
示例Employee类中的构造函数:
public Employee(String n, double s, int year, int month, int day) {
name = n;
salary = s;
hireDay = LocalDate.of(year, month, day);
}
构造Employee类的对象时,构造器会运行,以便将实例域初始化为所希望的状态。
创建Employee类实例对象示例:
new Employee("Kay", 10000, 1989, 1, 1);
注意:不能对一个已经存在的对象调用构造器来达到重新设置实例域的目的。
构造器特征
- 构造器与类同名
- 每个类可以有一个以上的构造器
- 构造器可以有0个、1个或多个参数
- 构造器没有返回值
- 构造器总是伴随着new操作一起调用
4. 隐式参数与显示参数
方法用于操作对象以及存取它们的实例域。
示例Employee类中的加薪方法:
public void raiseSalary(double byPercent) {
double raise = salary * byPercent / 100;
salary += raise;
}
raiseSalary方法有两个参数。第一个参数成为隐式(implicit)参数【方法调用的目标或接收者】,是出现在方法名前的Employee类对象。第二个参数位于方法名后面括号中的数值,这是一个显示(explicit)参数。
显示参数:double byPercent
隐式参数:通常用this关键字表示,实例double raise = this.salary * byPercent / 100;
5. 封装
通过封装public修饰的get(访问器方法,又称域访问器,boolean值用isXXX方法)、set(更改器方法)方法将private域暴露给外部获取和修改值。
方法示例:
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
public LocalDate getHireDay() {
return hireDay;
}
如果需要获得或设置实例域的值,应该提供一下三项内容:
- 一个私有的数据域;
- 一个公有的域访问器方法;
- 一个公有的域更改器方法。
封装实例域的get、set方法比简单的提供一个公有数据域更复杂,但有如下好处:
- 可以改变内部实现,除该类的方法外,不会影响其他代码。
例如,如果将存储名字的域改为:
String firstName;
String lastName;
那么getName方法可以改为返回:firstName + " " + lastName;
对于这点改变,程序的其他部分完全不可见。
- 可以在访问器方法和更改器方法中增加一些控制,例如可以在更改器方法中执行错误检查。
!警告
注意不要编写返回引用可变对象的访问器方法。在Employee类中就违反了这个设计原则,其中的getHireDay方法返回了一个Date类对象:class Employee { private Date hireDay; ... public Date getHireDay() { return hireDay;//Bad } ... }
LocalDate类没有更改器方法,与之不同,Date类有一个更改器方法setTime,可以再这里设置毫秒数。
Date对象是可变的,这一点就破坏了封装性!
请看下面这段代码:Employee harry = ...; Date d = harry.getHireDay(); double tenYearsInMilliSeconds = 10 * 365.25 * 24 * 60 * 60 * 1000; d.setTime(d.getTime() - (long) tenYearsInMilliSeconds); //let's give Harry ten years of added seniority
出错的原因很微妙。d和harry.hireDay引用同一个对象。对d调用更改器方法就可以自动地改变这个雇员对象的私有状态!
访问器返回值引用可变对象
如果需要返回一个可变对象的引用,应该首先对它进行克隆(clone)。对象clone是指存放在另一个位置上的对象副本。调整后代码如下:
class Employee { private Date hireDay; ... public Date getHireDay() { return (Date)hireDay.clone();//Ok } ... }
如果需要返回一个可变数据域的靠背,就应该使用clone。
6. 类的属性和方法访问权限
权限 | 类内 | 同包 | 不同包子类 | 不同包非子类 |
---|---|---|---|---|
private | √ | × | × | × |
default | √ | √ | × | × |
protected | √ | √ | √ | × |
public | √ | √ | √ | √ |
一个方法可以访问所属类的所有对象的私有数据
只要方法是私有的,类的设计者就可以确信:它不会被外部的其他类操作调用,可以将其删除。如果方法是公有的,就不能将其删除,因为其他的代码很可以依赖他。
7. final实例域
被final修饰的实例域,构建对象时必须初始化此域,即必须确保在每一个构造器执行后,这个域的值被设置,并且之后不再对其进行修改。
final修饰符通常用于基本(primitive)类型域或不可变(immutable)类的域(如果类中的每个方法都不会改变其对象,这种类就是不可变的类,如:String类)。
final修饰对象实例域时,则对象不可再引用其他实例对象,但是实例本身是可以被改变的。
网友评论