网站首页 > 精选教程 正文
对final关键字,作为java程序员相信对其并不陌生,我们在实际应用中经常使用到。
但为什么求职者时面试官总喜欢拿这样的基础知识点来考察求职者呢,
我有次面试时也遇到过这样的问题:
Java 里的final关键用过吗?
final,英文的意思是最终的,不可改变的,顾名思义就是已经确认好了,不会改变。
一、final关键字基本使用
1、final作用
final可以修饰类、方法、变量。分别有什么作用:
- 修饰类:表示类不可被继承
- 修饰方法:表示方法不可被覆盖
- 修饰变量:表示变量一旦被赋值就不可以更改它的值。
2、final修饰类
final修饰的类不能被继承!我们来验证下:
public class User {
class Man extends Person {
}
final class Person {
}
}
3、final修饰方法
final修饰的方法不能被重写。例如Object类中的getClass()方法就是final方法。不能被重写。父类中private的方法,在子类中不能访问该方法,但是子类中仍然可以定义一个与父类private方法有相同的方法名、相同的形参列表和相同的返回值的方法,不属于方法重写,只是定义了一个新的方法。final修饰的方法仅仅是不能被重写,并不是不能被重载。譬如
public class User
{
public final void getUserName(){}
public final void getUserName(int i){}
}
4、final关键字修饰变量
final关键字修饰变量,是相对比较麻烦的,
通常情况下,我们定义常量共有三种常见形式:
public class User {
// final修饰实例变量并初始化
private final String A = "a";
// final修饰类变量并初始化
private static final String B = "b";
public void setA(final String A) {
// A = "aa";//编译报错
}
public static void setB(final String B) {
}
}
通常情况这三种方式足够应付大多数的问题,但是事实上,我们定义的实例变量与类变量,并不一定非要在被定义时就初始化。
final在定义时不被初始化?平时使用final都是直接初始化,没有初始化编译不通过,可行吗?
接下来我们一步一步来分析。
01、修饰成员变量
如果final修饰的是类变量,只能在静态初始化块中指定初始值或者声明该类变量时指定初始值。
如果final修饰的是成员变量,可以在非静态初始化块、声明该变量或者构造器中执行初始值。
02、修饰局部变量
系统不会为局部变量进行初始化,局部变量必须由开发者初始化。
在使用final修饰局部变量时,即可以在定义时指定默认值(以后不能对变量再赋值),
也可以在定义时不被初始化,而在后面的代码中对final变量赋初值(仅一次)。
我们来验证下这两种情况:
public class Person {
final static int a = 0;//再声明的时候就需要赋值
public static void main(String[] args) {
final int age; //局部变量只声明没有初始化,不会报错,与final无关。
age = 28;//在使用之前一定要赋值
//age = 30; 但是不允许第二次赋值
}
}
03、final修饰基本类型变量和引用类型变量的区别?
final修饰基本类型变量时,不能对基本类型重新赋值。
但是,对于引用型变量,它仅仅保存的是一个引用,final保证的是这个引用类型的变量所引用的地址不会变。即一直引用同一个对象,但是这个对象的值可以改变。
public static void main(String [] args)
{
final int[] arr={1,2,3,4};
Arrays.sort(arr);//合法
arr[2]=-3;//合法
for(int i =0;i<arr.length;i++){
System.out.print(arr[i]+"\n");
}
// arr=null;//编译报错,arr不能重新赋值
final User user = new User(25);
user.setAge(24);//合法
//user=null;//编译报错
System.out.print(user.getAge());
}
public class User {
private int userName;
private int age;
public User() {
}
public User(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getUserName() {
return userName;
}
public void setUserName(int userName) {
this.userName = userName;
}
}
二、final关键字需要注意的两个问题
1、final和static的区别
static作用于成员变量用来表示只保存一份副本,而final的作用是用来保证变量不可变。
我们来验证下
public class FinalTest {
public static void main(String[] args) {
A a1 = new A();
A a2 = new A();
System.out.println(a1.i);
System.out.println(a2.i);
System.out.println(a1.j);
System.out.println(a2.j);
}
}
class A {
public final int i = (int) (Math.random() * 1000);
public static int j = (int) (Math.random() * 1000);
}
输出结果 : 696、463、273、273
无论run多少次,这个j值两个都一样,因为是static修饰的,全局只保留一份,i值不一样,两个对象可能产生两个不同的值。
2、为什么局部内部类和匿名内部类只能访问局部final变量
我们来验证下:
public class FinalTest {
public static void main(String[] args) {
}
//局部final变量age,userName
public void test(final String userName) {
int age = 28;
//匿名内部类
new Thread(){
public void run() {
System.out.println(userName);
System.out.println(age);
};
}.start();
}
}
上段代码中,在jdk8前,如果把变量userName和age前面的任一个final去掉,这段代码都编译不过,
如果我们在匿名内部类中需要访问局部变量,那么这个局部变量必须用final修饰符修饰。
这段代码会被编译成两个class文件:FinalTest.class和FinalTest.class。默认情况下,编译器会为匿名内部类和局部内部类起名为Outter1.class。
原因是为什么呢?这是因为test()方法里面的参数userName和age,在运行时,main线程快要结束,但是thread还没有开始。因此需要有一种机制,在使得运行thread线程时候能够调用a和b的值,怎办呢?java采用了一种复制的机制,
也就说如果局部变量的值在编译期间就可以确定,则直接在匿名内部里面创建一个拷贝。如果局部变量的值无法在编译期间确定,则通过构造器传参的方式来对拷贝进行初始化赋值。
在jdk8中如果我们在匿名内部类中需要访问局部变量,那么这个局部变量不需要用final修饰符修饰。
看似是一种编译机制的改变,实际上就是一个语法糖(底层会自动加上final)。
但通过反编译没有看到底层为我们加上final,但我们无法改变这个局部变量的引用值,如果改变就会编译报错。
在jdk8中去掉final 反编译后的
public class FinalTest {
public FinalTest() {
}
public static void main(String[] var0) {
}
public void test(final String var1) {
final byte var2 = 28;
(new Thread() {
public void run() {
System.out.println(var1);
System.out.println(var2);
}
}).start();
}
}
可以得知底层确实是帮我们加上了final
三、总结
final关键字主要用在三个地方:变量、方法、类。
对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
当用final修饰一个类时,表明这个类不能被继承。final类中的所有成员方法都会被隐式地指定为final方法。
final变量如果是静态的,要么定义时初始化,要么在静态代码块中初始化。final变量如果不是静态的,要么定义时初始化,要么在非静态代码块中初始化。
final修饰的变量可以在定义时不被初始化。平时工作中一定要注意总结和积累,查漏补缺,不断完善自己的知识体系。
一个关键字,竟然包含了这么多知识点,不仅能考验求职者的对该知识点的掌握程度,又能考验求职者的知识面,难怪阿里字节这样的大公司,经常在面试时用它来考验求职者了。
由于笔者水平有限,文中纰漏之处在所难免,权当抛砖引玉,不妥之处,请大家批评指正。
猜你喜欢
- 2024-10-30 java 你用了这么久,真的明白static和final的区别么?
- 2024-10-30 JAVA笔记(十五)面向对象——关键字abstract、final
- 2024-10-30 「java面试_01」String类为什么是final
- 2024-10-30 Java编程思想:final 关键字 java中finally关键字
- 2024-10-30 Java八股文:final、finally、finalize之间有什么区别
- 2024-10-30 在java中String类为什么要设计成final?Java面试常见问题
- 2024-10-30 lambda中的外部局部变量为什么需要声明final?
- 2024-10-30 java笔记static和final用法 java static与final
- 2024-10-30 Java中为什么String类被定义成final
- 2024-10-30 Java中的final修饰符 java中final可以修饰接口吗
你 发表评论:
欢迎- 04-11Java面试“字符串三兄弟”String、StringBuilder、StringBuffer
- 04-11Java中你知道几种从字符串中找指定的字符的数量
- 04-11探秘Java面试中问的最多的String、StringBuffer、StringBuilder
- 04-11Python字符串详解与示例(python字符串的常见操作)
- 04-11java正则-取出指定字符串之间的内容
- 04-11String s1 = new String("abc");这句话创建了几个字符串对象?
- 04-11java判断字符串中是否包含某个字符
- 04-11关于java开发中正确的发牌逻辑编写规范
- 最近发表
-
- Java面试“字符串三兄弟”String、StringBuilder、StringBuffer
- Java中你知道几种从字符串中找指定的字符的数量
- 探秘Java面试中问的最多的String、StringBuffer、StringBuilder
- Python字符串详解与示例(python字符串的常见操作)
- java正则-取出指定字符串之间的内容
- String s1 = new String("abc");这句话创建了几个字符串对象?
- java判断字符串中是否包含某个字符
- 关于java开发中正确的发牌逻辑编写规范
- windows、linux如何后台运行jar(并且显示进程名)
- 腾讯大佬私人收藏,GitHub上最受欢迎的100个JAVA库,值得学习
- 标签列表
-
- nginx反向代理 (57)
- nginx日志 (56)
- nginx限制ip访问 (62)
- mac安装nginx (55)
- java和mysql (59)
- java中final (62)
- win10安装java (72)
- java启动参数 (64)
- java链表反转 (64)
- 字符串反转java (72)
- java逻辑运算符 (59)
- java 请求url (65)
- java信号量 (57)
- java定义枚举 (59)
- java字符串压缩 (56)
- java中的反射 (59)
- java 三维数组 (55)
- java插入排序 (68)
- java线程的状态 (62)
- java异步调用 (55)
- java中的异常处理 (62)
- java锁机制 (54)
- java静态内部类 (55)
- java怎么添加图片 (60)
- java 权限框架 (55)
本文暂时没有评论,来添加一个吧(●'◡'●)