java static关键字介绍
导读:文章内容选自尚硅谷,JDK8,采用eclipse环境。
static关键字修饰属性
- static关键字可以修饰属性、方法、代码块和内部类,修饰属性时,该属性一般为此类创建的多个对象所共有的一个属性。
- static修饰的属性叫做静态属性,也叫做类属性,属性会随着类被加载到方法区的时候一同被加载到方法区。其创建时间要早于此类对象所创建的时间。
- 由于static属性是至于方法区中,且为多个对象所共有,当静态属性被修改后,所有此类对象的该属性也被修改了。
- 静态属性与非静态属性(实例变量)不同,实例变量就是类中的普通属性,一个类中创建的多个对象的同一种实例变量,彼此之间是相互独立的,可以为不同的值,而静态属性由于共享同一个内存空间,属性内容是一样的。
代码演示如下
package com.atguigu.java1;
public class StatiaTest {
public static void main(String[] args) {
Chinese c1 = new Chinese();
c1.name = "姚明";
c1.age = 40;
Chinese c2 = new Chinese();
c2.name = "malong";
c2.age = 30;
c1.nation = "CHN";
System.out.println(c2.nation);
}
}
class Chinese{
String name ;
int age;
static String nation;
}
运行结果为 CHN
这儿是通过对象来修改静态属性,我们没有对c2的nation属性赋值过,但是却能打印出c2的nation属性,而且和c1的nation属性一样,可见,nation作为静态属性,被该类多个对象所共享。
当通过其中一个对象修改静态属性后,其他对象的这个静态属性的内容也会发生改变。
package com.atguigu.java1;
public class StatiaTest {
public static void main(String[] args) {
Chinese c1 = new Chinese();
c1.name = "姚明";
c1.age = 40;
c1.nation = "CHN";
Chinese c2 = new Chinese();
c2.name = "malong";
c2.age = 30;
c2.nation = "China";
System.out.println(c1.nation);
}
}
class Chinese{
String name ;
int age;
static String nation;
}
输出结果为 China ,这是因为c2.nation对之前c1.nation赋值的内容做出了修改。输出c1.nation,由于共享同一块内存空间,输出结果肯定为修改后的值。
ps:介绍一下java中变量的分类
图片内容选自尚硅谷PPT
通过类名来调用静态变量
java的静态属性也叫做类变量,是因为java的静态属性是可以通过类名来直接调用的。
在之前的代码中
Chinese.nation = "中国";
通过类名的方法调用类变量是完全合法的。
在java中,通过类名来调用静态变量其实非常常见,比如System.out,比如Math.PI,其实都是通过类名来调用静态属性,System和Math首字母都是大写,他们其实都是类名。
比如System.out,其实定义为
public final static PrintStream out = null;
比如Math.PI,定义为
public static final double PI = 3.14159265358979323846;
- 类的对象可以创建多个,但是得先加载类到方法区,然后再创建类的对象。
- 类一般只加载一次,静态变量随着类的加载而加载,静态变量在内存中也只存在一份,存在于方法区中的静态域中。
- 类变量既可以通过对象调用,也可以通过类名调用,而实例变量只能通过对象调用。
静态变量的内存解析
图片选自尚硅谷PPT
图片右侧是执行的代码,这儿代码画在了堆空间内部,但其实代码存放位置不在堆空间中。
代码的执行过程为
- 1.第一行代码 Chinese.nation=”中国“; 首先在方法区中直接加载了类信息,同时在方法区中的静态域中创建了一个静态属性,属性值默认为null,然后再赋值”中国“。
- 2.先后创建了两个对象,并赋初值。这两个对象中的name属性为实例变量,存在于堆空间中,但是name变量中存放的内容并不是字符串的值,而是字符串的地址,因为name属性是String类型的,String类型的内容存放在字符串常量池中,存在于方法区内部,name变量中存放指向常量池的地址。(有分析说java1.8中字符串常量池在堆空间内部)
- 3.先后通过变量名,对静态变量的值进行修改。
static关键字修饰方法
与静态属性类似,static关键字修饰的方法也叫做静态方法和类方法,其使用和静态属性类似。
- 静态方法既可以通过对象调用,也可以通过类名调用。
- 静态方法内只能调用静态属性和静态方法,不能够调用非静态属性和非静态方法。而非静态方法可以调用静态属性和方法。
- 静态方法中不能使用this和super关键字,因为this和super关键字的使用,前提是必须要有对象,而静态方法的加载要早于对象的创建。
静态方法内不能调用非静态属性或方法,当调用非静态属性时,默认非静态属性前加上了this关键字,造成了生命周期冲突的问题。
演示代码如下
package com.atguigu.java1;
public class StatiaTest {
public static void main(String[] args) {
Chinese c1 = new Chinese();
c1.name = "姚明";
c1.age = 40;
c1.nation = "CHN";
Chinese c2 = new Chinese();
c2.name = "malong";
c2.age = 30;
c2.nation = "China";
Chinese.nation = "中国";
Chinese.show();
System.out.println("****** ****");
c1.eat();
}
}
class Chinese {
String name;
int age;
static String nation;
public void eat(){
System.out.println("人吃饭");
info();
walk();
System.out.println(nation);
}
public static void show(){
System.out.println("我是中国人");
// eat(); //编译器报错,静态方法内不能调用非静态属性和方法
// name = "tom";
System.out.println(nation);
walk();
}
public void info(){
System.out.println("name = "+name+",age = "+age);
}
public static void walk(){
System.out.println("walk");
}
}
ps:nation和walk都是静态的,他们在被静态方法和非静态方法调用的时候,默认省略掉了前缀Chinese.。
他们实际上是Chinese.nation和Chinese.walk。在静态方法内部,他们若把Chinese改为this会报错,这是因为前面讲的生命周期的问题,在非静态方法内,他们若把Chinese改为this不会报错,可以正常使用,这是因为此时是通过对象调用静态方法和属性。
- 开发中,操纵静态属性的方法,一般为静态方法,这是因为生命周期是一致的,若采用非静态方法,还得先造一个对象,才能调用非静态方法。
- 工具类中的方法,习惯上声明为静态方法。比如Math,Arrays、Collections,直接拿类去调用。