静态变量&静态方法
静态变量又叫做类变量,静态方法又被称为类方法——均被static修饰
未被static修饰的成员变量和方法分别被称为实例变量和实例方法
(1)静态方法中不需要它所属类的任何实例就可以访问,所以在静态方法中不可以使用this关键字
(2)静态方法中不可直接访问所属类的实例变量和实例方法,但可以直接访问所属类的静态变量和静态方法
如图所示,如果在静态方法中想要直接访问所属类的实例变量和实例方法或使用this关键字,编译器都会报错
(3)静态变量(方法)的访问方式:类名.变量名(方法名)
实例变量(方法)需要先将类实例化后才可访问:对象名.变量名(方法名)
如图所示,未实例化时使用“类名.变量名(方法名)”就可以直接访问静态变量(方法),但无法访问相应类的实例变量和实例方法
如图所示,当将类实例化后,使用“对象名.变量名(方法名)”就可以直接访问实例变量(方法),但无法访问相应类的静态变量和静态方法
2023/5/15更正:实例可以访问它所属类中的所有成员(包括静态成员和非静态成员),即使用“对象名.变量名(方法名)也可以访问相应类的静态变量和静态方法,但是并不建议这样做(感谢@6de1b的批评指正)
如图所示,通过实例访问时,IDEA并不会主动提示静态成员,这可能是因为通过实例化访问静态成员属于不规范的语法,IDE会给出警告Static member 'staticclass.method1()' accessed via instance reference,同时提示你使用类名来访问静态成员,但是程序可以正常运行,不会报错
之所以不建议通过实例化访问静态成员,是因为这样做可能会造成一些不必要的混淆
① 容易给人造成误解:使用实例对象访问静态成员,容易让程序员以为这个成员是非静态成员,从而可能造成理解上的偏差
② 可能会被编译器优化掉:实例对象访问静态成员可能会被编译器优化掉,导致代码逻辑不正确
于此同时,通过实例化访问静态成员,实际上也只根据实例对应的类型声明选择静态成员,并不会关注运行时具体值,这样确实也可能带来一些不必要的困扰,具体代码可参考stackoverflow上的一个回答,代码如下所示
Why isn't calling a static method by way of an instance an error for the Java compiler? - Stack Overflowhttps://stackoverflow.com/questions/610458/why-isnt-calling-a-static-method-by-way-of-an-instance-an-error-for-the-java-co
public class Base {
public static void foo() {
System.out.println("Base.foo()");
}
}
class Derived extends Base{
public static void foo() {
System.out.println("Derived.foo()");
}
public static void main(String[] args) {
Base b = new Derived();
b.foo(); // Prints "Base.foo()"
b = null;
b.foo(); // Still prints "Base.foo()"
}
}
(4)在内存中,实例/静态成员的存储形式大致如图
所有静态成员共享一份内存——静态变量某种意义上具有全局性质
而不同实例间的实例变量彼此互不干涉
详解:
当类的字节码文件被加载到内存时,类的实例方法不会被分配入口地址。当该类创建对象后,类中的实例方法才分配入口地址,从而实例方法可以被类创建的任何对象调用执行。
类方法在该类被加载到内存时,就分配了相应的入口地址。从而类方法不仅可以被类创建的任何对象调用执行,也可以直接通过类名调用。 类方法的入口地址直到程序退出时才被取消。
注意:
当我们创建第一个对象时,类中的实例方法就分配了入口地址,当再创建对象时,不再分配入口地址。
也就是说,方法的入口地址被所有的对象共享,当所有的对象都不存在时,方法的入口地址才被取消。
总结:
类变量和类方法与类相关联,并且每个类都会出现一次。 使用它们不需要创建对象。
实例方法和变量会在每个类的实例中出现一次。
以下是给出完整代码范例:
public class staticclass {
public static int count = 0; //静态变量
public int num =0; //非静态变量
public static void method1(){ //静态方法
System.out.println("正在访问静态方法(count++)....");
System.out.println("\tpre-count = "+count);
count++;
//num++; //静态方法中不可以访问实例变量
//method2(); //静态方法中不可以访问实例方法
//System.out.println(this.num); //静态方法中不可以使用this关键字
System.out.println("\tpst-count = "+count);
}
public void method2() //实例方法
{
System.out.println("正在访问实例方法(count+2,num+3)....");
System.out.println("\tpre-count = "+count);
System.out.println("\tpre-num = "+num);
count+=2;
num+=3;
System.out.println("\tpst-count = "+count);
System.out.println("\tpst-num = "+num);
}
public static void main(String[] args) {
System.out.println("初始时,count = "+staticclass.count); //访问静态变量:类名+变量名
staticclass.method1();//访问静态方法:类名+变量名
staticclass a = new staticclass();
System.out.println("\na未操作前,count = "+staticclass.count);
System.out.println(" num = "+a.num);
a.method2();
staticclass b = new staticclass();
System.out.println("\nb未操作前,count = "+staticclass.count);
System.out.println(" num = "+b.num);
b.method2();
}
}
运行结果如图,可印证以上三点:
如果还有其他问题,欢迎批评指正!!!