01.Java关键字与底层原理-static关键字
1.static关键字
1.1.含义
static方法就是没有this的方法,在static方法的内部不能调用非静态的方法,反过来倒是可以。而且可以在没有创建任何对象的前提下,仅仅通过类本身来调用static方法,这实际上正是static方法的主要用途。[1]
简单来说,就是不需要创建对象就能访问static修饰的方法,而static修饰的方法却不能直接调用非静态的方法。static关键字不仅仅能修饰方法,还可以修饰变量,那么引申开来,就是可以通过类直接访问由static修饰的变量。
当然这只是其中的两个应用的方向,static关键字不仅仅能修饰方法和变量,还可以修饰代码块、类以及做静态导包使用,这其中的含义将在用法当中解释。对于没有写static的关键字的构造方法,以及接口里面的变量,他们都是具有static特性的,也在下面做一并的描述。
1.2.用法与底层原理
1.2.1.static变量
- 用法
static变量,就是不需要创建对象,直接通过类就能访问的变量。访问的形式为 类名.变量名
,在类加载的时候就完成初始化,内存当中只有一份,所有的对象都共享这一个变量。需要对象之间共享变量的时候使用。
public class Static{
public static String staticVal = "static";
}
public class Main{
public void method1() {
System.out.println(Static.staticVal);
}
}
- 底层原理
这是一个简化版的内存解释,Java的内存包含了栈、堆和静态存储区,栈主要是存放对象的应用,堆是放对象,而静态存储区主要是放一些方法和常量。那么,如果我们有以下的对象:
public class Person{
public String name;
public static Integer age;
}
public class Main{
public static void main(String[] args) {
Person person = new Person();
person.name = "wusicheng";
person.age = 18;
}
}
那么他们在内存当中的分配情况如下图所示:
注意name
和age
存储位置的不同,name
是存储存储到堆上面的,和对象绑定在一起,而age
是Person类进行管理的,存储在静态存储区上面,不论建立多少个对象,都只指向这个一个地方。在类加载的时候,静态存储区中的内容已经初始化完成,所以在内存当中就只有这一份。
1.2.2.static方法
static方法就是可以使用 对象.方法名(方法参数...)
进行访问的方法,静态方法当中,不可以直接调用非静态的方法。具体的使用如下
public class Static{
public void notStatic() {
//todo
}
public static void staticMethod() {
System.out.println("===静态方法===");
notStatic();//error 不可以这么调用,会报错
}
}
public class Main{
public void method1() {
Static.staticMethod();
}
}
- 底层原理
略,后面进行补充。
1.2.3.static代码块
static静态代码块的作用是初始化一些静态变量使用的,在static代码块里面,可以调用静态变量方法。
public class Static{
public static List<String> staticList = new ArrayList<>(2);
static {
staticList.add("static1");
staticList.add("static2");
}
}
public class Main{
public void method1() {
System.out.println(Static.staticList.get(0));
}
}
- 底层原理
略,以后补充。
1.2.4.static类
static修饰类,只能够在静态内部类当中使用,所谓的静态内部类,就是在类里面套一层类,并且这个类是使用static修饰的。这样做可以把一个类隐藏到外部类里面,而且不需要内部类引用外围类对象。
public class Static{
public static class InnerStaticClass {
public void method() {
System.out.println("===InnerStaticClass====");
}
}
}
public class Main{
public void method1() {
Static.InnerStaticClass innerClass = new Static.InnerStaticClass();
}
}
1.2.5.静态导包
静态导包是简化书写使用的,使用了静态导包之后,就可以直接使用相关的静态变量,静态方法,不需要使用类名.变量
和类名.方法
来进行调用,一般的使用场景是junit判断是否相等的情况下使用。
import static java.lang.Math.PI;
public void method() {
System.out.println(PI);
}
1.2.6.接口当中的变量
接口当中可以定义成员变量,而且定义的成员变量一定是public static final
的,但是在实际的书写过程中,可以把public static final
给去掉,例如下面的两种写法是一样的。
public interface StaticInterface {
String staticVal = "static val in interface";
public static final String staticVal2 = "static val in interface";
}
public class Main {
public void mehod() {
/**接口当中的变量*/
System.out.println(StaticInterface.staticVal);
System.out.println(StaticInterface.staticVal2);
}
public static void main(String[] args) {
new Main().mehod();
}
}
- 底层原理
略,后面补上。
1.3. 实际应用
static关键字,一般都是用在工具类里面,工具类只需要一套,载入内存的时候对这些变量和方法进行初始化就可以了,不需要每一次都创建对象,然后调用相应的变量和方法,否则会浪费过多的内存资源。但是,如果static的变量和方法太多,而这些变量和方法是在JVM关闭的时候才进行清理的,在就会造成内存的浪费,而且可以回导致内存溢出。
对于程序当中常用的变量,而且基本不需要变更的,也可以使用static关键字进行修饰然后使用static静态代码块进行初始化,那么程序在调用的时候就可以直接在内存中拿来就用,不需要再堆上面再分配空间,然后初始化成一样的数据,这样会降低效率而且会浪费内存。基于这个原因,JUnit使用@BeforeClass注解的方法,必须是static方法的原因就显而易见了,因为在单元测试当中,@BeforeClass是在单元测试载入时候初始化相关的数据使用的,而且这些数据是一整类都通用,那么让他们初始化到内存当中,用的时候直接调可以大大节约时间和空间。
1.4. 总结
- static关键字修饰的会在类加载完成的时候就初始化到内存当中,直到JVM关闭;
- static关键字修饰的变量和方法是通过"类名.变量"和"类名.方法"调用的;
- static方法当中不能使用this,它是类管理的,不是对象管理的,同时也不能使用super;
- 接口里面的成员变量默认都是public static final的;
- static关键字常用在工具类当中或者只需要初始化一次的方法上。
https://www.jianshu.com/p/92327e300a6e
-
来自《Java编程思想》 ↩