1.static定义属性
在讲解static之前,首先观察一个代码
class Book{
private String title;
private Double price;
String pub = "清华大学出版社";
public Book(String title,Double price){
this.title = title;
this.price = price;
}
public String getInfo(){
return "书名:"+this.title+"、价格:"+this.price+"、出版社:"+this.pub;
}
}
public class Hello{
public static void main(String[] args){
Book book1 = new Book("Java",12.2);
Book book2 = new Book("python",132.2);
Book book3 = new Book("C++",52.2);
// static修饰共享前
// 更改一个对象的属性,只对该对象的该属性生效
book1.pub = "北京大学出版社";
System.out.println(book1.getInfo()); // 出版社信息是北京大学...
System.out.println(book2.getInfo()); // 出版社信息是清华大学...
System.out.println(book3.getInfo()); // 出版社信息是清华大学...
}
}
下面是内存分析
通过内存发现,每一个对象的出版社信息都是相同的,此时还有必要每个对象都独立占用相同的属性信息吗?如果说我们用这种方法存了1000W个图书的信息,出版社都是一样的,都独自占用着相同的属性。现要求要更变这1000W个书的出版社信息,就需要更改1000W次。
综上所述,用普通的方法定义属性,那么每个对象都将独立地保存各自的属性信息。这种结构式不方便维护的。进一步讲,如果每个对象都有相同的属性,那么应该将其共享。共享的方式就是用static关键字来修饰它
class Book{
private String title;
private Double price;
static String pub = "清华大学出版社";
public Book(String title,Double price){
this.title = title;
this.price = price;
}
public String getInfo(){
return "书名:"+this.title+"、价格:"+this.price+"、出版社:"+this.pub;
}
}
public class Hello{
public static void main(String[] args){
Book book1 = new Book("Java",12.2);
Book book2 = new Book("python",132.2);
Book book3 = new Book("C++",52.2);
// static修饰共享后
// 更改一个对象的属性,对所有对象的所有与此相同的属性一起生效
book1.pub = "北京大学出版社";
// 下面的出版社信息都是清华大学
System.out.println(book1.getInfo());
System.out.println(book2.getInfo());
System.out.println(book3.getInfo());
}
}
下面是内存分配
经过static关键字修饰的属性被放到一个独立的内存中,该区域是全局数据区,可以被多个对象同时指向。同时,每个对象就不单独存储该属性信息了。
那么既然static是一个公共的属性,那么在前面的代码中,用一个对象去修改该属性的做法的是不合适的,正确的做法是由所有对象的一个公共代表来修改该属性。这个代表就是类。简单来讲,由static关键字修饰的属性是可以由类直接调用的。因此,我们可使用下面的代码来更改pub属性,修改后对所有的对象都生效。
Book.pub = "北京大学出版社";
static和非static修饰的属性有一个最大的区别:static修饰的属性在无需实例化对象的情况下,直接由类调用。那么什么时候用static修饰,什么时候不用呢?
在编写类的过程中,你所选择的首要修饰符一定不是static,也就是95%的情况下不用static修饰。如果要描述出共享信息的时候再用static,因为这样可以被集体修改,也可以节省空间。
2.static定义方法
static定义方法也可以在没有实例化的时候被类直接调用。
class Book{
private String title;
private Double price;
private static String pub = "清华大学出版社";
public Book(String title,Double price){
this.title = title;
this.price = price;
}
public String getInfo(){
return "书名:"+this.title+"、价格:"+this.price+"、出版社:"+this.pub;
}
public static void setPub(String p){
pub = p;
}
}
public class Hello{
public static void main(String[] args){
// 调用由static修饰的方法,修改由static修饰的属性
Book.setPub("北京大学");
Book book1 = new Book("Java",12.2);
Book book2 = new Book("python",132.2);
Book book3 = new Book("C++",52.2);
// 下面的出版社信息都是北大
System.out.println(book1.getInfo());
System.out.println(book2.getInfo());
System.out.println(book3.getInfo());
}
}
可以发现,由static修饰的方法和属性都不受实例化类的限制,可由类直接调用。但此时就会出现一个特别麻烦的问题,类中的方法就变成了两组:一个是static方法,一个是非static。两组方法之间的相互访问也会受到限制:
- static方法不能访问非static修饰的属性或方法,只能调用static属性和方法
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修饰的方法不可访问非static修饰的属性
public static String getInfo(){
return "书名:"+this.title+"、价格:"+this.price+"、出版社:"+this.pub;
}
}
public class Hello{
public static void main(String[] args){
Book.getInfo(); //报错
}
}
- 非static方法可以访问非static修饰的属性或方法,不受任何限制
class Book{
private String title;
private Double price;
private static String pub = "清华大学出版社";
public Book(String title,Double price){
this.title = title;
this.price = price;
}
public static String getInfo(){
return "出版社:"+pub;
}
// 非static可以调用static修饰方法
public String getPub(){
return this.getInfo();
}
}
public class Hello{
public static void main(String[] args){
Book.getInfo(); // 无错
}
}
为什么会这样呢?
- 所有的非static修饰的属性和方法,只有在实例化了对象之后才会分配空间,才能被访问。
- 所有的static修饰的方法,在没有实例化对象的时候就已经有了空间,可被访问
在主类中的方法,一般会加上static方法进行修饰,如果不加会报错,如下:
public class Hello{
public static void main(String[] args){
fun();//报错
}
public void fun(){
System.out.println("hello");
}
}
如果先实例化主类,在调用fun方法,就不会报错
public class Hello{
public static void main(String[] args){
new Hello().fun();//无错
}
public void fun(){
System.out.println("hello");
}
}
那到底什么时候用static修饰方法呢?
- 定义类的时候,首先考虑非static方法
- 如果某个类中的方法,需要用到本类中的非static属性的时候,不要用static定义方法
class Book{
private boolean flag;
public Book(boolean falg){
this.flag = flag;
}
public void fun(){
if(this.flag){
System.out.println("可以操作");
}else{
System.out.println("不可以操作");
}
}
}
public class Hello{
public static void main(String[] args){
Book b1 = new Book(true);
Book b2 = new Book(false);
b1.fun();
b2.fun();
}
}
- 如果一个类中只有方法,没有属性,那完全可以定义static方法,因此这样不用产生类就能调用方法了。
class Book{
public static int sum(int x, int y){
return x + y;
}
}
public class Hello{
public static void main(String[] args){
System.out.println(Book.sum(5,6));
}
}
3.主方法
方法组成如下:
- public:主方法是程序的开始,所以一定要对任何的操作都是可见的,所以要用public描述一个公共的概念。
- static:证明此方法是由类名称调用的,因为执行一个类的时候是执行:java+一个类名称
- void:没有返回值
- main:系统指定好的方法名称,不能修改。
- String[] args:程序运行时传递的参数。
我们可以在执行编译文件的时候,在主类名后面加上,以空格隔开的参数,如果参数本身带有空格,那么可以使用双引号括起来。
public class Hello{
public static void main(String[] args){
for(int i = 0; i<args.length; i++){
System.out.println(args[i]);
}
}
}
//终端下输入
java Hello "hello world" hello word
//输出为
hello world
hello
word
3.static的实际应用
- 不管有多少对象,都是用同一类中用static定义的属性
- 使用static方法可以避免掉实例化对象调用方法的限制
3.1 实现类实例化对象个数的统计
希望每当实例化一个对象的时候可以打印一个信息,产生的第x个实例化对象。
因为只要是产生一个新的实例化对象,就会调用一次构造方法,因此我们可以在构造方法里面,给实例化对象的次数加一。
class Book{
private String title;
private static int num = 0;
public Book(String title){
this.title = title;
this.num = ++this.num;
System.out.println("这是实例化的第"+this.num+"个对象");
}
}
public class Hello{
public static void main(String[] args){
Book boo1 = new Book("java");
Book boo2 = new Book("java");
Book boo3 = new Book("java");
Book boo4 = new Book("java");
}
}
//输出
这是实例化的第1个对象
这是实例化的第2个对象
这是实例化的第3个对象
这是实例化的第4个对象
4.总结
- 开发中首选的属性不是static属性、方法不是static方法
- static属性和方法可以在没有实例化的情况下直接被类调用。
- static属性保存在全局数据区
- 内存有四块区域:栈内存、堆内存、全局数据区、全局代码区