一、static基本描述
对于关键字static ,大家应该不会陌生,因为一直编写:
public static void main(String args[]) {...}
那static表示什么呢?static表示“全局”或者“静态”的意思,用来修饰成员变量和成员方法,也可以形成静态static代码块,但是要注意一点:Java语言中没有全局变量的概念。
二、使用static定义属性
我们先来看一个表示图书的操作类,其中所有书的出版社都是清华出版社。
package com.wz.thisdemo;
class Book {
private String title;
private double price;
String pub = "清华大学出版社"; // 此处暂时不封装
public Book(String title, double price) {
super();
this.title = title;
this.price = price;
}
public String getInfo() {
return "书名:" + this.title + ",价格:" + this.price + ",出版社:" + this.pub;
}
}
public class TestDemo {
public static void main(String args[]) {
Book ba = new Book("Java开发",10.2);
Book bb = new Book("Android开发",11.2);
Book bc = new Book("Oracle开发",12.2);
System.out.println(ba.getInfo());
System.out.println(bb.getInfo());
System.out.println(bc.getInfo());
}
}
运行结果:
书名:Java开发,价格:10.2,出版社:清华大学出版社
书名:Android开发,价格:11.2,出版社:清华大学出版社
书名:Oracle开发,价格:12.2,出版社:清华大学出版社
内存分配图如下:
那么问题来了,既然这个类中所有的图书的出版社都是同一个,那么每个图示对象还有必要占有重复的属性信息么?
假设有1000万个Book类对象,要求将所有对象的出版社名称更换,意味着什么?
我们看代码,在主方法中添加如下:
public class TestDemo {
public static void main(String args[]) {
Book ba = new Book("Java开发",10.2);
Book bb = new Book("Android开发",11.2);
Book bc = new Book("Oracle开发",12.2);
ba.pub = "北京大学出版社";
bb.pub = "浙江大学出版社";
System.out.println(ba.getInfo());
System.out.println(bb.getInfo());
System.out.println(bc.getInfo());
}
}
运行结果:
书名:Java开发,价格:10.2,出版社:北京大学出版社
书名:Android开发,价格:11.2,出版社:浙江大学出版社
书名:Oracle开发,价格:12.2,出版社:清华大学出版社
这意味着要修改1000万个对象的内容!所以,如果将对象的属性定义为普通属性,这就意味着每一块堆内存空间都将保存着各自的信息。
既然每个Book类对象的pub内容是一样的,那么应该将其定义为一个共享的属性,即所有的对象都共享pub属性。于是,在这种情况下为了描述共享的概念,可以使用static来定义需要共享的这个属性。
在Book类的属性pub上使用static修饰:
static String pub = "清华大学出版社";
主方法为:
public class TestDemo {
public static void main(String args[]) {
Book ba = new Book("Java开发",10.2);
Book bb = new Book("Android开发",11.2);
Book bc = new Book("Oracle开发",12.2);
ba.pub = "北京大学出版社";
System.out.println(ba.getInfo());
System.out.println(bb.getInfo());
System.out.println(bc.getInfo());
}
}
运行结果:
书名:Java开发,价格:10.2,出版社:北京大学出版社
书名:Android开发,价格:11.2,出版社:北京大学出版社
书名:Oracle开发,价格:12.2,出版社:北京大学出版社
我们发现,一旦属性定义上使用了static 之后,只要有一个对象修改了属性的内容,那么所有的对象的这个static属性的内容都一起进行修改。此时,pub属性就成了一个公共属性。
于是,内存分配图也变成了如下这样:
static声明的属性与普通属性(非static)最大的区别就是:保存内存区域的不同。
还有一个问题:既然static定义的属性是公共属性,那么如果只是简单的由一个对象去修改这个属性的做法是不合适的。合适的做法就是由所有对象的公共代表 — 类来修改,通过“类名称.static属性”的方式来完成。
所以以上的:
ba.pub = "北京大学出版社";
最好用
Book.pub = "北京大学出版社";
来代替。
通过以上说明, 我们应该清楚以下几点:
(1)使用static定义的属性不在堆内存之中保存,保存在全局数据区;
(2)使用static定义的属性表示类属性,类属性可以由类名称直接进行调用;
(3)static属性虽然定义在类中,但是其可以在没有实例化对象的时候进行调用(普通属性保存在堆内存里,而static属性保存在全局数据区之中);
那么在定义属性的时候,什么时候使用static定义?什么时候不使用static定义呢?
在开发中,尽量不使用static定义属性,如果需要描述共享信息的时候,才使用static定义()主要是为了方便集体修改,且不重复分配内存空间)。
三、使用static定义方法
除了使用static定义属性之外,方法上也可以使用static进行定义,那么很明显,使用static定义的方法也可以在没有实例化对象产生的情况下由类名称直接进行调用。
来看一段代码:
package com.wz.thisdemo;
class Book {
private String title;
private double price;
private static String pub = "清华大学出版社";
public Book(String title, double price) {
this.title = title;
this.price = price;
}
//static方法
public static void setPub(String p){
pub = p;
}
public String getInfo() {
return "书名:" + this.title + ",价格:" + this.price + ",出版社:" + this.pub;
}
}
public class TestDemo {
public static void main(String args[]) {
//在对象产生前进行调用
Book.setPub("北京大学出版社");
Book ba = new Book("Java开发",10.2);
Book bb = new Book("Android开发",11.2);
Book bc = new Book("Oracle开发",12.2);
System.out.println(ba.getInfo());
System.out.println(bb.getInfo());
System.out.println(bc.getInfo());
}
}
运行结果:
书名:Java开发,价格:10.2,出版社:北京大学出版社
书名:Android开发,价格:11.2,出版社:北京大学出版社
书名:Oracle开发,价格:12.2,出版社:北京大学出版社
但是问题来了,类中的方法有两种,一种是static方法,一种是非static方法,这两种方法间的访问将受到限制!
(1) static方法不能直接调用非static的方法或属性;
(2)非static方法可以调用static的属性或方法,不受限制。
可是为什么会有这样的限制呢?
使用static定义的属性和方法,可以在没有实例化对象的时候使用;而非static定义的属性和方法,必须实例化对象之后才可以进行调用。
那么,我们时候使用static方法?什么时候使用static方法呢?
如果一个类中没有任何属性,只有方法,建议可以将所有的方法定义为static方法,这样每次调用方法的时候就可以不进行对象实例化了。在实际开发中,建议优先考虑非static方法和非static属性。
四、Java 主方法
Java方法定义时,如果一个方法在主类之中定义,并且由主方法直接调用的时候,那么前面必须有public static,格式如下:
public static 返回值类型 方法名称 (参数列表) {
[return [返回值] ;]
}
观察以下代码:
package com.wz.thisdemo;
public class TestDemo {
public static void main(String args[]) {
print() ; // 直接调用
}
public static void print() {
System.out.println("Hello World .") ;
}
}
运行结果:
Hello World .
此时,表示的是一个static方法调用其他的static方法,但是如果这个print()方法上没有static呢?
package com.wz.thisdemo;
public class TestDemo {
public static void main(String args[]) {
print() ; // 直接调用
}
public void print() {
System.out.println("Hello World .") ;
}
}
运行结果:
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
Cannot make a static reference to the non-static method print() from the type TestDemo
at com.wz.thisdemo.TestDemo.main(TestDemo.java:5)
所有的非static方法几乎都有一个特点:方法要由实例化对象调用。正确写法如下:
package com.wz.thisdemo;
public class TestDemo {
public static void main(String args[]) {
new TestDemo().print() ; // 对象调用
}
public void print() { // 非static方法
System.out.println("Hello World .") ;
}
}
运行结果:
Hello World .
现在,来观察主方法的组成:
public static void main(String args[]) {...}
可以发现里面有很多的关键字:
(1)public:是一种访问权限,表示公共;
(2)static:此方法由类名称直接调用;
(3)void:主方法是程序执行的开始;
(4)main:系统规定的一个方法名称,执行类的时候默认找到此名称,不能修改;
(5)String args[]:表示程序运行时传递的参数,通过字符串接收。
五、static使用实例
统计一个类产生的实例化对象个数
分析: 一个类肯定可以有多个实例化对象产生,增加一个统计操作,可以统计出一个类之中的所产生的实例化对象的个数。如果现在要产生新的实例化对象,则一定会调用类中的构造方法,所以可以在构造方法中增加统计,而且这个统计的变量是所有对象共享的,应该将其定义为static属性。
package com.wz.thisdemo;
class Obj{
private static int count = 0;
public Obj(){
System.out.println("count = " + ++count);
}
}
public class TestDemo {
public static void main(String args[]) {
new Obj();
new Obj();
new Obj();
new Obj();
new Obj();
}
}
运行结果:
count = 1
count = 2
count = 3
count = 4
count = 5