静态字段与静态方法

静态字段:

如果将字段定义为static,每个类中只有一个这样的字段。
而对于非静态的实例字段,每个对象都有自己的一个副本。

class Employee{
    private static int nextId = 1; // 静态字段nextId
    private int id; // 实例字段id
}

// 此时,每个Employee对象都有一个自己的id域(字段),但是这个类的所有实例将共享一个nextId域
// 比如:有100个Employee类的对象,则有100个实例域id
// 但是,只有一个静态域nextId

即使没有Employee对象,静态域nextId也存在。它属于类,而不属于任何独立对象

在绝大多数面向对象语言中,静态字段被称为类字段。静态只是沿用了C++的叫法,没有实际意义。
static在C中最开始指的是表示退出一个块后依然存在的局部变量,后来,为了表示不能被其他文件访问的全局变量和函数,重用了static。在第三次重用之后,static的含义是属于类且不属于类对象的变量和函数。这个含义与Java相同。

面试题

"static"关键字是什么意思?Java中是否可以覆盖(override)一个private或者是static的方法?

(1)"static"关键字表明一个成员变量或者是成员方法可以在没有所属的类的实例变量的情况下被访问。
(2)Java中static方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的,而static方法是编译时静态绑定的。static方法跟类的任何实例都不相关,所以概念上不适用。
(3)Java中也不可以覆盖private的方法,因为private修饰的变量和方法只能在当前类中使用,如果是其他的类继承当前类是不能访问到private变量或方法的,当然也不能覆盖。

是否可以在static环境中访问非static变量?

(1)static变量在Java中是属于类的,它在所有的实例中的值是一样的。
(2)当类被Java虚拟机载入的时候,会对static变量进行初始化。如果你的代码尝试不用实例来访问非static的变量,编译器会报错,因为这些变量还没有被创建出来,还没有跟任何实例关联上。
总结:因为静态的成员属于类,随着类的加载而加载到静态方法区内存,当类加载时,此时不一定有实例创建,没有实例,就不可以访问非静态的成员。


静态常量:

静态变量使用比较少,静态常量使用较多

public class Math{
      public static final double PI = 3.1415926;  // 在Math类中定义一个静态常量
}
// 在程序中,可以用Math.PI来访问这个常量。
// 如果省略关键字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 PrintStream(...); // Error ~ out is final
// 但是System类中有一个setOut方法可以修改System.out的值。
// 因为它是本地(原生)方法,本地方法可以绕过Java语言的访问控制机制,这是一种特殊的解决方法。

静态方法:

静态方法是不在对象上执行的方法

Math.pow(x, y); 
// 该方法是一个静态方法。运算时不使用任何Math对象。也就是没有隐式的参数。
// 可以认为静态方法是没有this参数的方法。比如this.method(),是一个非静态的方法,this参数指示这个方法的隐式参数。

// Employee类的静态方法不能访问id实例域,因为它不能在对象上执行操作。
// 但是静态方法可以访问自身类中的静态字段
public static int getNextId(){
    return nextId; // return static field
}
// 可以通过类名调用这个方法
int n = Employee.getNextId();
// 这个方法可以省略关键字static,但是需要通过Employee类对象的引用调用这个方法
  • 可以使用对象调用静态方法,但是容易混淆。所以更建议使用类名来调用静态方法

以下两种情况下使用静态方法:

  • 方法不需要访问对象状态,其所需参数都是通过显示参数提供(例如,Math.pow)
  • 一个方法只需要访问类的静态字段(例如,Employee.getNextId)

工厂方法:

  • 静态方法还有另一种常见用途。类似LocalDate和NumberFormat的类使用静态工厂方法(factory method)来构造对象。
// 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类不利用构造器完成这些操作?2个原因
// 1.无法命名构造器。构造器的名字必须与类名相同。但是,这里希望将得到两个不同的名字:货币实例和百分比实例
// 2.当使用构造器时,无法改变所构造的对象类型。而工厂(Factory)方法将返回一个DecimalFormat类对象,这是NumberFormat的子类

main方法:

  • 不需要使用对象调用静态方法。例如,不需要构造Math类对象就可以调用Math.pow
  • 同理,main方法也是一个静态方法。
public class Application{
    public static void main(String[] args){
        // construct objects here
    }
}
  • main方法不对任何对象进行操作
  • 事实上,在启动程序时还没有任何一个对象
  • 静态main方法将执行并创建程序所需要的对象。
  • 每个类可以有一个main方法,这是一个常用于对类进行单元测试的技巧。

笔记内容来自:
Java核心技术卷Ⅰ(第10版)第4章
https://www.nowcoder.com/ta/review-java/review?tpId=31&tqId=21071&query=&asc=true&order=&page=3

posted @ 2021-01-08 01:17  张三丰学Java  阅读(302)  评论(0编辑  收藏  举报