Java关键字static的作用和用法

上一篇文章复习了静态代码块、构造代码块、构造方法的执行顺序(跳转),这篇文章主要复习关键字static的作用和用法。

知识点回顾:
如果访问变量或者方法一般都是创建该类的对象,进行调用(当然反射也是一种情况,这里先不讨论)。所以,为了方便在没有创建对象的情况下进行调用变量或者方法,这时static关键字就诞生了。

static语法:
通过 类名.静态变量 或者 类名.静态方法

像下面这样:

public class StaticTest {
    
    
	//定义静态变量
    static String name;
    //定义静态方法
    public static void fly(){
    
    
        System.out.println(name+"翩翩起舞");
    }
    public static void main(String[] args) {
    
    
        StaticTest.name = "蒲公英";
        StaticTest.fly();
    }
}

那么除了这些,static关键字还有那些独特之处。让我们继续往下看。

1.static修饰的变量或者方法独立于该类的任何对象。也就是说变量和方法不属于任何一个实例对象,而是被该类的实例对象所共享。 如何理解被该类的实例对象共享?简单来说创建的每一个对象都可以调用静态变量和静态方法(但是不建议通过对象引用来访问)。

改造一下上面的代码:

public class StaticTest {
    
    
    static String name;
    public static void fly(){
    
    
        System.out.println(name+"翩翩起舞");
    }
    public static void main(String[] args) {
    
    
        StaticTest.name = "蒲公英";
        StaticTest s1 = new StaticTest();
        StaticTest s2 = new StaticTest();
        System.out.println(s1.name);
        s2.fly();
    }
}

分析:
对比上面的代码这次我分别创建了s1、s2两个对象,进行调用变量和方法。
很显然代码看起来不简洁。

对于成员变量来说,简化了通过有参构造和setter(设置器)来初始化变量的方式。对于方法来说,不需要创建对象就可以调用了(说明了被static修饰的变量和方法优先于对象存在)。

那么好处还有什么呢?

2.static变量值在类加载的时候分配空间,以后创建类对象的时候不会重新分配。

看一个例子:

public class Counter {
    
    
    static int count;
    public Counter() {
    
    
        count++;
        System.out.println(count);
    }
    public static void main(String[] args) {
    
    
        Counter counter = new Counter();
        Counter counter2 = new Counter();
        Counter counter3 = new Counter();
    }
}

运行结果:

1
2
3

分析:简单来说静态变量只会获取一次内存空间,任何对象对他的修改都会保留。所以,每创建一个对象count都会加1,最终结果为3。

反例验证:

public class Counter {
    
    
    int count;
    public Counter() {
    
    
        count++;
        System.out.println(count);
    }
    public static void main(String[] args) {
    
    
        Counter counter = new Counter();
        Counter counter2 = new Counter();
        Counter counter3 = new Counter();
    }
}

运行结果:

1
1
1

分析:创建成员变量count,并且在构造方法中自增。成员变量count会在创建对象的时候获取内存,每个对象都会有一个count副本,因此count不会随着对象的增多而递增。

3.静态代码块随着JVM加载类时执行,且执行一次。
public class PropertiesTest {
    
    
    public static List<String> list = new ArrayList<String>();

    static {
    
    
        list.add("奋斗志向从处何来?");
        list.add("从读书学习中来,从勤奋中来,");
    }

    static {
    
    
        list.add("从思考中来,从实践中而来。");
    }

    public static void print() {
    
    
        if (list.size() == 0) {
    
    
            throw new RuntimeException("集合为空");
        }

        for (String ele : list) {
    
    
            System.out.print(ele);
        }
    }

    public static void main(String[] args) {
    
    
        print();
    }
}

输出正能量:
奋斗志向从处何来?从读书学习中来,从勤奋中来,从思考中来,从实践中而来。

(语录来自奋斗一篇)

什么时候使用静态代码块呢?
一般在进行初始化操作时,比如读取配置文件信息等。

补充: 静态代码块优先于main方法执行。
验证如下:

public class MainTest {
    
    
    static {
    
    
        System.out.println("静态代码块");
    }

    public static void main(String[] args) {
    
    
        System.out.println("main方法");
    }
}

运行结果:

静态代码块
main方法

4.静态内部类
定义一个内部类加上static,就成了静态内部类。包装类Integer中有一段代码是这样的。

 private static class IntegerCache {
    
    
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
    
    
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
    
    
                try {
    
    
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
    
    
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;
            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }
        private IntegerCache() {
    
    }
    }

这样设计的巧妙之处就是将Integer静态内部类中的代码块预先对范围[-128~127]的值进行了实例化,并且存放在cache数组中。

我们熟悉的单例模式

public class Singleton {
    
    
    //私有构造方法
    private Singleton() {
    
    }
    //静态内部类
    private static class SingletonHolder{
    
    
       public static final Singleton instance = new Singleton();
    }
    //公共的静态方法
    public static Singleton getInstance() {
    
    
        return SingletonHolder.instance;
    }
    public static void main(String[] args) {
    
    
        Singleton instance = Singleton.getInstance();
        Singleton instance1 = Singleton.getInstance();
        System.out.println(instance);
        System.out.println(instance1);
    }
}

分析:
第一次加载 Singleton 类时并不会初始化 instance,只有第一次调用 getInstance() 方法时 Java 虚拟机才开始加载 SingletonHolder 并初始化 instance,这样不仅能确保线程安全,也能保证 Singleton 类的唯一性。

注意事项:

1.静态只能访问静态的
2.非静态既可以访问静态的,也可以访问非静态的。(因为被static修饰的变量和方法优先于对象存在)

以上就是关键字static的学习。如果对你有用记得三连哦。

猜你喜欢

转载自blog.csdn.net/lirui1212/article/details/115254985
今日推荐