Java关键字之static

简介: Java static 关键字

1 前言

static意思是静态的、全局的,在java中一旦被static修饰,说明被修饰的东西在一定范围内是共享的,谁都可以访问,这时候需要注意并发读写时的线程安全问题。被static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问,这使得我们可以很方便的在没有创建对象的情况下来进行方法/变量的调用/访问。

2 修饰的对象

static 可以用来修饰变量、方法、内部类和代码块。

2.1 静态变量

使用 static 修饰的变量是属于类的,而不是属于对象的,静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。

注意,static 关键字并不能修改变量的访问权限,也就是说 private 变量即使使用 static 关键字修饰,依然只能由本类及本类的对象访问,而用 static 关键字修饰的 public 变量可以被任何类直接访问,而无需初始化类,直接使用 类名.static变量 这种形式访问即可。

在使用多个线程对静态变量进行读写时,需要注意它的线程安全问题,比如对于public static List<String> list = new ArrayList();这样的共享变量在并发环境下进行读写时就会产生线程安全问题,这时我们可以使用线程安全的 CopyOnWriteArrayList 或者在读写时手动进行加锁来保证多线程读写下的线程安全。

静态变量在字节码层面是使用访问标志位来进行标识的,如下图所示:


static变量的访问标志位.jpg


也就是说,在类加载时,JVM会通过class文件中的访问标志位来判断某个变量是否为静态变量。

2.2 静态方法

和静态变量一样,静态方法也可以不依赖于任何对象就直接进行访问,因此对于静态方法来说,是没有 this 的,因为它不依附于任何对象,而 this 本身就是个对象。由于这个特性,在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都是必须依赖具体的对象才能够被调用/访问的,而反过来,非静态成员方法是可以调用/访问静态成员方法/变量的。

static 方法内部的变量在执行时是没有线程安全问题的。方法执行时,数据运行在栈里面,栈里面的数据每个线程都是隔离开的,所以不会有线程安全的问题。

static 方法在字节码层面是使用一个ACC_STATIC的标志位来进行标识的,如下图所示:


static方法的标志位.jpg


2.3 静态内部类

普通类是不允许声明为静态的,只有内部类才可以,使用 static 修饰的内部类可以直接由外部类来创建和访问,而没有用 static 修饰的内部类则必须要先实例化一个外部类的对象,再通过外部类的对象来创建内部类的实例。

2.4 静态代码块

static 关键字还可以修饰代码块用于在类启动之前,初始化一些值。static 块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。在静态代码块中只能调用同样被 static 修饰的变量,并且 static 的变量需要写在静态块的前面,不然编译会报错。

和 static 方法一样,静态代码块在字节码层面使用一个ACC_STATIC的标志位来进行标识。

3 初始化时机

我们通过一个演示程序来测试一下被 static 修饰的类变量、代码块和方法的初始化时机:

publicclassParent {
privatestaticList<String>parentList=newArrayList(){{
System.out.println("父类静态变量初始化");
    }};
static {
System.out.println("父类静态代码块初始化");
    }
publicParent() {
System.out.println("父类构造器初始化");
    }
publicstaticvoidtestStatic() {
System.out.println("父类静态方法被调用");
    }
}

publicclassChildextendsParent {
static {
System.out.println("子类静态代码块初始化");
    }
privatestaticList<String>childList=newArrayList(){{
System.out.println("子类静态变量初始化");
    }};
publicChild() {
System.out.println("子类构造器初始化");
    }
publicstaticvoidmain(String[] args) {
System.out.println(" main 方法执行");
newChild();
    }
}

运行程序,发现打印的结果是:

父类静态变量初始化
父类静态代码块初始化
子类静态代码块初始化
子类静态变量初始化
 main 方法执行
父类构造器初始化
子类构造器初始化

因此,我们可以得出以下结论:

  1. 父类的静态变量和静态代码块比子类优先初始化;
  2. 静态变量和静态块比类构造器优先初始化;
  3. 静态变量和静态代码块按照定义的顺序依次进行初始化;
  4. 被 static 修饰的方法,在类初始化的时候并不会执行,只有当自己被调用时,才会被执行。

4 面试题

如何证明 static 静态变量和类无关?

  1. 不需要初始化类就可直接使用静态变量;
  2. 在类中写个 main 方法运行,即便不写初始化类的代码,静态变量都会自动初始化;
  3. 静态变量只会初始化一次,初始化完成之后,不管再 new 多少个类出来,静态变量都不会再初始化了。
目录
相关文章
|
10天前
|
存储 Java
Java 中的静态(static)
【10月更文挑战第15天】静态是 Java 语言中一个非常重要的特性,它为我们提供了一种方便、高效的方式来管理和共享资源。然而,在使用过程中,我们需要谨慎考虑其优缺点,以确保代码的质量和可维护性。
|
23天前
|
Java 程序员
Java 面试高频考点:static 和 final 深度剖析
本文介绍了 Java 中的 `static` 和 `final` 关键字。`static` 修饰的属性和方法属于类而非对象,所有实例共享;`final` 用于变量、方法和类,确保其不可修改或继承。两者结合可用于定义常量。文章通过具体示例详细解析了它们的用法和应用场景。
25 3
|
30天前
|
Java 编译器
在Java中,关于final、static关键字与方法的重写和继承【易错点】
在Java中,关于final、static关键字与方法的重写和继承【易错点】
20 5
|
1月前
|
Java
Java关键字 —— static 与 final 详细解释!一看就懂 有代码实例运行!
这篇文章详细解释了Java中static和final关键字的用法,包括它们修饰类、方法、变量和代码块时的行为,并通过代码示例展示了它们的具体应用。
160 0
Java关键字 —— static 与 final 详细解释!一看就懂 有代码实例运行!
|
2月前
|
存储 Java
Java之静态(static)与实例(instance)
Java之静态(static)与实例(instance)
|
3月前
|
Java
【Java基础面试四十一】、说一说你对static关键字的理解
这篇文章主要介绍了Java中static关键字的概念和使用规则,强调了类成员与实例成员的区别及其作用域的限制。
|
4月前
|
存储 SQL Java
Java实现关键字模糊查询的高效方法及实践
实现关键字模糊查询的方法有多种,每种方法都有其适用场景。在选择合适的方法时,应考虑实际需求、数据量大小、性能要求等因素。正则表达式适用于处理简单文本或小数据集;数据库模糊查询适用于存储在RDBMS中的数据;而第三方库,则适合需要进行复杂搜索的大型项目。选用合适的工具,可以有效提升搜索功能的性能和用户体验。
79 6
|
3月前
|
存储 Java 对象存储
【Java基础面试四十三】、 static和final有什么区别?
由于网络原因,我无法获取到您提供的链接内容。如果需要我解析该网页,请确保链接有效并重试,或者提供其他问题,我会尽力帮助您。
|
3月前
|
Java
【Java基础面试四十二】、 static修饰的类能不能被继承?
这篇文章讨论了Java中static关键字修饰的类是否可以被继承,解释了静态内部类的概念、规则以及如何实例化。
|
4月前
|
算法 Java API
多线程线程池问题之synchronized关键字在Java中的使用方法和底层实现,如何解决
多线程线程池问题之synchronized关键字在Java中的使用方法和底层实现,如何解决