• 零基础学Java(12)静态字段与静态方法


    静态字段与静态方法

      之前我们都定义的main方法都被标记了static修饰符,那到底是什么意思?下面我们来看看
     

    静态字段

      如果将一个字段定义为static,每个类只有一个这样的字段。而对于非静态的实例字段,每个对象都有自己的一个副本。例如,假设需要给每一个员工赋予唯一的标识码。这里给Employee类添加一个实例字段id和一个静态字段nextId

    highlighter- Java
    class Employee {
        // 定义静态字段nextId
        private static int nextId = 1;
        private int id;
    }

      现在,每一个Employee对象都有一个自己的id字段,但这个类的所有实例将共享一个nextId字段。换句话说,如果有1000个Employee类对象,则有1000个实例字段id,分别对应每一个对象。但是,只有一个静态字段nextId。即使没有Employee对象,静态字段nextId也存在。它属与类,而不属于任何单个的对象。
      下面实现一个简单的方法:

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

      假定为harry设置员工标识码:

    highlighter-
    harry.setId();

      harry的id字段被设置为静态字段nextId当前的值,并且静态字段nextId的值加1:

    highlighter- Python
    harry.id = Employee.nextId;
    Employee.nextId++

     

    静态常量

      静态变量使用的比较少,但静态常量却很常用。例如,在Math类中定义一个静态常量:

    highlighter- Java
    public class Math {
        ...
        public static final double PI = 3.14159265358979323846;
        ...
    }

      在程序中,可以用Math.PI来访问这个常量。
      如果省略关键字staticPI就变成了Math类的一个实例字段。也就是说,需要通过Math类的一个对象来访问PI,并且每一个Math对象都有它自己的一个PI副本。
      你已经多次使用的另一个静态常量是System.out。它在System类中声明如下:

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

      前面曾经多次提到过,由于每个类对象都可以修改公共字段,所以,最好不要有公共字段。然而,公共常量(即final字段)却没问题。因为out被声明为final,所以,不允许再将它重新赋值为另一个打印流:

    highlighter- Java
    System.out = new PrintStream(...);  // ERROR -- out is final

     

    静态方法

      静态方法是不在对象上执行的方法。例如,Math类的pow方法就是一个静态方法。表达式Math.pow(x, a)会计算幂x的a次方。在完成运算时,它并不使用任何Math对象。换句话说,它没有隐式参数。
      可以认为静态方法是没有this参数的方法(在一个非静态的方法中,this参数指示这个方法的隐式参数)
      Employee类的静态方法不能访问id实例字段,因为它不能在对象上执行操作。但是,静态方法可以访问静态字段。下面是这样一个静态方法的示例:

    highlighter- Java
    public static int getNextId() {
        return nextId;  // returns static field
    }

      可以提供类名来调用这个方法:

    highlighter- Python
    int n = Employee.getNextId();

      这个方法可以省略关键字static吗?答案是肯定的。但是,这样一来,你就需要通过Employee类对象的引用来调用这个方法。

    注意:可以使用对象调用静态方法,这是合法的。例如,如果harry是一个Employee对象,可以用harry.getNextId()代替Employee.getNextId()。不过,这种写法很容易造成混淆,其原因是getNextId方法计算的结果与harry毫无关系。我们建议使用类名而不是对象来调用静态方法。

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

    • 方法不需要访问对象状态,因为它需要的所有参数都通过显式参数提供(例如:Math.pow)
    • 方法只需要访问类的静态字段(例如:Employee.getNextId)。

     

    工厂方法

      静态方法还有另外一种常见的用途。类似LocalDateNumberFormat的类使用静态工厂方法来构造对象。我们之前使用过工厂方法LocalDate.nowLocalDate.ofNumberFormat类如下生成不同风格的格式化对象:

    highlighter- Java
    NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance();
    NumberFormat percentFormatter = NumberFormat.getPercentInstance();
    double x = 0.1;
    System.out.println(currencyFormatter.format(x));  // prints $0.10
    System.out.printIn(percentFormatter.format(x));  // prints 10%

      为什么NumberFormat类不利用构造器完成这些操作呢?这主要有两个原因:

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

    main方法

      需要注意,可以调用静态方法而不需要任何对象。例如,不需要构造Math类的任何对象就可以调用Math.pow
      同理,main方法也是一个静态方法。

    highlighter- Java
    public class Application {
        public static void main(String[] args) {
            // construct objects here
            ...
        }
    }

      main方法不对任何对象进行操作。事实上,在启动程序时还没有任何对象。静态的main方法将执行并构造程序所需要的对象。

      提示:每一个类可以由一个main方法。这是常用于对类进行单元测试的一个技巧
     

    例子

      接下来我们创建Employee类,其中有一个静态字段nextId和一个静态方法getNextId。这里将三个Employee对象填入一个数组,然后打印员工信息。最后,打印出下一个可用的员工标识码来展示静态方法。
     

    cnb-over-length-code-block cnb-over-length-code-block-expanded highlighter- Java
    // 文件StaticTest.java
    
    
    public class StaticTest {
        public static void main(String[] args) {
            System.out.println("1111");
            Employee[] 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 {
        // 静态字段nextId
        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) {
            Employee e = new Employee("Harry", 5000);
            System.out.println(e.getName() + " " + e.getSalary());
        }
    }
    折叠 

      这里我们定义了2个类StaticTestEmployee,这两个类分别有一个main函数
      执行命令以下命令

    highlighter-
    java Employee

      结果如下:

    highlighter-
    Harry 5000.0

      当我们执行

    highlighter-
    java StaticTest

      结果如下:

    highlighter- Python
    name=Tom, id=1, salary=40000.0
    name=Dick, id=2, salary=60000.0
    name=Harry, id=3, salary=65000.0
    Next available id=4

      两者会分别执行各自的main方法


    __EOF__

  • 本文作者: Silent丿丶黑羽
  • 本文链接: https://www.cnblogs.com/jiakecong/p/16442792.html
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。
  • 相关阅读:
    A-Level数学例题解析及练习Natural logarithms
    vue项目中页面跳转传参的方法
    纺织企业如何选好网关实现数据采集和远程监控?
    【数据结构】排序6——桶排序、基数排序、计数排序
    编程语言介绍
    HashMap自动扩容机制源码详解
    要想后期修改少,代码重构要趁早
    图扑数字孪生屋顶光伏,推进光伏建筑一体化监管
    单例模式设计
    (十一)Python模块和包
  • 原文地址:https://www.cnblogs.com/jiakecong/p/16442792.html