Java 关键字static 还搞不懂?看这一篇文章就足够了!详细解释!

1、static存在的主要意义

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

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

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

2、静态:关键字static的概述

2.1 理解static

static关键字可以用来修饰代码块表示静态代码块修饰成员变量表示全局静态成员变量修饰方法表示静态方法。(注意:不能修饰普通类,除了内部类,这是为什么?)

class A {
    
    
	static {
    
    
		System.out.println("A : 静态代码块");
	}
	
	static int i ;  // 静态变量
	
	static void method() {
    
    
		System.out.println("A: 静态方法");
	}
}

简而言之,被static关键字修饰的内容都是静态的。

静态是相对于动态的。

动态是指Java程序在JVM上运行时,JVM会根据程序的需要动态创建对象并存储对象(分配内存) ,对象使命结束后,对象会被垃圾回收器销毁,即内存回收由JVM统一管理并分配给其他新创建的对象;

静态是指Java程序还没有运行时,JVM就会为加载的类分配空间存储被static关键字修饰的内容 ;如静态成员变量,Java类加载到JVM中,JVM会把类以及类的静态成员变量存储在方法区,我们知道方法区是线程共享且很少发生GC的区域,所以被static关键字修饰的内容都是全局共享的,且只会为其分配一次存储空间

所以当类的某些内容不属于对象,而由对象共享即属于类的时候,就可以考虑是否用static关键字进行修饰

怎么理解“类的加载在虚拟机运行期间只加载一次”?就是说,类(图纸)是用来创建对象(房子)的,图纸打印一张就够了,房子就可以造无数个。静态指的是打印图纸(加载类)时一起做的事情,比如在图纸(类)上加个批注(赋值静态属性)或者给图纸贴个膜(执行静态代码块),全体的对象(房子)都会使用同样的批注(静态属性);图纸只需要一张膜也只需要贴一次。
其实再往深里讲,类(图纸)也是一个对象,是class类的对象,虚拟机一运行,就先打印了各种各样的图纸(加载了类),但图纸是住不了人的,只有变成房子(类创建了对象)才能住人(操作对象的方法和属性)。

2.2 概念小结

static表示“静态”的意思,可以用来修饰成员变量和成员方法(后续还会学习 静态代码块 和 静态内部类)。

static的主要作用在于创建独立于具体对象域变量或者方法

简单理解:

  • 被static关键字修饰的方法或者变量不需要依赖于对象来进行访问只要类被加载了,就可以通过类名去进行访 问
  • 并且不会因为对象的多次创建 ,而在内存中建立多份数据

2.3 重点

静态成员 在类加载时加载并初始化。

  • 无论一个类存在多少个对象 , 静态的属性, 永远在内存中只有一份( 可以理解为所有对象公用 )

类变量,是类的共享内容,只要赋值一次,都会使用第一次赋值的内容,注意这是第一次用就要初始化,后面根据需要是可以再次赋值的。

比如 人类都有一个国家,国家使用static属性,假设国家为中国,创建6个对象,如果只赋值一次。6个对象的国家属性都是中国。如果第4个对象的国家改为英国,第5个和第6个国家属性值就为英国

  • 在访问时: 静态不能访问非静态 , 非静态可以访问静态 !(静态表示对象未创建也可调用【只要类被加载即可】,而非静态必须等到对象创建完毕。所以使用静态时,对象可能还未创建,因此不能访问非静态)

3、 静态:关键字static的用法

static关键字的作用

A. 修饰代码块

类中用static关键字修饰的代码块称为静态代码,反之没有用static关键字修饰的代码块称为实例代码块。

实例代码块会随着对象的创建而执行,即每个对象都会有自己的实例代码块,表现出来就是实例代码块的运行结果会影响当前对象的内容,并随着对象的销毁而消失(内存回收);而静态代码块是当Java类加载到JVM内存中而执行的代码块,由于类的加载在JVM运行期间只会发生一次,所以静态代码块也只会执行一次。

因为静态代码块的主要作用是用来进行一些复杂的初始化工作,所以静态代码块跟随类存储在方法区的表现形式是静态代码块执行的结果存储在方法区,即初始化量存储在方法区并被线程共享。

	static {
    
    
		//这里是被static修饰的代码段内容
	}

B.修饰成员变量

类中用static关键字修饰的成员变量称为静态成员变量,因为static不能修饰局部变量(为什么?),因此静态成员变量也能称为静态变量。

静态变量跟代码块类似,在类加载到JVM内存中,JVM会把静态变量放入方法区并分配内存,也由线程共享。访问形式是:类名.静态成员名

public class StaticTest {
    
    
	public static void main(String[] args) {
    
    
		System.out.println(D.i);
		System.out.println(new D().i);
	}
}
class D {
    
    
	static {
    
    
		i = 2;
		System.out.println("D : 静态代码块1");
	}
	static int i;
}

运行结果

D : 静态代码块1
2
2

静态变量存储在类的信息中,且可以在线程间共享,那么它当然也属于该类的每个对象,因此可以通过对象访问静态变量,但编译器并不支持这么做,且会给出警告。

注意:

  • 一个类的静态变量和该类的静态代码块的加载顺序。类会优先加载静态变量,然后加载静态代码块,但有多个静态变量和多个代码块时,会按照编写的顺序进行加载。
class D {
    
    
	static {
    
    
		i = 2 ;
		System.out.println("D:静态代码块1");
	}
	
	static {
    
    
		i = 6;
		System.out.println("D:静态代码块2");
	}
	static int i ;
}
  • 静态变量可以不用显式的初始化,JVM会默认给其相应的默认值。如基本数据类型的byte为0,short为0,char为\u0000,int为0,long为0L,float为0.0f,double为0.0d,boolean为false,引用类型统一为null。
  • 静态变量既然是JVM内存中共享的且可以改变,那么对它的访问会引起线程安全问题(线程A改写的同时,线程B获取它的值,那么获取的是修改前的值还是修改后的值呢?),所以使用静态变量的同时要考虑多线程情况。如果能确保静态变量不可变,那么可以用final关键字一起使用避免线程安全问题;否则需要采用同步的方式避免线程安全问题,如与volatile关键字一起使用等。
  • static关键不能修饰局部变量,包括实例方法和静态方法,不然就会与static关键字的初衷-共享相违背。

C.修饰方法

用static关键字修饰的方法称为静态方法,否则称为实例方法。

通过类名.方法名调用,但需要注意静态方法可以直接调用类的静态变量和其他静态方法,不能直接调用成员变量和实例方法(除非通过new一个对象调用)。

class D {
    
    
	static {
    
    
		i = 2;
		System.out.println("D : 静态代码块");
	}
	static final int i;
	int j;
	
	static void method() {
    
    
		System.out.println(i);
		System.out.println(new D().j);
		
		method1();
		new D().method2();
	}
	
	static void method1() {
    
    
		System.out.println(i);
	}
	void method2() {
    
    
		System.out.println(i);
	}
}

注意: 既然类的实例方法需要对象调用才能访问,而静态方法直接通过类名就能访问,那么在不考虑部署服务器的情况下,一个类是如何开始执行的呢?最大的可能就是通过“类名.静态方法”启动Java,而我定义那么多静态方法,JVM又是如何知道主入口呢?

或许,你想到了main方法。

没错,就是main方法被Java规范定义成Java类的主入口。Java类的运行都由main方法开启:

public static void main(String[] args) {
    
    
	for (String arg : args) {
    
       // 参数由外部定义
		System.out.println(arg);
	}
}

但注意main并不是Java关键字,它只是一个规定的程序入口的方法名字;另外main方法可以重载。

注意: static关键字虽然不能修饰普通类,但可以用static关键字修饰内部类使其变成静态内部类。 static关键字本身的含义就是共享,而Java类加载到JVM内存的方法区,也是线程共享的,所以没必要用static关键字修饰普通类。

static关键字的缺点

封装是Java类的三大特性之一,也是面向对象的主要特性。因为不需要通过对象,而直接通过类就能访问类的属性和方法,这有点破坏类的封装性;所以除了Utils类,代码中应该尽量少用static关键字修饰变量和方法。

4、static的独特之处(***)

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

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

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

怎么理解“类的加载在虚拟机运行期间只加载一次”?就是说,类(图纸)是用来创建对象(房子)的,图纸打印一张就够了,房子就可以造无数个。静态指的是打印图纸(加载类)时一起做的事情,比如在图纸(类)上加个批注(赋值静态属性)或者给图纸贴个膜(执行静态代码块),全体的对象(房子)都会使用同样的批注(静态属性);图纸只需要一张膜也只需要贴一次。
其实再往深里讲,类(图纸)也是一个对象,是class类的对象,虚拟机一运行,就先打印了各种各样的图纸(加载了类),但图纸是住不了人的,只有变成房子(类创建了对象)才能住人(操作对象的方法和属性)。

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

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

5、举例

1,假设员工有属性:姓名、公司。声明若干个对象时,原先的做法如下:
在这里插入图片描述
2,当公司迁址,那么每个对象中的地址属性都需要进行修改(时间开销大)。此外公司地址都是相同的,不同对象重复的创建地址变量,有浪费空间的嫌疑(空间开销大);

当前内存状况如下:
在这里插入图片描述
3,解决方法:static修饰

使用static修饰的变量,存放在方法区中:
在这里插入图片描述
4,static修饰的变量,可以理解为类的属性,直接通过类名来调用,所以在创建对象之前就可以调用
在这里插入图片描述

总结

这块内容比较抽象,先把概念过一遍再结合代码执行,就将抽象变简单,祝各位在学习路上一路畅通!

猜你喜欢

转载自blog.csdn.net/weixin_46312449/article/details/112633175