在讲解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,因为这样可以被集体修改,也可以节省空间。
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。两组方法之间的相互访问也会受到限制:
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(); //报错
}
}
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方法进行修饰,如果不加会报错,如下:
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修饰方法呢?
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();
}
}
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));
}
}
方法组成如下:
我们可以在执行编译文件的时候,在主类名后面加上,以空格隔开的参数,如果参数本身带有空格,那么可以使用双引号括起来。
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
希望每当实例化一个对象的时候可以打印一个信息,产生的第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个对象