static关键字

主要用法

static主要有四种用法:

  • 用来修饰成员变量,将其变为类的成员,从而实现该成员对于所有对象的共享
  • 用来修饰成员方法,将其变为类方法,可以直接使用类名.方法名的方式调用,常用于工具类(Math.max());
  • 静态块用法,将多个类成员放在一起初始化,使得程序更加规整,其中理解对象的初始化过程非常关键;
  • 静态导包用法,将类的方法直接导入到当前类中,从而直接使用“方法名”即可调用类方法,更加方便。

静态变量与实例变量的区别

  • 语法上的区别:静态变量前面需要加static关键字,而实例变量不需要。
  • 实例变量属于某个对象的属性必须创建了实例对象,其中的实例变量才会被分配空间,才能使用这个实例变量。
  • 静态变量不属于某个实例对象,而是属于整个类只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,静态变量就可以被使用了。
public class Solution {
    static String solution = "chenzf";

    public static void main(String[] args) {
        System.out.println(Solution.solution);
        
        // 不建议使用该方式访问静态变量
        Solution result = new Solution();
        System.out.println(result.solution);
        
        solution = "chenzufeng";
        System.out.println(Solution.solution);
    }
}

/*
chenzf
chenzf
chenzufeng
 */

修饰成员变量

static变量在Java中是属于类的,它在所有的实例中的值是一样的。当类被Java虚拟机载入的时候,会对static变量进行初始化。而如果代码尝试不用实例,来访问非static的变量,编译器会报错,因为这些变量还没有被创建出来,还没有跟任何实例关联上。执行new来创建对象时,数据存储空间才被分配,其方法才供外界调用。

public class StaticTest
{
    static int i = 47;

    public static void main(String[] args)
    {
        StaticTest st1 = new StaticTest();
        StaticTest st2 = new StaticTest();

        System.out.println("st1: " + st1 + " st2: " + st2);
        // st1: Object.StaticTest@3b07d329 st2: Object.StaticTest@41629346
        
        System.out.println("st1:" + st1.i + " st2:" + st2.i);  // st1:47 st2:47
	System.out.println(StaticTest.i);  // 47
    }
}

即使创建了两个StaticTest对象,StaticTest.i也只有一份存储空间,这两个对象共享同一个i。因此,st1.ist2.i指向同一存储空间。

引用static变量有两种方法:可以通过一个对象去定位它,如st1.i。也可以通过其类名直接引用(StaticTest.i),这对于非静态成员则不行。使用类名是引用static变量的首选方式,这不仅是因为它强调了变最的static结构,而且在某些情况下它还为编译器进行优化提供了更好的机会。

static变量在内存中的位置

public class Person {
    String name;
    int age;
    
    public String toString() {
        return "Name:" + name + ", Age:" + age;
    }
    
    public static void main(String[] args) {
        Person p1 = new Person();
        p1.name = "zhangsan";
        p1.age = 10;
        Person p2 = new Person();
        p2.name = "lisi";
        p2.age = 12;
        System.out.println(p1);
        System.out.println(p2);
    }
    /**Output
     * Name:zhangsan, Age:10
     * Name:lisi, Age:12
     *///~
}

两个Person对象的方法实际上只是指向了同一个方法定义这个方法定义是位于内存中的一块不变区域——元空间(包括类元信息、字段、静态属性、方法、常量等[JDK8])。这一块存储区不仅存放了方法的定义,实际上从更大的角度而言,它存放的是各种类的定义。当通过new来生成对象时,会根据这里定义的类的定义去创建对象。

多个对象仅会对应同一个方法,不管多少个对象,它们的方法总是相同的,尽管最后的输出会有所不同。

public class Person {
    String name;
    static int age;
    
    /* 其余代码不变... */

    /**Output
     * Name:zhangsan, Age:12
     * Name:lisi, Age:12
     */
}

age属性加了static关键字之后,Person对象就不再拥有age属性了,age属性会统一交给Person类去管理,即多个Person对象只会对应一个age属性,一个对象如果对age属性做了改变,其他的对象都会受到影响。此时的age和toString()方法一样,都是交由类去管理。

虽然static可以让对象共享属性,但是实际中很少这么用,也不推荐这么使用。这样会让该属性变得难以控制,因为它在任何地方都有可能被改变。如果想共享属性,一般会采用其他的办法:

public class Person {
    private static int count = 0;
    int id;
    String name;
    int age;
    
    public Person() {
        id = ++count;
    }
    
    public String toString() {
        return "Id:" + id + ", Name:" + name + ", Age:" + age;
    }
    
    public static void main(String[] args) {
        Person p1 = new Person();
        p1.name = "zhangsan";
        p1.age = 10;
        Person p2 = new Person();
        p2.name = "lisi";
        p2.age = 12;
        System.out.println(p1);
        System.out.println(p2);
    }
    /**Output
     * Id:1, Name:zhangsan, Age:10
     * Id:2, Name:lisi, Age:12
     *///~
}

上面的代码起到了,给Person的对象创建一个唯一id以及记录总数的作用。其中count由static修饰,是Person类的成员属性,每次创建一个Person对象,就会使该属性自加1,然后赋给对象的id属性。这样,count属性记录了创建Person对象的总数,由于count使用了private修饰,所以从类外面无法随意改变。

修饰成员方法

static修饰成员方法最大的作用,就是可以使用类名.方法名的方式操作方法,避免了先要new出对象的繁琐和资源消耗:

public class PrintHelper {

    public static void print(Object o){
        System.out.println(o);
    }
    
    public static void main(String[] args) {
        PrintHelper.print("Hello world");
    }
}

在static方法的内部不能调用非静态方法,反之可以。

静态块

class Book{
    public Book(String msg) {
        System.out.println(msg);
    }
}

public class Person {

    Book book1 = new Book("book1成员变量初始化");
    static Book book2;
    
    static {
        book2 = new Book("static成员book2成员变量初始化");
        book4 = new Book("static成员book4成员变量初始化");
    }
    
    public Person(String msg) {
        System.out.println(msg);
    }
    
    Book book3 = new Book("book3成员变量初始化");
    static Book book4;
    
    public static void funStatic() {
        System.out.println("static修饰的funStatic方法");
    }
    
    public static void main(String[] args) {
        System.out.println("****************");
        Person p1 = new Person("p1初始化");
        Person.funStatic();
    }
    /**Output
     * static成员book2成员变量初始化
     * static成员book4成员变量初始化
     * ***************
     * book1成员变量初始化
     * book3成员变量初始化
     * p1初始化
     * static修饰的funStatic方法
     */
}
posted @ 2021-03-11 20:14  chenzufeng  阅读(130)  评论(0编辑  收藏  举报