final关键字/编译时常量与运行时常量/继承与初始化

final可以用来修饰数据、方法、类。

final数据

1、final修饰变量
final修饰基本数据类型的变量时,必须赋予初始值且不能被改变。
static final修饰的是常量,常量分为编译时常量与运行时常量,定义为static,强调只有一份。
比如static final int a = 10;就是编译时常量,只要是该常量带入的语句,在编译过后都会替换。
这段代码
在这里插入图片描述
在反编译后变成
在这里插入图片描述
而且只要这种常量在的算式,在编译时期就会被计算。(只有基本数据类型和String)
在这里插入图片描述
反编译后
在这里插入图片描述
很明显了。
关于运行时常量:static final int a = “123”.length();就是一个运行时常量,它的赋值会引起类的初始化。
编译器常量有一个风险。就比如A类定义了常量,B类使用了常量。但现在需要修改A类源码的常量值,对A类重新编译,但没对B类重新编译,这就导致A类与B类的常量值不一样。
2、final修饰引用
对于对象引用,final使引用恒定不变。就是一旦引用指向一个对象,就无法把他再指向另一个对象,但对象是可以修改的。数组也一样,毕竟数组也是对象。
3、空白final
所谓空白final就是被声明为final但未赋初值的域,但在使用前,必须保证赋初值,这样就可以灵活的为其进行赋值,但是又保持了其不可更改的特性 。
必须在域定义处(final修饰局部变量)或每个构造器里(final修饰成员变量)用表达式对final进行赋值。

public class Tests {
	private final int j;
	public Tests(){
		j = 0;
	}
	public Tests(int x){
		j = x;
	}
	public static void main(String[] args) {
		new Tests(12);
		final int k;
		k = 2;
		System.out.println(k);
    }
}

3、final参数
被final修饰的参数称位final参数。意思是你无法更改参数引用所指向的变量,可以读参数,但不可修改参数。主要用于向内部类传递数据。
4、final方法
被final修饰的方法,只能被继承,不能被覆盖
final与private关键字:类中所有private方法都被隐式指定是final的,但如果在子类定义一个与父类方法名、参数列表、返回值都相同的private方法也可以,但调用的时候,还是按照private的法则,private方法只在本类中使用,而且用@Override也会报错,也就是说父类与子类的相同的private方法没有任何关系。
5、final类
当类被final修饰时,表示类是不可被继承的。final类中的所有方法都是隐式的指定为final的,无法被覆盖。但成员变量不是final,你可以人为指定某个成员变量是final。



继承与初始化

如下代码:

public class Battle extends Insect {
	private int k = printInit("Battle.k initialized");
	public Battle(){
		System.out.println("k = " + k);
		System.out.println("j = " + j);
	}
	private static int x2 = printInit("static Battle.x2 initialized");
	public static void main(String[] args) {
		System.out.println("Battle constructor");
		Battle t = new Battle();
    }
}
class Insect{
	private int i = 9;
	protected int j;
	Insect(){
		System.out.println("i = " + i + ", j = " + j);
		j = 39;
	}
	private static int x1 = printInit("static Insect.x1 initialized");
	static int printInit(String s){
		System.out.println(s);
		return 47;
	}
}

输出
在这里插入图片描述
在Battle上运行Java时,程序想要访问Battle类的main方法,这时加载器就要找出Battle的.class文件,对它进行加载。加载过程中,发现它有一个父类,于是就要对父类进行加载,如果发现父类还有父类,那就要加载“爷爷类”,不要认为加载父类是由于本类调用了父类的静态方法,实际上去掉调用父类的成员变量,父类还是先加载。
父类的static初始化完后,就轮到自己的static执行,执行完后,也就输出了前两行。
执行到这,说明必要的类已经加载完毕了。进入main方法,输出Battle constructor。开始创建对象。先把对象内所有的基本类型设置为默认值,引用类型设置为null(通过将对象内存设为二进制零值一举生成的),然后,构造器开始调用,我们知道,子类构造器会默认在第一行添加super(),先调用父类构造器,输出i = 9, j = 0;,然后子类构造器执行。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值