对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

System.out.print(arr[i]+"");

}

// 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修饰的变量可以在定义时不被初始化。平时工作中一定要注意总结和积累,查漏补缺,不断完善自己的知识体系。

一个关键字,竟然包含了这么多知识点,不仅能考验求职者的对该知识点的掌握程度,又能考验求职者的知识面,难怪阿里字节这样的大公司,经常在面试时用它来考验求职者了。

由于笔者水平有限,文中纰漏之处在所难免,权当抛砖引玉,不妥之处,请大家批评指正。

为什么要写this在访问成员变量的时候_终于知道阿里字节这样的公司,为什么经常拿final来考验求职者了...相关推荐

  1. 为什么要写this在访问成员变量的时候_java面向对象(局部变量和成员属性)

    局部变量: 我们定义在一个方法内部的变量称为局部变量.局部变量需要给初始值,才能使用. 成员属性: 实例属性: 直接定义在类中的属性,我们称为成员变量.成员变量jdk会给默认值.只属于某一实例. 类属 ...

  2. 为什么要写this在访问成员变量的时候_C++幕后故事(一) --对象模型this指针调整...

    1.什么叫this指针调整? 在c++中多继承过程,根据访问不同的父类成员变量或者是成员函数,同一个实例对象会出现不同的基址(对象的地址,类似于你在不同的场合就会有身份的转换,在家的身份,在学校,在公 ...

  3. java访问成员变量和方法_如何访问类的成员变量和方法

    如何访问类的成员变量和方法 在Java应用程序中,经常需要访问类的成员变量和方法,以实现程序的功能,请问应该如何访问类的成员变量和方法?在Java中通过类的实例(即对象)点(.)运算符来访问类的成员变 ...

  4. java 反射 成员变量_java使用反射访问成员变量的值示例

    本文实例讲述了java使用反射访问成员变量的值.分享给大家供大家参考,具体如下: 一 代码 import java.lang.reflect.*; class Person { private Str ...

  5. Cpp 对象模型探索 / 对象访问成员变量的原理

    一.栗子 1.源码 #include <iostream> #include <stdio.h>class Base { public:Base() { std::cout & ...

  6. Java通过反射访问成员变量

    通过下列任意一个方法访问成员变量时将返回 Field 类型的对象或数组. getFields()getField(String name)getDeclaredFields()getDeclaredF ...

  7. php学校成员,php访问成员变量和成员方法

    php访问成员变量和成员方法 php教程:访问PHP类中成员变量或方法 在访问PHP类中的成员变量或方法时,如果被引用的变量或者方法被声明成const或者static,那么就必须使用操作符::, 反之 ...

  8. 【C++】空指针调用成员函数及访问成员变量

    最近在review代码的时候发现,使用了空指针调用成员函数,并且成员函数内部有使用到成员变量,居然没有出错.很是奇怪,就用一篇博客把关于空指针调用成员函数相关的内容总结起来. 空指针调用成员函数 调用 ...

  9. java程序如何访问成员变量,java如何访问成员变量

    通过Class对象的getField()方法,可以获得这种包含的所有或指定的成员变量Field,Filed提供以下两种方法阅读和设定成员变量值 1.getxx(Objectobj): 获得obj对象的 ...

最新文章

  1. steamvr unity 连接眼镜_150度FOV,自研显示方案,Kura公布全新AR眼镜Gallium
  2. C/C++代码静态检查工具PC-lint在VS2008开发环境中的安装配置和使用
  3. UWP: ListView 中与滚动有关的两个需求的实现
  4. lua 开发环境搭建(windows 平台)
  5. leetcode(226)—— Invert Binary Tree(Python/C++)
  6. 更新日志_CargoWare系统云平台更新日志2020.11.24
  7. shell 脚本执行报错/bin/bash^M: bad interpreter: No such file or directory
  8. Tensorflow2.0学习(八) — tf.dataset自定义图像数据集
  9. 红帽子linux 命令,红帽子_Linux_命令全解
  10. java实习收获与体会_java实习心得体会.doc
  11. Java前后端分离项目部署
  12. 2017计算机基础教学大纲,《计算机应用基础》教学大纲
  13. 2021年9月电子学会图形化四级编程题解析含答案:成语接龙
  14. IMDB评分排名算法
  15. Vue进阶(幺贰叁):v-for 实现一行展示 n 个元素
  16. originPro2021(4)origin导出图片且去除demo水印
  17. AndroidManifest--详细理解
  18. Caffeine - Caches - Eviction
  19. kali Linux单用户模式的退出方法
  20. 出主意:阻止了对方倒苦水(错误行为)

热门文章

  1. Centos7.0系统下Rsync+sersync实现多文件数据实时增量同步
  2. (转)Clang 比 GCC 编译器好在哪里?
  3. POJ1275Cashier Employment(查分约束系统)
  4. SakaiCLE2.9数据库迁移
  5. 关于vue的语法规则检测报错问题
  6. (7)-(Reverse Integer)-(将整数按位反转成另外一个整数,考虑溢出)-(知道最大整数和最小整数值)...
  7. 给Lisp程序员的Python简介
  8. 百度地图开发的时候遇到的问题(二)
  9. 泛型TListT Generics.Collections单元
  10. 几款远程工具介绍(Xshell)(SecureCRT)(putty)