static关键字
static 可以修饰变量,方法,代码块和内部类。
1 、static修饰变量
static修饰成员变量时:static修饰成员变量时,那么该成员变量的数据就是一个共享的数据.
静态成员变量的访问方式:
方式一: 使用对象进行访问。
对象.属性名
方式二:可以使用类名进行访问。
类名.属性名
注意:
-
非静态成员变量不能类名直接访问,只能使用对象进行访问。
-
千万不要为了方便访问成员变量而使用static修饰,一定要是该数据是共享数据 时才使用static修饰。
2、static修饰方法
static方法称作静态方法,由于静态方法不依赖于任何对象就可以进行访问,因此对于静态方法来说是没有this和super的。因为他不依赖于任何对象,既然都没有对象,就谈不上this和super了。
并且由于这个特性,在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都是必须依赖具体的对象才能够被调用。
但是要注意的是,虽然在静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法/变量的。
举个例子来看:
package com.cc;
public class StaticTest {
private static String str1 = "moon";
private String str2 = "cmy";
public StaticTest() {
}
public void print1() {
System.out.println(str1);
System.out.println(str2);
print2();
}
public static void print2() {
System.out.println(str1);
//System.out.println(str2);//静态方法中不能调用非静态成员变量
//print1();//静态方法中不能调用非静态方法
}
}
3、static修饰类
static一般用来修饰成员变量和方法,但是有一种特殊的用法是用static修饰内部类,普通类是不允许用static修饰的,只有内部类可以。
看个例子更清晰:
package com.cc;
public class StaticTest {
//static关键字修饰内部类
public static class InnerClass {
public InnerClass() {
System.out.println("静态内部类");
}
public void InnerMethod() {
System.out.println("静态内部方法");
}
}
public static void main(String[] args) {
//直接通过类名StaticTest访问静态内部类InnerClass
InnerClass inner = new StaticTest.InnerClass();
inner.InnerMethod();
}
}
/* 输出结果:
* 静态内部类
* 静态内部方法
*/
如果没有用static修饰InterClass,则只能new 一个外部类实例。再通过外部实例创建内部类。
4、static修饰代码块
静态代码块在类第一次被加载的时候执行,咱们这里来验证一下类初始化的顺序。
父类构造函数
父类静态代码块
子类构造函数
子类静态代码块
package com.cc;
public class Father {
static {
System.out.println("父类静态代码块");
}
public Father() {
System.out.println("父类构造函数");
}
}
package com.cc;
public class Son extends Father{
static {
System.out.println("子类静态代码块");
}
public Son() {
System.out.println("子类构造函数");
}
public static void main(String[] args) {
new Son();
}
}
//输出结果:
父类静态代码块
子类静态代码块
父类构造函数
子类构造函数
咱们再通过内存的角度来分析一下static关键字。
这就涉及到了JVM运行时数据区,关于各个区域的介绍,可参考此篇博客!
内存分析static
从上图我们可以发现,静态变量存放在方法区中,并且是被所有线程所共享的。这里要说一下java堆,java堆存放的就是我们创建的一个个实例变量。
堆区:
1、存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令)
2、jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身
栈区:
1.每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中
2、每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。
3、栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。、
方法区:
1、又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
2、方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。
下面通过一个案例说明一下,从内存的角度来看,static关键字为什么会有这样的特性。
package com.cc;
public class Person {
static String firstName;
String lastName;
public void showName() {
System.out.println(firstName + lastName);
}
public static void viewName() {
System.out.println(firstName);
}
public static void main(String[] args) {
Person p1 = new Person();
Person.firstName = "张";
p1.lastName = "三";
p1.showName();
Person p2 = new Person();
Person.firstName = "李";
p2.lastName = "四";
p2.showName();
}
}
/*
输出结果:
张三
李四*/
看一下内存图:
从上面可以看到,我们的方法在调用的时候,是从方法区调用的,但是堆内存不一样,堆内存中的成员变量lastname是随着对象的产生而产生。随着对象的消失而消失。静态变量是所有线程共享的,所以不会消失。这也就能解释上面static关键字的真正原因。