一:static关键字之基本用法:
1. static关键字概念:
一句话描述就是:方便在没有创建对象的情况下进行调用。也就是说:被static修饰的成员不需要通过创建对象来调用,直接根据类名就可以访问。
2.static修饰变量和方法:
用static声明的成员变量为静态成员变量(类变量);
static声明的成员方法为static方法(静态方法)。
* static是不允许用来修饰局部变量。不要问为什么,这是Java语法的规定
3. static修饰代码块:
static块:也叫静态初始化块,用于类的初始化操作,在类第一次被加载时执行,并且只执行一次;
注意:在静态初始化块中不能直接方法非静态成员;
作用:提升程序性能。(该作者的文章里有最好的例子:https://blog.csdn.net/kuangay/article/details/81485324)
静态初始化块可以置于类中的任何地方,类中可以有多个静态初始化块。
在类初次被加载时,会按照静态初始化块的顺序来执行每个块,并且只会执行一次。
4. static修饰类:
static只可以修饰内部类,普通类是不允许被声明为静态的。代码如下:
public class StaticTest{
//内部类
public static class InnerTest(){
public InnerTest(){
System.out.println("-----静态内部类-----");
}
public void InnerMethod(){
System.out.println("-----静态内部方法-----");
}
}
//main方法
public static void main(String[] args){
//直接通过类名访问静态内部类
InnerTest test = new StaticTest.InnerTest();
//静态内部类可以和普通类一样使用
test.InnerMethod();
}
}
上面这段代码静态内部类的写法,输出结果:
-----静态内部类-----
-----静态内部方法-----
* 如果没有用static修饰InterTest,则只能new 一个外部类实例。再通过外部实例创建内部类。
这里再顺便说一下类初始化的顺序:
父类静态变量
父类静态代码块
子类静态变量
子类静态代码块
父类普通变量
父类普通代码块
父类构造函数
子类普通变量
子类普通代码块
子类构造函数
*有关类初始化输出顺序的面试题,请查看该作者的文章,很不错:https://blog.csdn.net/kuangay/article/details/81485324
5.static修饰和普通的区别:
* static声明的成员变量和方法,从属于类;生命周期和类相同,在整个应用程序执行期间都有效;不依赖于任何对象(也就是没有this);
* 普通变量和方法从属于对象;必须依赖于具体的对象才能被调用
* 静态方法不能调用非静态的方法和变量,编译会报错;但是非静态的方法可以调用静态的成员方法和成员变量。
* 静态变量被所有对象共享,在内存中只有一个副本,在类初次加载时加载;
* 非静态变量是对象所拥有的,在内存中存在多个副本,各个对象所拥有的副本互不影响;每创建一个对象就初始化一个副本。
代码实例:
从代码中看出:
* 非静态成员方法test2可以访问静态成员方法test1和静态成员变量name的;
* 静态成员方法test1调用非静态成员变量sex,编译不通过;原因:编译器没有对象生成,所以sex变量不存在;
* 静态成员方法test1调用非静态成员方法test2,编译不通过;原因:编译器无法预知在test2方法中是否访问了非静态成员变量,所以禁止在静态成员方法调用非静态成员方法。
特别说明:static方法是属于类的,非实例对象,在JVM加载类时,就已经存在内存中,不会被虚拟机GC回收掉,这样内存负荷会很大,但是非static方法会在运行完毕后被 虚拟机GC掉,减轻内存压力
二:深入分析static关键字:
问题:想要了解为什么static会有上面的那些特性?
解决:我们需要从JVM内存开始,先附一张java的内存结构图:
从上图问你可以看出,静态变量存放在方法区里,并且被所有的线程所共享,下面说一下各个结构。
1. 堆区:
* 存储的全部为对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令);
* JVM 只有一个堆区(heap)被所有对象所共享,堆中不存放基本类型和对象引用,只存放对象本身。
2. 栈区:
* 每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象,对象都存放在堆区中);
* 每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问;
* 栈分为3部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
3. 方法区:
* 又叫静态区,跟堆一样,被所有的线程所共享。该区包含所有的class和static变量;
* 方法区包含的都是在整个程序中永远唯一的元素,如:class、static变量;
通过一个案例从内存角度来看:
public class Person{
//静态变量
static String firstName;
//普通变量
String lastName;
//普通方法
pubic void showName(){
System.out.println(firstName+lastName);
}
//静态方法
public static void viewName(){
System.out.println(firstName);
}
public static void main(String[] args){
Person p = new Person();
Person.firstName = "张";
p.lastName="三";
p.showName();
Person p2 = new Person();
Person.firstName = "李";
p2.lastName="四";
p2.showName();
}
}
内存角度:
解释:从上面可以看到,我们的方法在调用的时候,是从方法区调用的,但是堆内存不一样,堆内存中的成员变量lastName是随着对象的的产生而产生,对象的消失而消失;
静态变量是被所有线程所共享的,所以不会消失。