引言
我们在学习Java的过程中,static属于我们最先遇到的几个关键字之一
你可能会问,我怎么没印象
java
public class StaticKeyword {
// 我们写第一个java程序,运行Hello World的时候,main方法就用了static修饰
public static void main(String[] args) {
System.out.println("Hello World");
}
}
事实上,作为程序入口的main方法,是一个静态方法
static是我们的老熟人,但是你真的了解她吗?
static关键字简介
static使用
static关键字,是java的关键字之一,表示"静态"
可以用来修饰变量,方法(比如main方法),代码块 ,不能修饰类
static可以和四大访问修饰符组合使用
java
public class StaticKeyword {
private static int a;
protected static int b;
static int c;
public static int d;
private static void test1() {
}
protected static void test2() {
}
static void test3() {
}
public static void test4() {
}
public static void main(String[] args) {
System.out.println("Hello World");
}
}
static内存存储
只要是static修饰的,都存储在JVM的方法区当中
准确来说,是存储在方法区的类信息之中,所以静态变量/静态方法/静态代码块,本质上归类所有
静态变量存储于静态常量池中;
当静态常量池不满时,静态方法可以存储于其中;否则要存储到方法区的其他区域
静态代码块比较特殊,我们稍后单独处理
静态变量+静态方法
静态变量+静态方法在本类之中 共享,本类的任何对象都可以直接调用
代码+内存图的讲解
java
public class StaticKeyword {
private String c;
private String f;
private static int a;
public static int d;
public static void main(String[] args) {
StaticKeyword s1 = new StaticKeyword();
StaticKeyword s2 = new StaticKeyword();
s1.c = "c1";
s2.c = "c2";
s1.a = 1;
s2.a = 2;
System.out.println(s1.c + "," + s1.a);
System.out.println(s2.c + "," + s2.a);
}
}
输出结果
静态变量a根属于类StaticKeyword,在整个JVM中只有一份,后赋值会覆盖前赋值;
但是普通变量c根属于StaticKeyword的对象,堆内存中的对象可以创建很多个,所以c变量赋值互不干扰
静态方法也是如此,在JVM中只存在一份
java
public class StaticKeyword {
public static void test1() {
System.out.println("test1");
}
public void test2(){
System.out.println("test2");
}
public static void main(String[] args) {
StaticKeyword s1 = new StaticKeyword();
StaticKeyword s2 = new StaticKeyword();
s1.test1();
s2.test1();
}
}
运行结果
s1.test1()执行时,从方法区的静态常量池当中拷贝一份入栈,运行完出栈;
s2.test1()执行时,也从方法区的静态常量池的相同位置拷贝一份入栈,运行完出栈。
test2()在对象实例化的时候从类信息拷贝,但是实际执行时,是从堆内存的不同对象当中拷贝入栈
静态方法不存在相互覆盖的情况,但是需要注意的是别的内容
-
静态方法不能调用非静态方法,
因为有类不一定有对象,类不一定实例化创建了对象,所以存储于对象当中的非静态方法的调用就无从谈起(没有蒸锅怎么煮米)
-
非静态方法可以调用静态方法
因为有对象一定有类,而且静态方法是类共享的
静态变量和静态方法习惯调用形式
既然静态变量和静态方法都是属于类,那么我们实际上可以使用类名+变量名/方法名来访问
事实上,这也是大多数情况下的访问方法,我们都习惯如此
如果不这样做,用对象访问,其实也没什么问题,但是我们通常不会这么做
IDEA也会抛出警告
用类名访问,则不会抛出警告
静态代码块
代码块
代码块中的代码,只能在代码块的范围内执行,代码块中定义的变量是局部变量
代码块根属于对象,存储在堆内存中
代码块只有对象实例化之后才能执行,这一点非常重要
demo1
java
public class StaticKeyword {
public static int a;
public static void test1() {
System.out.println("test1");
}
public void test2() {
System.out.println("test2");
}
// 代码块
{
int aaa = 100;
System.out.println(aaa);
System.out.println("代码块执行!");
}
}
在其他类里面的main方法运行
java
public class OtherPackageClass {
public static void main(String[] args) {
StaticKeyword s1 = new StaticKeyword();
s1.test2();
}
}
运行结果:
我们可以看到代码块的运行顺序是非常靠前的,只要类实例化(构造方法运行之后),代码块就会运行
但是若类不实例化,那么就不会执行
demo2
需要注意的是,每次创建一个对象,代码块都会执行一次
java
public class OtherPackageClass {
{
System.out.println("代码块执行啦!!!");
}
public static void main(String[] args) {
OtherPackageClass o1 = new OtherPackageClass();
OtherPackageClass o2 = new OtherPackageClass();
}
}
运行结果
静态代码块
所谓静态代码块,就是被static修饰的代码块
它也具有代码块的一些特征,比如静态代码块中的代码,只能在静态代码块的范围内执行,静态代码块
中定义的变量是局部变量等
区别
- 静态代码块根属于类 ,存储在方法区当中;代码块根属于对象,存储在堆中
- 只要类加载,静态代码块就会自动执行,而且最优先执行;代码块的执行不光需要类加载,还需要类实例化(创建对象)
demo1
java
public class StaticKeyword {
{
int aaa = 100;
System.out.println(aaa);
System.out.println("代码块执行!");
}
static{
System.out.println("静态代码块执行!");
}
public static void main(String[] args) {
}
}
运行结果:
我们发现只有静态代码块执行了
demo2
如果我们再创建一个对象
java
public class StaticKeyword {
{
int aaa = 100;
System.out.println(aaa);
System.out.println("代码块执行!");
}
static{
System.out.println("静态代码块执行!");
}
public static void main(String[] args) {
StaticKeyword s1 = new StaticKeyword();
}
}
运行结果:
静态代码块和代码块都执行,并且静态代码块先执行
总结
有对象(创建对象将类实例化)则一定有类(类加载),但是有类(类加载)不一定有对象(创建对象将类实例化)
类加载之后的代码执行顺序
- main方法之前的静态代码块
- main方法之后的静态代码块
- 类实例化之前的main方法代码
- main方法之前的代码块(需要类实例化)
- main方法之后的代码块(需要类实例化)
- 类实例化之后的main方法代码
demo
看懂这个代码,你的静态代码块就过关啦!!!
java
public class StaticKeyword {
private static String name;
private Integer age;
{
System.out.println("4. StaticKeyword类实例化之后,main方法之前的代码块执行!");
}
static {
System.out.println("1. main方法之前的静态代码块执行!");
}
public void hhh() {
System.out.println("哈哈哈哈哈哈");
}
public static void main(String[] args) {
// StaticKeyword类实例化之前
StaticKeyword.name = "樱岛麻衣天下第一";
System.out.println("3. StaticKeyword类实例化之前的部分main代码执行:" + StaticKeyword.name);
System.out.println("=====================================");
// StaticKeyword类实例化之后
StaticKeyword s1 = new StaticKeyword();
s1.age = 18;
System.out.println("6-1. StaticKeyword类实例化之后的部分main方法代码执行," + "s1对象创建:" + s1.age);
StaticKeyword s2 = new StaticKeyword();
s2.age = 30;
System.out.println("6-2. StaticKeyword类实例化之后的部分main方法代码执行," + "s2对象创建:" + s2.age);
}
{
System.out.println("5. StaticKeyword类实例化之后,main方法之后的代码块执行!");
}
static {
System.out.println("2. main方法之后的静态代码块执行!");
}
}
运行结果
如果你觉得我写的不错,可以给我点个赞,这对我真的很重要,谢谢!!!