java修饰符final深入理解

    final关键字可以用于修饰类,变量和方法。final修饰变量时候,一旦获取初始值就不可被改变,final修饰变量时候,变量一旦获得初始值就不可被改变,final可以修饰成员变量(类变量,实例变量),也可以是局部变量,形参。final修饰的变量不可被改变,一旦获得初始值,该final变量的值就不能被重新赋值。


  final成员变量:

成员变量是随类初始化或对象初始化而设计的。必须由程序员显示的指定初始值。

(1)类变量:必须在静态代码块中指定或者声明该变量的时候指定初始值。

(2)实例变量:必须在非静态代码块,声明该实例变量或构造器中指定初始值。

实例变量不能再静态代码块中指定初始值,因为静态初始化代码块是静态成员,不可以访问实例变量--非静态成员。

总结:在定义使用final成员变量时候,由于final变量后期不能修改,所以前期在初始化加载类跟对象时候,用final定义的属性变量或者final static 定义的类变量要么直接定义时候显示给出初始值,或者在代码块中给出初始值,实例变量还可以在构造函数中给出初始值。总之在加载初始化时候,final必须被显示赋值。加载生成完成以后,final修饰的成员变量是不可以改变的。


final局部变量:

    系统不会对局部变量进行初始化,局部变量必须由程序员显示初始化,因此使用final修饰局部变量时,既可以在定义时候指定默认值,也可以不指定,如果没有指定默认值,可以在后续代码中对final变量赋值初始值,但是只能一次,一旦赋予,就不能改变。如果刚开始一旦局部变量 在定义时候指定默认值,则在后面不能再给变量赋值。

    局部变量不同于成员变量的是,成员变量是存在于类跟对象,在调用类或者创建对象时候,此时需要初始化成员变量,成员变量。局部变量,存在于程序部分,可以先定义该变量为final变量,当系统走到该出时候,再给出值,但是一旦给与,后面就不可以改变。


final修饰基本数据类型和引用数据类型的区别:

当final修饰基本数据类型时候,不能对基本数据类型重新赋值,但是当final修饰的是基本数据类型时候,final只能保证这个引用地址不会改变,即一只引用同一个对象,但是这个对象完全可以发生改变。

package project1;

class Person{
	
	private int age;

	public Person(int age) {
		super();
		this.age = age;
	}

	public Person() {
		super();
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}
	
}

public class Student {

	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		final Person person=new Person(45);
		//显示45
		System.out.println(person.getAge());
		//用set方法做改变
		person.setAge(100);
		//显示100,做了修改
		System.out.println(person.getAge());
		
		//但是person不能被赋值
		//错误句:person=new Person();
	}

}


可执行“宏替换”的final变量

对于一个final变量来说,只要该变量满足三个条件:

(1)使用final修饰符修饰。

(2)在定义该final变量时指定了初始值。

(3)该初始值可以在编译时候就被确定下来。

如下:

public static void main(String[] args) {
		
		final int a=5;
		System.out.println(a);
	}

final修饰的一个重要用途就是定义宏变量,当定义final变量时就为该变量指定了初始值,而且该初始值可以在编译时候就确定下来,name这个final变量本质上就是一个宏变量,编译器会在编译时候在所有用到该变量的地方替换成该变量的值。当然,final变量赋值语句如果是基本算数表达式或者字符串加号拼接,没有访问普通变量,调用方法,java编译器同样会将这种final变量当做宏变量。

	public static void main(String[] args) {
		//宏变量
		final int a=5;
		final String s="123"+3;
		final double d=12/5;
		
		//非宏变量
		int b=10;
		final String s2="1213"+String.valueOf(99);
		final String s3="1111"+b;
		System.out.println(a);
	}

final方法:

final方法是不可以被重写的,如果出于某些原因,不希望子类重写父类的某个方法,则可以使用final修饰该方法。比如java的object类里面就有个getClass()方法,因为java不希望任何类都重写该方法,所以使用final将其密封起来

final类:

final修饰的类不可以有子类,例如java.lang.Math类就是一个final类,他不可以有子类。当子类继承父类时,将可以的访问到父类的内部数据,并可以通过重写父类方法来改变父类方法的实现细节,这可能导致一些不安全的因素被继承,则可以用final修饰这个类。

不可变类:

不可变类意思是创建该类的实例后,该实例的实例变量是不可以被改变的,java的8个包装类和java.lang.String类都是不可变类,当创建他们的实例后,其实例都不可别改变。但是前面我们介绍过,当final修饰引用类型后,仅表示这个引用变量不可被重新赋值,但是引用变量所指向的对象依然可以改变。那么问题来了,当创建不可变类时候,如果它包含的成员变量的类型是可变的那么其成员变量的值依然是可变的--这个不可变类使我门不想看到的。

package project1;

class Name{
	private String firstName;
	private String lastName;
	public Name(){
		
	}
	public Name(String firstName,String lastName){
		this.firstName=firstName;
		this.lastName=lastName;
	}
	public String getFirstName() {
		return firstName;
	}
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}
	public String getLastName() {
		return lastName;
	}
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
	
}

public class Person {
	private Name name;
	public Person(){
		
	}
	public Person(Name name){
		this.name=name;
	}
	 
	public Name getName() {
		return name;
	}
	public void setName(Name name) {
		this.name = name;
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Name n=new Name("悟空","sun");
		Person p=new Person(n);
		//输出悟空
		System.out.println(p.getName().getFirstName());
		n.setFirstName("bajie");
		//输出bajie
		System.out.println(p.getName().getFirstName());
	}

}

从上述例子可以看出,我们在person类中有个成员变量是Name类的name属性,因为对象是引用变量,当我们使用Name n=new Name("悟空","sun");Person p=new Person(n);这里的对象n是引用传递,虽然Person类的Name属性是final类,但是final只是保证引用地址不可改变,即name保存的是对象n的地址,但是不保证n的属性字段做出改变。所以当n.setFirstName("bajie")时候,n对象属性字段做出了改变,影响到了Person类,因为对象是地址传递。所以System.out.println(p.getName().getFirstName());输出的是改变后的值,那么我们应该怎么办?缘起引用传递,那么我们就用值传递来解决。

package project1;

class Name{
	private String firstName;
	private String lastName;
	public Name(){
		
	}
	public Name(String firstName,String lastName){
		this.firstName=firstName;
		this.lastName=lastName;
	}
	public String getFirstName() {
		return firstName;
	}
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}
	public String getLastName() {
		return lastName;
	}
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
	
}

public class Person {
	private Name name;
	public Person(){
		
	}
	public Person(Name name){
		this.name=new Name(name.getFirstName(),name.getLastName());
	}
	 
	public Name getName() {
		//return new Name(name.getFirstName(),name.getLastName());
		return name;
	}

	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Name n=new Name("悟空","sun");
		Person p=new Person(n);
		//输出悟空
		System.out.println(p.getName().getFirstName());
		n.setFirstName("bajie");
		//输出bajie
		System.out.println(p.getName().getFirstName());
	}}


如此这般,我们把person的构造方法改了

public Person(Name name){
		this.name=new Name(name.getFirstName(),name.getLastName());
	}

这样当实现时候Name n=new Name("悟空","sun");Person p=new Person(n);,n就不会是跟我们final修饰的地址一样了,避免了引用传递造成的不好效果。

不可变类的遵守规则:

1,使用private与final修饰符来修饰该类的成员变量

2,提供带参数的构造器,用于根据传入参数来初始化类里面的成员变量

3,仅为该类的成员变量提供setter方法,因为普通方法无法修改final修饰的成员变量

4,如果有必要,重写object的hashCode()和equals()方法。equals()方法根据关键成员变量来作为两个对象那个是否相等的标准,除此之外,还应该保证两个用equals方法判断是否为相等的对象的hashCode




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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值