C/C++中的static关键字在C或C++中的效果不同,修饰变量和修饰方法的作用也不同,同样JAVA中的static也会根据使用场合不同有不同的效果。
static修饰属性
JAVA是借由类对代码进行管理的,而一个类主要分为属性和方法两个部分,同时每一个对象都拥有各自的属性内容(不同对象的属性保存在不同的堆内存中),如果类中的某个属性希望对于类的所有对象来说是共有的,可以使用static进行修饰。
class Book {
static String author;
private String name;
private int price;
public Book() {
System.out.println("This is Book's constructer.");
}
public Book(String name, int price) {
this.name = name;
this.price = price;
}
public Book(String name, int price, String author) {
this.name = name;
this.price = price;
this.author = author;
}
public void getInfo() {
System.out.println("Book name is " + name + ",book price is " + price + ",author is " + author);
}
public void setName(String var) {
name = var;
}
public String getName() {
return name;
}
public void setPrice(int var) {
price = var;
}
public int getPrice() {
return price;
}
}
比如上面的代码中,使用static修饰了author这一属性,同时添加了一个构造函数,然后执行:
public class Demo {
public static void main(String args[]) {
Book bk1 = new Book("English",10, "CONBA");
Book bk2 = new Book("Math",20);
bk1.getInfo();
bk2.getInfo();
}
}
结果为:
Book name is English,book price is 10,author is CONBA
Book name is Math,book price is 20,author is CONBA
也就是说,此时的author已经修改,便适用于所有的类对象。不过这样的公共属性,实际中也很少会进行更改。
而在实际当中,更可能是采用这样一种用法:
class Book {
static String AUTHOR = "CONBA";
private String name;
private int price;
public Book() {
System.out.println("This is Book's constructer.");
}
public Book(String name, int price) {
this.name = name;
this.price = price;
}
public void getInfo() {
System.out.println("Book name is " + name + ",book price is " + price);
}
public void setName(String var) {
name = var;
}
public String getName() {
return name;
}
public void setPrice(int var) {
price = var;
}
public int getPrice() {
return price;
}
}
调用形式为:
public class Demo {
public static void main(String args[]) {
System.out.println(Book.AUTHOR);
}
}
结果为:
CONBA
在上面的调用中,并没有实例化Book对象,但是仍然能够访问到Book的AUTHOR属性。也就是说AUTHOR属性可以直接借由类型进行访问。当然通过对象名访问也是可以的。
而同时由于类static属性属于整个类,那么便希望可以在任何时候都可以访问到该属性,尤其是在没有对象存在的时候,因此便不能够使用private进行修饰。也就是说,此时同样能够在外部不通过setter方法直接修改该属性。
同时需要了解的还有一点,JAVA中主要存在4块内存空间:
- 栈内存空间:保存所有的对象名称(保存引用的堆内存空间的地址)
- 堆内存空间:保存每个对象的具体属性内容
- 全局数据区:保存static类型的属性
- 全局代码区:保存所有的方法定义
static修饰方法
与static修饰属性类似,static修饰方法也可以在没有实例化对象产生的情况下由类名称直接进行调用。
比如,在上边的类方法中添加一个static方法:
public static String getAuthor() {
return AUTHOR;
}
那么可能会存在如下调用:
public class Demo {
public static void main(String args[]) {
System.out.println("Book author is " + Book.getAuthor());
Book bk = new Book("English",12);
System.out.println("Book author is " + bk.getAuthor());
}
}
因此结果为:
Book author is CONBA
Book author is CONBA
这里便使用static对方法进行了修饰,从而可以直接使用Book这个类名调用该static方法。也可以通过对象进行调用。
但同时static方法和非static方法之间也会存在某些限制:
- static方法不能直接访问非static属性或方法,只能调用static属性或方法
- 非static方法可以访问static的属性或方法,不受任何的限制
之所以会存在这些限制,就涉及到之前提到的内存空间:
- 所有的非static定义的结构,必须在类已经明确产生实例化对象时才会分配堆空间,才可以使用
- 所有的static定义的结构,不受实例化对象的控制,即可以在没有实例化对象的时候访问
也就是说,若允许static方法访问非static方法或属性,那么就意味着允许在不存在实例化对象的时候对某个实际不存在的堆空间进行访问,这是不应该被允许的。
main
首先看一下main方法的定义:
public static void main(String[] args)
这就意味这main方法调用的必须是static方法,这也就是说在main所在的类中如果其它的方法要经过或者间接经过main调用,那么这种方法都应该是static修饰的。
而换句话说,在其它类中,可以不使用static进行修饰,此时的方法调用则必须使用实例化对象进行调用。而实例化对象会为对象属性构建内存,那么如果某个方法并不需要访问实例化对象的属性,那么便可以使用static修饰。
再者说,JAVA是由JVM对.class文件进行解释和执行的,而.class文件是跟类名相关的,这也就是说,在执行java xxx命令时,就是通过该xxx定位到该类下的static main方法,然后找到程序的入口的。而若xxx类中不存在static main方法时,虽然会编译通过,但是也会由于找不到static main方法报错。
其实关于static,总的来说:
- static就表示这个属性或方法是公共的,所有对象共有该属性
- 使用static方法可以避免实例化对象调用方法的限制
鉴于上面的限制和性质,static属性和方法可以用于:
- 结合static属性和构造方法,统计该类对象的个数
- 结合static属性和构造方法,进行该类对象的自动化设置
class Book {
static int count = 0;
private String ISBN;
private String name;
private int price;
public Book() {
++count;
this.ISBN = "" + "978711564215" + count;
}
public Book(String name, int price) {
this();
this.name = name;
this.price = price;
}
public void getInfo() {
System.out.println("Book name is " + name + ",book price is " + price);
}
public void setName(String var) {
name = var;
}
public String getName() {
return name;
}
public void setPrice(int var) {
price = var;
}
public int getPrice() {
return price;
}
public String getISBN() {
return ISBN;
}
}
实际调用可能是:
class Demo {
public static void main(String args[]) {
System.out.println("The Book object count is " + Book.count);
Book bk1 = new Book("English",10);
System.out.println("The Book object ISBN is " + bk1.getISBN());
System.out.println("The Book object count is " + Book.count);
Book bk2 = new Book("Math",20);
System.out.println("The Book object ISBN is " + bk2.getISBN());
System.out.println("The Book object count is " + Book.count);
}
}
结果为:
The Book object count is 0
The Book object ISBN is 9787115642151
The Book object count is 1
The Book object ISBN is 9787115642152
The Book object count is 2
上边的代码中结合static属性和构造方法,实现了对象计数器和属性的自动设置。