Java中static修饰符对属性与方法的用法
Java中的static修饰符十分常见,先谈谈关于修饰属性时需要注意的。
先来观察一段关于修饰属性的例子:
class Fruit {
private String name; //水果类型
private double value; //水果价格
String area = "产地A"; //水果产地
public Fruit(String name,double value) { //定义构造方法
this.name=name;
this.value=value;
}
public String getData() {
return"水果名:"+this.name+",价格:"+this.value+"元/斤"+",产地:"+this.area;
}
}
public class StaticDemo {
public static void main(String[] args) {
Fruit fruit1 = new Fruit("苹果",5);
Fruit fruit2 = new Fruit("香蕉",6);
Fruit fruit3 = new Fruit("梨子",7);
fruit1.area="产地B";
System.out.println(fruit1.getData());
System.out.println(fruit2.getData());
System.out.println(fruit3.getData());
}
}
运行结果:
水果名:苹果,价格:5.0元/斤,产地:产地B
水果名:香蕉,价格:6.0元/斤,产地:产地A
水果名:梨子,价格:7.0元/斤,产地:产地A
以上仅仅是个修改其中单一对象的属性值,若把其中area属性定义为static private String area;
static private String area = "产地A"; //水果产地
fruit1.area="产地B";//水果产地
会出现以下结果:
水果名:苹果,价格:5.0元/斤,产地:产地B
水果名:香蕉,价格:6.0元/斤,产地:产地B
水果名:梨子,价格:7.0元/斤,产地:产地B
所以在这里可以发现,用static修饰过的属性,属性会变成“共有”的,不仅fruit1对象可以更改该属性,fruit2同样也可以更改,但是我们不建议在这里使用对象名.属性的方式对有static修饰符修饰过的属性进行修改,原因将在后文叙述。
当一个属性被static修饰符修饰后,这个属性将成为“类属性”,同理,当一个方法被static修饰后,也将称为“类方法”,意味这个属性或者方法将同class文件一同被加载入方法区,下面来张内存图方便理解:
简单的叙述一下整个流程:
首先先是Class文件StaticDemo.class最先进入方法区,然后读入main()方法,由于main()方法是一个静态方法,所以进入静态区。
其次读入Fruit类,Fruit.class文件进入方法区,Fruit类中name属性和value属性进入非静态区,area属性进入静态区,构造方法与getInfo()方法进入栈内存,作为读入与取出数据的方法,而area属性被显式初始化为:产地A,在图中表示为由Null改变为产地A。
随着主方法中创建fruit1、fruit2、fruit3三个Fruit类对象,堆内存中出现三块区域,并且每个区域有一个自己的内存地址,并且栈内存中的fruit1、fruit2、fruit3分别指向这三个地址,这三块内存中分别存储着三个对象各自的成员属性,在这里就是name与value,而area属性是静态成员属性,它并不在堆内存中,而是在方法区对应的静态区中,由于尚未初始化值,所以成员属性的初始值就是对应数据类型的默认值,name是String类型数据,对应默认值是Null,value是double类型数据,对应默认值是0.0。
随着构造方法实例化对象后,name、value值会相应改变为对应的实例化数据。在图中可以看出,三个对象的area属性是公共的,它被存放在Fruit.class的静态区中,随着类一同出现,而当Fruit类对象fruit1修改了area属性时,三个对象的area属性同时发生了变化。
所以我们可以得知,static修饰过的属性不进入堆内存,而是跟随class文件一同产生,因此也叫类变量,它并不受到类的实例化对象影响,因此,它的产生顺序是优先于对象的,我们不建议使用对象.属性来修改属性值,若要这样做,建议使用类名.属性名来操作。
下面再说说static修饰方法。
先举个例子:
public class StaticDemo {
public static void main(String[] args) {
a();
}
public void a(){
System.out.println("Hello World");
}
}
这样程序是没法通过编译的,提示Error:(3, 9) java: 无法从静态上下文中引用非静态 方法 a();
main()方法是一个static修饰过的方法,而它没法调用非static修饰的方法a(),(如果一定要用主方法来调用非static方法,可以使用new StaticDemo().a()) 从对属性的分析也可以拿到这里来,因为static修饰过的方法是同时与类进入方法区的,而此时非static方法还尚未加载进入栈内存,因此这就形成了一个先来后到的次序,main()来的太早,没法调用尚未加载的方法a(),从而报错。那么反过来呢?
public class StaticDemo {
public static void main(String[] args) {}
public void a(){
b();
}
public static void b(){
System.out.println("Hello World");
}
}
这样程序是不会报错并且顺利执行的,因为后来的肯定能调用先前已经在方法区的方法,因此可以得知,非static方法可以调用static方法,也可以调用非static方法,而static方法只能调用static方法不能调用非static方法。
那么问题来了,我们什么时候使用static来定义方法??
当一个类没有属性成员,而只有方法时,使用static来定义方法,好比Java.lang.math类,这是个工具类,只需要用户拿来使用它其中的各种数学方法,而不需要去实例化成员,这时候就需要将类中的方法用static修饰,省去了构造方法实例化对象的繁琐步骤。