4.Static关键字

static关键字

1.详解

  1. static翻译为静态。
  2. 所有static修饰的都是类相关的,和类一起加载。
  3. 所有static修饰的都采用”类名.“的方式访问。
  4. static修饰的变量:静态变量
  5. static修饰的方法:静态方法

2.变量的分类

根据作用域的不同,分为:

  1. 定义在方法中的称为局部变量
  2. 定义在方法外部的称为成员变量

成员变量中,带static修饰的称为静态变量(类变量),不带static修饰的称为实例变量

public class StaticTest01{
	//成员变量中的实例变量
	int i;
	//实例方法
	public void m1(){
		int i;//局部变量
	}
	
	//成员变量中的静态变量
	static int j;
	//静态方法
	public static void m2(){
		int i;//局部变量
	}
}

注意:

  1. 实例变量,实例方法都是对象相关的,采用”引用.“的方式访问,必须先new对象

  2. 实例相关的,必须先有对象才能访问,且通过引用去访问可能出现空指针异常

  3. 静态变量和静态方法都是类相关的,通过”类名.“的方式访问,不需要创建对象

  4. 类相关的不需要对象的参与即可访问,也没有空指针异常发生

那么,应该思考:什么时候变量声明成实例变量,什么时候声明成静态变量

3.static变量和实例变量在内存中区别:

1.属性中未定义静态变量
public class StaticTest02{
	public static void main(String[] args){
		Chinese c1 = new Chinese("111","张三","中国");
		System.out.println(c1.idCard);
		System.out.println(c1.name);
		System.out.println(c1.country);
		Chinese c2 = new Chinese("222","李四","中国");
		System.out.println(c2.idCard);
		System.out.println(c2.name);
		System.out.println(c2.country);
	}
	
}

class Chinese{
	String idCard;
	String name;
	String country;
	
	public Chinese(){}
	
	public Chinese(String s1,String s2,String s3){
		this.idCard = s1;
		this.name = s2;
		this.country = s3;
	}
}

上述代码执行内存图:

在这里插入图片描述

不难看出,对于中国人类中的国籍中国,是一个类级别的变量,不论对象多么因人而异,中国人的国籍都是中国。此时,将国籍定义为实例变量,会浪费内存空间。

2.属性中含有静态变量
public class StaticTest03{
	public static void main(String[] args){
		System.out.println(Chinese.country);
		System.out.println("========================");
		Chinese c1 = new Chinese("111","张三");
		System.out.println(c1.idCard);
		System.out.println(c1.name);
		//System.out.println(c1.country);
		System.out.println("========================");
		Chinese c2 = new Chinese("222","李四");
		System.out.println(c2.idCard);
		System.out.println(c2.name);
		//System.out.println(c2.country);
	}
	
}

class Chinese{
	String idCard;
	String name;
	static String country = "中国";
	
	public Chinese(){}
	
	public Chinese(String s1,String s2){
		this.idCard = s1;
		this.name = s2;
	}
}

上述代码内存图:

在这里插入图片描述

可以发现,static修饰的变量和方法是和类一起加载初始化的

而实例变量是通过构造方法创建对象时初始化的

4.空指针异常

什么情况下会出现空指针异常?

通过“空引用”去访问实例相关的,都会出现空指针异常

public class StaticTest04{
	public static void main(String[] args){
		
		//静态变量通过”类名.“的方式来访问
		System.out.println(Chinese.country);
		System.out.println("========================");
		Chinese c1 = new Chinese("111","张三");
		System.out.println(c1.idCard);
		System.out.println(c1.name);
		//静态变量也可以通过”引用.“方式访问,
		//但本质上还是编译System.out.println(Chinese.country);
		System.out.println(c1.country);
		c1 = null;
		//即使指针指向空,下一行仍能访问静态变量,因为静态变量不需要对象的存在
		System.out.println(c1.country);
		//下一行会出现空指针异常
		System.out.println(c1.idCard);
	}
	
}

class Chinese{
	String idCard;
	String name;
	static String country = "中国";
	
	public Chinese(){}
	
	public Chinese(String s1,String s2){
		this.idCard = s1;
		this.name = s2;
	}
}

总之:静态变量是类相关的,不需要对象的存在。虽然也可以通过“引用.的”方式进行访问,但是本质上编译器会转化成“类名.”的方式去访问。所以不会出现空指针异常。

5.什么时候方法定义为实例方法?

从面向对象的思想上来说,实例方法是对象级别的方法,对象的行为因人而异,方法也应该定义为实例方法。

从代码的角度来看,一个方法的方法体中访问了实例变量的话,该方法必须定义为实例方法而不是静态方法,否则系统会报错。

一般工具类为了方便调用,都定义为static类型,静态方法的优点就是可以通过“类名.”的方式去引用

6.静态代码块

1.使用static关键字定义静态代码块:

public class A{
    static{
        //java 语句;
        //java语句;
    }
}

2.static静态代码块什么时候执行呢?

A:**类加载的时候执行,并且只执行一次。**注意,任何一个代码的执行都是从类加载开始的,所以静态代码块在main方法之前执行

B: 一个类中可以定义多个静态代码块,类加载时,静态代码块按照自上而下的顺序执行:

public class StaticTest05{
	int i;
	//静态代码块
	static{
		System.out.println("A");
	}
	//静态代码块
	static{
		System.out.println("B");
	}
	public static void main(String[] args){
		System.out.println("main begin!");
		
	}
	//静态代码块
	static{
		System.out.println("C");
	}
	/*
	//执行结果
	   A
       B
       C
       main begin!
	*/
	
}

通过上述代码看出,不论静态代码块在类中的什么位置,都自上向下依次执行。

3.静态代码块有什么作用?

对程序员而言,可以把静态代码块当作一个类加载的时机:

项目经理说了:大家注意了,所有我们编写的程序中,只要是类加载了,请记录一下
类加载的日志信息(在哪年哪月哪日几时几分几秒,哪个类加载到JVM当中了)。
思考:这些记录日志的代码写到哪里呢?

写到静态代码块当中。

4.静态代码块与静态变量的代码执行顺序

静态代码块与静态变量都是在类加载时执行或初始化的,那么就要思考两者之间的执行顺序问题:

由于都是在类加载时执行,所以二者的执行顺序取决于二者在代码中出现的先后顺序

public class StaticTest06{
	
	static int i;
	//这里静态变量在静态代码块之前,
	//所以在方法区中先定义静态变量i,再执行静态代码块
	static{
		//这里能访问i吗?
		System.out.println(i);
	}
	/*
	static{
		//这里能访问name吗?
		//不能!
		//错误: 非法前向引用
		System.out.println(name);
	}
	*/
	static String name;
	
    public static void main(String[] args){
		System.out.println("main begin");
		System.out.println("main over");
	
}
}
//0
//main begin
//main over

5.静态代码块/实例代码块(匿名代码块 】)/构造方法的执行顺序问题

  1. 什么是实例代码块?

语法机制:

{
    //java语句;
}

实例代码块并没有在类加载时执行

public class InstanceCode{
	//实例代码块(又叫匿名代码块)
	//实例代码块并没有在类加载时执行
	{
		System.out.println("实例代码块");
	}

	public static void main(String[] args){
		System.out.println("main begin!");
	}
	/*
	结果:
	main begin!
	
	*/
}
  1. 实例代码块和构造方法的执行顺序问题:

    实例代码块在每次调用构造方法时都会自动先执行,这也是SUN公司提供的一种对象构建机制

public class InstanceCode{
	//实例代码块(又叫匿名代码块)
	//实例代码块并没有在类加载时执行
	{
		System.out.println("实例代码块执行了");
	}
	public InstanceCode(){
		System.out.println("无参构造器执行了");
	}
	public InstanceCode(String name){
		System.out.println("有参构造器执行了");
	}
	public static void main(String[] args){
		System.out.println("main begin!");
		new InstanceCode();
		new InstanceCode("abc");
	}
	/*
	结果:
	main begin!
    实例代码块执行了
    无参构造器执行了
    实例代码块执行了
    有参构造器执行了
	*/
}
  1. 三者的执行顺序问题:

  2. 静态方法在类加载的时执行,并且按照静态代码块自上而下的顺序逐个执行每个静态代码块,并且只执行一次。

  3. 实例代码块在调用构造方法时,自动在构造器执行前执行一次。

public class CodeOrder{
	int i;
	//静态代码块
	static{
		System.out.println("A");
	}
	//实例代码块
	{
		System.out.println("B");
	}
	public CodeOrder(){
		System.out.println("C");
	}
	public static void main(String[] args){
		System.out.println("D");
		new CodeOrder();
		
	}
	
	static{
		System.out.println("E");
	}
	
	static{
		System.out.println("F");
	}
}
/*
执行顺序: A>E>F>D>B>C

*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值