上一篇文章复习了静态代码块、构造代码块、构造方法的执行顺序(跳转),这篇文章主要复习关键字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不会随着对象的增多而递增。
总结:
如何确定一个属性是否要声明为static的?
- 属性可以被多个对象所共享。
- 类中的常量也常声明为static,比如Math类中的PI。
public static final double PI = 3.14159265358979323846;
总如何确定一个方法是否要声明为static的?
- 操作静态变量时,通过方法声明为static。
- 工具类中的的方法,习惯声明为static的。比如Math、Collections、Arrays。
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的学习。如果对你有用记得三连哦。