Java基础(十):关键字static、代码块、关键字final

Java基础系列文章

Java基础(一):语言概述

Java基础(二):原码、反码、补码及进制之间的运算

Java基础(三):数据类型与进制

Java基础(四):逻辑运算符和位运算符

Java基础(六):数组

Java基础(七):面向对象编程

Java基础(八):封装、继承、多态性

Java基础(九):Object 类的使用

Java基础(十):关键字static、代码块、关键字final


目录

  • 一、关键字static
    • 1、静态变量
    • 2、静态方法
  • 二、理解main方法的语法
  • 三、代码块
    • 1、静态代码块
    • 2、非静态代码块
    • 3、实例变量赋值顺序
  • 四、final关键字
    • 1、final修饰类
    • 2、final修饰方法
    • 3、final修饰变量


一、关键字static

  • 如果想让一个成员变量被类的所有实例所共享,就用static修饰即可,称为类变量(或类属性)!!!
  • 在类中声明的实例方法
    • 在类的外面必须要先创建对象,才能调用
    • 但是有些方法的调用者和当前类的对象无关,这样的方法通常被声明为类方法
    • 由于不需要创建对象就可以调用类方法,从而简化了方法的调用

static关键字

  • 使用范围:可用static修饰属性、方法、代码块、内部类
  • 被修饰后的成员具备以下特点:
    • 随着类的加载而加载
    • 优先于对象存在
    • 修饰的成员,被所有对象所共享
    • 访问权限允许时,可不创建对象,直接被类调用

1、静态变量

静态变量的特点

  • 静态变量值是所有对象共享
  • 静态变量在本类中,可以在任意方法、代码块、构造器中直接使用
  • 如果权限修饰符允许,在其他类中可以通过“类名.静态变量”直接访问,也可以通过“对象.静态变量”的方式访问
    (但是更推荐使用类名.静态变量的方式)
  • 静态变量的get/set方法也静态的,当局部变量与静态变量重名时,使用“类名.静态变量”进行区分

对比静态变量与实例变量

  1. 个数
    • 静态变量:在内存空间中只有一份,被类的多个对象所共享
    • 实例变量:类的每一个实例(或对象)都保存着一份实例变量
  2. 内存位置
    • 静态变量:jdk6及之前:存放在方法区。 jdk7及之后:存放在堆空间
    • 实例变量:存放在堆空间的对象实体中
  3. 加载时机
    • 静态变量:随着类的加载而加载,由于类只会加载一次,所以静态变量也只有一份
    • 实例变量:随着对象的创建而加载。每个对象拥有一份实例变量
  4. 调用者
    • 静态变量:可以被类直接调用,也可以使用对象调用
    • 实例变量:只能使用对象进行调用
  5. 消亡时机
    • 静态变量:随着类的卸载而消亡
    • 实例变量:随着对象的消亡而消亡

举例

Java基础(十):关键字static、代码块、关键字final_第1张图片

2、静态方法

静态方法的特点

  • 在static方法内部只能访问类的static修饰的属性或方法,不能访问类的非static的结构
  • 静态方法可以被子类继承,但不能被子类重写
  • 因为不需要实例就可以访问static方法,因此static方法内部不能有this,也不能有super。如果有重名问题,使用“类名.”进行区别

二、理解main方法的语法

  • 由于JVM需要调用类的main()方法,所以该方法的访问权限必须是public
  • 又因为JVM在执行main()方法时不必创建对象,所以该方法必须是static
  • 该方法接收一个String类型的数组参数,该数组中保存执行Java命令时传递给所运行的类的参数
  • 又因为main() 方法是静态的,我们不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员

举例:

Java基础(十):关键字static、代码块、关键字final_第2张图片
Java基础(十):关键字static、代码块、关键字final_第3张图片

Java基础(十):关键字static、代码块、关键字final_第4张图片

三、代码块

1、静态代码块

  • 随着类的加载而执行
  • 由于类的加载只会执行一次,进而静态代码块的执行,也只会执行一次
  • 作用:用来初始化类的信息
  • 内部可以声明变量、调用属性或方法、编写输出语句等操作
  • 静态代码块的执行要先于非静态代码块的执行
  • 如果声明有多个静态代码块,则按照声明的先后顺序执行
  • 静态代码块内部只能调用静态的结构(即静态的属性、方法),不能调用非静态的结构(即非静态的属性、方法)

2、非静态代码块

  • 随着对象的创建而执行
  • 每创建当前类的一个实例,就会执行一次非静态代码块
  • 作用:用来初始化对象的信息
  • 内部可以声明变量、调用属性或方法、编写输出语句等操作
  • 静态代码块的执行要先于非静态代码块的执行
  • 如果声明有多个非静态代码块,则按照声明的先后顺序执行
  • 非静态代码块内部可以调用静态的结构(即静态的属性、方法),也可以调用非静态的结构(即非静态的属性、方法)

3、实例变量赋值顺序

  • 先加载父类,通过构造器第一行,隐藏的super()方法找父类
  • 代码块在构造器之前执行
  • 显示赋值和代码块中赋值顺序,由先后顺序决定

Java基础(十):关键字static、代码块、关键字final_第5张图片

举例1

class Root{
	static{
		System.out.println("Root的静态初始化块");
	}
	{
		System.out.println("Root的普通初始化块");
	}
	public Root(){
		System.out.println("Root的无参数的构造器");
	}
}
class Mid extends Root{
	static{
		System.out.println("Mid的静态初始化块");
	}
	{
		System.out.println("Mid的普通初始化块");
	}
	public Mid(){
		System.out.println("Mid的无参数的构造器");
	}
	public Mid(String msg){
		//通过this调用同一类中重载的构造器
		this();
		System.out.println("Mid的带参数构造器,其参数值:"+ msg);
	}
}
class Leaf extends Mid{
	static{
		System.out.println("Leaf的静态初始化块");
	}
	{
		System.out.println("Leaf的普通初始化块");
	}	
	public Leaf(){
		//通过super调用父类中有一个字符串参数的构造器
		super("尚硅谷");
		System.out.println("Leaf的构造器");
	}
}
public class LeafTest{
	public static void main(String[] args){
		new Leaf(); 
		//new Leaf();
	}
}

结果:

Root的静态初始化块
Mid的静态初始化块
Leaf的静态初始化块
Root的普通初始化块
Root的无参数的构造器
Mid的普通初始化块
Mid的无参数的构造器
Mid的带参数构造器,其参数值:尚硅谷
Leaf的普通初始化块
Leaf的构造器
  • 先加载父类静态,再到子类静态
  • 先顺序执行父类普通代码块、构造器,再执行子类普通代码块、构造器

举例2

  • 第一个静态代码中的x作用域只在当前代码块中,与静态成员变量x是两个变量
public class Test02 {
    static int x, y, z;

    static {
        int x = 5;
        x--;
    }

    static {
        x--;
    }

    public static void method() {
        y = z++ + ++z;
    }

    public static void main(String[] args) {
        System.out.println("x=" + x);
        z--;
        method();
        System.out.println("result:" + (z + y + ++z));
    }
}

举例3

public class Test03 {
    public static void main(String[] args) {
        Sub s = new Sub();
    }
}
class Base{
    Base(){
        method(100);
    }
    {
        System.out.println("base");
    }
    public void method(int i){
        System.out.println("base : " + i);
    }
}
class Sub extends Base{
    Sub(){
        super.method(70);
    }
    {
        System.out.println("sub");
    }
    public void method(int j){
        System.out.println("sub : " + j);
    }
}

结果:

base
sub : 100
sub
base : 70
  • 父类Base的构造函数中调用method,其实是this.method
  • 当前对象是sub,那么method则是被重写的method

四、final关键字

  • final:最终的,不可更改的

1、final修饰类

  • 表示这个类不能被继承,没有子类。提高安全性,提高程序的可读性
  • 例如:String类、System类、StringBuffer类
final class Eunuch{//太监类
	
}
class Son extends Eunuch{//错误
	
}

2、final修饰方法

  • 表示这个方法不能被子类重写
  • 例如:Object类中的getClass()
class Father{
	public final void method(){
		System.out.println("father");
	}
}
class Son extends Father{
	public void method(){//错误
		System.out.println("son");
	}
}

3、final修饰变量

  • final修饰某个变量(成员变量或局部变量),一旦赋值,它的值就不能被修改,即常量,常量名建议使用大写字母
  • 例如:final double MY_PI = 3.14
  • 如果某个成员变量用final修饰后,没有set方法,并且必须初始化(可以显式赋值、或在初始化块赋值、实例变量还可以在构造器中赋值)
  • 修饰成员变量
public final class Test {
    public static int totalNumber = 5;
    public final int ID;

    public Test() {
        ID = ++totalNumber; // 可在构造器中给final修饰的“变量”赋值
    }
    public static void main(String[] args) {
        Test t = new Test();
        System.out.println(t.ID);
    }
}
  • 修饰局部变量
public class TestFinal {
    public static void main(String[] args){
        final int MIN_SCORE ;
        MIN_SCORE = 0;
        final int MAX_SCORE = 100;
        MAX_SCORE = 200; //非法
    }
}
  • 错误演示
class A {
    private final String INFO = "atguigu";  //声明常量

    public void print() {
        //The final field A.INFO cannot be  assigned
        //INFO = "尚硅谷"; 
    }
}

你可能感兴趣的:(java基础,java,jvm,面试)