深入理解static关键字

文章目录

  • 1、static存在的主要意义
  • 2、static的独特之处
  • 3、static应用场景
  • 4、静态变量和实例变量的概念
  • 5、静态变量和实例变量区别【重点常用】
  • 6、访问静态变量和实例变量的两种方式
  • 7、static静态方法
  • 8、static静态代码块
  • 9、static变量与普通变量区别
  • 10、静态内部类
  • 11、静态导包
  • 12、static注意事项
  • 13、final与static的藕断丝连

提到static关键字,相信大家都不陌生,这是相对比较难以理解的一个关键字,相信各位也都能深深感受的到!本篇文章将好好总结一下static这个关键字。

在开始讲static之前,我想让各位看一段有意思的代码:

public class Test {static{System.out.println("test static 1");}static{System.out.println("test static 2");}public static void main(String[] args) {}
}

看完程序,小白童鞋发话了:啥玩意?main方法中啥都没有,能运行啥?博主你个星星星…

运行结果:
test static 1
test static 2

小白童鞋:那啥…那啥…博主我说啥了,我啥都没说…

其实,上面的代码懂的自然懂,不懂的自然就不懂了,因为上面的代码涉及到JVM的类加载了!当然不在本篇博客文章的范畴内,如果有兴趣理解上面的程序,这篇文章可能会对你有所帮助

这篇文章绝对让你深刻理解java类的加载以及ClassLoader源码分析

1、static存在的主要意义

static的主要意义是在于创建独立于具体对象的域变量或者方法。以致于即使没有创建对象,也能使用属性和调用方法

static关键字还有一个比较关键的作用就是 用来形成静态代码块以优化程序性能。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。

为什么说static块可以用来优化程序性能,是因为它的特性:只会在类加载的时候执行一次。因此,很多时候会将一些只需要进行一次的初始化操作都放在static代码块中进行。

2、static的独特之处

1、被static修饰的变量或者方法是独立于该类的任何对象,也就是说,这些变量和方法不属于任何一个实例对象,而是被类的实例对象所共享

怎么理解 “被类的实例对象所共享” 这句话呢?就是说,一个类的静态成员,它是属于大伙的【大伙指的是这个类的多个对象实例,我们都知道一个类可以创建多个实例!】,所有的类对象共享的,不像成员变量是自个的【自个指的是这个类的单个实例对象】…我觉得我已经讲的很通俗了,你明白了咩?

2、在该类被第一次加载的时候,就会去加载被static修饰的部分,而且只在类第一次使用时加载并进行初始化,注意这是第一次用就要初始化,后面根据需要是可以再次赋值的。

3、static变量值在类加载的时候分配空间,以后创建类对象的时候不会重新分配。赋值的话,是可以任意赋值的!

4、被static修饰的变量或者方法是优先于对象存在的,也就是说当一个类加载完毕之后,即便没有创建对象,也可以去访问。

3、static应用场景

因为static是被类的实例对象所共享,因此如果某个成员变量是被所有对象所共享的,那么这个成员变量就应该定义为静态变量

因此比较常见的static应用场景有:

1、修饰成员变量
2、修饰成员方法
3、静态代码块
4、修饰类【只能修饰内部类也就是静态内部类】
5、静态导包

以上的应用场景将会在下文陆续讲到…

4、静态变量和实例变量的概念

静态变量:
static修饰的成员变量叫做静态变量【也叫做类变量】,静态变量是属于这个类,而不是属于是对象。

实例变量:
没有被static修饰的成员变量叫做实例变量,实例变量是属于这个类的实例对象。

还有一点需要注意的是:static是不允许用来修饰局部变量,不要问我问什么,因为java规定的!

5、静态变量和实例变量区别【重点常用】

静态变量:
静态变量由于不属于任何实例对象,属于类的,所以在内存中只会有一份,在类的加载过程中,JVM只为静态变量分配一次内存空间。

实例变量:
每次创建对象,都会为每个对象分配成员变量内存空间,实例变量是属于实例对象的,在内存中,创建几次对象,就有几份成员变量。

6、访问静态变量和实例变量的两种方式

我们都知道静态变量是属于这个类,而不是属于是对象,static独立于对象。

但是各位有木有想过:静态成员变量虽然独立于对象,但是不代表不可以通过对象去访问,所有的静态方法和静态变量都可以通过对象访问【只要访问权限足够允许就行】,不理解没关系,来个代码就理解了

public class StaticDemo {static int value = 666;public static void main(String[] args) throws Exception{new StaticDemo().method();}private void method(){int value = 123;System.out.println(this.value);}}

猜想一下结果,我猜你的结果是123,哈哈是咩?其实

运行结果: 666

当然肯定有一些基础非常扎实的大佬会问为什么会有同学认为输出是123 。里面定义的value=123只是一个很普通的局部变量而已 。和成员变量没有半毛钱关系 。和静态变量也没有关系。是的!确实如这位大佬说的一样!只是博主我说的同学是小白同学,站在小白童鞋的角度上,所以还望大佬理解见谅!我举的这个StaticDemo例子主要目的是说明一下this也是可以访问static的变量的!!!value=123只是一个跑龙套的配角,旨在让小白同学认清其中的关系。

回过头再去品味一下上面的那段话,你就能非常客观明了了,这个思想概念要有只是这种用法不推荐!

因此小结一下访问静态变量和实例变量的两种方法:

静态变量:

类名.静态变量

对象.静态变量(不推荐)

静态方法:

类名.静态方法

对象.静态方法(不推荐)

7、static静态方法

static修饰的方法也叫做静态方法,不知道各位发现咩有,其实我们最熟悉的static静态方法就是main方法了小白童鞋:喔好像真的是哦。由于对于静态方法来说是不属于任何实例对象的,this指的是当前对象,因为static静态方法不属于任何对象,所以就谈不上this了。

还有一点就是:构造方法不是静态方法

8、static静态代码块

先看个程序吧,看看自个是否掌握了static代码块,下面程序代码继承关系为 BaseThree——> BaseTwo——> BaseOne

BaseOne类

package com.gx.initializationblock;public class BaseOne {public BaseOne() {System.out.println("BaseOne构造器");}{System.out.println("BaseOne初始化块");System.out.println();}static {System.out.println("BaseOne静态初始化块");}}

BaseTwo类

package com.gx.initializationblock;public class BaseTwo extends BaseOne {public BaseTwo() {System.out.println("BaseTwo构造器");}{System.out.println("BaseTwo初始化块");}static {System.out.println("BaseTwo静态初始化块");}
}

BaseThree 类

package com.gx.initializationblock;public class BaseThree extends BaseTwo {public BaseThree() {System.out.println("BaseThree构造器");}{System.out.println("BaseThree初始化块");}static {System.out.println("BaseThree静态初始化块");}
}

测试demo2类

package com.gx.initializationblock;/*注:这里的ABC对应BaseOne、BaseTwo、BaseThree * 多个类的继承中初始化块、静态初始化块、构造器的执行顺序在继承中,先后执行父类A的静态块,父类B的静态块,最后子类的静态块,然后再执行父类A的非静态块和构造器,然后是B类的非静态块和构造器,最后执行子类的非静态块和构造器*/
public class Demo2 {public static void main(String[] args) {BaseThree baseThree = new BaseThree();System.out.println("-----");BaseThree baseThree2 = new BaseThree();}
}

运行结果

BaseOne静态初始化块
BaseTwo静态初始化块
BaseThree静态初始化块
BaseOne初始化块BaseOne构造器
BaseTwo初始化块
BaseTwo构造器
BaseThree初始化块
BaseThree构造器
-----
BaseOne初始化块BaseOne构造器
BaseTwo初始化块
BaseTwo构造器
BaseThree初始化块
BaseThree构造器

至于static代码块运行结果不是很清晰的童鞋,详细讲解请看这篇Static静态代码块以及各代码块之间的执行顺序

以上仅仅是让各位明确代码块之间的运行顺序,显然还是不够的,静态代码块通常用来对静态变量进行一些初始化操作,比如定义枚举类,代码如下:

public enum WeekDayEnum {MONDAY(1,"周一"),TUESDAY(2, "周二"),WEDNESDAY(3, "周三"),THURSDAY(4, "周四"),FRIDAY(5, "周五"),SATURDAY(6, "周六"),SUNDAY(7, "周日");private int code;private String desc;WeekDayEnum(int code, String desc) {this.code = code;this.desc = desc;}private static final Map<Integer, WeekDayEnum> WEEK_ENUM_MAP = new HashMap<Integer, WeekDayEnum>();// 对map进行初始化static {for (WeekDayEnum weekDay : WeekDayEnum.values()) {WEEK_ENUM_MAP.put(weekDay.getCode(), weekDay);}}public static WeekDayEnum findByCode(int code) {return WEEK_ENUM_MAP.get(code);}public int getCode() {return code;}public void setCode(int code) {this.code = code;}public String getDesc() {return desc;}public void setDesc(String desc) {this.desc = desc;}
} 

当然不仅仅是枚举这一方面,还有我们熟悉的单例模式同样也用到了静态代码块,如下:

public class Singleton {private static Singleton instance;static {instance = new Singleton();}private Singleton() {}public static Singleton getInstance() {return instance;}
}

9、static变量与普通变量区别

static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。

还有一点就是static成员变量的初始化顺序按照定义的顺序进行初始化。

10、静态内部类

静态内部类与非静态内部类之间存在一个最大的区别,我们知道非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围内,但是静态内部类却没有。没有这个引用就意味着:

1、它的创建是不需要依赖外围类的创建。
2、它不能使用任何外围类的非static成员变量和方法。

代码举例(静态内部类实现单例模式)

public class Singleton {// 声明为 private 避免调用默认构造方法创建对象private Singleton() {}// 声明为 private 表明静态内部该类只能在该 Singleton 类中被访问private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}public static Singleton getUniqueInstance() {return SingletonHolder.INSTANCE;}
}

Singleton 类加载时,静态内部类 SingletonHolder 没有被加载进内存。只有当调用 getUniqueInstance()方法从而触发 SingletonHolder.INSTANCESingletonHolder 才会被加载,此时初始化 INSTANCE 实例,并且 JVM 能确保 INSTANCE 只被实例化一次。

这种方式不仅具有延迟初始化的好处,而且由 JVM 提供了对线程安全的支持。

11、静态导包

静态导包格式:import static

这两个关键字连用可以指定导入某个类中的指定静态资源,并且不需要使用类名调用类中静态成员,可以直接使用类中静态成员变量和成员方法

//  Math. --- 将Math中的所有静态资源导入,这时候可以直接使用里面的静态方法,而不用通过类名进行调用
//  如果只想导入单一某个静态方法,只需要将换成对应的方法名即可import static java.lang.Math.;
//  换成import static java.lang.Math.max;具有一样的效果public class Demo {public static void main(String[] args) {int max = max(1,2);System.out.println(max);}
}

静态导包在书写代码的时候确实能省一点代码,可以直接调用里面的静态成员,但是会影响代码可读性,所以开发中一般情况下不建议这么使用。

12、static注意事项

1、静态只能访问静态。
2、非静态既可以访问非静态的,也可以访问静态的。

13、final与static的藕断丝连

到这里文章本该结束了的,但是static的使用始终离不开final字眼,二者可谓藕断丝连,常常繁见,我觉得还是很有必要讲讲,那么一起来看看下面这个程序吧。

package Demo;class FinalDemo {public final double i = Math.random();public static double t = Math.random();
}public class DemoDemo {public static void main(String[] args) {FinalDemo demo1 = new FinalDemo();FinalDemo demo2 = new FinalDemo();System.out.println("final修饰的  i=" + demo1.i);System.out.println("static修饰的 t=" + demo1.t);System.out.println("final修饰的  i=" + demo2.i);System.out.println("static修饰的 t=" + demo2.t);System.out.println("t+1= "+ ++demo2.t );
//      System.out.println( ++demo2.i );//编译失败}
}
运行结果:final修饰的  i=0.7282093281367935static修饰的 t=0.30720545678577604final修饰的  i=0.8106990945706758static修饰的 t=0.30720545678577604t+1= 1.307205456785776

static修饰的变量没有发生变化是因为static作用于成员变量只是用来表示保存一份副本,其不会发生变化。怎么理解这个副本呢?其实static修饰的在类加载的时候就加载完成了(初始化),而且只会加载一次也就是说初始化一次,所以不会发生变化!

至于final修饰的反而发生变化了?是不是巅覆你对final的看法?关于final详细讲解博主也准备好了一篇文章程序员你真的理解final关键字吗?

ok,文章就先到这里了,希望这篇文章能够帮助到你对static的认识,若有不足或者不正之处,希望谅解并欢迎批评指正!

参考:
《java编程思想》
http://baijiahao.baidu.com/s?id=1601254463089390982&wfr=spider&for=pc
https://blog.csdn.net/qq_34337272/article/details/82766943
https://www.cnblogs.com/dolphin0520/p/3799052.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/556024.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

kafka数据不丢失不重复_如何配置 KAFKA 使其消息不会丢失

不可靠的KAFKA这里的不可靠是指代KAFKA其设计之初就为高性能而设计&#xff0c;其是允许消息丢失的&#xff0c;但经过多个版本的升级之后&#xff0c;通过KAFKA的相关配置&#xff0c;我们可以将其作为可靠的队列(不丢消息的队列)。在本文里&#xff0c;不会具体列出要改哪个参…

聊聊 vue 生命周期

一、常用的常用的生命周期钩子&#xff1a; mounted: 发送 ajax 请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】 mounted() {console.log(vm 实例被挂载之后&#xff1a;mounted);this.timer setInterval(() > {...} }beforeDestroy: 清除定时器、解绑自定…

程序员你真的理解final关键字吗?

文章目录1、修饰类2、修饰方法3、修饰变量4、final变量修饰变量&#xff08;成员变量、局部变量&#xff09;4、1 final修饰成员变量&#xff1a;4、2 final修饰局部变量&#xff1a;5、final变量和普通变量的区别6、final与static的藕断丝连7、关于final修饰参数的争议前言 提…

axios vue 动态date_Web前端Vue系列之-Vue.js 实战

课程简介&#xff1a;课程目标&#xff1a;通过本课程的学习&#xff0c;让大家掌握Vue.js的进阶知识并在项目中应用。适用人群&#xff1a;具有一定vue开发基础的开发人员。课程概述&#xff1a;Vue (读音 /vjuː/&#xff0c;类似于 view) 是一套用于构建用户界面的渐进式Jav…

Static静态代码块以及各代码块之间的执行顺序

文章目录代码块的分类1、静态代码块&#xff08;也叫静态块、静态初始化块&#xff09;2、构造代码块&#xff08;也叫构造初始化块&#xff09;3、代码块&#xff08;又叫普通代码块、初始化块&#xff09;执行顺序的代码测试继承中各代码块的执行顺序前言 在Java中&#xff0…

vue 组件 - 非单文件组件

一、定义组件 const school Vue.extend({name: xuexiao, // ----------------------------> 指定组件在开发者工具中显示的名字template: // ----------------------------> 模板<div>... 此处是结构</div>,data() { // ---------------------------->…

matlab安装好 启动总是闪退_在Ubuntu16.04下安装MATLAB2017b

1. 前言最近在折腾Ubuntu系统的高端机子&#xff0c;matlab这家伙的交互和可视化太方便了&#xff01;于是想在Linux下安装matlab&#xff0c;在各个版本中&#xff0c;matlab2017的性价比是非常高的&#xff0c;因此选择安装介个。阅读了很多帖子&#xff0c;综合找到几个靠谱…

vue 脚手架

一、 脚手架 初始 全局安装脚手架 npm i -g vue/cli切换到项目根目录&#xff0c;使用 vue create 项目名称 创建项目使用 npm run serve | yarn serve 启动项目 如果中途卡顿 使用 npm 淘宝镜像 npm config set registry --> https://registry.npm.taobao.orgvue 隐藏了…

这篇文章绝对让你深刻理解java类的加载以及ClassLoader源码分析

前言 package com.jvm.classloader;class Father2{public static String strFather"HelloJVM_Father";static{System.out.println("Father静态代码块");} }class Son2 extends Father2{public static String strSon"HelloJVM_Son";static{Syste…

bigdecimal js 判断等于0_为啥阿里禁用BigDecimal的equals方法做等值比较

BigDecimal&#xff0c;相信对于很多人来说都不陌生&#xff0c;很多人都知道它的用法&#xff0c;这是一种java.math包中提供的一种可以用来进行精确运算的类型。很多人都知道&#xff0c;在进行金额表示、金额计算等场景&#xff0c;不能使用double、float等类型&#xff0c;…

脚手架 - ref

被用来给元素或子组件注册引用信息&#xff08;id的替代者&#xff09;应用在 html 标签上获取的是真实 DOM 元素&#xff0c;应用在组件标签上的是组件实例对象 VueComponent{}使用方式 (1). 打标识 <!-- 给真实 DOM 元素打标识 --> <h1 ref"xxx">...&…

git提交到github总是要输入密码_GitHub不为人知的秘密

本来想叫《GitHub骚操作》的&#xff0c;发现相关的文章已经有很多&#xff0c;而且和我本章要讲的内容完全不同&#xff0c;所以就换了这个也算贴切的标题。起因之前写过一篇文章《VM Manager 插件分享》&#xff0c;事后我发现有个地方不对劲&#xff01;看之前文章标题大家应…

MyBatis中大于号以及小于号的表达方式

mybatis使用的是xml格式的文件。使用>和<号的时候&#xff0c;会存在与xml的标签的规范冲突。 1.场景还原 在实际项目中&#xff0c;有很多需求需要通过设定一个具体的时间段来搜索或过滤所需的数据&#xff0c;今天笔者就mybatis中时间比较涉及到的大于&#xff0c;小于…

脚手架 - props

一、props — 让组件接收外部传过来的数据 传递数据 <Example name"xxx"/>接收数据 // 1. 只接收 props: [name]// 2. 接收并限制类型 props: {name: String }// 3. 接收、限制类型、必要性、默认值 props: {name: {type: String, // 类型required: true, /…

mybatis plus当月数据查询_Springboot+mybatis(plus)+druid多数据源

前言:我不太喜欢AOP读自定义注解来切换数据源.因为如果我一个业务里需要同时查2个数据源的数据而又不想把这个业务拆成2个方法的时候,就比较麻烦了.所以我打算根据package来扫描注入不同的DataSource.可能是我搜索的姿势不太对 , 资料比较少.也会碰到不少坑.所以记录一下.正文:…

mybatis里mapper.xml中SQL语句if语句嵌套if语句

为了实现一个sql可以根据条件不同实现sql语句的动态查询&#xff0c;所以在使用mybatis时&#xff0c;对应的mapper.xml的sql语句可以根据条件值的不同执行不同的sql语句&#xff0c; 最开始在我的where子句中我的if语句是这么写的&#xff1a; <where><if test"…

脚手架 mixin (混入)

可以把多个组件共用的配置提取成一个混入对象 使用方式&#xff1a; 定义混合 {data() {},methods() {}... }使用混合 // 1. 引入混合 import {hunhe1, hunhe2} from /..mixin/// 1. 在 main.js 中全局混入&#xff1a; Vue.mixin(hunhe1)// 2. 局部混入 mixins: [hunhe1]

ug冲模标准件库_昆山兴模lt;携手gt;武汉益模,打通冲模“智能设计+精益管理”的最后一道关卡...

武汉益模科技股份有限公司是国内领先的工业互联网及智能制造的解决方案提供商&#xff0c;为模具、装备、军工、汽车、家电及等有柔性化生产需求的工厂提供专业的智能设计/信息化管理/智能加工/数字化工厂等工业软件、工业机器人自动化以及智能装备&#xff0c;拥有麦格纳、安波…

SpringBoot 异常回滚 事务的使用___Springboot @Transactional 事务不回滚

Springboot中事务的使用&#xff1a; 1、启动类加上EnableTransactionManagement注解&#xff0c;开启事务支持&#xff08;其实默认是开启的&#xff09;。 2、在使用事务的public&#xff08;只有public支持事务&#xff09;方法&#xff08;或者类-相当于该类的所有public…

简述网络调研的作用_利用龙伯球透镜天线提升高铁4G网络覆盖

透镜天线&#xff0c;一种能够通过电磁波&#xff0c;将点源或线源的球面波或柱面波转换为平面波从而获得笔形、扇形或其他形状波束的天线。通过合适设计透镜表面形状和折射率 n&#xff0c;调节电磁波的相速以获得辐射口径上的平面波前。透镜天线吸收了许多光信息工程技术&…