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方法之后的静态代码块执行!");
    }
}

运行结果

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

相关推荐
Joy T10 小时前
Agent 确定性上下文设计:从 Prompt 参数注入到能力分层
java·开发语言·微服务·架构·prompt·agent context
m0_5873830013 小时前
广州 24 小时自助健身房解决方案技术实现与实战指南
java·spring·小程序·架构·需求分析
·云扬·13 小时前
Prompt 工程与安全:从 System 设计到注入防御
java·人工智能·安全·ai·prompt
代码不停14 小时前
自动化测试(java)
java·开发语言·selenium·自动化
鬼手点金16 小时前
opencode-rules 插件
java·开发语言·前端·人工智能·chrome·git·github
paopaokaka_luck17 小时前
滑雪场管理系统(AI智能助手流式对话,ECharts 运营数据分析、教练、雪具与场地一体化预约,钱包支付退款、订单核销与服务评价)
java·人工智能·spring boot·网络协议·数据分析·echarts
Wang's Blog17 小时前
Java 项目实战: 交友项目 - Dubbo 的内部结构与一次调用的完整链路
java·dubbo
Wang's Blog17 小时前
Java 项目实战: 交友项目 - Dubbo 入门引导
java·dubbo·交友
蜗牛互联网18 小时前
Python Agent工具调用的白名单、参数校验与审批门禁
java·服务器·前端·人工智能·后端·python
SL_staff18 小时前
当规则引擎开始‘懂业务’:JVS-Rules金融风控四大原生能力拆解
java·python·全栈