夯实Java基础(七)——Static关键字
1、static介绍
static关键字一直是各大企业中面试常常会问到的问题,主要考察面试者的基础是否扎实,下面来介绍一下static关键字。
Java中static表示“全局”或者“静态”的意思,可以用来修饰成员变量、成员方法、代码块、内部类和导包。在Java中并不存在全局变量的概念,但是我们可以通过static来实现一个“伪全局”的概念,被static修饰的成员变量和成员方法独立于该类的任何对象。也就是说,它不依赖类特定的实例,被类的所有实例共享。只要这个类被加载了,Java虚拟机就能根据类名在运行时数据区的方法区内定找到他们。因此,static对象可以在它的任何对象创建之前访问,无需引用任何对象,所以被static修饰的成员变量和成员方法可以直接使用类名调用。
class Person{ private static int num=0; public Person() { num++; } public static void plus(){ System.out.println(Person.num); } public static void main(String[] args) { new Person(); new Person(); Person.plus(); plus(); } } //结果:2、2
2、static变量
static修饰的成员变量称作静态变量,静态变量被所有的对象所共享,在内存中只有一个,它会随着类的加载而加载。
另外主要:static是不允许用来修饰局部变量。
提到静态变量我们来看静态变量和非静态变量的区别:
静态变量(类变量):静态变量被所有的对象所共享,也就是说我们创建了一个类的多个对象,多个对象共享着一个静态变量,如果我们修改了静态变量的值,那么其他对象的静态变量也会随之修改。
非静态变量(实例变量):如果我们创建了一个类的多个对象,那么每个对象都有它自己该有的非静态变量。当你修改其中一个对象中的非静态变量时,不会引起其他对象非静态变量值得改变。
class Person{ private static int num; private int num1; public static void main(String[] args) { Person p1 = new Person(); Person p2 = new Person(); Person.num=10; p1.num1=11; Person.num=100; p2.num1=111; System.out.println(Person.num); System.out.println(p1.num); System.out.println(p2.num); System.out.println(p1.num1); System.out.println(p2.num1); } }
从运行结果来看,static变量的值是相同的,说明是共享的,而非静态变量他们的值则不相同,说明依赖于实例。虽然static修饰的变量它不依赖类特定的实例,但它毕竟也是类中的一个属性,也是可以通过类的实例来调用的,只不过属性的值是共享的而已。但是最好还是用类名调用。
3、static方法
static修饰的成员方法称作静态方法,这样我们就可以通过“类名. 方法名”进行调用。由于静态方法在类加载的时候就存在了,所以它不依赖于任何对象的实例就可以进行调用,因此对于静态方法而言,是木有当前对象的概念,即没有this、super关键字的。因为static方法独立于任何实例,因此static方法必须被实现,而不能是抽象的abstract。
并且由于独立于任何实例,在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都是必须依赖具体的对象才能够被调用。但是要注意的是,虽然在静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法/变量的。举个简单的例子:
class Person{ public static void main(String[] args) { Person p=new Person(); p.show1(); } public void show1(){ System.out.println("非静态方法show1()..."); show2(); show3(); } public static void show2(){ System.out.println("静态方法show2()..."); //这里编译会报错Non-static method 'show1()' cannot be referenced from a static context //show1(); } public static void show3(){ System.out.println("静态方法show3()..."); show2(); } }
4、static代码块
被static修饰的代码块也叫静态代码块,会随着JVM加载类的时候而加载这些静态代码块,并且会自动执行。它们可以有多个,可以存在于该类的任何地方。JVM会按照它们的先后顺序依次执行它们,而且每个静态代码块只会被初始化一次,不会进行多次初始化。
示例:
public class Person{ static { System.out.println("Person类静态块"); } public Person() { System.out.println("Person类构造器"); } public static void main(String[] args) { new Son(); System.out.println("-------"); new Son(); } } class Son extends Person{ static { System.out.println("Son类静态块"); } public Son() { System.out.println("Son类构造器"); } }
运行结果:
从运行结果分析:首先运行main()方法,然后JVM就会加载类,因为Son类继承了Person类,所以会先加载父类Person类,再去加载子类Son。由于静态代码块会随着类的加载而加载,所以先输出父类中静态代码块内容”Person类静态块”,然后输出子类中静态代码块内容”Son类静态块”。加载完类之后执行main()方法内容,先new了第一个Son实例,由于子类构造器中默认调用了super(),所以先输出父类构造器的内容,再输出子类构造器的内容。之后又new了第二个Son实例,却是输出的构造器的内容,说明static静态块只加载了一次。结论:静态代码块是先加载父类的静态代码块,然后再加载子类静态代码块,是随着类的加载而加载,而且只会加载一次。
补充:因为入口main()是个方法,也需要用类去调用,所以类的加载优先级>main()方法。
5、static内部类
static修饰内部类的用法很少,毕竟内部类用的就不是很多,一般在源码中才能看见。但是就是有这么一个特殊的用法是用static修饰内部类。普通类是不允许声明为静态的,只要内部类才可以,被static修饰的内部类可以直接作为一个普通类来使用,而不需先实例一个外部类。
static修饰内部类注意几点:
- 静态内部类只能访问外部类的静态成员,否则编译会报错。
- 不管是静态方法还是非静态方法都可以在非静态内部类中访问。
- 如果需要调用内部类的非静态方法,必须先new一个OuterClass的对象outerClass,然后通过outer。new生成内部类的对象,而static内部类则不需要。
简单举例:
public class OuterClass { private static int num=6; // 静态内部类 public static class InnerStaticClass{ public void print() { System.out.println("静态内部类方法print()=="+num); } } //非静态内部类 public class InnerClass{ public void display(){ System.out.println("非静态内部类方法display()=="+num); show(); } } public void show(){ System.out.println("外部类的show()方法=="+num); } public static void main(String[] args) { //非static对象实例 OuterClass outer = new OuterClass(); OuterClass.InnerClass innerClass = outer.new InnerClass(); innerClass.display(); //static对象实例 OuterClass.InnerStaticClass staticClass=new OuterClass.InnerStaticClass(); staticClass.print(); } }
6、static导包
这个知识点非常的冷门,基本上很少的地方会用,我们只需要了解一下即可,用static修饰导包的格式是 import static 包名,static不能写在import前面,这样可以指定导入某个类中的指定静态资源,并且不需要使用类名.资源名,可以直接使用资源名。
来看一下案例:
import static java.lang.Math.*; public class StaticTest { public static void main(String[] args) { System.out.println(sqrt(9)); System.out.println(abs(-12)); } } //结果:3、12
从上面的案例看出,使用import static我们导入了Math类下的所有静态资,所以我们就可以直接使用sqrt(9)、abs(-12)静态方法了。
这样在写代码的时候确实能省一点代码,但是会影响代码可读性,所以一般情况下不建议这么使用。
7、总结
static是非常重要的一个关键字,它的用法也很丰富,以下总结为:
①、static可以用来修饰成员变量、成员方法、代码块、内部类和导包。
②、用来修饰成员变量,将其变为静态变量,从而实现所有对象对于该成员的共享,通过“类名.变量名”即可调用。
③、用来修饰成员方法,将其变为静态方法,可以直接使用“类名.方法名”的方式调用,常用于工具类。
④、静态方法中不能使用关键字this和super 。
⑤、静态块用法,将多个类成员放在一起初始化,使得程序更加规整,其中理解对象的初始化过程非常关键。
⑥、静态代码块是先加载父类的静态代码块,然后再加载子类静态代码块,而且只会加载一次。
⑦、static修饰成员变量、成员方法、代码块会随着类的加载而加载。
⑧、静态导包用法,将类的方法直接导入到当前类中,从而直接使用“方法名”即可调用类方法,更加方便,但是代码的可读性降低。
相关文章