文章目录
- 类的加载特性与时机
- 类加载的特性
- 类加载的时机
- static的三个常用地方
- 什么是静态块?
- 特点
- 写法
- 静态块 static
- 怎么用?
类的加载特性与时机
在介绍static之前可以先看看类的相关
类加载的特性
在JVM的生命周期里,每个类只会被加载一次。
类加载的原则:延迟加载,能少加载就少加载,因为虚拟机的空间是有限的。
类加载的时机
1)第一次创建对象要加载类.
2)调用静态方法时要加载类,访问静态属性时会加载类。
3)加载子类时必定会先加载父类。
4)创建对象引用不加载类.
5)子类调用父类的静态方法时
(1)当子类没有覆盖父类的静态方法时,只加载父类,不加载子类
(2)当子类有覆盖父类的静态方法时,既加载父类,又加载子类
6)访问静态常量,如果编译器可以计算出常量的值,则不会加载类,例如:public static final int a =123;
否则会加载类,例如:public static final int a = math.PI。
static的三个常用地方
- 修饰成员变量
- 修饰成员方法
- 静态块
这里简单介绍一下修饰成员方法, 下面继续介绍静态块
同C++是一样的概念。但是在JVM里面,JVM也会划分一个暂称静态存储区,用于存放方法的定义。实际上从更大的角度而言,它存放的是各种类的定义,当我们通过new来生成对象时,会根据这里定义的类的定义去创建对象。
下面观察两段代码的输出结果,加了static和没有加static的区别:
public class Person {
String name;
int age;
public String toString() {
return "Name:" + name + ", Age:" + age;
}
public static void main(String[] args) {
Person p1 = new Person();
p1.name = "zhangsan";
p1.age = 10;
Person p2 = new Person();
p2.name = "lisi";
p2.age = 12;
System.out.println(p1);
System.out.println(p2);
}
/**输出结果
* Name:zhangsan, Age:10
* Name:lisi, Age:12
*/
}
public class Person {
String name;
// 给age加上static
static int age;
/* 其余代码不变... */
/**输出结果
* Name:zhangsan, Age:12
* Name:lisi, Age:12
*/
}
观察输出结果的结论:通过运行结果,可以看到 age都为12,只保存了最后一次给age赋的值。这是为什么呢,在内存里面发生了什么?
给age属性加了static关键字之后,Person对象就不再拥有age属性了,age属性会统一交给Person类去管理,即多个Person对象只会对应一个age属性,一个对象如果对age属性做了改变,其他的对象都会受到影响。
什么是静态块?
**静态代码块:**执行优先级高于非静态的初始化块,它会在类初始化的时候执行一次,执行完成便销毁,它仅能初始化类变量,即static修饰的数据成员。
特点
随着类的加载而执行,而且只执行一次
写法
static{
}
对应看看非静态代码块
非静态代码块: 执行的时候如果有静态初始化块,先执行静态初始化块再执行非静态初始化块,在每个对象生成时都会被执行一次,它可以初始化类的实例变量。非静态初始化块会在构造函数执行时,在构造函数主体代码执行之前被运行。
非静态代码块的写法:
{
}
静态块 static
(1) static关键字还有一个比较关键的作用,用来形成静态代码块( static{}
即static块 )以优化程序性能。
(2) static 块可以置于类中的任何地方,类中可以有多个 static 块。
(3) 在类初次被加载的时候执行且仅会被执行一次(这是优化性能的原因!!!),会按照static块的顺序来执行每个static块,一般用来初始化静态变量和调用静态方法。
下面通过两段代码,说明 static{} 为什么能优化程序性能。
示例:
/**
* 每次调用isBornBoomer的时候
* 都会生成startDate和birthDate两个对象,造成了空间浪费
*/
class Person{
private Date birthDate;
public Person(Date birthDate) {
this.birthDate = birthDate;
}
boolean isBornBoomer() {
Date startDate = Date.valueOf("1997");
Date endDate = Date.valueOf("2019");
return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0;
}
}
/**
* 这里使用了static块
* 只需要进行一次的初始化操作
* 节省内存空间,优化性能
*/
class Person{
private Date birthDate;
private static Date startDate,endDate;
static{
startDate = Date.valueOf("1997");
endDate = Date.valueOf("2019");
}
public Person(Date birthDate) {
this.birthDate = birthDate;
}
boolean isBornBoomer() {
return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0;
}
}
怎么用?
在类中定义一个静态代码块就行了,然后在里面写对应的代码
小知识点
静态代码块的执行顺序:静态代码块----->非静态代码块-------->构造函数
面试中可能会连带着其他的知识点一起出现
比如可能会和继承知识点一起出现
示例:
父类:
public class Fathers {
static{
System.out.println("父类中的静态代码块");
}
Fathers(){
System.out.println("父类中的构造函数");
}
{
System.out.println("父类中的非静态代码块");
}
public static void main(String[] args) {
System.out.println("父类中的main方法");
}
}
子类
public class Sons extends Fathers{
static {
System.out.println("子类中的静态代码块");
}
Sons(){
System.out.println("子类中的构造方法");
}
{
System.out.println("子类中的非静态代码块");
}
public static void main(String[] args) {
System.out.println("子类中的main方法");
new Sons();
}
}
执行子类中main方法后输出
如果你想看静态代码块的特征,随着类的加载而执行,而且只执行一次,就在父类中new一个子类就可以看出来了。
这次执行父类中的main方法:
然后跟上面的在子类中new一个sons类的结果进行对比,下面这张图是父类执行main方法,上面的图是子类执行main方法,
可以看出来,原本在子类中执行main方法,由于子类继承父类,所以父类中的静态代码块优先执行一次,
但是在下图中,是在父类中执行了main方法,本身父类执行main方法就会执行一次静态代码块,但是在父类中main方法new了一次子类,按继承关系,父类中的静态代码块应该还会执行,但是控制台中却没有打印,这就是因为静态代码块的特征的原因所致,随着类的加载而执行,而且只执行一次,
参考文章(侵删):
Java中的静态块(static{})
Java static关键字与static{}语句块
java中静态代码块详解