提到static关键字,相信你们都不陌生,这是相对比较难以理解的一个关键字,相信各位也都能深深感觉的到!本篇文章将好好总结一下static这个关键字。javascript
在开始讲static以前,我想让各位看一段有意思的代码:html
public class Test {
static{
System.out.println("test static 1");
}
static{
System.out.println("test static 2");
}
public static void main(String[] args) {
}
}
复制代码
看完程序,小白童鞋发话了:啥玩意?main方法中啥都没有,能运行啥?博主你个星星星...java
运行结果:
test static 1
test static 2
复制代码
小白童鞋:那啥...那啥...博主我说啥了,我啥都没说...程序员
static的主要意义是在于建立独立于具体对象的域变量或者方法。以至于即便没有建立对象,也能使用属性和调用方法!编程
static关键字还有一个比较关键的做用就是 用来造成静态代码块以优化程序性能。static块能够置于类中的任何地方,类中能够有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每一个static块,而且只会执行一次。安全
为何说static块能够用来优化程序性能,是由于它的特性:只会在类加载的时候执行一次。所以,不少时候会将一些只须要进行一次的初始化操做都放在static代码块中进行。dom
一、被static修饰的变量或者方法是独立于该类的任何对象,也就是说,这些变量和方法不属于任何一个实例对象,而是被类的实例对象所共享。ide
怎么理解 “被类的实例对象所共享” 这句话呢?就是说,一个类的静态成员,它是属于大伙的【大伙指的是这个类的多个对象实例,咱们都知道一个类能够建立多个实例!】,全部的类对象共享的,不像成员变量是自个的【自个指的是这个类的单个实例对象】...我以为我已经讲的很通俗了,你明白了咩?性能
二、在该类被第一次加载的时候,就会去加载被static修饰的部分,并且只在类第一次使用时加载并进行初始化,注意这是第一次用就要初始化,后面根据须要是能够再次赋值的。测试
三、static变量值在类加载的时候分配空间,之后建立类对象的时候不会从新分配。赋值的话,是能够任意赋值的!
四、被static修饰的变量或者方法是优先于对象存在的,也就是说当一个类加载完毕以后,即使没有建立对象,也能够去访问。
由于static是被类的实例对象所共享,所以若是某个成员变量是被全部对象所共享的,那么这个成员变量就应该定义为静态变量。
所以比较常见的static应用场景有:
一、修饰成员变量 二、修饰成员方法 三、静态代码块 四、修饰类【只能修饰内部类也就是静态内部类】 五、静态导包
以上的应用场景将会在下文陆续讲到...
静态变量: static修饰的成员变量叫作静态变量【也叫作类变量】,静态变量是属于这个类,而不是属因而对象。
实例变量: 没有被static修饰的成员变量叫作实例变量,实例变量是属于这个类的实例对象。
还有一点须要注意的是:static是不容许用来修饰局部变量,不要问我问什么,由于java规定的!
静态变量: 静态变量因为不属于任何实例对象,属于类的,因此在内存中只会有一份,在类的加载过程当中,JVM只为静态变量分配一次内存空间。
实例变量: 每次建立对象,都会为每一个对象分配成员变量内存空间,实例变量是属于实例对象的,在内存中,建立几回对象,就有几份成员变量。
怎么理解呢?打个比喻吧...就比方说程序员小王是一个比较温柔阳光的男孩子,这1024的这一天,老板闲的没事,非要拉着程序员小王来玩耍,怎么个玩法呢?老板和小王一人拿着一把菜刀,规则很简单,互相伤害,一人一刀,你一刀,我一刀....游戏一开始,老板二话不说,跳起来就是一刀,程序员小王二话也没说反手就是一菜刀回去,这个时候老板发飙了,双眼瞪得忒大,跳起来又是一刀,这个时候程序员小王不敢还手了,就没动手。没想到老板愈来愈生猛,左一刀右一刀全程下来差很少砍个半个时....程序员小王一直没有还过手,由于小王知道他是老板...
这个程序员小王只会在老板第一次挥刀的时候,回老板一刀,以后就不还手了,这个时候咱们把程序员小王看作是静态变量,把老板第一次向小王挥刀看作是类加载,把小王回老板一刀看出是分配内存空间,而一人一刀这个回合过程当作是类加载的过程,以后老板的每一刀都当作是建立一次对象。
连贯起来就是static变量值在类第一次加载的时候分配空间,之后建立类对象的时候不会从新分配
以后这个老板挨了一刀以后躺医院了一年,一出院回到公司第一件事就是拉程序员宜春出来玩耍,老板却不知其然,这个博主程序员宜春性格异常暴躁,老板递给程序员宜春一把菜刀,博主宜春一接过菜刀,猝不及防的被老板跳起来就是一刀,程序员宜春痛的嗷了一声,暴躁的程序员宜春还没嗷完,在嗷的同时跳起来就是给老板一刀,接着老板跳起来又是一刀,程序员宜春嗷的一声又是回一刀,老板跳起来又一刀,程序员宜春嗷的一声又是回一刀,只要老板没停程序员宜春就没停,由于程序员宜春知道,就本身这曝脾气,暴躁起来si都敢摸,确定有几个老铁知道....
程序员宜春就相似实例变量,每次建立对象,都会为每一个对象分配成员变量内存空间,就像老板来一刀,程序员宜春都会回一刀这样子的...
咱们都知道静态变量是属于这个类,而不是属因而对象,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
复制代码
回过头再去品味一下上面的那段话,你就能很是客观明了了,这个思想概念要有只是这种用法不推荐!
所以小结一下访问静态变量和实例变量的两种方法:
静态变量:
类名.静态变量
对象.静态变量(不推荐)
静态方法:
类名.静态方法
对象.静态方法(不推荐)
static修饰的方法也叫作静态方法,不知道各位发现咩有,其实咱们最熟悉的static静态方法就是main方法了~小白童鞋:喔好像真的是哦~。因为对于静态方法来讲是不属于任何实例对象的,this指的是当前对象,由于static静态方法不属于任何对象,因此就谈不上this了。
还有一点就是:构造方法不是静态方法!
先看个程序吧,看看自个是否掌握了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;
}
}
复制代码
static变量也称做静态变量,静态变量和非静态变量的区别是:静态变量被全部的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在建立对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。
还有一点就是static成员变量的初始化顺序按照定义的顺序进行初始化。
静态内部类与非静态内部类之间存在一个最大的区别,咱们知道非静态内部类在编译完成以后会隐含地保存着一个引用,该引用是指向建立它的外围内,可是静态内部类却没有。没有这个引用就意味着:
一、它的建立是不须要依赖外围类的建立。 二、它不能使用任何外围类的非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.INSTANCE
时 SingletonHolder
才会被加载,此时初始化 INSTANCE
实例,而且 JVM 能确保 INSTANCE
只被实例化一次。
这种方式不只具备延迟初始化的好处,并且由 JVM 提供了对线程安全的支持。
静态导包格式: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);
}
}
复制代码
静态导包在书写代码的时候确实能省一点代码,能够直接调用里面的静态成员,可是会影响代码可读性,因此开发中通常状况下不建议这么使用。
一、静态只能访问静态。 二、非静态既能够访问非静态的,也能够访问静态的。
到这里文章本该结束了的,可是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.7282093281367935
static修饰的 t=0.30720545678577604
final修饰的 i=0.8106990945706758
static修饰的 t=0.30720545678577604
t+1= 1.307205456785776
复制代码
static修饰的变量没有发生变化是由于static做用于成员变量只是用来表示保存一份副本,其不会发生变化。怎么理解这个副本呢?其实static修饰的在类加载的时候就加载完成了(初始化),并且只会加载一次也就是说初始化一次,因此不会发生变化!
至于final修饰的反而发生变化了?是否是巅覆你对final的见解?关于final详细讲解博主也准备好了一篇文章程序员你真的理解final关键字吗?
ok,文章就先到这里了,但愿这篇文章可以帮助到你对static的认识,如有不足或者不正之处,但愿谅解并欢迎批评指正!
若是本文章对你有帮助,哪怕是一点点,那就请点一个赞呗,谢谢~
参考: 《java编程思想》 baijiahao.baidu.com/s?id=160125… blog.csdn.net/qq_34337272… www.cnblogs.com/dolphin0520…
最后,欢迎各位关注个人公众号,一块儿探讨技术,向往技术,追求技术