一、静态概念
1 被静态修饰的数据,不再是对象特有的内容,而是所有对象的共享内容。
如上图所示,student类中,每个同学都有自己的姓名和年龄,但是所有同学的学校名都是“itcast”。如果当作普通变量的话,在创建对象时,每个对象都会创建这个变量,造成浪费。
因此把schoolName对象拿出来,让大家共享。这样节约空间。
2 被static修饰的成员,可以通过类名直接调用。
例1:
//Person.java
package cn.itcast.demo01;
public class Person{
String name;
static String className;
}
|
//Test.java
package cn.itcast.demo01;
public class Test{
public static void main(String[] args){
System.out.println(Person.className); //null。
说明在内存中,对象还没创建,但是静态变量已经存在了。
Person p1 = new Person();
Person p2 = new Person();
p1.name = "haha";
p1.className = "aaa";
System.out.println(p1.name); //haha
System.out.println(p1.className); //aaa
System.out.println(p2.name); //null。特有数据。
System.out.println(p2.className); //aaa。
说明static修饰的变量是共享的。
System.out.println(Person.className); //aaa
说明静态变量可以通过类名直接调用。
}
}
|
二、static的内存图
左边是栈,右边是堆,下方是方法区(方法区很广,其中右边一部分是静态区)。
步骤1:
class文件进入到方法区,首先要加载自己的静态成员。
step1.1:Test.class进入到方法区,加载静态成员main到静态区(类名Test也一起过来)。
step1.2:Person.class进入到方法区,加载静态成员className到静态去(所属Person也加载过来),并赋默认值null。
步骤2:程序开始执行,运行main方法。JVM到静态区将main方法复制出来一份,压栈执行。
步骤3:执行main的第一句:System.out.println(Person.className);,JVM到静态区找属于Person类的静态属性className,输出null。
此时还没创建对象呢。
说明在内存中,静态是优于非静态存在的。
步骤4:在堆中创建对象。
三、static的注意事项
在静态中不能调用非静态。
因为静态先存在,非静态才存在。静态是先人,非静态是后人。静态方法中既不能写this,也不能写super。
//Person.java
package cn.itcast.demo01;
public class Person{
private String name;
private static int age;
public static void fun(){
//System.out.println(name); //错误。
因为静态存在的时候,非静态还不存在呢。
System.out.println(age); //正确。因为都是静态的。生命周期相同。
}
public void fun2(){
System.out.println(age); //正确。非静态的可以调用静态的。后人可以调用前人。
}
}
|
四、静态修饰什么时候使用?应用场景是什么?
static是成员修饰符。可以修饰成员方法,也可以修饰成员变量。不能修饰局部变量。
成员变量什么时候加static,要具体分析:当定义事物的时候,要看多个事物有没有共性。如,每个人的学校名称。可以定义为static
成员方法什么时候加static:跟着变量走。如果方法调用的都是静态变量,那么就把这个方法定义为static。如果方法调用了非静态的 变量,那么只能定义为非静态方法了。
记住
:用到了非静态成员变量,就只能非静态。没用到非静态成员变量,就用静态。能静态就静态。
//Person.java
package cn.itcast.demo01;
public class Person{
private String name;
private static int age;
public
static void fun(){
//应该定义成静态的方法。因为用到的成员变量都是static的。
System.out.println(age);
}
public void fun2(){
//必须定义成非静态的方法。因为用到了非静态的成员变量。
System.out.println(name);
}
public static int sum(int a, int b){
//应该定义为静态方法。因为根本没有用到任何成员变量。仅需要类名就能调用了。
//调用这个方法根本不用对象。
return a+b;
}
}
|
五、多态中的静态调用
静态和对象没有什么关系。
1.静态成员变量:
//Fu.java
package cn.itcast.demo01;
public class Fu{
int a = 1;
static int b = 1;
}
//Zi.java
package cn.itcast.demo01;
public class Zi{
int a = 2;
static int b = 2;
}
//Zi.java
package cn.itcast.demo01;
public class Test{
public static void main(String[] args){
Fu f = new Zi();
System.out.println(f.a);
//1。
System.out.println(f.b);
//1。
}
}
|
//Fu.java
package cn.itcast.demo01;
public class Fu{
}
//Zi.java
package cn.itcast.demo01;
public class Zi{
int a = 2;
static int b = 2;
}
//Zi.java
package cn.itcast.demo01;
public class Test{
public static void main(String[] args){
Fu f = new Zi();
System.out.println(f.a);
System.out.println(f.b);
}
}
//运行结果:
程序报错。
//编译看父类。父类没有,则编译失败。
|
//Fu.java
package cn.itcast.demo01;
public class Fu{
int a = 1;
static int b = 1;
}
//Zi.java
package cn.itcast.demo01;
public class Zi{
}
//Zi.java
package cn.itcast.demo01;
public class Test{
public static void main(String[] args){
Fu f = new Zi();
System.out.println(f.a);
//1。
System.out.println(f.b);
//1。
}
}
|
2.静态成员函数
//Fu.java
package cn.itcast.demo01;
public class Fu{
public static void show(){
System.out.println("fu...");
}
}
//Zi.java
package cn.itcast.demo01;
public class Zi{
public static void show(){
System.out.println("zi...");
}
}
//Zi.java
package cn.itcast.demo01;
public class Test{
public static void main(String[] args){
Fu f = new Zi();
f.show();
//fu...
}
}
//因为静态跟对象无关。静态属于类,不属于对象。f毕竟是父类的引用。
|
//Fu.java
package cn.itcast.demo01;
public class Fu{
}
//Zi.java
package cn.itcast.demo01;
public class Zi{
public static void show(){
System.out.println("zi...");
}
}
//Zi.java
package cn.itcast.demo01;
public class Test{
public static void main(String[] args){
Fu f = new Zi();
f.show();
}
}
//运行结果:
编译失败。
//编译看父类。
|
总结:
在多态的方法调用中:
编译:都是看父类,父类有则编译成功;父类没有则编译失败。
运行:静态方法:运行的是父类中的静态方法。 //因为静态跟对象无关。
非静态方法:运行的是子类的重写方法。
多态的成员变量:
编译运行全部看父类。
六、定义静态常量
public static final String COMPANY_NAME = "传智播客"; //java中,常量的名字要求全大写,两个词之间以下划线相连。
注意:
接口中的每个成员变量都默认使用public static final 修饰。
所有接口中的成员变量已经是静态常量,由于接口没有构造方法,所以必须显示赋值。可以直接用接口名访问。