JavaSE:static关键字详解

引言

我们在学习Java的过程中,static属于我们最先遇到的几个关键字之一

你可能会问,我怎么没印象

java 复制代码
public class StaticKeyword {
	// 我们写第一个java程序,运行Hello World的时候,main方法就用了static修饰
    public static void main(String[] args) {
        System.out.println("Hello World");
    }
}

事实上,作为程序入口的main方法,是一个静态方法

static是我们的老熟人,但是你真的了解她吗?

static关键字简介

static使用

static关键字,是java的关键字之一,表示"静态"

可以用来修饰变量,方法(比如main方法),代码块不能修饰类

static可以和四大访问修饰符组合使用

java 复制代码
public class StaticKeyword {

    private static int a;

    protected static int b;

    static int c;

    public static int d;


    private static void test1() {

    }

    protected static void test2() {

    }


    static void test3() {

    }

    public static void test4() {

    }

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

static内存存储

只要是static修饰的,都存储在JVM的方法区当中

准确来说,是存储在方法区的类信息之中,所以静态变量/静态方法/静态代码块,本质上归类所有

静态变量存储于静态常量池中;

当静态常量池不满时,静态方法可以存储于其中;否则要存储到方法区的其他区域

静态代码块比较特殊,我们稍后单独处理

静态变量+静态方法

静态变量+静态方法在本类之中 共享,本类的任何对象都可以直接调用

代码+内存图的讲解
java 复制代码
public class StaticKeyword {

    private String c;

    private String f;

    private static int a;

    public static int d;
    

    public static void main(String[] args) {
        StaticKeyword s1 = new StaticKeyword();
        StaticKeyword s2 = new StaticKeyword();
        s1.c = "c1";
        s2.c = "c2";
        s1.a = 1;
        s2.a = 2;

        System.out.println(s1.c + "," + s1.a);
        System.out.println(s2.c + "," + s2.a);

    }

}

输出结果

静态变量a根属于类StaticKeyword,在整个JVM中只有一份,后赋值会覆盖前赋值

但是普通变量c根属于StaticKeyword的对象,堆内存中的对象可以创建很多个,所以c变量赋值互不干扰

静态方法也是如此,在JVM中只存在一份

java 复制代码
public class StaticKeyword {
    
    public static void test1() {
        System.out.println("test1");
    }
        public void test2(){
        System.out.println("test2");
    }

    public static void main(String[] args) {
        StaticKeyword s1 = new StaticKeyword();
        StaticKeyword s2 = new StaticKeyword();

        s1.test1();
        s2.test1();

    }

}

运行结果
s1.test1()执行时,从方法区的静态常量池当中拷贝一份入栈,运行完出栈;

s2.test1()执行时,也从方法区的静态常量池的相同位置拷贝一份入栈,运行完出栈。

test2()在对象实例化的时候从类信息拷贝,但是实际执行时,是从堆内存的不同对象当中拷贝入栈

静态方法不存在相互覆盖的情况,但是需要注意的是别的内容

  1. 静态方法不能调用非静态方法,

    因为有类不一定有对象,类不一定实例化创建了对象,所以存储于对象当中的非静态方法的调用就无从谈起(没有蒸锅怎么煮米)

  2. 非静态方法可以调用静态方法

    因为有对象一定有类,而且静态方法是类共享的

静态变量和静态方法习惯调用形式

既然静态变量和静态方法都是属于类,那么我们实际上可以使用类名+变量名/方法名来访问

事实上,这也是大多数情况下的访问方法,我们都习惯如此

如果不这样做,用对象访问,其实也没什么问题,但是我们通常不会这么做

IDEA也会抛出警告

用类名访问,则不会抛出警告

静态代码块

代码块

代码块中的代码,只能在代码块的范围内执行,代码块中定义的变量是局部变量

代码块根属于对象,存储在堆内存中

代码块只有对象实例化之后才能执行,这一点非常重要

demo1
java 复制代码
public class StaticKeyword {

    public static int a;

    public static void test1() {
        System.out.println("test1");
    }

    public void test2() {
        System.out.println("test2");
    }
		
		// 代码块
    {
        int aaa = 100;
        System.out.println(aaa);
        System.out.println("代码块执行!");
    }

}

在其他类里面的main方法运行

java 复制代码
public class OtherPackageClass {

    public static void main(String[] args) {
        StaticKeyword s1 = new StaticKeyword();
        s1.test2();
    }
}

运行结果:

我们可以看到代码块的运行顺序是非常靠前的,只要类实例化(构造方法运行之后),代码块就会运行

但是若类不实例化,那么就不会执行

demo2

需要注意的是,每次创建一个对象,代码块都会执行一次

java 复制代码
public class OtherPackageClass {
    {
        System.out.println("代码块执行啦!!!");

    }

    public static void main(String[] args) {
        OtherPackageClass o1 = new OtherPackageClass();
        OtherPackageClass o2 = new OtherPackageClass();

    }
}

运行结果

静态代码块

所谓静态代码块,就是被static修饰的代码块

它也具有代码块的一些特征,比如静态代码块中的代码,只能在静态代码块的范围内执行,静态代码块

中定义的变量是局部变量等

区别
  1. 静态代码块根属于类 ,存储在方法区当中;代码块根属于对象,存储在堆中
  2. 只要类加载,静态代码块就会自动执行,而且最优先执行;代码块的执行不光需要类加载,还需要类实例化(创建对象)
demo1
java 复制代码
public class StaticKeyword {

    {
        int aaa = 100;
        System.out.println(aaa);
        System.out.println("代码块执行!");
    }

    static{
        System.out.println("静态代码块执行!");
    }


    public static void main(String[] args) {

    }
}

运行结果:

我们发现只有静态代码块执行了

demo2

如果我们再创建一个对象

java 复制代码
public class StaticKeyword {

    {
        int aaa = 100;
        System.out.println(aaa);
        System.out.println("代码块执行!");
    }

    static{
        System.out.println("静态代码块执行!");
    }


    public static void main(String[] args) {
        StaticKeyword s1 = new StaticKeyword();
    }
}

运行结果:

静态代码块和代码块都执行,并且静态代码块先执行

总结

有对象(创建对象将类实例化)则一定有类(类加载),但是有类(类加载)不一定有对象(创建对象将类实例化)

类加载之后的代码执行顺序

  1. main方法之前的静态代码块
  2. main方法之后的静态代码块
  3. 类实例化之前的main方法代码
  4. main方法之前的代码块(需要类实例化)
  5. main方法之后的代码块(需要类实例化)
  6. 类实例化之后的main方法代码
demo

看懂这个代码,你的静态代码块就过关啦!!!

java 复制代码
public class StaticKeyword {

    private static String name;

    private Integer age;

    {
        System.out.println("4. StaticKeyword类实例化之后,main方法之前的代码块执行!");
    }

    static {
        System.out.println("1. main方法之前的静态代码块执行!");
    }

    public void hhh() {
        System.out.println("哈哈哈哈哈哈");
    }

    public static void main(String[] args) {
        // StaticKeyword类实例化之前
        StaticKeyword.name = "樱岛麻衣天下第一";
        System.out.println("3. StaticKeyword类实例化之前的部分main代码执行:" + StaticKeyword.name);
        System.out.println("=====================================");
        // StaticKeyword类实例化之后
        StaticKeyword s1 = new StaticKeyword();
        s1.age = 18;
        System.out.println("6-1. StaticKeyword类实例化之后的部分main方法代码执行," + "s1对象创建:" + s1.age);
        StaticKeyword s2 = new StaticKeyword();
        s2.age = 30;
        System.out.println("6-2. StaticKeyword类实例化之后的部分main方法代码执行," + "s2对象创建:" + s2.age);
    }

    {
        System.out.println("5. StaticKeyword类实例化之后,main方法之后的代码块执行!");
    }

    static {
        System.out.println("2. main方法之后的静态代码块执行!");
    }
}

运行结果

如果你觉得我写的不错,可以给我点个赞,这对我真的很重要,谢谢!!!

相关推荐
九圣残炎4 分钟前
【从零开始的LeetCode-算法】3354. 使数组元素等于零
java·算法·leetcode
天天扭码39 分钟前
五天SpringCloud计划——DAY1之mybatis-plus的使用
java·spring cloud·mybatis
程序猿小柒1 小时前
leetcode hot100【LeetCode 4.寻找两个正序数组的中位数】java实现
java·算法·leetcode
不爱学习的YY酱1 小时前
【操作系统不挂科】<CPU调度(13)>选择题(带答案与解析)
java·linux·前端·算法·操作系统
丁总学Java1 小时前
Maven项目打包,com.sun.tools.javac.processing
java·maven
kikyo哎哟喂2 小时前
Java 代理模式详解
java·开发语言·代理模式
duration~2 小时前
SpringAOP模拟实现
java·开发语言
小码ssim2 小时前
IDEA使用tips(LTS✍)
java·ide·intellij-idea
潜洋2 小时前
Spring Boot教程之五:在 IntelliJ IDEA 中运行第一个 Spring Boot 应用程序
java·spring boot·后端
暮志未晚Webgl3 小时前
109. UE5 GAS RPG 实现检查点的存档功能
android·java·ue5