static
在Java中,static是一个关键字,它用于修饰类变量、方法或代码块。static关键字的主要目的是允许我们创建不依赖于对象实例的类变量和方法。以下是static关键字在Java中的主要用途和详细解释:
一. 用法及注意事项
- 静态变量(类变量) 使用static修饰的变量称为静态变量或类变量。 静态变量属于类,不属于类的任何实例。这意味着,无论创建了多少个类的实例,静态变量在内存中只会有一个副本。 静态变量可以在没有创建类实例的情况下访问,通过类名直接访问。
示例:
public class MyClass {
static int myStaticVar = 42; // 静态变量
public static void main(String[] args) {
System.out.println(MyClass.myStaticVar); // 直接通过类名访问静态变量
}
}
注意: 如定义了: public static List list = new ArrayList(); 这样的共享变量。这个 list 如果同时被多个线程访问的话,就有线程安全的问题,这时候 一般有两个解决办法:
- 把线程不安全的 ArrayList 换成 线程安全的 CopyOnWriteArrayList;
- 每次访问时,手动加锁。 所以在使用 static 修饰类变量时,如何保证线程安全是我们常常需要考虑的。
- 静态方法(类方法) 使用static修饰的方法称为静态方法或类方法。 静态方法只能访问静态变量或静态方法,不能直接访问类的非静态成员(变量和方法)。 静态方法可以在没有创建类实例的情况下调用,通过类名直接调用。
示例:
public class MyClass {
static int myStaticVar = 42;
static void myStaticMethod() {
System.out.println(myStaticVar); // 访问静态变量
}
public static void main(String[] args) {
MyClass.myStaticMethod(); // 直接通过类名调用静态方法
}
}
注意: 该方法内部只能调用同样被 static 修饰的方法,不能调用普通方法,我们常用的 util 类里面的各种方法,我们比较喜欢用 static 修饰方法,好处就是调用特别方便。 static 方法内部的变量在执行时是没有线程安全问题的。方法执行时,数据运行在栈里面,栈的数据每 个线程都是隔离开的,所以不会有线程安全的问题,所以 util 类的各个 static 方法,我们是可以放心使 用的。
- 静态代码块 静态代码块是在类加载时自动执行的代码块。 静态代码块通常用于初始化静态变量或执行只需执行一次的代码。 静态代码块在类的构造函数之前执行,并且只执行一次。
示例:
public class MyClass {
static int myStaticVar;
static {
myStaticVar = 42; // 静态代码块中初始化静态变量
System.out.println("Static block executed.");
}
public static void main(String[] args) {
System.out.println(MyClass.myStaticVar);
}
}
注意:static 的变量需要写在静态块的前面,不然编译会报错。
二.初始化时机
- 父类的静态变量和静态块优先于子类初始化
- 静态变量和静态块优先于类构造器初始化
- 被 static 修饰的方法,在类初始化的时候并不会初始化,只有当自己被调用时,才会被执行
volatile
一.基础知识
线程与CPU缓存的交互:在多核CPU环境中,为了提高效率,线程在访问共享变量的值时,通常直接与CPU的缓存(而非主内存)交互。这是因为CPU缓存的访问速度远快于主内存。
二.问题描述
数据一致性问题:由于CPU缓存和主内存之间的异步更新,可能导致线程读取到的数据不是最新的。即,当一个线程修改了共享变量的值(这个修改是在主内存中完成的),其他线程可能仍然从自己的CPU缓存中读取旧的值,这可能导致数据不一致和计算错误。
三.解决机制
内存与CPU缓存的同步机制:当主内存中的共享变量值发生变化时,有一种机制可以通知CPU缓存,使其失效并重新从主内存中加载最新的值。 volatile关键字的作用
- volatile的可见性:volatile关键字的主要作用是确保共享变量的可见性。当一个volatile变量被修改时,它会触发上述的同步机制,确保所有线程都能立即看到最新的值。
- volatile作为共享变量的标识:volatile变量被识别为共享变量,任何对它的写操作都会立即被通知到所有CPU缓存,确保它们也进行相应的更新。
四.总结
volatile的作用:volatile关键字主要用于解决多线程环境下的可见性问题。它通过触发CPU缓存与主内存之间的同步机制,确保所有线程都能及时看到共享变量的最新值。
注意事项:
虽然volatile可以确保可见性,但它并不能解决所有的并发问题,比如原子性和有序性问题。因此,在复杂的并发场景中,可能需要结合其他同步机制(如锁、原子类、同步块等)来确保数据的完整性和一致性。
String不可变原因
在Java中,`String`类被设计为不可变的(immutable),这一设计决策基于以下几个关键原因:
-
安全性:
- 字符串常量池中存储的是字符串对象的引用。如果字符串对象是可变的,那么在字符串对象被修改后,所有引用该对象的代码都可能受到影响,导致安全问题和程序行为的不确定性。
- 不可变性使得字符串在多线程环境下是线程安全的,多个线程可以同时访问和共享字符串对象,而无需额外的同步机制。
-
性能:
- 不可变的字符串可以被缓存,因为它们的值是固定的,可以重复使用。这减少了内存占用,并且在字符串操作中避免了频繁的复制和创建新对象的开销。
String类在创建时会计算并缓存其哈希值,由于字符串的不可变性,可以在创建时计算一次,并在后续使用中重复利用,提高字符串的哈希算法的性能。
-
简化设计:
String类被final修饰,意味着它不能被继承,这简化了类层次结构,防止了潜在的重写问题。- 字符串的成员变量
value(在Java 9之前是char[],之后是byte[])也是final的,这保证了字符串一旦创建,其内容就不能被改变。
-
一致性:
- 不可变性确保了字符串的一致性,这对于字符串作为键在
HashMap和其他集合中使用非常重要。如果字符串是可变的,键的值变化会导致查找失败,破坏容器的功能。
- 不可变性确保了字符串的一致性,这对于字符串作为键在
-
垃圾回收效率:
- 由于
String对象不会改变,垃圾回收器可以更高效地管理内存,因为它知道一旦一个String对象不再被引用,它就可以被安全地回收。
- 由于
总之,String的不可变性是Java设计者为了增强程序的安全性、提升性能和简化编程模型而做出的关键决定。这种设计鼓励了良好的编码实践,避免了许多常见的编程陷阱。