JAVA关键字final、static详解

final关键字

Java关键字final有“这是无法改变的”或者“终态的”含义,它可以修饰非抽象类、非抽象类成员方法和变量

  • final类不能被继承,没有子类,final类中的方法默认是final的。
  • final方法不能被子类的方法覆盖,但可以被继承。
  • final成员变量表示常量,只能被赋值一次,赋值后值不再改变。
  • final不能用于修饰构造方法。

注意:父类的private成员方法是不能被子类方法覆盖的,因此private类型的方法默认是final类型的。

  1. final类
    final类不能被继承,因此final类的成员方法没有机会被覆盖,默认都是final的。在设计类时候,如果这个类不需要有子类,类的实现细节不允许改变,并且确信这个类不会载被扩展,那么就设计为final类。
  2. final方法
    如果一个类不允许其子类覆盖某个方法,则可以把这个方法声明为final方法。
  3. final变量(常量)
    用final修饰的成员变量表示常量,值一旦给定就无法改变!
    final修饰的变量有三种:静态变量、实例变量和局部变量,分别表示三种类型的常量。
    final变量定义的时候,可以先声明,而不给初值,这种变量也称为final空白,无论什么情况,编译器都确保空白final在使用之前必须被初始化。但是,final空白在final关键字final的使用上提供了更大的灵活性,为此,一个类中的final数据成员就可以实现依对象而有所不同,却有保持其恒定不变的特征。
public class Demo1 {

	private final String S = "final实例变量S";
	private final int A = 100;
	public final int B = 90;

	public static final int C = 80;
	private static final int D = 70;

	public final int E; // final空白,必须在初始化对象的时候赋初值

	public Demo1(int e) {
		E = e;
	}

	public static void main(String[] args) {
		Demo1 demo = new Demo1(66);
		// demo.A=101; //出错,final变量的值一旦给定就无法改变
		// demo.B=91; //出错,final变量的值一旦给定就无法改变
		// demo.C=81; //出错,final变量的值一旦给定就无法改变
		// demo.D=71; //出错,final变量的值一旦给定就无法改变
		System.out.println(demo.A);
		System.out.println(demo.B);
		System.out.println(demo.C); // 不推荐用对象方式访问静态字段
		System.out.println(demo.D); // 不推荐用对象方式访问静态字段
		System.out.println(Demo1.C);
		System.out.println(Demo1.D);
		// System.out.println(Demo1.E); //出错,因为E为final空白,依据不同对象值有所不同.
		System.out.println(demo.E);
		Demo1 t1 = new Demo1(3);
		System.out.println(t1.E); // final空白变量E依据对象的不同而不同
	}

	private void test() {
		System.out.println(new Demo1(1).A);
		System.out.println(Demo1.C);
		System.out.println(Demo1.D);
	}

	public void test1() {
		final int a; // final空白,在需要的时候才赋值
		final int b = 4; // 局部常量--final用于局部变量的情形
		final int c; // final空白,一直没有给赋值.
		a = 3;
		//a=4; // 出错,已经给赋过值了.
		//b=2; // 出错,已经给赋过值了.
	}
}
  1. final参数
    当函数参数为final类型时,你可以读取使用该参数,但是无法改变该参数的值。
public void fun(final int a) {
    // a++; //a是final类型的,值不允许改变的.
    System.out.println(a);
}

static关键字

static方法就是没有this的方法。在static方法内部不能调用非静态方法,反过来是可以的。而且可以在没有创建任何对象的前提下,仅仅通过类本身来调用static方法。被static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问。

static可以用来修饰类的成员方法、类的成员变量,另外可以编写static代码块来优化程序性能。

  1. static方法
    static方法一般称作静态方法,由于静态方法不依赖于任何对象就可以进行访问,因此对于静态方法来说,是没有this的,因为它不依附于任何对象,既然都没有对象,就谈不上this了。并且由于这个特性,在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都是必须依赖具体的对象才能够被调用。但是在非静态成员方法中是可以访问静态成员方法/变量的。
public class Demo1 {

	private static String str1 = "张三";
	private String str2 = "李四";
	
	public void print1() {
		System.out.println(str1);
		System.out.println(str2);
		print2();
	}
	
	public static void print2() {
		System.out.println(str1);
		// System.out.println(str2);//静态方法不能访问非静态变量
		// print1();//静态方法不能调用非静态方法
	}
	
}
  1. static变量
    static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。static成员变量的初始化顺序按照定义的顺序进行初始化。
  2. static代码块
    static关键字还有一个比较关键的作用就是 用来形成静态代码块以优化程序性能。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。
class Person{
    private Date birthDate;
    private static Date startDate,endDate;
    static{
        startDate = Date.valueOf("1946");
        endDate = Date.valueOf("1964");
    }
     
    public Person(Date birthDate) {
        this.birthDate = birthDate;
    }
     
    boolean isBornBoomer() {
    	// Date startDate = Date.valueOf("1946");//使用static之前
        // Date endDate = Date.valueOf("1964");
        return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0;
    }
}
  1. static笔试题
public class Test {
    Person person = new Person("Test");
    static{
        System.out.println("test static");
    }
     
    public Test() {
        System.out.println("test constructor");
    }
     
    public static void main(String[] args) {
        new MyClass();
    }
}
 
class Person{
    static{
        System.out.println("person static");
    }
    public Person(String str) {
        System.out.println("person "+str);
    }
}
 
 
class MyClass extends Test {
    Person person = new Person("MyClass");
    static{
        System.out.println("myclass static");
    }
     
    public MyClass() {
        System.out.println("myclass constructor");
    }
}

输出内容

test static
myclass static
person static
person Test
test constructor
person MyClass
myclass constructor

首先加载Test类,因此会执行Test类中的static块。接着执行new MyClass(),而MyClass类还没有被加载,因此需要加载MyClass类。在加载MyClass类的时候,发现MyClass类继承自Test类,但是由于Test类已经被加载了,所以只需要加载MyClass类,那么就会执行MyClass类的中的static块。在加载完之后,就通过构造器来生成对象。而在生成对象的时候,必须先初始化父类的成员变量,因此会执行Test中的Person person = new Person(),而Person类还没有被加载过,因此会先加载Person类并执行Person类中的static块,接着执行父类的构造器,完成了父类的初始化,然后就来初始化自身了,因此会接着执行MyClass中的Person person = new Person(),最后执行MyClass的构造器。

博客参考:

https://blog.51cto.com/lavasoft/18771

感谢作者。