4.4 静态字段与静态方法

4.4 静态字段与静态方法

之前的程序中,main方法被标记了static。这节就学习这个修饰符。

4.4.1 静态字段

将一个字段定义成static,这个字段在这个类中只有一份。对于非static的实例字段,每个对象都有自己的一个副本。

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

上面的代码,每个对象都有一个自己的id字段。但这个类所有实例共享一个nextId字段。
通俗一点,有1000个Employee对象就有1000个id。但,只有一个静态字段nextId。
即使没有Employee对象,静态字段nextId也是存在的。它属于类,不属于任何单个对象。

public void setId(){
	id=nextId;
	nextId++;
}

现在为harry设置员工标识码:

harry.setId();

harry的id字段被设置为静态字段nextId当前的值,并且静态字段nextI的值+1。

harry.id=Employee.nextId;
Employee.nextId++;

4.4.2 静态常量

静态常量相当的常用。

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

在程序中,可以使用Math.PI来访问常量。

我加一句:静态字段是属于类的,实例字段是对象的。(静态就是类的,实例就是对象的)

上面的代码中,如果去掉static,PI就是属于对象的实例字段。就需要通过Math的一个对象来访问。

已经多次使用的另一个静态变量是System.out。它在System类中的声明如下:

public class System{
	...
	public static final PrintStream out=...;
	...
}

final意味着不能再被改变。

特别地,System类中有一个setOut()方法可以将System.out设置为不同的流。这个方法竟然可以修改final修饰的东西。原因是,它是一个原生方法,不是在java语言中实现的。原生方法可以绕过java语言的访问控制机制。这是一种特殊的解决方法。

4.4.3 静态方法

比如,

Math.pow(x,a);
//计算x的a次方

这样的方法不使用任何Math的对象。
静态方法不能访问实例字段,因为它不能在对象上执行操作。但是静态方法可以访问静态字段。示例:

public static int getNextId(){
	return nextId;
}

可以提供类名来调用静态方法:

int n= Employee.getNextId();

对象也是可以调用静态方法的。但是,这样做很容易产生混淆。因为静态方法与任何一个对象都没有关系。建议最好使用类名来调用静态方法。

什么时候该定义静态方法呢?

  1. 这个方法不需要访问对象的状态。它所需要的参数都显示提供。
  2. 这个方法只需要访问类的静态字段。

我的理解:就是只要与对象无关的方法定义为静态方法。

4.4.4 工厂方法

静态方法的另一种常见用途就是静态工厂方法。LocalDate和NumberFormat类都是使用静态工厂方法来构造对象的。
如:LocalDate.now()和LocalDate.of()。

NumberFormat currencyFormatter=NumberFormat.getCurrencyInstance();
NumberFormat percentFormatter=NumberFormat.getPercentInstance();
double x=0.1;
System.out.println(currencyFormatter.format(x));//0.10
System.out.println(percentFormatter.format(x));//10%

为什么NumberFormat类不利用构造器完成这些操作呢?

  • 无法命名构造器。构造器的名字必须和类名相同。但是,这里希望有两个不同的名字。分别去得到货币实例和百分比实例。
  • 使用构造器无法改变所构造对象的类型。工厂方法实际上将返回DecimalFormat类的对象,这是NumbetFormat的一个子类。

4.4.5 main方法

main方法不对任何对象进行操作。事实上,程序启动时还没有任何对象。静态的main方法将执行并构造程序所需要的对象。
每个类可以有一个main方法。这是对类进行单元测试的技巧。
程序代码:

public class StaticTest {
    public static void main(String[] args) {
        var staff=new Employee[3];
        staff[0]=new Employee("tom", 40000);
        staff[1]=new Employee("dick", 60000);
        staff[2]=new Employee("harry", 65000);
        for (Employee e:staff){
            e.setId();
            System.out.println("name="+e.getName()+","+"id="+e.getId()+","+"salary="+e.getSalary());
        }

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

    public static void main(String[] args) {//unit test
        var e= new Employee("harry", 50000);
        System.out.println(e.getName()+" "+e.getSalary());
    }
}


java.util.Objects中的一些方法:

static <T> void requireNonNull(T obj)
static <T> void requireNonNull(T obj,String message)
static <T> void requireNonNull(T obj,Supplier<String> messageSupplier)
//如果obj为null,这些方法会抛出一个NullPointerException异常,而没有消息或者给定的消息。
//第八章(泛型程序设计)会解释<T>的语法。

static <T> T requireNonNullElse(T obj,T defaultObj)
static <T> T requireNonNullElseGet(T obj,Supplier<T> defaultSupplier)
//如果obj不为null就返回obj,如果obj为null就返回默认对象。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值