static是什么
static关键字修饰的代码属于类,属于类的代码是被共享的。
static修饰的变量叫做类变量,是被所有该类产生的对象所共享的。
static修饰的方法属于静态方法,静态方法能够直接被main方法所使用,非静态方法不能直接被main方法所使用。只有静态方法才能调用静态方法(即除了main方法外只有static关键字修饰的方法才能调用static修饰的方法)。
static修饰的代码块 static修饰的代码块在main方法运行之前运行,其目的是优化程序。
public class Person {
public String name="张三 ";
public static String adress="中国";
public static void run(String num){
System.out.println("跑了"+num+"米");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
"adress='"+adress+'\''+
'}';
}
}
public class Test1 {
public static void main(String[] args) {
Person p1=new Person();
Person p2=new Person();
p1.name="李四";
p1.adress="北京";
p1.run("200");
p2.name="王五";
p2.adress="上海";
p1.run("100");
System.out.println(p1.toString());
System.out.println(p2.toString());
}
}
输出结果为:
跑了200米
跑了100米
Person{name='李四'adress='上海'}
Person{name='王五'adress='上海'}
运行过程:
static修饰的address存放方法区的静态变量池当中,当main方法入栈后,创建p1和p2两个对象,此时对象可以对静态常量池中的静态变量进行修改赋值操作,在执行完p2的修改操作后,静态常量池中的address常量变为上海,再执行打印时,会都输出上海(两个对象共用address静态变量)。
设置static关键字的原因
方便java中的类去更方便直接地使用自己的方法和变量。
方法和变量上面没有static关键字
即方法和变量属于非静态,只有在对象中才会被分配内存空间,只有对象才能调用。
方法和变量上有static关键字
即方法和变量属于静态,方法和变量会被存储在方法区的静态常量池中,能够直接被类调用。上一张图中可以看出如果没有static关键字,那么类是不可以调用自己的属性和方法的,只能用对象去调用。
static关键字怎么用
以下是static关键字的用法。
static修饰变量
static修饰的变量被称为类变量,这个变量与普通变量不同,类变量是属于整个类的,此类产生的所有对象都可以调用此变量。可以算得上是“公共资源”。类变量(即static修饰的变量)存储在方法区的静态常量池中,被所有对象由该类产生的对象所共享。
在代码中使用 static 修饰一个变量时,该变量就不再属于某个对象实例,而是属于类本身。
public class Person {
public String name="张三 ";
public static String adress="中国";
public static void run(String num){
System.out.println("跑了"+num+"米");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
"adress='"+adress+'\''+
'}';
}
}
public class Test1 {
public static void main(String[] args) {
Person p1=new Person();
Person p2=new Person();
p1.name="李四";
p1.adress="北京";
p1.run("200");
p2.name="王五";
p2.adress="上海";
p1.run("100");
System.out.println(p1.toString());
System.out.println(p2.toString());
}
}
输出为:
跑了200米
跑了100米
Person{name='李四'adress='上海'}
Person{name='王五'adress='上海'}
分析:
address变量被 static 修饰,存储在方法区的静态常量池中,由该类产生的所有对象实例( p1、p2)共享此静态变量。
p1和p2 是两个实例对象,两个对象的name变量存储在各自的堆内存中,彼此独立。
当我们通过 p1修改 address的值时,类变量address的值就被修改为 "北京"。由于address是类变量,p2再次修改它的值时,所有实例访问到的address都变成了 "上海"。
static修饰方法
static修饰的静态方法无法调用非静态方法
public class A{
public static String name="admin";
public static String address="中国";
public static void H(){
F();
}
public void F(){
}
static修饰的方法称为静态方法。静态方法与类关联,而不是与某个对象关联。这意味着我们可以在没有创建对象实例的情况下调用静态方法。
静态方法与非静态方法的互相调用
静态方法中无法直接调用非静态方法。因为非静态方法依赖于对象实例,而静态方法不依赖于任何对象实例。
解决无法调用问题
1、将非静态方法转换为静态方法:可以通过将非静态方法添加 static 关键字,使其成为静态方法。
2、通过对象调用非静态方法:在静态方法中创建一个对象实例,然后通过该对象来调用非静态方法。
在非静态方法中可以调用静态方法
在非静态方法中可以调用静态方法的原因:在对象实例创建之前,类已经加载且静态方法已存在。静态方法是类的一部分,并且在类加载时就已经存在,因此在任何时候都可以被调用。
static修饰代码块
static修饰的代码块(即静态代码块)在类被加载时自动执行,通常用于在类加载时执行一些初始化工作。static修饰的代码块在main方法执行之前运行(真正意义上说,是在static代码块所在的类被调用进方法区时进行static代码块的运行),主要用于优化程序或进行一些预处理。
代码一般运行顺序
当程序启动时,首先会寻找含有 main() 函数的类,并启动该程序。
扫描检查该类是否有父类。如果有,则继续扫描父类并加载父类(递归地进行)。
当找到最顶层的父类后,首先加载父类,并执行父类中的 static 代码块。
接着加载子类,并执行子类中的 static 代码块。
最后,进入 main() 方法,程序开始执行。
例子:
public class Test extends Base {
static {
System.out.println("test static");
}
public Test(){
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 Test();
}
}
输出:
base static
test static
base constructor
test constructor
运行逻辑:
程序执行先去找main()方法启动程序
1、程序执行先去扫描Base类,发现Base类没有父类
2、将Base.class加载进方法区,此时Base类当中的static代码块执行
3、main方法入栈,执行new Test();创建Test类对象
4、扫描Test类,其有父类且父类已经被扫描过
5、将Test.class加载进方法区,此时Base类当中的static代码块执行
6、继续执行new Test();创建Test类对象
7、创建子类对象之前先创建父类对象,所以先执行Base()构造器,再执行Test()构造器
main方法调用方法
两种方式可以让某个方法在main方法中调用:
将方法设为静态方法
某个方法可以直接被main方法调用,只需在该方法前添加 static 关键字即可(将方法变为类方法,直接调用方法)。
public class ceshidaima1 {
public static void ceshi() {
System.out.println("这是类方法");
}
}
public class Test {
public static void main(String[] args) {
ceshidaima1.ceshi(); // 直接调用静态方法
}
}
输出:
这是类方法
通过对象调用非静态方法
若方法是非静态的,则需要先创建对象,然后通过对象调用该方法。
public class ceshidaima1 {
public void ceshi() {
System.out.println("这是非静态方法");
}
}
public class Test {
public static void main(String[] args) {
ceshidaima1 a = new ceshidaima1();
a.ceshi(); // 通过对象调用非静态方法
}
}
输出:
这是非静态方法
具体实例
具体的代码示例,分析 static 关键字的加载顺序和使用。
public class Demo {
public Demo(String aa) {
System.out.println("====" + aa);
}
static {
System.out.println("11");
}
public static Demo demo = new Demo("+++");
static {
System.out.println("22");
}
}
class Test {
public static void main(String[] args) {
Demo demo = new Demo("----");
}
}
输出:
11
====+++
22
====----
分析:
首先,程序执行main方法,定位到 Test 类。
在执行 main 方法中的语句 Demo demo = new Demo("----"); 时,需要调用 Demo 类的构造器。
由于 Demo 类被引用,JVM 会首先加载 Demo.class 到内存,并初始化其中的静态部分。
static 代码块和静态变量按顺序执行,因此输出顺序如下:
11(第一个静态代码块)
====+++(静态变量 demo 调用了 Demo 的构造函数)
22(第二个静态代码块)
====---(main 方法中 new 了一个 Demo 对象)
至此,此篇文章结束。