final关键字表示最终的、不可改变的
可以修饰以下维度:
1、修饰类
目的:不能被其他类继承
该类为最终类,不能被其他类继承
该类中的所有方法都是最终方法(隐式的被final修饰)
如图:即使手动添加final修饰方法,IDEA也会提醒该final可以移除
在开发中,哪些类需要定义为final呢?
final修饰类的初衷是不能让其他类继承该类,实际开发中,我们一般遵循"低耦合,高内聚合"的原则
如果一个类被其他类继承,那么当父类被修改时,可能会造成子类报错,从而子类也需要同步修改,该条继承链为"高耦合"
综上,对于经常需要修改的类(迭代更新会经常改动的类),可以使用final修饰
2、修饰方法
目的:子类不能重写(覆盖)该方法
如图所示:子类重写父类中的最终方法,会出现错误
哪些方法需要定义为最终方法?
对继承没有太大意义的方法,或者父类自身特有的方法,这些方法不需要给子类使用,从而定义为最终方法,在类继承时,可以降低耦合度
3、修饰局部变量
作用:final修饰的变量是不可改变的
优点:不需要重复的创建对象
这里的不可改变又分为2种:
对于基本数据类型,指的是数值不可改变
对于引用数据类型,指的是变量对应的地址不可改变
这里区别来源于java内存分配方式:
在java中,基本数据类型定义的值是存储在栈中的
引用数据类型定义的名称是存储在栈中的,而对应的实例(值),是存储在堆中
3.1、基本数据类型
如图:内存中的值不可改变
3.2、引用数据类型
注意:这里不能使用String来举例,String是一个特殊的存储机制
以User类为例
@Data
@AllArgsConstructor
public class User{
String name;
Integer age;
}
fina修饰User对象,地址更改的示例代码:
public static void main(String[] args) {
//1、声明User最终的局部变量,并name=张三/age=18存储在堆中
final User user = new User("张三", 18);
System.out.println("堆中的值name发生改变前,user对象存储的结果为:" + user);
//2、修改最终变量user中,地址指向的堆中的值name=李四
user.setName("李四");
System.out.println("堆中的值name发生改变后,user对象存储的结果为:" + user);
}
示意图如下:user对象内存地址是不可改变的,而实际的值可以一致修改,耗用内存比重复创建对象小
4、修饰全局变量(属性)
作用与上述局部变量一致
注意以下4点
4.1、final修饰全局变量,必须手动赋初始值
全局变量在没有final修饰的情况下,默认是存在初始值的,如下:
public class FinalStudy {
/** 为了能在主方法中直接获取到全局变量,这里使用static修饰 */
//定义一个基本数据类型的全局变量a
static int a;
//定义一个包装数据类型的全局变量b
static Integer b;
//定义一个引用类型的全局变量c
static String c;
public static void main(String[] args) {
System.out.println("int的默认值为:" + a);
System.out.println("Integer的默认值为:" + b);
System.out.println("String的默认值为:" + c);
}
}
结果为:
其实:引用数据类型的默认值都为null,而每一种基本数据类型都有其对应的默认值。
改造下上面的代码,当我们使用final修饰时,会报错,提示需要给初始值:
4.2、对于final修饰的全局变量,要么直接赋值,要么通过构造方法赋值
还是以上面的代码为例:
注意:由于构造方法为:与类同名,无返回类型且不允许static修饰的方法,从而需要去除上述的static修饰符
示例图如下:
4.3、对于未直接赋值的final类型的全局变量,所有的构造方法都必须对该变量进行赋值
案列1:
即:
我们也可以给无参构造放方法添加参数