static关键字修饰属性、方法、代码、以及静态代码块非静态代码块和静态的构造方法非静态的构造方法等一切的执行顺序的问题练习和说明

static

1、static 是一个关键字,同时也是一个修饰符

2、static 可以修饰什么?
(1)属性
(2)方法
(3)代码块
(4)内部类

3、static修饰属性
用static修饰的属性,称之为静态变量、类变量。

静态变量和非静态变量的属性:
(1)存储的位置不同
非静态属性:堆
静态变量:方法区
(2)值的初始化(赋值)的时机是不同的
非静态的属性:创建实例对象时,在()实例初始化方法中完成初始化
静态变量:在类初始化的时候,在()类初始化方法中完成的初始化
静态变量的初始化比非静态的要早
(3)共享性不同
非静态的属性:每一个对象是独立的,各自存了一份
静态变量:所有该类的对象共享同一份
(4)生命周期不同
非静态的属性:随着创建对象而存在,当对象被垃圾回收器收回就消失
静态变量:随着类的初始化而初始化,随着类的卸载而卸载

静态变量的生命周期 = 类的生命周期
非静态的属性的生命周期 = 对象的生命周期,每一个对象的生命周期是独立的
(5)get/set不同
非静态的属性,对应get/set也是非静态的,如果局部变量与属性重名了用“this.”区别
静态变量:对应get/set也是静态的,如果局部变量与属性重名了,用“类名.”区别

4、static修饰方法
static修饰的方法 成为静态方法、类方法

(1)可以通过“类名.”调用,当然了也可以通过“对象.”调用
(2)静态方法方法中是不可以出现this,super这些关键字的
(3)静态方法中是不能直接使用本类中的非静态的成员(属性、方法、内部类)
(4)静态方法是不能被重写的

5、static修饰的代码块
(1)作用
代码块的作用:为属性初始化
代码块分为非静态代码块和静态代码块
非静态代码块的作用为非静态的属性初始化
静态代码块的作用,为静态属性,静态变量初始化

(2)语法结构
【修饰符】 class 类名 【extends 父类】{
{
非静态的代码块
}

static{
静态代码块
}
}

(3)静态代码块执行的特点是什么

  • A.无论创建几个对象,静态代码块只执行一次,如果有多个静态代码块按照顺讯执行

  • B.静态代码块肯定是优先与非静态代码快的
    因为静态代码块的执行是在类初始化是在类初始化是执行,类初始化执行的是一个叫做的类初始化方法

      类初始化的<clinit>()方法由如下两个部分组成
      第一  静态变量的显式赋值
      第二  静态代码块
      第三  第二部分谁在上面谁先执行,按照顺序想、执行
    
  • C.子类初始化时,如果发现父类还没有初始化,会先初始化父类,即先执行父类的方法

public class TestStatic {
	public static void main(String[] args) {
		Chinese c1 = new Chinese();
		Chinese c2 = new Chinese();
		
		c1.name = "张三";
		c2.name = "李四";
		
		c1.country = "中国";
		c2.country = "中华人名共和国";
		
		System.out.println(c1.country + c1.name);
		System.out.println(c2.country + c2.name);
	}
}
class Chinese{
	static String country;
	String name;
	public static String getCountry() {
		return country;
	}
	public static void setCountry(String country) {
		Chinese.country = country;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}	
}


在这里插入图片描述

说明一

这里就涉及到类的初始化,两部分
(1)静态变量的显式初始化
i = getInt();
(2)静态代码块
System.out.println(“静态代码块”);
类初始化只执行一次

public class TestClinit1 {
	public static void main(String[] args) {
		A a1 = new A();
		A a2 = new A();		
	}
}
class A{
	private static int i = getInt();
	
	static {
		System.out.println("静态代码块");
	}
	
	public static int getInt() {
		System.out.println("A类的getInt()方法");
		return 1;
	}
}

在这里插入图片描述

说明二

调用D类的静态方法,先要初始化D类
类的初始化只需要一次
但是方法是调用一次就执行一次

public class TestClinit2 {
	public static void main(String[] args) {
		D.test();
		D.test();
	}
}
class D{
	static {
		System.out.println("(1)静态代码块");
	}
	
	public static void test() {
		System.out.println("(2)静态方法");
	}
}

在这里插入图片描述

说明三

1、 这里就涉及到类的初始化,两部分
(1)静态变量的显式初始化
i = getInt();
(2)静态代码块
System.out.println(“静态代码块”);

2、 类初始化只执行一次

3、子类初始化时如果父类没有初始化就会先初始化父类,就是执行父类的

public class TestClinit3 {
	public static void main(String[] args) {
		SubB s1 = new SubB();
		SubB s2 = new SubB();
	}
}
class B{
	private static int i = getInt();
	
	static {
		System.out.println("静态代码块");
	}
	
	public static int getInt() {
		System.out.println("B类的getInt()方法");
		return 1;
	}
}

class SubB extends B {
private static int j = getIntValue();
	
	static {
		System.out.println("静态代码块");
	}
	
	public static int getIntValue() {
		System.out.println("SubB类的getIntValue()方法");
		return 1;
	}
}

在这里插入图片描述

练习一

被静态修饰的在类的初始化的时候就已经加载了,所以先是静态的加载出来
然后在按照顺序进行

public class TestStaticExer1 {
	public static void main(String[] args) {
		Son s = new Son();
	}
}

class Father{
	static {
		System.out.println("(1)父类的静态代码块");
	}
	
	{
		System.out.println("(2)父类的构造器");
	}
	
	Father(){
		System.out.println("(3)父类的无参构造");
	}
}

class Son extends Father{
	static {
		System.out.println("(4)子类的静态代码块");
	}
	
	{
		System.out.println("(5)子类的构造器");
	}
	
	Son(){
		System.out.println("(6)子类的无参构造");
	}
}

/*
 (1)父类的静态代码块
(4)子类的静态代码块
(2)父类的构造器
(3)父类的无参构造
(5)子类的构造器
(6)子类的无参构造
 */

在这里插入图片描述

练习二
public class TestStaticExer2 {
	public static void main(String[] args) {
		Zi zi = new Zi();
	}
}

class Fu{
	private static int i = getNum("(1)i");
	private int j = getNum("(2)j");
	
	static {
		print("(3)父类静态代码块");
	}
	
	{
		print("(4)父类构造代码块");
	}
	
	Fu(){
		print("(5)父类构造器");
	}
	
	public static void print(String str) {
		System.out.println(str + "->" + i);
	}
	
	public static int getNum(String str) {
		print(str);
		return ++i; //1  2
	}
}

class Zi extends Fu{
	private static int k = getNum("(6)k");
	private int h = getNum("(7)h");
	
	static {
		print("(8)子类静态代码块");
	}
	
	{
		print("(9)子类构造代码块");
	}
	
	Zi(){
		print("(10)子类构造器");
	}
	
	public static void print(String str) {
		System.out.println(str + "->" + k);
	}
	
	public static int getNum(String str) {
		print(str);
		return ++k;//1 2
	}
}

/*
 (1)i
 (3)父类静态代码块 -> 0
 (6)k
 (8)子类静态代码块
 (2)j
 (4)父类构造代码块 -> 1
 (5)父类构造器 -> 1
 */


在这里插入图片描述

练习三

(这个主要讲的就是关于先声明后使用的问题,如果就是不先声明,也就是我先使用,后声明怎么办呢,在静态中虽然类加载的时候就已经加载了静态的声明了,但是在java中还是认为这个是后声明,在java的开发文档8.3.3(第8版)中有介绍)

public class TestStaticExer3 {
	public static void main(String[] args) {
		MyClass obj = new MyClass();
	}
}

class MyClass{
	
	static {
		i = 100;//可以的
//		i++;//报错,i是静态的,虽然是类存在的时候就已经加载了,
		//但是需要先声明在使用,如果没有声明静态的就得用  类名. 来调用的,这个是java规定的 
		MyClass.i++;
//		System.out.println("(1)静态代码块i=" + i);//报错
		//通俗易懂的方式就是,这个没有提前声明的变量 这个i 只能出现在“=”等号的左边不可以出现在等号的右边
		System.out.println("(1)静态代码块i=" + MyClass.i);
	}
	
	{
		j = 100;//可以的
//	    j++;//报错,同样的这个的声明在后面,所以只能出现在等号的左边,不可以出现在等号的右边
		//如果非要使用这个样子去使用的话,非静态的就用this.  调用。
		this.j++;
//	System.out.println("(2)静态代码块j=" + j);//报错
		System.out.println("(1)静态代码块j=" + this.j);
	}
	
	MyClass(){
		j = 200;
		j++;
		System.out.println("(3)构造器j=" + j);
	}
	
	private static int i = getNum("(4)i");
	private int j = getNum("(5)j");
	
	public static void print(String str) {
		System.out.println(str + "->" + i);
	}
	
	public static int getNum(String str) {
		print(str);
		return ++i;
}
}

在这里插入图片描述


版权声明:本文为qq_41005604原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。