19 staic关键字
static
1 静态变量
- 静态变量:又称为类变量,也就是说这个变量属于类的,类所有的实例都共享静态变量,可以直接通过类名来访问它。静态变量在内存中只存在一份。
- 实例变量:每创建一个实例就会产生一个实例变量,它与该实例同生共死。
- static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。static成员变量的初始化顺序按照定义的顺序进行初始化。所以我们一般在这两种情况下使用静态变量:对象之间共享数据、访问方便。
1 | public class A { |
2 静态方法
静态方法在类加载的时候就存在了,它不依赖于任何实例。所以静态方法必须有实现,也就是说它不能是抽象方法。
1 | public abstract class A { |
只能访问所属类的静态字段和静态方法,方法中不能有 this 和 super 关键字,因为这两个关键字与具体对象关联。
1 | public class A { |
3 静态语句块
静态语句块在类初始化时运行一次。
1 | public class A { |
1 | 123 |
4 静态内部类
非静态内部类依赖于外部类的实例,也就是说需要先创建外部类实例,才能用这个实例去创建非静态内部类。而静态内部类不需要。
1 | public class OuterClass { |
静态内部类不能访问外部类的非静态的变量和方法。
内部类一般情况下使用不是特别多,如果需要在外部类里面定义一个内部类,通常是基于外部类和内部类有很强关联的前提下才去这么使用。
在说静态内部类的使用场景之前,我们先来看一下静态内部类和非静态内部类的区别:
非静态内部类对象持有外部类对象的引用(编译器会隐式地将外部类对象的引用作为内部类的构造器参数);而静态内部类对象不会持有外部类对象的引用
由于非静态内部类的实例创建需要有外部类对象的引用,所以非静态内部类对象的创建必须依托于外部类的实例;而静态内部类的实例创建只需依托外部类;
并且由于非静态内部类对象持有了外部类对象的引用,因此非静态内部类可以访问外部类的非静态成员;而静态内部类只能访问外部类的静态成员;
两者的根本性区别其实也决定了用static去修饰内部类的真正意图:
- 内部类需要脱离外部类对象来创建实例
- 避免内部类使用过程中出现内存溢出
第一种是目前静态内部类使用比较多的场景,比如JDK集合中的Entry、builder设计模式。
HashMap Entry:

builder设计模式:
1 | public class Person { |
第二种情况一般出现在多线程场景下,非静态内部类可能会引发内存溢出的问题,比如下面的例子:
1 | public class Task { |
上面这段代码中的:
1 | new Thread() { |
声明并创建了一个匿名内部类对象,该对象持有外部类Task实例的引用,如果在在run方法中做的是耗时操作,将会导致外部类Task的实例迟迟不能被回收,如果Task对象创建过多,会引发内存溢出。
优化方式:
1 | public class Task { |
5 静态导包
在使用静态变量和方法时不用再指明 ClassName,从而简化代码,但可读性大大降低。
1 | import static com.xxx.ClassName.* |
6 初始化顺序
静态变量和静态语句块优先于实例变量和普通语句块,静态变量和静态语句块的初始化顺序取决于它们在代码中的顺序。
1 | public static String staticField = "静态变量"; |
1 | static { |
1 | public String field = "实例变量"; |
1 | { |
最后才是构造函数的初始化。
1 | public InitialOrderTest() { |
存在继承的情况下,初始化顺序为:
- 父类(静态变量、静态语句块)
- 子类(静态变量、静态语句块)
- 父类(实例变量、普通语句块)
- 父类(构造函数)
- 子类(实例变量、普通语句块)
- 子类(构造函数)
关于静态方法的思考
面相对象的各种对象之间存在依赖和集成关系,根据依赖和继承关系可以绘制面相对象的拓扑。两个对象可能关联同一个关键对象,当其中一个对象修改了关键对象的状态,另一个对象也会感知到这个关键对象的状态变化。
但是静态方法是无状态的,如果静态方法调用N次,其内部创建的对象关系就会生成N次。
静态方法是系统的启动放发,main方法也是静态方法。如果一个模块相对独立,应该也使用静态方法启动。调用者无需创建对象,只需要调用该模块的静态方法,在该静态方法中组建该模块的内部对象拓扑关系。实现一定程度的相互隔离。
关于父类和子类的作用的说明。记得在写毕设的时候,自己把父类当做工具类,子类在调用很多方法的时候,为了少写代码,就直接调用父类中的方法,然后导致父类中的流程函数,会调用子类中的方法,子类中的函数又会调用父类中的方法,非常凌乱,两个类相互冗余。当时也在思考,这些工具函数写在父类中供所有的子类调用与写一个util类有什么区别?
现在发现,应该遵循一些默认的编码规则,父类用来构建整体的流程,而子类用来完善丰富一些子流程。相当于父类在构建主流程的时候,空出一些细节实现的部分,让子类来完善。而不是写一写类似的工具函数,让子类来调用,子类能够看到更加全面丰富的流程,那么父类就没有存在的必要了,父类的作用可能就是一个接口了,只提供一些对外的方法声明。
综上所属:
- 接口:只提供对外的方法声明
- 父类:提供完整的流程,由父类调用未经实现的抽象方法完成整体流程的构建。
- 子类:提供丰富的细节实现,由子类实现抽象方法的细节。
- 工具类:提供给所有子类的通用的处理函数。一般是静态函数









