static关键字定义
- static修饰的代码属于类,不被修饰的代码属于对象
- 定义的变量存储在方法区的静态常量池中
为什么设计static关键字
方便类直接去调用某些方法
如果方法和变量属于非静态的方法,他们只有在对象中才会被分配内存空间,也就是说只有对象才能调用,而以下代码只会以信息的方式存储在方法区中:
public class A {
public void run(){
System.out.println("111");
}
public void fly(){
System.out.println("222");
}
}
调用方式只能通过对象的方式调用:
A a = new A();
a.run();
a.fly();
A.run(); // 会报错,提示将run()改为static 如下图:
当我们加上static关键字之后:
public class A {
public static String name = "admin";
public static void run(){
System.out.println("111");
}
public static void fly(){
System.out.println("222");
}
}
我们就可以通过类来调用我们的方法和变量:
A.name = "xxx";
A.run();
A.fly(); // 不再报错
但是同时也可以通过对象来调用这些变量和方法,因为对象来源于类,但是这种情况下不推荐,更推荐通过类调用
static关键字使用方法
-
修饰变量
static修饰的变量属于类变量,存储在方法区中的静态常量池中,被所有对象共享
-
修饰方法
-
static修饰的方法无法调用非静态方法 如果想要在main方法中调用:
- 在非静态方法上加入static关键字
- 在main()方法中创建对象
-
在非静态方法中可以调用静态方法 原因如下:
非静态方法在表达是已经创建了对象,而有对象一定有类,静态方法修饰的方法属于类方法,存储在方法区中的静态常量池中,那么可以被对象共享并调用
-
-
修饰代码块
所修饰的代码块在main()方法之前运行,目的是优化程序执行
举个栗子:
例1
/* * 流程 * 1. 程序执行先去找main()方法启动程序 * 2. 首先先去扫描Base类,发现Base类没有父类 * 3. 将Base.class加载进方法区,此时Base类中的static代码块执行 * 4. main方法入栈--->执行 new test1();去创建test1类对象,扫描test1类,发现有父类Base类,但是已经扫描过了不再进行扫描 * 5. 将test1.class加载进方法区,此时test1类在当前的static代码块执行 * 6. 创建子类对象之前先创建父类对象,所以先执行Base()构造器,再执行test1()构造器 * */ public class test1 extends Base{ // 子类 static{ System.out.println("test static"); } public test1(){ System.out.println("test constructor"); } } class Base{ // 父类 static{ System.out.println("base static"); } public Base(){ System.out.println("base constructor"); } public static void main(String[] args){ new test1(); } }
例2
/** * 流程: * 扫描---> 编译 ---> 执行 * 1. 扫描main()方法所在的Test3类,发现没有父类,开始编译,编译过程中发现有static修饰的代码,按顺序执行static修饰的代码 * 2. 执行private static int a; 在方法区的静态常量池中开辟内存存储a = 0 * 3. 将modify()方法入栈,并创建局部变量int a = 0 * 4. 回到main()方法开始执行modify方法,将a++赋值给局部变量int a,此时局部变量a的值为1 * 5. modify()方法出栈,此时静态常量池中的a值仍为0 * 5. 执行System.out.println(a); 输出0 */ public class Test3 { private static int a; public static void main(String[] args) { modify(a); System.out.println(a); } public static int modify(int a){ // 局部变量,只在方法内部起作用 // java只进行值传递 return a++; // 0 } }
最终modify()方法出栈,将局部变量a干掉,并且因为java语言只进行值传递,并不会将局部变量a传递给方法区中的a,所以最终在输出的时候,a的值仍旧为0
例3
/** * 扫描---> 编译---> 运行 * 1. main()方法在哪首先扫描哪个类---> 首先扫描Test类,发现没有父类,也没有static修饰的代码,那么编译当前Test类,并且将main()方法入栈 * 2. 执行new Test4("---");,执行之前扫描Test4类,发现没有父类,所以编译Test4类,在编译过程中发现有static代码按顺序执行 * 3. 首先输出的是11 * 4. 执行 public static Test4 test = new Test4("+++"); 此时会设计到调用Test4构造器,所以输出 ===+++ * 5. 输出22 * 6. 编译完毕,回到main()方法中继续执行new Test4,现在可以创建对象了!!进入运行阶段 * 7. 通过调用构造器,创建对象,输出 ===--- */ public class Test4 { public Test4(String aa){ System.out.println("==="+aa); } static { System.out.println("11"); } public static Test4 test = new Test4("+++"); static { System.out.println("22"); } } class Test{ public static void main(String[] args) { Test4 test = new Test4("---"); } }
其中Test4并不会被放在静态常量池中而是作为类信息储存在方法区,其中如果有static修饰的值或者是静态方法的话,会被存储在静态常量池