目录
一、static与JVM
此处引用一段内容:
Java 把内存分为栈内存和堆内存,其中栈内存用来存放一些基本类型的变量、数组和对象的引用,堆内存主要存放一些对象。在 JVM 加载一个类的时候,若该类存在 static 修饰的成员变量和成员方法,则会为这些成员变量和成员方法在固定的位置开辟一个固定大小的内存区域,有了这些“固定”的特性,那么 JVM 就可以非常方便地访问他们。
二、static修饰符
static关键字修饰的类成员可以是变量、方法、不属于方法的代码块。对静态类成员的使用不依赖于该类的任何对象。在类加载的时候初始化,一直存在内存中,直到程序结束JVM关闭销毁。一些使用频繁的方法使用static可以提升性能,全局变量可酌情使用static。
方法声明为静态时具有某些限制:
- 它们只能调用其他静态方法。
- 它们只能访问静态。
- 不能使用关键字this或super
三、酌情使用static方法可以提升性能的原因
- 非static方法,每次实例化类对象调用时会为类分配内存空间,很消耗内存资源。但不会常驻内存,当实例对象被JVM回收之后销毁。
- 非static方法所消耗的内存远大于static方法所占用的内存。
四、示例代码
以下示例代码本人是在IntelliJ IDEA中创建springboot项目执行的。
1. static代码块
static代码块当类被加载时就会执行这块代码
package com.sbootwo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
/**
* SpringBoot在启动时会自动扫描启动类所在包下的注解类。
* 注意:只要包名的开头和启动类的包名一致即可,即使与启动类不在同级目录下也可以被扫描。
* 如果想改变启动类自动扫描的包,可以使用@ComponentScan指定扫描的包。
*/
@SpringBootApplication
//@ComponentScan(basePackages = {"com.sbootwo","com.conf"}) //多分包指定
@ComponentScan(basePackages = {"com"}) //直接指定根目录包名
public class SbootwoApplication {
public static void main(String[] args) {
SpringApplication.run(SbootwoApplication.class, args);
}
//静态代码块在类加载时初始化到内存中,并执行
static {
System.out.println("SbootwoApplication静态代码块执行!.........");
}
//静态方法在类加载时初始化到内存中,不执行
public static void outTestStr(){
System.out.println("SbootwoApplication静态方法执行!...");
}
}
静态代码块在类加载时初始化到内存中,并执行。静态方法在类加载时初始化到内存中,不执行。所以代码执行结果如下:
2. 静态方法中调用静态和非静态方法
静态方法中可以直接调用静态方法也可以示例化对象调用(但通常通过类名称调用),而非静态方法需要实例化类对象调用。示例代码中不安此规则调用会提示报错。
package com.sbootwo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
/**
* SpringBoot在启动时会自动扫描启动类所在包下的注解类。
* 注意:只要包名的开头和启动类的包名一致即可,即使与启动类不在同级目录下也可以被扫描。
* 如果想改变启动类自动扫描的包,可以使用@ComponentScan指定扫描的包。
*/
@SpringBootApplication
//@ComponentScan(basePackages = {"com.sbootwo","com.conf"}) //多分包指定
@ComponentScan(basePackages = {"com"}) //直接指定根目录包名
public class SbootwoApplication {
public static void main(String[] args) {
SpringApplication.run(SbootwoApplication.class, args);
}
static String strvalue="静态变量!";
//静态代码块在类加载时初始化到内存中,并执行
static {
System.out.println("SbootwoApplication静态代码块执行!.........");
//类名调用静态方法
SbootwoApplication.outTestStr();
}
public void testOne(){
System.out.println("testOne获得变量strvalue的值:"+strvalue);
}
public static void testTwo(){
System.out.println("------testTwo获得变量strvalue的值:"+strvalue);
}
//静态方法在类加载时初始化到内存中,不执行
public static void outTestStr(){
SbootwoApplication sbt=new SbootwoApplication();
//非静态方法需要实例化对象调用
sbt.testOne();
//静态方法中直接调用静态方法
testTwo();
System.out.println("SbootwoApplication静态方法执行!...");
}
}
执行结果如下:
3. 静态方法中访问静态和非静态变量
静态方法中只能访问静态
package com.sbootwo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
/**
* SpringBoot在启动时会自动扫描启动类所在包下的注解类。
* 注意:只要包名的开头和启动类的包名一致即可,即使与启动类不在同级目录下也可以被扫描。
* 如果想改变启动类自动扫描的包,可以使用@ComponentScan指定扫描的包。
*/
@SpringBootApplication
//@ComponentScan(basePackages = {"com.sbootwo","com.conf"}) //多分包指定
@ComponentScan(basePackages = {"com"}) //直接指定根目录包名
public class SbootwoApplication {
public static void main(String[] args) {
SpringApplication.run(SbootwoApplication.class, args);
}
static String strvalue="静态变量!";
String strvalueTwo="strvalueTwo变量!";
//静态代码块在类加载时初始化到内存中,并执行
static {
System.out.println("SbootwoApplication静态代码块执行!.........");
//类名调用静态方法
SbootwoApplication.outTestStr();
}
//静态方法在类加载时初始化到内存中,不执行
public static void outTestStr(){
System.out.println("获得静态变量值:"+strvalue);
System.out.println("获得变量值:"+strvalueTwo); //报错,因为strvalueTwo为非静态变量
System.out.println("SbootwoApplication静态方法执行!...");
}
}
代码报错结果如下: