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

AGX orin硬件设计
AGX orin简介 从硬件组成来说,AGX orin可以分为核心板和扩展板。 核心板 核心板就是英伟达原装板卡,如下图所示: 核心板分为32G内存版本和64内存版本,两个版本除去内存不同之外,CPU也略有差异。核心板通过…...

AI大模型开发——2.深度学习基础(1)
学习大模型开发之前,我们需要有足够的储备知识,类似于基础的python语法相信大家也都是十分熟悉了。所以笔者也是考虑了几天决定先给大家补充一些深度学习知识。 首先问大家一个问题,学习大模型之前为什么要先学习深度学习知识呢? …...

go语言day22 gin-vue-admin全栈项目的依赖安装
flipped-aurora/gin-vue-admin: 🚀ViteVue3Gin的开发基础平台,支持TS和JS混用。它集成了JWT鉴权、权限管理、动态路由、显隐可控组件、分页封装、多点登录拦截、资源权限、上传下载、代码生成器【可AI辅助】、表单生成器和可配置的导入导出等开发必备功能…...

PHP之docker学习笔记
Docker学习笔记 前言: 之前学过一遍忘了 那就再来一遍没啥好说的就是可以直接构建一个环境 然后方便部署官网 http://www.docker.com仓库 https://hub.docker.comDocker的基本组成 镜像 容器 仓库 安装与卸载 卸载 sudo yum remove docker \docker-client \dock…...

基于树莓派4B与STM32的UART串口通信实验(代码开源)
在现代嵌入式系统中,树莓派和STM32的结合使用已成为一种流行趋势,它们各自承担不同的角色,实现优势互补。树莓派以其强大的计算能力处理复杂算法,而STM32则以其高效的控制能力执行实际的硬件操作。本文将详细介绍如何实现基于树莓…...

【云服务器系列】基于华为云OBS实现Picgo和Typora的完美融合
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...

IIC协议
一、IIC协议 1.1 IIC协议概述 IIC全称Inter-Integrated Circuit (集成电路总线) 是由PHILIPS(飞利浦)公司在80年代开发的两线式串行总线,用于连接微控制器及其外围设备。IIC属于半双工同步通信方式 特点 简单性和有效性。 由于接口直接在组件之上,…...

如何在linux系统上部署nginx
1)首先去 nginx.org/download 官网下载你所需要的版本 我这里是下载的 nginx-1-23-3.tar.gz 2)然后执行 yum -y install lrzsz 安装文件上传软件 执行 rz 选择你下载nginx的位置进行上传 yum -y install lrzsz 3)执行 tar -zxvf nginx-1.23…...

香港网站服务器抵御恶意攻击的一些措施
香港网站服务器因为在互联网中扮演着重要的角色,因此也在面临着网络中各种恶意攻击的威胁,为了确保香港网站服务器的安全和稳定运行,可以通过安全措施来进行防御,本文就来分享一些香港网站服务器来抵御恶意攻击的关键措施。 一、网…...

实战:docker部署filesite.io完美解决家庭相册需求-2024.8.10(测试成功)
https://wiki.onedayxyy.cn/docs/filesite.io-photot-install-full...