static关键字
1.1 基本概念
- static意为静态,修饰类级别的属性和方法
- static修饰的变量称为静态变量,static修饰的方法称静态方法
- 静态变量/方法的特点:
- 静态方法和静态变量都存储在JVM的方法区内存,通过“类名.” 的方式调用,不需要new一个实例对象
- 静态变量/方法在类加载时初始化
- 注意
- 通过“引用.”的方式也可以调用只是不建议这样,因为会让程序员误以为是实例变量
- 静态变量和静态方法不可能出现空指针异常,因为根本没有引用指向他们,直接通过“类名.”调用
public class StaticTest
{
public static void main(String[] args)
{
VarTest v=new VarTest();
System.out.println(v.i);//调用实例变量i
System.out.println(v.j);//通过“引用.”的方式调用静态变量j,可以但不建议
System.out.println(VarTest.j);//通过“类名.”的方式调用静态变量j
//System.out.println(VarTest.i); 通过“类名.”的方式调用静态变量i,报错,实例变量只能通过“引用.”的方式调用
v.m1();//调用方法m1
v.m2();//通过“引用.”的方式调用静态方法m2,可以但不建议
VarTest.m2();//通过“类名.”的方式调用静态方法m2
//VarTest.m1(); 通过“类名.”的方式调用方法m1,报错,实例方法只能通过“引用.”的方式调用
}
}
class VarTest
{
int i=10;
static int j=11;
public void m1()
{
System.out.println("方法m1正在被调用");
}
public static void m2()
{
System.out.println("静态方法m2正在被调用");
}
}
1.2 静态变量的使用
public class StaticTest
{
public static void main(String[] args)
{
Chinese c1=new Chinese();
Chinese c2=new Chinese();
c1.name="张三";
c1.idCard="21423432134";
c2.name="李四";
c2.idCard="90239459435";
System.out.println(c1.idCard);
System.out.println(c1.name);
System.out.println(Chinese.country);
System.out.println(c2.idCard);
System.out.println(c2.name);
System.out.println(Chinese.country);
}
}
class Chinese
{
String idCard;
String name;
static String country="中国";
}
- 静态变量最大的优点就是节省空间
- 国籍使用非静态和静态变量的方式内存图比较:
- 如图对于中国人这个类来说,国籍永远都是中国,如果用实例变量来表示,每次创建对象都会重新开辟一个空间,非常浪费空间,所以推荐使用静态变量
1.3 空指针异常
public class StaticTest
{
public static void main(String[] args)
{
Chinese c1=new Chinese();
c1=null;
// System.out.println(c1.name);//报错,空指针异常
System.out.println(c1.country);//不报错,自动转换为“Chinese.country”
}
}
class Chinese
{
String name="zhangsan";
static String country="中国";
}
- 调用成员c1.name的时候空指针异常
- 调用静态变量country的时候不报错,因为程序在执行该语句的时候会自动转换为“Chinese.country”在执行,跟c1无关
1.4 静态方法的使用
如果方法和实例变量密切相关,则不能用静态方法
- 因为实例变量是需要new一个对象的,静态方法与对象无关
- 只有方法与实例变量无关的时候可以用static修饰,变为静态方法
1.5 静态代码块
-
用static关键字可以定义静态代码块
-
static代码块特点:
-
只在类加载的时候执行一次
-
static代码块中可以访问静态变量(因为静态变量在类加载时期初始化)
public class StaticTest { /* int k=10; static { System.out.println(k); //无法访问实例变量k,因为在类加载时,对象还没有被创建出来,不存在实例变量k System.out.println(l); //无法访问l,因为执行该代码块的时候l还没有定义 } static int l=20; */ public static void main(String[] args) { System.out.println("main begin"); } }
-
静态变量和静态代码块是按编写顺序执行的
-
如图,静态代码块在静态变量l前,先执行静态代码块,在定义静态变量l,所以无法访问l
-
如果在静态代码块之前定义静态变量l,就可以访问了
-
实例变量k在创建实例对象的时候才会生成,在类加载之后,所以也不能访问
-
-
静态代码块是按编写顺序执行的
public class StaticTest { static//静态代码块 { System.out.println("A"); } static { System.out.println("B"); } public static void main(String[] args) { System.out.println("D"); } static { System.out.println("C"); } }
该代码输出顺序:ABCD
-
3.静态代码块作用
- 项目需求中需要在类加载时刻记录信息时,可利用该代码
1.6 实例代码块
public class InstanceCode
{
public static void main(String[] args)
{
System.out.println("main begin");
new InstanceCode();
new InstanceCode(1);
}
//实例代码块
{
System.out.println("调用实例代码块");
}
public InstanceCode()
{
System.out.println("调用无参数的构造方法");
}
public InstanceCode(int a)
{
System.out.println("调用有参数的构造方法");
}
}
输出结果:
- 调用实例代码块
调用无参数的构造方法
调用实例代码块
调用有参数的构造方法
1.实例代码块执行时机:
-
每次构造函数调用前都先执行实例代码块,在调用构造函数
无论实例代码块写在构造函数前后,都是如此
2.实例代码块作用:
- 为程序员节省时间
- 如果编写了很多构造函数而且开头一部分代码都相同,就可以把相同部分写在实例代码块里