static关键字详解
文章目录
- static
 - 使用示例
 - static底层原理
 - 静态初始化顺序
 - 静态与线程安全
 
static
static是Java中的一个关键字,用于定义类级别的成员,类级别的成员是指那些属于整个类,而不是特定对象实例的成员。在Java中,类级别的成员包括静态变量和静态方法。
public class Example {// 静态变量(类级别的成员)public static int count = 0;// 静态方法(类级别的成员)public static void incrementCount() {count++;}
}
 
因为静态成员属于整个类而不是特定实例,所以它们在所有实例之间共享,减少了内存消耗。例如,静态变量用于保存所有对象共享的状态,而静态方法可用于提供通用的工具函数,这些功能可以直接通过类名访问,无需实例化对象。
但是由于静态成员在类加载时被初始化并在整个应用程序运行期间存在,可能导致测试变得复杂,因为静态变量的状态会影响所有实例。此外,静态成员无法通过继承进行扩展,这限制了其灵活性。多个线程访问静态变量时,还需处理线程安全问题。
static使用场景包括工具类、常量和共享状态。例如,工具类中的静态方法,如java.lang.Math类的数学函数,可以通过类名直接访问,而无需实例化对象。常量通过public static final声明,使其在整个应用中保持一致,并方便访问。静态变量用于存储所有类实例共享的数据,如计数器,所有实例可以访问和修改相同的值。
使用示例
static关键字可以用于声明静态变量,这些变量在所有实例之间共享;定义静态方法,允许通过类名直接调用;以及创建静态块,用于在类加载时执行初始化操作。还可用于定义静态内部类,这些类与外部类的实例无关,但可以访问外部类的静态成员。
- 静态变量:声明属于类的变量,而不是实例。所有对象共享同一个静态变量。
public class Example {public static int count = 0; } - 静态方法:定义不依赖于实例的方法,可以直接通过类名调用。
public class Example {public static void printMessage() {System.out.println("Hello, World!");} } - 静态块:用于在类加载时初始化静态变量,静态块在类加载时执行一次。
public class Example {static {System.out.println("Static block executed");} } - 静态内部类:定义与外部类实例无关的内部类,静态内部类可以直接访问外部类的静态成员。
public class OuterClass {static class StaticInnerClass {void display() {System.out.println("Static inner class");}} } 
除了静态变量、静态方法、静态块和静态内部类这几种常见的使用方式外,static关键字还可用于静态导入和静态方法引用。
- 静态导入:允许在代码中直接使用类的静态成员,无需使用类名。这可以使代码更简洁。
import static java.lang.Math.*;public class Example {public static void main(String[] args) {double result = sqrt(25); // 直接使用 sqrt 方法,无需 Math.sqrt()System.out.println(result);} } - 静态方法引用:在
lambda表达式或方法引用中,可以使用静态方法作为目标。这种用法使代码更具表达力。public class Example {public static int multiplyByTwo(int x) {return x * 2;}public static void main(String[] args) {Function<Integer, Integer> function = Example::multiplyByTwo;System.out.println(function.apply(5)); // 输出 10} } 
static底层原理
在JVM中,静态变量、静态方法和静态代码块都存储在方法区。方法区是JVM的一部分,用于存储类的结构信息,包括类的元数据、静态变量、静态方法和常量池等。类加载时,JVM在方法区为这些静态成员分配内存,这块内存被所有类的实例共享,并在整个类的生命周期内保持不变,所以静态变量不需要为每个对象实例重新创建。
类加载的过程包括三个步骤:
- 加载:JVM通过类加载器读取
.class文件的字节码,并将其加载到内存中的方法区; - 连接:包括验证(确保字节码文件的正确性)、准备(为静态变量分配内存并赋初始值)、解析(将常量池中的符号引用转换为直接引用);
 - 初始化:这是类加载机制的最后一步,在这个阶段,Java程序代码才开始真正执行,此阶段负责执行静态变量和执行静态块。初始化的时候才会为普通成员变量赋值,而在准备阶段已经为静态变量赋过一次值、静态方法已经初始过。也就是说如果我们在静态方法中调用非静态成员变量会超前,可能会调用了一个还未初始化的变量。因此编译器会报错。
 
静态变量在类加载时被初始化,并在方法区中分配一块内存。这块内存被所有类的实例共享,所有实例访问的是同一份静态变量,不需要为每个实例单独创建。静态方法也存在于方法区,可以通过类名直接调用,不需要创建类的实例。静态代码块在类加载时执行一次,用于初始化静态资源。静态成员与对象实例无关,它们的内存分配和初始化在类加载阶段完成,并在整个应用中保持一致。举个例子:
public class Example {public static int count = 0; // 静态变量
}
 
当类Example被加载时,count变量在方法区中被分配内存,并初始化为0。这段内存空间只存在一份,并且所有对Example.count的访问都指向这段内存空间。
静态初始化顺序
掌握静态变量、静态代码块和静态方法的加载顺序,有助于合理安排代码逻辑,解决因依赖关系引起的问题。对于调试复杂的类加载过程也很重要,可以更快地定位和解决问题。除此之外,这种理解有助于优化类的加载性能,减少不必要的初始化开销,并能够正确实现一些设计模式,确保类在多线程环境下的稳定性。
// 父类
class Parent {// 静态变量public static int parentStaticVar = initializeParentStaticVar();// 静态代码块static {System.out.println("Parent static block 1 executed");}// 静态代码块static {System.out.println("Parent static block 2 executed");}// 静态方法public static void parentStaticMethod() {System.out.println("Parent static method called");}// 实例变量public int parentInstanceVar;// 构造方法public Parent() {System.out.println("Parent constructor executed");this.parentInstanceVar = 1;}// 静态变量初始化方法private static int initializeParentStaticVar() {System.out.println("Initializing parentStaticVar");return 100;}
}// 子类
class Child extends Parent {// 静态变量public static int childStaticVar = initializeChildStaticVar();// 静态代码块static {System.out.println("Child static block executed");}// 静态方法public static void childStaticMethod() {System.out.println("Child static method called");}// 实例变量public int childInstanceVar;// 构造方法public Child() {super(); // 调用父类构造方法System.out.println("Child constructor executed");this.childInstanceVar = 2;}// 静态变量初始化方法private static int initializeChildStaticVar() {System.out.println("Initializing childStaticVar");return 200;}
}// 主方法
public class StaticExample {public static void main(String[] args) {System.out.println("Creating Child instance...");Child c = new Child(); // 创建子类实例// 调用静态方法Child.childStaticMethod();Parent.parentStaticMethod();}
}
 
执行顺序:
- 类加载:
 
-  
首先加载
Parent类:- 静态变量 
parentStaticVar初始化,输出:Initializing parentStaticVar - 静态代码块按声明顺序执行,输出:
Parent static block 1 executed Parent static block 2 executed 
 - 静态变量 
 -  
然后加载
Child类:- 静态变量 
childStaticVar初始化,输出:Initializing childStaticVar - 静态代码块执行,输出:
Child static block executed 
 - 静态变量 
 
- 创建实例:
 
- 创建 
Child类实例时,首先执行Parent类的构造方法,输出:Parent constructor executed - 接着执行 
Child类的构造方法,输出:Child constructor executed 
- 调用静态方法:
 
- 调用子类的静态方法 
Child.childStaticMethod(),输出:Child static method called - 调用父类的静态方法 
Parent.parentStaticMethod(),输出:Parent static method called 
静态与线程安全
静态变量在类加载时初始化,并且在整个JVM中只有一份。所有线程访问的都是同一个静态变量,这代表不同线程对静态变量的操作可能会相互影响。如果静态变量在多个线程中被同时修改,可能会导致数据不一致或者其他线程安全问题。例如,如果两个线程同时修改一个静态计数器,没有同步机制的话,计数器的值可能会出现错误。
静态不安全解决方案:
- 通过在静态方法或静态代码块中使用
synchronized关键字,这样可以避免多个线程同时访问或修改静态变量。public class SynchronizedExample {private static int sharedCounter = 0;// 静态同步方法public static synchronized void incrementCounter() {sharedCounter++;}public static void main(String[] args) {Runnable task = () -> {for (int i = 0; i < 1000; i++) {incrementCounter();}};Thread t1 = new Thread(task);Thread t2 = new Thread(task);t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Shared counter value: " + sharedCounter);} } - 有时候可以使用原子类,它们提供了线程安全的操作。原子类提供了无锁的线程安全操作,用于处理并发访问的场景。
public class AtomicExample {private static final AtomicInteger atomicCounter = new AtomicInteger(0);public static void incrementAtomicCounter() {atomicCounter.incrementAndGet();}public static void main(String[] args) {Runnable task = () -> {for (int i = 0; i < 1000; i++) {incrementAtomicCounter();}};Thread t1 = new Thread(task);Thread t2 = new Thread(task);t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Atomic counter value: " + atomicCounter.get());} } - 如果每个线程需要独立的静态变量副本,可以使用
ThreadLocal类。ThreadLocal为每个线程提供一个独立的变量副本,避免了共享状态。public class ThreadLocalExample {private static final ThreadLocal<Integer> threadLocalCounter = ThreadLocal.withInitial(() -> 0);public static void incrementThreadLocalCounter() {threadLocalCounter.set(threadLocalCounter.get() + 1);}public static void main(String[] args) {Runnable task = () -> {for (int i = 0; i < 1000; i++) {incrementThreadLocalCounter();}System.out.println("Thread local counter value: " + threadLocalCounter.get());};Thread t1 = new Thread(task);Thread t2 = new Thread(task);t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}} } 
相关文章:
static关键字详解
文章目录 static使用示例static底层原理静态初始化顺序静态与线程安全 static static是Java中的一个关键字,用于定义类级别的成员,类级别的成员是指那些属于整个类,而不是特定对象实例的成员。在Java中,类级别的成员包括静态变量…...
使用 Java RestClient 与 Elasticsearch 进行索引管理的示例
文章目录 准备工作测试连接创建索引查询索引是否存在删除索引总结 在这篇博客中,我将和大家分享如何使用 Java RestClient 与 Elasticsearch 进行简单的索引管理操作。如果你在开发过程中需要对海量数据进行高效搜索和分析,Elasticsearch 可能是个不错的…...
编程-设计模式 10:外观模式
设计模式 10:外观模式 定义与目的 定义:外观模式(Facade Pattern)提供了一个统一的接口,用来访问子系统中的一群接口。它定义了一个高层接口,让子系统更容易使用。目的:简化复杂的子系统的使用…...
非范型ArrayList和泛型List<T>
ArrayList 是 C# 中的一个非泛型集合类,它属于 System.Collections 命名空间。它提供了动态数组的功能,允许你在运行时添加、删除和访问元素。然而,需要注意的是,ArrayList 并不是类型安全的,因为你可以向其中添加任何…...
魔众文库系统v7.0.0版本推荐店铺功能,管理菜单逻辑优化
推荐店铺功能,管理菜单逻辑优化 [新功能] RandomImageProvider 逻辑升级重构,支持更丰富的随机图片生成 [新功能] 资源篮订单参数字段 [新功能] 首页推荐店铺功能,需要在后台 文库系统 → 文库店铺 开启推荐 [系统优化] Grid 快捷编辑请求…...
03、流程控制语句
01、位运算符 一、位运算符:是针对二进制数据(补码)的运算。(0看成false,1看成true) &:按位与——求出两个数字对应的二进制,有0则0 | :按位或——求出两个数字对应的二进制,有1则1 ^ :按位异或 ——求出两个数字对应的二进制,…...
[Android] [解决]Bottom Navigation Views Activity工程带来的fragment底部遮盖的问题
创建了Bottom Navigation Views Activity之后,在fragment_home.xml,加了一个RecyclerView, 后来添加了item之后发现底部会被盖住一部分。 解决:在layout里面加两句: android:paddingBottom"?attr/actionBarSize&…...
Armv8/Armv9架构中的原子性
B2.2 Arm 架构中的原子性 原子性是内存访问的一个特性,描述为原子访问。Arm 架构描述涉及两种类型的原子性:单次复制原子性和多次复制原子性。在 Arm 架构中,内存访问的原子性要求取决于内存类型,以及访问是显式的还是隐式的。有关更多信息,请参见: 单次复制原子性的要求…...
读零信任网络:在不可信网络中构建安全系统15协议和过滤
1. 协议 1.1. IKE/IPSec 1.1.1. 因特网密钥交换协议(Internet Key Exchange,IKE)用于执行IPSec认证和密钥交换 1.1.1.1. 通常以后台守护进程的方式实现,使用预共享密钥或X.509证书来认证对端并创建一个安全会话 1.1.2. IKEv1与IKEv2 1.1.2.1…...
C语言学习笔记 Day11(指针--中2)
Day11 内容梳理: 目录 Chapter 7 指针 7.6 指针 & 函数 (1)形参改变实参的值 (2)字符数组作为函数参数 1)合并字符串 2)删掉字符串中空格 (3)指针作为函数返…...
Golang 并发编程
Golang 并发编程 Goroutine 什么是协程 创建 Goroutine 主 goroutine (main函数)退出后,其它的工作 goroutine 也会自动退出 package mainimport ("fmt""time" )func myFunc() {i : 0for {ifmt.Println("func: …...
【数据结构详解】——选择排序(动图详解)
目录 🕒 1. 直接选择排序🕒 2. 堆排序 🕒 1. 直接选择排序 💡 算法思想:第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始(末尾)位置…...
杂项命令(笔记)
ifconfig :http://t.csdnimg.cn/gT2AR echo :http://t.csdnimg.cn/6DSoO ps和top的区别 http://t.csdnimg.cn/f1XWt...
代码随想录算法训练营Day38||完全背包问题、leetcode 518. 零钱兑换 II 、 377. 组合总和 Ⅳ 、70. 爬楼梯 (进阶)
一、完全背包问题 相较于01背包,完全背包的显著特征是每个物品可以用无数次,遍历顺序也不需要为了保证每个物品只去一次而倒序遍历。 #include<iostream> #include<vector> using namespace std; int main(){int N,V;cin>>N>>V…...
超越链端:Web3的无边界技术革命
Web3,作为互联网技术的第三代变革,正以其去中心化、开放透明的特性,重新定义着我们的数字生活。在这一背景下,“链端”概念逐渐成为热点,意味着我们不仅仅局限于区块链技术本身,而是探索其在更广泛领域的应…...
127. Go反射基本原理
文章目录 反射基础 - go 的 interface 是怎么存储的?iface 和 eface 的结构体定义(runtime/iface.go):_type 是什么?itab 是什么? 反射对象 - reflect.Type 和 reflect.Value反射三大定律Elem 方法reflect.…...
提高PDF电子书的分辨率
解决方法出处 1. 安装ImageMagick brew install imagemagick brew install ghostscript2. 按流程进行 convert -density 600 your_pdf_filename.pdf output-%02d.jpg convert output*.jpg -normalize -threshold 80% final-%02d.jpg convert final*.jpg my_new_highcontras…...
Spring Cloud全解析:注册中心之zookeeper注册中心
zookeeper注册中心 使用zookeeper作为注册中心就不需要像eureka一样,在写一个eureka-server的服务了,因为zookeeper本身就是一个服务端,只需要编写需要进行服务注册的客户端即可 依赖 <!-- zookeeper 注册中心 --> <dependency&g…...
解决戴尔台式电脑休眠后无法唤醒问题
近期发现有少量戴尔的台式机会有休眠后无法唤醒的问题,具体现象就是电脑在休眠后,电源指示灯以呼吸的频率闪烁,无论怎么点鼠标和键盘都没有反应,并且按开机按钮也没法唤醒,只能是长按开机键强制关机再重启才行…...
MySQL运维-分库分表
介绍 问题分析 拆分策略 垂直拆分 水平拆分 实现技术 Mycat概述 介绍 概念介绍 Mycat配置 schema.xml schema标签 schema标签(table) datanode标签 datahost标签 rule.xml sever.xml system标签 user标签 Mycat分片 分片规则-范围 分片规则-取模 分…...
MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例
一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...
聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...
c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制
在数字化浪潮席卷全球的今天,数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具,在大规模数据获取中发挥着关键作用。然而,传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时,常出现数据质…...
安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)
船舶制造装配管理现状:装配工作依赖人工经验,装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书,但在实际执行中,工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...
并发编程 - go版
1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...
第八部分:阶段项目 6:构建 React 前端应用
现在,是时候将你学到的 React 基础知识付诸实践,构建一个简单的前端应用来模拟与后端 API 的交互了。在这个阶段,你可以先使用模拟数据,或者如果你的后端 API(阶段项目 5)已经搭建好,可以直接连…...
leetcode73-矩阵置零
leetcode 73 思路 记录 0 元素的位置:遍历整个矩阵,找出所有值为 0 的元素,并将它们的坐标记录在数组zeroPosition中置零操作:遍历记录的所有 0 元素位置,将每个位置对应的行和列的所有元素置为 0 具体步骤 初始化…...
数据结构:泰勒展开式:霍纳法则(Horner‘s Rule)
目录 🔍 若用递归计算每一项,会发生什么? Horners Rule(霍纳法则) 第一步:我们从最原始的泰勒公式出发 第二步:从形式上重新观察展开式 🌟 第三步:引出霍纳法则&…...
