static关键字

static关键字的四种用法

1、修饰成员变量

  我们通常将static修饰的成员变量称为类变量或者静态变量,这是相对于对象的属性来说的。请看下面的例子:

 1 public class Person {
 2     String name;
 3     int age;
 4     
 5     public String toString() {
 6         return "Name:" + name + ", Age:" + age;
 7     }
 8     
 9     public static void main(String[] args) {
10         Person p1 = new Person();
11         p1.name = "zhangsan";
12         p1.age = 10;
13         Person p2 = new Person();
14         p2.name = "lisi";
15         p2.age = 12;
16         System.out.println(p1);
17         System.out.println(p2);
18     }
19     /**Output
20      * Name:zhangsan, Age:10
21      * Name:lisi, Age:12
22      *///~
23 }
View Code

   Person类构造出的每个对象都是独立存在的,如p1和p2都有自己的成员变量,互不影响,它们在内存的示意图如下图1.1:

图1.1

  p1和p2两个引用变量指向的对象分别存储在堆区域的不同地址中,所以它们互不影响。对象的成员变量都在这了,由每个对象自己保存,但是对象的方法却不在堆区域中。其实,无论创建多少对象,它们都共用同一个方法,如下图1.2所示:

图1.2

  从图1.2中可以看到,两个对象指向同一个方法定义,且该方法定义在一块不变的区域(有JVM划分),暂时称为静态存储区。这块区域不仅存放了方法的定义,各种类的定义也存放在该区域,当用new创建对象时,会根据该区域定义的类去创建对象。

  那么,用static关键字修饰成员变量,让它们变成类所属,会变成不同的样子,请看下面的例子:

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

    /**Output
     * Name:zhangsan, Age:12
     * Name:lisi, Age:12
     *///~
}
View Code

图1.3

  我们可以看到,static修饰的age变量同方法一样由类统一管理,都存放的静态存储区,该变量让对象共享,但实际中很少这么用。

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
     *///~
}
View Code

  上面的例子可以用count记录一共创建了多少个对象,且count是由private修饰的,在类外边无法随意改变。

 2、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");
    }
}
View Code

  上面的例子可以看出,print()方法相当于一个全局函数,只要导入该类的包,就可以使用“类名.方法名”方式调用它。但是在一个static修饰的方法中,不能使用非静态的方法和成员变量,因为static修饰的方法是属于类的,它不能直接访问对象的成员变量,它不知道使用哪个对象的属性。

3、静态块

  在说明static的第三种用法时,先介绍一下对象的初始化过程。以下面的代码为例:

package com.dotgua.study;

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

public class Person {

    Book book1 = new Book("book1成员变量初始化");
    static Book book2 = new Book("static成员book2成员变量初始化");
    
    public Person(String msg) {
        System.out.println(msg);
    }
    
    Book book3 = new Book("book3成员变量初始化");
    static Book book4 = new Book("static成员book4成员变量初始化");
    
    public static void main(String[] args) {
        Person p1 = new Person("p1初始化");
    }
    /**Output
     * static成员book2成员变量初始化
     * static成员book4成员变量初始化
     * book1成员变量初始化
     * book3成员变量初始化
     * p1初始化
     *///~
}
View Code

  上面的例子可以看出,new对象时,会先初始化static修饰成员变量book2和book4,接着再初始化普通的成员变量book1和book2,最后调用方法的构造器完成初始化。并且如果有多个static修饰的成员变量时,会按定义的先后顺序进行初始化。

  实际上,static修饰的成员变量变量可以被更早的初始化,以下面的代码为例:

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

public class Person {

    Book book1 = new Book("book1成员变量初始化");
    static Book book2 = new Book("static成员book2成员变量初始化");
    
    public Person(String msg) {
        System.out.println(msg);
    }
    
    Book book3 = new Book("book3成员变量初始化");
    static Book book4 = new Book("static成员book4成员变量初始化");
    
    public static void funStatic() {
        System.out.println("static修饰的funStatic方法");
    }
    
    public static void main(String[] args) {
        Person.funStatic();
        System.out.println("****************");
        Person p1 = new Person("p1初始化");
    }
    /**Output
     * static成员book2成员变量初始化
     * static成员book4成员变量初始化
     * static修饰的funStatic方法
     * ***************
     * book1成员变量初始化
     * book3成员变量初始化
     * p1初始化
     *///~
}
View Code

  上面的例子可以看出,未new对象,只是调用static修饰的成员方法,并且方法中未使用任何成员变量,仍会触发static修饰的成员变量初始化,普通的成员变量不会初始化。还可以看到,紧接着new对象的时候,static修饰的成员方法不会再次被初始化,只需要初始化一次。

  接着看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) {
        Person.funStatic();
        System.out.println("****************");
        Person p1 = new Person("p1初始化");
    }
    /**Output
     * static成员book2成员变量初始化
     * static成员book4成员变量初始化
     * static修饰的funStatic方法
     * ***************
     * book1成员变量初始化
     * book3成员变量初始化
     * p1初始化
     *///~
}
View Code

  当我们初始static修饰的成员变量时,可以将它们统一放在一个以static开始,花括弧裹起来的块状语句中。

4、静态导包

  相比上边三种用法,这种用法很少见,以下面的代码为例:

/* PrintHelper.java文件 */
package com.dotgua.study;

public class PrintHelper {

    public static void print(Object o){
        System.out.println(o);
    }
}
View Code
/* App.java文件 */

import static com.dotgua.study.PrintHelper.*;

public class App 
{
    public static void main( String[] args )
    {
        print("Hello World!");
    }
    /**Output
     * Hello World!
     *///~
}
View Code

  上面代码来自两个Java文件,PrintHelper类包含一个static修饰的静态方法。在App.java中,导入PrintHelper类使用了static关键字,并且在引入类的最后加了“.*”。这样,再不与当前类的方法名冲突的情况下,就可以直接采用“方法名”的方式调用方法,就像该类在使用自己的方法一样。

总结

  1.用来修饰成员变量,将成员变量由类来统一管理,所有实现该类的对象共享该成员变量;

  2.修饰成员方法,可以使用“类名.方法名”调用方法,常用于工具类;

  3.静态块用法,将多个static修饰的成员变量放在一起初始化,使程序更加规整,理解对象的初始化过程非常关键;

  4.静态导包法,将类的方法直接导入到当前类中,用“方法名”直接调用类方法,更加方便。 

5、修饰内部类

  内部类可以当普通类 来使用,实例化static内部类的时候不需要依赖外部类。

  

原文地址:https://www.cnblogs.com/lmmblogs/p/8583039.html