静态域与静态方法

静态域
静态修饰符为static。
如果一个域被定义为静态域,那么一个类中只能有一个静态域。而每一个对象的所有实例域都有自己的一份拷贝。例如:

class Employee
{
	private static int nextid=1;
	private int id;
}

我们现在创建100个Employee类的对象,那么对于这100个Employee类的对象,则有100个实例域id。它们一一对应。但是,它们共享一个静态域nextid。即使,没有一个Employee对象,静态域nextid也存在。他属于类,但是不属于任何一个对象。

静态常量
静态变量使用的比较少,一般来说,都是使用静态常量。比如说,Math中的PI

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

所以,我们才能在程序中使用Math.PI的形式调用这个常量。
另一个我们经常使用的静态常量是System.out。它在System类中的声明:

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

之前说过,由于每个类的对象都可以对于公有域进行修改。所以,域尽量不要声明为public。但是,公有常量(即final域)没有问题。因为out被final修饰,不允许将其他打印流赋给它。

静态方法
只有静态才能调用静态。这是一定要注意的。
静态方法是一种不能向对象实施操作的方法。例如,Math的pow方法。
我们可以认为静态方法是没有this参数的方法(在一个非静态方法中,this参数表示这个方法的隐式参数.)
Employee类中的静态方法不能访问Id实例域,因为它不能操作对象。但是,静态方法可以访问自身类中的静态域。例如:

public static int getNextId(){
	return nextId;
}

可以通过类名调用这个方法。

int n=Employee.getNextId();

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

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

main方法
需要注意,不需要使用对象调用静态方法。main方法也是个静态方法。main方法不对任何对象进行操作。事实上,在启动程序的时候还没任何一个对象。静态的main方法将执行并创建程序所需要的对象。每一个类可以有一个main方法。这是一个常用于对类进行单元测试的技巧。

我们写一个程序,体会一下静态的使用。

public class test1 {
    public static void main(String[] args) {
        Employee[] staff = new Employee[3];
        staff[0] = new Employee("aaa", 500);
        staff[1] = new Employee("bbb", 200);
        staff[2] = new Employee("ccc", 400);
        for (Employee employee : staff) {
            employee.setId();
            System.out.println(employee.getName()+"  "+employee.getId()+"  "+employee.getSalary());
        }
        int i = Employee.getnextid();
        System.out.println(i);
    }
}
public class Employee {
    private static int nextid=1;
    private String name;
    private  int id ;
    private double salary;

    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }
    
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getId() {
        return id;
    }

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

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public static int getnextid(){
        return nextid;
    }

    public static void main(String[] args) {
        Employee harry = new Employee("harry", 500);
        System.out.println(harry.getName()+"   "+harry.salary);
    }
}

方法参数

在程序设计语言当中,有关参数传递给方法的方法有两类。其中,Java用的是按值调用,表示方法接收的是调用者提供的值。也就是说,方法得到的是所有参数值的一个拷贝 ,特别的是,方法不能修改传递给它的任何参数变量内容。例如:
double percent=10;
harry.raiseSalary(percent);
在方法调用之后,percent的值还是10。
我们来剖析一下这种情况。

public static void test(){
        x=3*x;
    }

调用这个方法:
test(percent);
调用之后,percent的值还是10。
下面看一下具体执行:

  • x被初始化为percent值的一个拷贝。(10)
  • x被乘3后等于30.但是,percent还是10,拷贝的x为30。
  • 这个方法结束之后,参数变量x不再使用,被舍弃。

然而,方法参数的基本类型有两种:

  • 基本数据类型
  • 对象引用

从以上看出,一个方法不可能修改一个基本数据类型。但是,对象引用作为参数就不一样。例如:

public static void test(Employee x) {
        x.raiseSalary(200);
    }

harry=new Employee (“aaaa”,120);
test(harry);
调用之后,一个雇员的薪金就提高了两倍。
具体执行过程为:

  • x被初始化为harry值的拷贝,这里是一个对象的引用。
  • raiseSalary方法应用用于这个对象的引用。x和harry同时引用的那个Employee 对象的薪金提高了200%。
  • 方法结束后,x被抛弃。但是,harry继续引用那个薪金增至3倍的雇员对象。

从上面可以看出,Java程序设计语言对对象采用的不是引用调用,事实上,对象引用是按值传递的。
我们总计一下Java中方法参数的使用情况:

  • 一个方法不能修改一个基本数据类型的参数。(数值型和布尔型)
  • 一个方法可以改变一个对象参数的状态
  • 一个方法不能让对象参数引用一个新的对象

我们以一个程序来总结一下:

/**
 * @Author: 13679
 * @CreateTime: 2019-10-26 16:12
 */
public class test0 {
    public static void main(String[] args) {
        System.out.println("Testing tripleValue:");
        double percent=10;
        System.out.println("before percent is "+percent);
        tripleValue(percent);
        System.out.println("after percent is " + percent);

        System.out.println();
        System.out.println("----------------------");
        System.out.println("testing tripleSalary:");
        Employee aaa = new Employee("aaa", 500);
        System.out.println("before salary is "+aaa.getSalary());
        tripleSalary(aaa);
        System.out.println("after salary is " + aaa.getSalary());

        System.out.println("");
        System.out.println("-----------------------");
        System.out.println("testing swap:");
        Employee bbb = new Employee("bbb", 999);
        Employee ccc = new Employee("ccc", 888);
        System.out.println("before bbb is name "+bbb.getName());
        System.out.println("before ccc is name " + ccc.getName());
        System.out.println();
        swap(bbb,ccc);
        System.out.println();
        System.out.println("after bbb is name " + bbb.getName());
        System.out.println("after ccc is name " + ccc.getName());
    }

    public static void tripleValue(double x){
        x=3*x;
        System.out.println("End of method:x="+x);
    }
    public static void tripleSalary(Employee x){
        x.raiseSalary(200);
        System.out.println("End of method: salary="+x.getSalary());
    }
    public static void swap(Employee x,Employee y){
        Employee z=x;
        x=y;
        y=z;
        System.out.println("End of method:x= " + x.getName());
        System.out.println("End of method:y= " + y.getName());

    }

}
class Employee{
    private String name;
    private double salary;

    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

    public double getSalary() {
        return salary;
    }

    public void raiseSalary(double x){
        double raise=salary*x/100;
        salary+=raise;
    }
}

.