Java核心技术第4章(4)

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());
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值