Java中的final修饰符

final可以修饰类、方法、变量,用来表示这些东西都不可再变
1、修饰成员变量:必须程序员显式地指定初始值
  • 类变量:声明该变量时指定初始值、静态初始化块中指定初始值(二者选其一)
  • 实例变量:声明该变量时指定初始值、非静态初始化块中指定初始值、构造器中指定初始值(三者选其一)
宏变量效果:对于final修饰的实例变量来说,如果在定义该变量时就指定初始值,那么这个变量就有了“宏变量”的效果。
public class Test{
	//实例变量
	final int a=1;    //声明变量时指定初始值
	final String str;  //普通初始化块中指定初始值
	final int c;  //构造器中指定
	//类变量
	final static double d=3.234;  //声明变量时指定初始值
	final static String op;
	{
		str="hello world";  // 普通初始化块中指定没有赋值的实例变量初始值
		//a=9;  //错误,a已经指定值了,此处不可再指定
	}
	static {
		op="hello China";  普通初始化块中指定没有赋值的类变量初始值
	}
	public Test() {
		c=6;  //构造器中,指定实例变量的初始值
	}
	public static void main(String[] args) {
		System.out.println(Test.d);
		System.out.println(Test.op);
		Test t=new Test();
		System.out.println(t.a);
		System.out.println(t.str);
		System.out.println(t.c);
	}
}
2、修饰局部变量:只能赋值一次
public class Test{
	public static void main(String[] args) {
		final String str;
		final int age;
		str="hello";  //第一次赋值,成功
		age=29;   //第一次赋值,成功
		str="hi";  //第二次赋值,报错
	}
}
3、修饰形参:不可被赋值(由于形参是在调用方法时,根据系统传入的参数来完成初始化的)
public class Test{
	public void test(final String str) {
		str="hello";  //报错,形参不可以被赋值
	}
	public static void main(String[] args) {
		final int age=37;
	}
}
4、修饰引用类型变量:引用的对象永远不会改变

对于基本类型来说,final修饰后不可再改变其值,但是引用类型不同,此时final修饰的变量保存的只是堆内存中的地址,即一直引用的是同一个对象,但是这个对象内的值会不会改变,跟final没有关系。

class Person{
	private String name;
	private int age;
	public Person() {}  //无参构造器
	//提供有参构造器
	public Person(String name,int age) {
		this.setName(name);
		this.setAge(age);
	}
	//name变量的setter和getter方法
	public void setName(String name) {
		if(name.length()<2||name.length()>6) {
			System.out.println("输入的名字不符合要求");
		}
		else {
			this.name=name;
		}
	}
	public String getName() {
		return this.name;
	}
	//age变量的setter和getter方法
	public void setAge(int age) {
		if(age<0||age>100) {
			System.out.println("输入的年龄不符合要求");
		}
		else {
			this.age=age;
		}
	}
	public int getAge() {
		return this.age;
	}
	//重写toString方法
	public String toString() {
		return "Person[name="+getName()+",age="+getAge()+"]";
	}
}
public class Test {
	public static void main(String[] args) {
		final Person p=new Person("张三",42); //创建一个不可变的引用变量p
		System.out.println(p);
		p.setAge(28); //修改该引用中的实例变量,正确
		System.out.println(p);
		//p=null;  重新给p赋值,错误 
	}
}

实际中的内存如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ewL3Kjs1-1589868967982)(C:\Users\24973\AppData\Roaming\Typora\typora-user-images\image-20200519094127109.png)]

虽然p引用中的值改变了,但是p这个引用没有变,因此不存在错误,但是重新给p赋值为null,表示将p当前这个引用删除,这改变了p,所以出错

同理下面这个例子相似。

public class Test {
	public static void main(String[] args) {
		final int[] arr=new int[] {1,2,3,4};
		System.out.println(Arrays.toString(arr));  //输出:[1,2,3,4]
		arr[2]=8;  //正确
		System.out.println(Arrays.toString(arr));  //输出:[1,2,8,4]
        arr=null; //错误
	}
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6D1BALJL-1589868967986)(C:\Users\24973\AppData\Roaming\Typora\typora-user-images\image-20200519095224674.png)]

5、修饰方法:final修饰的方法不可被重写

如果不希望子类重写父类的某个方法,可以将该类用final修饰。

class Person{
	final void test() {
		//该类不可被重写
	}
}
public class Test extends Person{
	//编译错误:该类不可以被重写
	public void test() {
	}
}

如果将final改为private呢?

class Person{
	private void test() {}
    private final void show(){}
}
public class Test extends Person{
	//编译正常
	public void test() {
	}
    public void show(){}
}
这并不是方法重写,因为父类的test方法是private修饰的,子类无法访问,而子类只是重新定义了一个新方法,恰好这个方法名也为test,但是这并不是方法重写或者方法重载。同理方法show也是,即使采用了privatefinal等修饰了,并不代表不能再子类中新建一个和父类同名字的方法。

final修饰的方法不可以被重写,但是可以被重载。(方法重写与重载的知识详见《Java方法重载与方法重写》)

public class Test extends Person{
	public final void test() {}
	public final void test(String str) {}  //编译正确:方法重载
}
6、修饰类:final修饰的类不能被继承,即它不可以有子类
final class Person{
}
//编译错误:Person类不可以被继承
public class Test extends Person{
	public final void test(String str) {}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值