Java核心技术第4章(4)
2015-10-05 15:55
567 查看
4.4 静态域与静态方法
在前面给出的示例程序中,main方法都被标记为 static 修饰符.下面讨论这个修饰符的含义.4.4.1 静态域
如果将域定义为 static,每个类中只有一个这样的域.而每一个对象对于所有的实例域却都有自己的一份拷贝.例如,假定需要给每一个Employee赋予唯一的标识符.这里给Employee类添加一个实例域id和一个静态域nextId:class Employee { private static int nextId = 1; private int id; ... }现在,每一个Employee对象都有一个自己的id域,但这个类的所有实例都将共享一个nextId域.换句话说,如果有1000个Employee类的对象,则有1000个实例域id,但是只有一个静态域nextId.即使没有一个Employee对象,静态域nextId也存在.它属于类,而不属于任何独立的对象.
注释:在绝大多数的面向对象程序设计语言中,静态域被称为类域.
下面实现一个简单的方法:
public void setId() { id = nextId; nextId++; }假定为harry设置Employee标识码:
harry.setId();harry的id域被设置为静态域nextId当前的值,并且静态域nextId的值加1:
harry.id = Employee.nextId; Employee.nextId++;
4.4.2 静态常量
静态变量使用的比较少,但静态常量却使用的比较多.例如,在Math类中定义了一个静态常量:public class Math { ... public static final double PI = 3.14159265358979; ... }在程序中,可以采用Math.PI的形式获得这个常量.
如果关键字 static 被省略,PI就变成了Math类的一个实例域.需要通过Math类的对象访问PI,并且每一个Math对象都有它自己的一份PI拷贝.
另一个多次使用的静态常量是System.out,它在System类中声明:
public class System { ... public static final PrintStream out = ...; ... }每个类对象都可以对公有域进行修改,所以,最好不要将域设计为 public,然而,公有常量(即 final 域)却没有问题.因为out被声明为 final,所以,不允许再将其他打印流赋给它.
Syste.out = new PrintStream(...); // ERROR, out is final
4.4.3 静态方法
静态方法是一种不能面向对象实施操作的方法.例如,Math类的pow方法就是一个静态方法.表达式Math.pow(x, a)计算x的a次幂.运算时,不使用任何Math对象.换句话说,没有隐式的参数.
可以认为静态方法是没有 this 参数的方法(在一个非静态的方法中,this 参数表示这个方法的隐式参数).
因为静态方法不能操作对象,所以不能再静态方法中访问实例域.但是,静态方法可以访问自身类中的静态域.下面是使用这种静态方法的一个示例:
public static int getNextId() { return nextId; // return static field }可以通过类名调用这个方法:
int n = Employee.getNextId();这个方法可以省略关键字 static 吗?答案是肯定的.但是需要通过Employee类对象的引用调用这个方法.
注释:可以使用对象调用静态方法.但是很容易造成混淆,其原因是静态方法的结算与对象毫无关系.最好使用类名而不是对象来调用静态方法.
在下面两种情况下使用静态方法:
一个方法不需要访问对象状态,其所需参数都是通过显式参数提供(例如Math.pow)
一个方法只需要访问类的静态域(例如Employee.getNextId)
注释:Java中的静态域与静态方法在功能上与C++相同.但是语法书写上却不同.在C++中使用::操作符访问自身作用域之外的静态域和静态访问,例如Math::PI .
术语"static"有一段不寻常的历史.起初,C引入关键字 static 是为了表示退出一个块后依然存在的局部变量.在这种情况下,术语"static"是由意义的:变量一直存在,当再次进入该块时仍然存在.随后,static 在C中有了第二种含义,表示不能被其他文件访问的全局变量和函数.为了避免引入一个新的关键字,关键字 static 被重用了.最后,C++第三次重用了这个关键字,与前面赋予的含义完全不一样,这里将其解释为:属于类且不属于类对象的变量和函数,这个含义与Java相同.
4.4.4 工厂方法
静态方法还有一种常见的用途,NumberFormat类使用工厂方法产生不同风格的格式对象.NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance(); NumberFormat percentFormatter = NumberFormat.getPercentInstance(); double x = 0.1; System.out.println(currencyFormatter.format(x)); // prints $0.10 System.out.println(percentFormatter.format(x)); // prints 10%为什么NumberFormat类不利用构造器完成这些操作呢?这主要有两个原因:
无法命名构造器.构造器的名字必须与类名相同.但是,这里希望将得到的货币实例和百分比实例采用不同的名字.
当使用构造器时,无法改变所构造的对象类型.而Factory方法将返回一个DecimalFormat类对象,这是NumberFormat的子类.
4.4.5 main方法
需要注意,不需要使用对象调用静态方法.例如,不需要构造Math类对象就可以调用Math.pow.同理,main方法也是一个静态方法.
public class Application { public static void main(String[] args) { // construct objects here ... } }main方法不对任何对象进行操作.事实上,在启动程序时还没有任何一个对象,静态的main方法将执行并创建程序所需要的对象.
提示:每一个类可以有一个main方法.这是一个常用于对类进行单元测试的技巧.例如,可以在Employee类中添加一个main方法:
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); hireDay = calendar.getTime(); } ... public static void main(String[] args) // unit test { Employee e = new Employee("Romeo", 50000, 2000, 1, 31); e.raiseSalary(10); System.our.println(e.getName() + " " + e.getSalary()); } ... }如果想要独立地测试Employee类,只需要执行
java Employee如果Employee类是一个大型应用程序的一部分,就可以使用下面这条语句运行程序
java Application并且Employee类的main方法永远不会执行.
示例程序StaticTest中的Employee类也有一个静态的main方法用于单元测试.程序staticTest.java如下所示:
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", 4000); staff[1] = new Employee("Jerry", 5000); staff[2] = new Employee("Harry", 6000); // 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 int getId() { return id; } public double getSalary() { return salary; } public void setId() { id = nextId; // set id to next available id nextId++; } public static int getNextId() { return nextId; } public static void main(String[] args) // unix test { Employee e = new Employee("harry", 5000); System.out.println(e.getName() + " " + e.getSalary()); } }
相关文章推荐
- Java数据库操作
- Java数据库操作
- 冒泡排序算法(Java)
- Java:对象的强、软、弱和虚引用
- java泛型,原始类型和桥方法
- 用两个栈实现一个队列 JAVA实现
- Java并发编程(一)线程定义、状态和属性
- Java并发编程(一)线程定义、状态和属性
- Java+Windows+ffmpeg实现视频剪切
- java反射机制及简单工厂模式
- Spring整合Shiro做权限控制模块详细案例分析
- (转)java反射机制及简单工厂模式
- java 二维数组 杨辉三角
- Java编程:定义功能去除ArrayList中的重复元素
- java 二维数组 99乘法表
- Java基础知识强化之集合框架笔记33:Arrays工具类中asList()方法的使用
- Java基础知识强化之集合框架笔记32:集合之可变参数的概述和使用
- myeclipse 删除不再使用的工作空间记录
- 线程缓存的探索
- JAVA反射机制理解