static关键字常见但是易忽略。下面就先讲述一下static关键字的用法和平常容易误解的地方,最后列举了一些面试笔试中常见的关于static的考题。以下是本文的目录大纲:
一.static关键字的用途
1.static方法
2.静态变量
3.静态代码块
4.静态导包
1.static方法
使用static修饰的静态方法是属于整个类的类方法,它在内存中的代码段会随类的定义而被分配和装载;而非静态方法是属于具体对象的方法,当这个对 象创建时,在对象的内存中会拥有此方法的专用代码段。在引用静态方法时,可以使用对象名加前缀,也可以使用类名加前缀
public class Person{
public int age;
public static double height;
public static String Address(String x){
System.out.println("address:"+x);
}
public int Birthday(int x){
System.out.println("birthday is "+x+" month");
}
public static void main(String[] args){//静态方法
age=24; //引用了非静态数据成员
height=173;
Address("xi'an");
Birthday(5); //引用了非静态方法
}
}
因此,如果说想在不创建对象的情况下调用某个方法,就可以将这个方法设置为static。我们最常见的static方法就是main方法,至于为什么main方法必须是static的,现在就很清楚了。因为程序在执行main方法的时候没有创建任何对象,因此只有通过类名来访问。
另外记住,即使没有显示地声明为static,类的构造器实际上也是静态方法。
2.静态变量
static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。
static成员变量的初始化顺序按照定义的顺序进行初始化。
static int a = 3;
3.静态代码块
static关键字还有一个比较关键的作用就是 用来形成静态代码块以优化程序性能。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。
为什么说static块可以用来优化程序性能,是因为它的特性:只会在类加载的时候执行一次。
class Test{
private Date birthDate;
public Test(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;
}
}
class Test{
private Date birthDate;
private static Date startDate,endDate;
static{
startDate = Date.valueOf("1946");
endDate = Date.valueOf("1964");
}
public Test(Date birthDate) {
this.birthDate = birthDate;
}
boolean isBornBoomer() {
return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0;
}
}
4 .静态导包
import static静态导入是JDK1.5中的新特性。一般我们导入一个类都用 import com…..ClassName;而静态导入是这样:import static com…..ClassName.*;这里的多了个static,还有就是类名ClassName后面多了个
.* ,意思是导入这个类里的静态方法。当然,也可以只导入某个静态方法,只要把 .* 换成静态方法名就行
了。然后在这个类中,就可以直接用方法名调用静态方法,而不必用ClassName.方法名 的方式来调用。
这种方法的好处就是可以简化一些操作,例如打印操作System.out.println(…);就可以将其写入一个静态方
法print(…),在使用时直接print(…)就可以了。
但是这种方法建议在有很多重复调用的时候使用,如果仅有一到两次调用,不如直接写来的方便
在静态导入之前:
public class TestStatic {
public static void main(String[] args) {
System.out.println(Integer.MAX_VALUE);
System.out.println(Integer.toHexString(42));
}
}
在静态导入之后:
import static java.lang.System.out;
import static java.lang.Integer.*;
public class TestStaticImport {
public static void main(String[] args) {
out.println(MAX_VALUE);
out.println(toHexString(42));
}
}
注:
1、虽然该特性通常称为“静态导入”,但语法必须是import static,后面跟你想导入的static成员的完全限
定名称,或者通配符。在本例中,我们在System类的out对象上进行静态导入。
2、在本例中,我们可能想使用java.lang.Integer类的几个static成员。该静态导入语句使用通配符来表达“
我想在此类中的所有静态成员上进行静态导入”。
3、现在我们终于看到静态导入特性的好处!我们不必在System.out.println中键入System。太好了!另外,
我们不必在Integer.MAX_VALUE中键入Integer。因此,在这行代码中,我们能够将快捷方式用于静态方法和一
个常量。
4、最后,我们进行更多的快捷操作,这次针对Integer类的方法。
关于该特性,我们已经有点儿讽刺意味儿了,但不仅我们是这样的。我们不认为节省少量的击键次数会让代码
难于阅读一点,但许多开发人员要求将它添加到语言中。
下面是使用静态导入的几条原则:
你必须说import static, 不能说static import。
提防含糊不清的命名static成员。例如,如果你对Integer类和Long类执行了静态导入,引用MAX_VALUE将导致
一个编译器错误,因为Integer和Long都有一个MAX_VALUE常量,并且Java不会知道你在引用哪个MAX_VALUE。
你可以在static对象引用、常量(记住,它们是static 或final)和static方法上进行静态导入。
二.static关键字的误区
1.能通过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);
}
}
这里面主要考察队this和static的理解。this代表什么?this代表当前对象,那么通过new Main()来调用printValue的话,当前对象就是通过new Main()生成的对象。而static变量是被对象所享有的,因此在printValue中的this.value的值毫无疑问是33。在printValue方法内部的value是局部变量,根本不可能与this关联,所以输出结果是33。在这里永远要记住一点:静态成员变量虽然独立于对象,但是不代表不可以通过对象去访问,所有的静态方法和静态变量都可以通过对象访问(只要访问权限足够)。
2.static是不允许用来修饰局部变量
三.常见的笔试面试题
计算输出下面结果
class MyClass extends Test {
Person person = new Person("MyClass");
static{
System.out.println("myclass static");
}
public MyClass() {
System.out.println("myclass constructor");
}
}
public class Test {
Person person = new Person("Test");
static{
System.out.println("test static");
}
public Test() {
System.out.println("test constructor");
}
public static void main(String[] args) {
new MyClass();
}
}
class Person{
static{
System.out.println("person static");
}
public Person(String str) {
System.out.println("person "+str);
}
}
test static
myclass static
person static
person Test
test constructor
person MyClass
myclass constructor
类似地,我们还是来想一下这段代码的具体执行过程。首先加载Test类,因此会执行Test类中的static块。接着执行new MyClass(),而MyClass类还没有被加载,因此需要加载MyClass类。在加载MyClass类的时候,发现MyClass类继承自Test类,但是由于Test类已经被加载了,所以只需要加载MyClass类,那么就会执行MyClass类的中的static块。在加载完之后,就通过构造器来生成对象。而在生成对象的时候,必须先初始化父类的成员变量,因此会执行Test中的Person person = new Person(),而Person类还没有被加载过,因此会先加载Person类并执行Person类中的static块,接着执行父类的构造器,完成了父类的初始化,然后就来初始化自身了,因此会接着执行MyClass中的Person person = new Person(),最后执行MyClass的构造器。