java的static关键字
1、在类中,用static声明的成员变量为静态成员变量,他为该类的公用变量,在第一次使用时被初始化,对于该类的所有对象来说static成员变量只有一份
2、用static声明的方法为静态方法,在调用该方法时,不会将对象的引用传递给他,所以在static方法中不可访问非static的成员
静态方法不在是针对某个对象调用,所以不能访问非静态成员
3、可以通过对象引用或类名(不需要实例化)访问静态成员
static 方法:
static 修饰的方法称为静态方法,可以不用创建对象即可使用,对于静态方法来说没有this的,因为它不依附于任何对象,既然没有对象就谈不上this,并且这个由于特性,在静态方法中不能访问类的非静态成员变量和非静态方法,因为非静态成员方法/变量都是必须依赖于具体的对象才可以被调用,注意:在静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中可以访问静态成员方法/变量的
下面例子:
在上面的代码中,由于print2方法是独立于对象存在的,可以直接用过类名调用。假如说可以在静态方法中访问非静态方法/变量的话,那么如果在main方法中有下面一条语句:
MyObject.print2();
此时对象都没有,str2根本就不存在,所以就会产生矛盾了。同样对于方法也是一样,由于你无法预知在print1方法中是否访问了非静态成员变量,所以也禁止在静态成员方法中访问非静态成员方法。
而对于非静态成员方法,它访问静态成员方法/变量显然是毫无限制的。
因此,如果说想在不创建对象的情况下调用某个方法,就可以将这个方法设置为static。我们最常见的static方法就是main方法,至于为什么main方法必须是static的,现在就很清楚了。因为程序在执行main方法的时候没有创建任何对象,因此只有通过类名来访问。
static变量:
static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。
static成员变量的初始化顺序按照定义的顺序进行初始化。
static代码块:
static关键字还有一个比较关键的作用就是 用来形成静态代码块以优化程序性能。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。
为什么说static块可以用来优化程序性能,是因为它的特性:只会在类加载的时候执行一次。下面看个例子:
class Person{ private Date birthDate; public Person(Date birthDate) { this.birthDate = birthDate; } boolean isBornBoomer() { Date startDate = Date.valueOf("1946"); Date endDate = Date.valueOf("1964"); return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0; } }
isBornBoomer是用来这个人是否是1946-1964年出生的,而每次isBornBoomer被调用的时候,都会生成startDate和birthDate两个对象,造成了空间浪费,如果改成这样效率会更好:
class Person{ private Date birthDate; private static Date startDate,endDate; static{ startDate = Date.valueOf("1946"); endDate = Date.valueOf("1964"); } public Person(Date birthDate) { this.birthDate = birthDate; } boolean isBornBoomer() { return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0; } }
static关键字的误区:
1.static关键字不会改变类中成员的访问权限
Java中的static关键字不会影响到变量或者方法的作用域。在Java中能够影响到访问权限的只有private、public、protected(包括包访问权限)这几个关键字。看下面的例子就明白了:
提示错误"Person.age 不可视",这说明static关键字并不会改变变量和方法的访问权限。
2.能通过this访问静态成员变量吗?
public class Main { static int value = 33; public static void main(String[] args) throws Exception{ new Main().printValue(); } private void printValue(){ int value = 3; System.out.println(this.value); } }
输出33
这里面主要考察队this和static的理解。this代表什么?this代表当前对象,那么通过new Main()来调用printValue的话,当前对象就是通过new Main()生成的对象。而static变量是被对象所享有的,因此在printValue中的this.value的值毫无疑问是33。在printValue方法内部的value是局部变量,根本不可能与this关联,所以输出结果是33。在这里永远要记住一点:静态成员变量虽然独立于对象,但是不代表不可以通过对象去访问,所有的静态方法和静态变量都可以通过对象访问(只要访问权限足够)。
3.static能作用于局部变量么?
static是不允许用来修饰局部变量。
下面利用画内存学习更加深刻的需东西这一小节:
package satic; public class Cat { private static int sid = 0; // sid是静态的可以直接用类名变量名直接用
private String name; //Cat类私有的属性name 初始值为0 int id; //变量 Cat(String name) { this.name = name; //将调用Cat方法时传入的值赋值给name属性 id = sid++; //将sid的值赋值给id并且sid自增1 } //info方法输入name和id的值 public void info(){ System.out.println ("My name is "+name+" No."+id); } public static void main(String arg[]){ Cat.sid = 100; //将sid的值变为100 /* * 调用Cat 方法更改name与id的值并打印 * */ Cat mimi = new Cat("mimi"); Cat pipi = new Cat("pipi"); mimi.info(); pipi.info(); } }
先从main函数入口开始,
Cat.sid = 100在数据区当中有一个sid,被赋值为100,Cat mimi = new Cat("mimi")时,在栈内存当中有一个叫mimi的内存具体内容不知,但他指向堆内存当中的一块内存,在堆内存当中有两个分别是id和name的属性,而mimi这个字符串是存储在数据区的,字符串也是个引用类型,所以说字符串可以看作是一个对象,执行
Cat(String name) { this.name = name; //将调用Cat方法时传入的值赋值给name属性 id = sid++; //将sid的值赋值给id并且sid自增1 }
时在栈内存里面有一个name的内存然后,这个"mimi"的引用交到name中,现在name指向"mimi"字符串,this.name = name,这句话中
将栈内存的name的值赋值给堆内存中mimi这个对象的name,也就是mimi这个对象的name也指向数据区里面的“mimi”字符串,然后将sid的
值赋值给id,sid自身+1,这时候的内存中的图如下:
然后构造方法执行完毕,给这个构造方法分配的局部变量所占的内存空间全部消失,所以位于栈内存的name这块内存消失了,栈内存里面指向数据区里面的字符串对象"mimi"的引用也随之消失,此时只剩下堆内存里面的字符串对象“mimi”的引用没有消失,此时的内存布局如下
执行Cat pipi = new Cat("pipi"),执行过程u第一次一样,这里就不过多解释了,此时的内存布局如下图:
The result is:
My name is mimi No.100
My name is pipi No.2000
由此可以看出这个静态的sid可以计数用的
这里调用构造方法Cat(String name) 创建出两只猫,首先在栈内存里面分配两小块空间mimi和pipi,里面分别装着可以找到这两只猫的地址,mimi和pipi对应着堆内存里面的两只猫的引用。这里的构造方法声明有字符串类型的变量,字符串常量是分配在数据区里面的,所以这里会把传过来的字符串mimi和pipi都存储到数据区里面。所以数据区里面分配有存储字符串mimi和pipi的两小块内存,里面装着字符串“mimi”和“pipi”,字符串也是引用类型,除了那四类8种的基础数据类型之外,其他所有的数据类型都是引用类型。所以可以认为字符串也是一个对象。
这里是new了两只猫出来,这两只猫都有自己的id和name属性,所以这里的id和name都是非静态成员变量,即没有static修饰。所以每new出一只新猫,这只新猫都有属于它自己的id和name,即非静态成员变量id和name是每一个对象都有单独的一份。但对于静态成员变量来说,只有一份,不管new了多少个对象,哪怕不new对象,静态成员变量在数据区也会保留一份。如这里的sid一样,sid存放在数据区,无论new出来了多少只猫在堆内存里面,sid都只有一份,只在数据区保留一份。
静态成员变量是属于整个类的,它不属于专门的某个对象。那么如何访问这个静态成员变量的值呢?首先第一点,任何一个对象都可以访问这个静态的值,访问的时候访问的都是同一块内存。第二点,即便是没有对象也可以访问这个静态的值,通过“类名.静态成员变量名”来访问这个静态的值,所以以后看到某一个类名加上“.”再加上后面有一个东西,那么后面这个东西一定是静态的,如”System.out”,这里就是通过类名(System类)再加上“.”来访问这个out的,所以这个out一定是静态的
下面再看这段代码:
package satic; public class Cat { /* * 这里的sid不在是静态的了 */ private int sid = 0; /* * 这里的name和id是非静态的成员变量 * */ private String name; //Cat类私有的属性name 初始值为0 int id; //变量 Cat(String name) { this.name = name; //将调用Cat方法时传入的值赋值给name属性 id = sid++; //将sid的值赋值给id并且sid自增1 } //info方法输入name和id的值 public void info(){ System.out.println ("My name is "+name+" No."+id); } public static void main(String arg[]){ //Cat.sid = 100; 上面的sid不在是静态的了,所以不能这样来访问了 /* * 调用Cat 方法更改name与id的值并打印 * */ Cat mimi = new Cat("mimi"); mimi.sid = 2000; Cat pipi = new Cat("pipi"); mimi.info(); pipi.info(); } }
这段代码与上面的代码不同的是,将sid变成了非静态的,就不再有计数功能了,sid和name与id一样都属于属性,当然在面的代码当中也不能使用Cat.sid访问了,把Cat.sid那行去掉即可,这样的话,内存图就变成了下面的:
成员变量(属性)与局部变量的区别在于,成员变量(属性)初始的值为0,而局部变量没有初始化不能使用,所以,每次执行Cat方法,sid由0变为1,这样sid就没有了计数的功能
在一个静态方法里,如果想访问一个非静态的成员变量,是不能直接访问的,必须在静态方法里new一个对象出来才能访问。如果是加了static的成员变量,那么这个成员变量就是一个静态的成员变量,就可以在main方法里面直接访问了。
main方法是一个静态的方法,main方法要执行的时候不需要new一个对象出来。
动态方法是针对于某一个对象调用的,静态方法不会针对某一个对象来调用,没有对象照样可以用。所以可以使用”classname.method()”.的形式来调用静态方法。所以想在main方法里面访问非静态成员变量是不可以的,想在main方法里面访问非静态方法也是不可以的,因为非静态方法只能针对于某个对象来调用,没有对象,就找不到方法的执行者了。
成员变量只有在new出一个对象来的时候才在堆内存里面分配存储空间。局部变量在栈内存里面分配存储空间。
静态方法不再是针对某一个对象来调用,所以不能访问非静态的成员。
非静态成员专属于某一个对象,想访问非静态成员必须new一个对象出来才能访问。
静态的变量可以通过对象名去访问,也可以通过类名去访问,两者访问的都是同一块内存。