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分片 分片规则-范围 分片规则-取模 分…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

Razor编程中@Html的方法使用大全
文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...
【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error
在前端开发中,JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作(如 Promise、async/await 等),开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝(r…...

uniapp 小程序 学习(一)
利用Hbuilder 创建项目 运行到内置浏览器看效果 下载微信小程序 安装到Hbuilder 下载地址 :开发者工具默认安装 设置服务端口号 在Hbuilder中设置微信小程序 配置 找到运行设置,将微信开发者工具放入到Hbuilder中, 打开后出现 如下 bug 解…...
React核心概念:State是什么?如何用useState管理组件自己的数据?
系列回顾: 在上一篇《React入门第一步》中,我们已经成功创建并运行了第一个React项目。我们学会了用Vite初始化项目,并修改了App.jsx组件,让页面显示出我们想要的文字。但是,那个页面是“死”的,它只是静态…...

高端性能封装正在突破性能壁垒,其芯片集成技术助力人工智能革命。
2024 年,高端封装市场规模为 80 亿美元,预计到 2030 年将超过 280 亿美元,2024-2030 年复合年增长率为 23%。 细分到各个终端市场,最大的高端性能封装市场是“电信和基础设施”,2024 年该市场创造了超过 67% 的收入。…...
13.10 LangGraph多轮对话系统实战:Ollama私有部署+情感识别优化全解析
LangGraph多轮对话系统实战:Ollama私有部署+情感识别优化全解析 LanguageMentor 对话式训练系统架构与实现 关键词:多轮对话系统设计、场景化提示工程、情感识别优化、LangGraph 状态管理、Ollama 私有化部署 1. 对话训练系统技术架构 采用四层架构实现高扩展性的对话训练…...