静态域与静态方法
静态域
如果将域定义为 static, 每个类中只有一个这样的域。而每一个对象对于所有的实例域 却都有自己的一份拷贝。例如, 假定需要给每一个雇员賦予唯一的标识码。这里给 Employee 类添加一个实例域 id 和一个静态域 nextld:
class Employee { private static int nextld = 1 ; private int id; ... }
现在, 每一个雇员对象都有一个自己的 id 域, 但这个类的所有实例将共享一个 iiextld 域。
静态常量
静态变量使用得比较少,但静态常量却使用得比较多。例如, 在 Math类中定义了一个 静态常量:
public class Hath { ... public static final double PI = 3.14159265358979323846; ... }
如果关键字 static 被省略, PI 就变成了 Math 类的一个实例域。需要通过 Math类的对象 访问 PI,并且每一个 Math 对象都有它自己的一份 PI 拷贝。 另一个多次使用的静态常量是 System.out。它在 System 类中声明:
public class System { ... public static final PrintStream out = . . .; ... }
由于每个类对象都可以对公有域进行修改,所以,最好不要将域设计 为 public。然而, 公有常量(即 final 域)却没问题。因为 out 被声明为 final, 所以,不允许 再将其他打印流陚给它:
System.out = new PrintStrean(. . . ); // Error out is final
System.out的源码
public static final PrintStream out = null;
public static void setOut(PrintStream out) { checkIO(); setOut0(out); }
静态方法
静态方法是一种不能向对象实施操作的方法。例如, Math类的 pow 方法就是一个静态方法。表达式
Math.pow(x, a)
计算幂 x^a 在运算时, 不使用任何 Math 对象。换句话说,没有隐式的参数。
可以认为静态方法是没有 this 参数的方法(在一个非静态的方法中,this参数表示这个方法的隐式参数
public static int getNextld(){ return nextld; // returns static field }
在下面两种情况下使用静态方法:
•一 方法不需要访问对象状态,其所需参数都是通过显式参数提供(例如: Math.pow) 。
•一个方法只需要访问类的静态域(例如:Employee.getNextldh)
工厂方法
已经见过工厂方法 LocalDate.now 和 LocalDate.of;
NumberFormat 类如下使用工厂方法生成不同风格的格式化对象:
NumberFormat currencyFormatter = NumberFormat.getCurrencylnstance(); NumberFormat percentFormatter = NumberFormat.getPercentlnstance(); double x = 0.1 ; System.out.println(currencyFormatter.format(x)); // prints SO.10 System.out.println(percentFomatter.format(x)); // prints 10%
为什么 NumberFormat 类不利用构造器完成这些操作呢? 这主要有两个原因:
•无法命名构造器。构造器的名字必须与类名相同。但是, 这里希望将得到的货币实例 和百分比实例采用不用的名字。
•当使用构造器时,无法改变所构造的对象类型。而 Factory方法将返回一个 DecimalFormat 类对象,这是 NumberFormat 的子类。
main方法
main方法也是一个静态方法。
public class Application { public static void main(String[] args) { // construct objects here . . . } }
main方法不对任何对象进行操作。事实上,在启动程序时还没有任何一个对象。静态的 main方法将执行并创建程序所需要的对象。
Employee 类的一个简单版本, 其中有一个静态域 nextld 和一个静态方法 getNextld 这里将5个 Employee 对象写人数组, 然后打印雇员信息。最后, 打印出下一个可用的员T标识码来展示静态方法。
/** * This program demonstrates static methods. * @version 1.01 2004-02-19 * @author Cay Horstmann */ public class StaticTest { public static void main(String[] args) { // fill the staff array with three Employee objects Employee[] staff = new Employee[3]; staff[0] = new Employee("Tom", 40000); staff[1] = new Employee("Dick", 60000); staff[2] = new Employee("Harry", 65000); // print out information about all Employee objects for (Employee e : staff) { e.setId(); System.out.println("name=" + e.getName() + ",id=" + e.getId() + ",salary=" + e.getSalary()); } int n = Employee.getNextId(); // calls static method System.out.println("Next available id=" + n); } } 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; // set id to next available id nextId++; } public static int getNextId() { return nextId; // returns static field } public static void main(String[] args) // unit test { Employee e = new Employee("Harry", 50000); System.out.println(e.getName() + " " + e.getSalary()); } }
需要注意,Employee 类也有一个静态的 main 方法用于单元测试。试试运行
java Employee 和 java StaticTest
执行两个 main方法。
方法参数
按 值调用(call by value) 表示方法接收的是调用者提供的值。而按引用调用 (call by reference) 表示方法接收的是调用者提供的变量地址。一个方法可以修改传递引用所对应的变量值,而 不能修改传递值调用所对应的变量值。
Java 程序设计语言总是采用按值调用。也就是说, 方法得到的是所有参数值的一个拷 贝,特别是,方法不能修改传递给它的任何参数变量的内容。 例如, 考虑下面的调用:
double percent = 10; harry.raiseSalary(percent);
不必理睬这个方法的具体实现, 在方法调用之后, percent 的值还是 10。
如果 Java 对对象采用的是按引用调用,那么这个方法就应该能够实现交换数据的效果:
Employee a = new Employee("Alice", .. .); Employee b = new Employee("Bob", . ..); swap(a, b); // does a now refer to Bob, b to Alice?
但是,方法并没有改变存储在变量 a 和 b 中的对象引用。swap 方法的参数 x 和 y 被初始 化为两个对象引用的拷贝,这个方法交换的是这两个拷贝。
下面总结一下 Java中方法参数的使用情况:
•一个方法不能修改一个基本数据类型的参数(即数值型或布尔型) 。
•一个方法可以改变一个对象参数的状态。
•一个方法不能让对象参数引用一个新的对象。
/** * This program demonstrates parameter passing in Java. * @version 1.00 2000-01-27 * @author Cay Horstmann */ public class ParamTest { public static void main(String[] args) { /* * Test 1: Methods can't modify numeric parameters */ System.out.println("Testing tripleValue:"); double percent = 10; System.out.println("Before: percent=" + percent); tripleValue(percent); System.out.println("After: percent=" + percent); /* * Test 2: Methods can change the state of object parameters */ System.out.println("\nTesting tripleSalary:"); Employee harry = new Employee("Harry", 50000); System.out.println("Before: salary=" + harry.getSalary()); tripleSalary(harry); System.out.println("After: salary=" + harry.getSalary()); /* * Test 3: Methods can't attach new objects to object parameters */ System.out.println("\nTesting swap:"); Employee a = new Employee("Alice", 70000); Employee b = new Employee("Bob", 60000); System.out.println("Before: a=" + a.getName()); System.out.println("Before: b=" + b.getName()); swap(a, b); System.out.println("After: a=" + a.getName()); System.out.println("After: b=" + b.getName()); } public static void tripleValue(double x) // doesn't work { x = 3 * x; System.out.println("End of method: x=" + x); } public static void tripleSalary(Employee x) // works { x.raiseSalary(200); System.out.println("End of method: salary=" + x.getSalary()); } public static void swap(Employee x, Employee y) { Employee temp = x; x = y; y = temp; System.out.println("End of method: x=" + x.getName()); System.out.println("End of method: y=" + y.getName()); } } class Employee // simplified Employee class { private String name; private double salary; public Employee(String n, double s) { name = n; salary = s; } public String getName() { return name; } public double getSalary() { return salary; } public void raiseSalary(double byPercent) { double raise = salary * byPercent / 100; salary += raise; } }