当面试官问我————为什么String是final的?

面试官:你好,能看得清下面这张图吗?

:可以的。

面试官:恩,好的。呃,你能不能说一说为什么String要用final修饰?

:final意味着不能被继承或者被重写,String类用final修饰是Java的设计人员不希望客户端程序员继承String类,并有可能改写String类中的方法。使用String对象的最佳实践,应该是关联或者依赖,而不是继承。

面试官:恩,你还没有说到点儿上,能再展开谈谈吗?

:恩,好的。具体来说,String类被定义为final的主要是从两个方面来考虑:安全性能,也就是说,String被设计成final的,即考虑到了安全性,也兼顾了性能问题。

我们可以看到上面这张图中,出现了两个final。一个final是修饰了String类,而另一个final修饰了char数组。我们知道,String的本质实际上就是这个char数组,先来说一说 final char[] 的这个 final。

用final修饰char数组的原因,还需要从我们日常的实际开发中说起。

在日常的实际开发中,开发者会用到大量的字符串对象,可以说我们无时无刻不在和字符串打交道。大量的字符串被轻易的创建出来,这就涉及到一个非常严重的问题,即性能的开销,我们知道分配给Java虚拟机的内存是有限的,如果不加节制的创建字符串对象,那么弊端显而易见:内存迅速被占满,程序执行缓慢!!!于是Java的设计者采用了一种非常有效的解决办法,即:共享字符串。共享字符串对象的方法是将字符串对象存放到虚拟机中的方法区里面的常量池里,不同的类,不同的方法,甚至是不同的线程,可以使用同一个字符串对象,而不需要再在内存中开辟新的内存空间,从而极大的降低了内存的消耗,也提升了程序运行效率。

因此,字符串共享是解决内存消耗以及庞大的性能开销的必然选择。但是到这里为止,还不能解释为什么这个char数组要用final修饰。用final修饰的原因来自于另一个必须要考虑的问题:安全性。什么是安全性?这里的安全性,指的是线程安全性,这个很好理解,首先,我们已经确定了一个大的前提:字符串要共享,否则内存将瞬间挤爆、性能将严重下降。

但是共享的问题在于:不同的线程有可能会修改这个共享对象

比如,thread_1正在循环一个List,每个元素和 “abc” 进行比较,同时thread_2也在使用这个 “abc” 对象,如果thread_2改变了这个共享字符串,结果会怎样?很明显,thread_1 的结果将不可预测!!因此,解决共享变量安全性的最好的手段,就是禁止修改共享对象,于是字符串对象的这个char数组就必然要被 final 修饰了,因为 final 意味着禁止改变

面试官:恩恩,没想到你的想法这么透彻!那么,这是char数组final的作用,那为什么还要给String类本身加一个final呢?

:恩,这也是另一个Java设计者需要考虑的问题。既然共享字符数组已经确定是final的不能改变的了,那为什么要给String也加一个final呢?原因依然是性能安全性两个方面。

但是,此时需要考虑的性能和安全性却和 final char[] 的final 不太一样了。

首先,如果假设String可以被继承,那么方法也可以被重写,这里面涉及到一个C++中的概念,叫做:虚函数表

面试官:哦?你还懂C++?

: 是的。同样是面向对象的语言,Java和C++有着共通的地方。首先,虚函数是指可以定义一个父类的指针, 其指向一个子类对象, 当通过父类的指针去调用函数时, 可以在运行时决定应该调用父类的函数还是子类的函数。虚函数是实现多态的基础。前面说了,如果String可以被继承,那么势必就会有人通过创建String引用并指向String子类对象的方式,来使用子类的方法,比如像这样写:

String aa = new SubString("abcd");
aa.length();

这看似没有什么问题,但是问题在于性能,前面提到了,在程序开发过程中,字符串对象是非常常用的,上述代码在调用对象aa.length() 时,虚拟机就会去虚函数表中查找并判定究竟是应该调用哪个子类的length()方法。在大量使用字符串对象的场景下,势必会降低程序运行效率。

其次又是安全性,这个安全性的解释为语义的安全性,面向对象的语言本身就要求要有清晰的语义和明确的表达。String的各个方法都围绕着一个char数组进行,所有方法的语义都是最直接、最有效的。重写String的方法意味着:不一样的语义或者错误的语义。这将直接导致String行为的不确定性使用String对象的代码将会是不安全的代码。因此,Java设计者才会禁止任何人继承String类,主要是为了String对象的操作语义不被改变,确保使用String对象的代码是绝对安全的

面试官:原来如此,没想到你的理解这么到位!年薪50万,明天就来上班吧!

总结

当面试官问道为什么 String 是final的时候,要答出两方面:第一就是final char value[] 的final ;第二就是 final class 的final。

这两个final都要紧扣安全性能两个方面阐述。

1、final char value[] 的final 要抓住几个关键点是:value[]数组的final用于限制字符数组的修改。字符串将会被大量使用,从性能上考虑迫使Java语言的设计者将 char[] 设计为共享的,又因为字符串是共享的再次迫使设计者考虑到线程安全性,这才需要用final来修饰,避免并发场景下行为不可预测

2、final class 的final 要抓住几个关键点是:类上的final用于限制产生子类(或限制多态/或限制行为的变化)。字符串的使用是频繁的,如果通过多态的方式使用String子类对象及其方法将会一定程度上导致性能下降(多态的实现原理:底层的虚函数表),同时String中的方法也可能面临被Override重写的危险导致程序语义不安全、甚至是逻辑错误,与Java自始至终强调的安全性理念相违背

综上,我觉得只要对照总结部分和上面的对话内容回答出这个问题,不论是简答还是展开讨论基本都应该是满分的节奏了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/558831.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

当面试官问我————Java是值传递还是引用传递?

面试官:你好,你能说出下面个程序的执行结果吗? public class Test {public static void main(String[] args) {String name "Scott";int age 5;User user new User();user.setName(name);user.setAge(age);System.out.println(…

ubuntu系统下Jenkins和tomcat的安装与配置

ubuntu 安装 JDK ubuntu的安装我们采取最简单的方式安装 直接用apt-get的方式 sudo apt-get install openjdk-8-jdk 安装器会提示你同意 oracle 的服务条款,选择 ok 然后选择yes 即可 ubuntu 安装tomcat8 通过apt安装 tomcat8 sudo apt-get install tomcat8 tomcat8-docs t…

String字符串拼接小例

>>>写出下面程序运行结果: public class StringTest {public static void main(String[] args) {String s1 "Programming";String a "Program";String b "ming";String s2 "Program" "ming";Stri…

看完这篇文章,还不懂nginx,算我输

看完这篇文章,还不懂nginx,算我输 参考:https://mp.weixin.qq.com/s/PeNWaCDf_6gp2fCQa0Gvng 1. Nginx产生~ Nginx 同 Apache 一样都是一种 Web 服务器。基于 REST 架构风格,以统一资源描述符(Uniform Resources Id…

一篇博客读懂设计模式之---工厂模式

设计模式之—工厂模式 工厂模式: 创建过程: 创建Shape接口 public interface Shape {void draw(); }创建实现类: public class Circle implements Shape {Overridepublic void draw() {System.out.println("this is a circle!"…

一篇博客读懂设计模式之-----策略模式

设计模式之策略模式 在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的对象 定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。 主要解决:在有多种算法相似的情况下,使用 if…else 所带来的复杂和…

Java并发编程实战————恢复中断

中断是一种协作机制,一个线程不能强制其他线程停止正在执行的操作而去执行其他操作。 什么是中断状态? 线程类有一个描述自身是否被中断了的boolean类型的状态,可以通过调用 .isInterrupted() 方法来查看。官方解释如下: 简单来…

一篇博客读懂设计模式之---模板方法模式

设计模式之模板方法模式: 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 简而言之就是:父类定义了骨架(调用哪些方法及其顺序)…

一篇读懂--mybatis的缓存

一篇读懂–mybatis的缓存 MyBatis的缓存指的是缓存查询结果,当以后使用相同的sql语句、传入相同的参数进行查询时,可直接从mybatis本地缓存中获取查询结果,而不必查询数据库。 mybatis的缓存包括一级缓存、二级缓存,一级缓存默认…

Spring Boot————BeanCreationNotAllowedException异常分析

引言 在对数据库进行新增记录的JUnit测试时,抛出一个BeanCreationNotAllowedException异常: 异常分析与解决 异常信息太长,图片截不下,粘贴来看: Exception in thread "pool-2-thread-1" org.springframew…

一篇博客读懂设计模式之---委派模式

一篇博客读懂设计模式之—委派模式 委派模式可能大家听起来不太熟悉,但是在代码开发的时候却很好用,下面从几个方面来介绍一下 what:是什么? 委派模式:顾名思义,委托其他对象或者实例来帮我们完成任务&am…

Jackson快速入门

引言 上一篇博客《XML模板解析————Dom4j解析xml案例分析》简单讲解了关于xml模板的解析,使用到了dom4j,这篇文章其实算是个姊妹篇,都是对于目前工作中的一些任务,如xml、json相互解析所涉及到的知识。 但是相对于xml而言&am…

一篇文章读懂MySQL的各种联合查询

一篇文章读懂MySQL的各种联合查询 联合查询是指将两个或两个以上的表的数据根据一定的条件合并在一起! 联合查询主要有以下几种方式: 全连接:将一张表的数据与另外一张表的数据彼此交叉联合查询出来 举例如下: 先建两张表: CR…

一篇文章看懂@Scheduled定时器/@Async/CompletableFuture

一篇文章看懂Scheduled定时器/Async/CompletableFuture Scheduled注解解析: 1.cron:最重要的一个参数 cron表达式[秒] [分] [小时] [日] [月] [周] [年]([年]可省略) 简单了解一下,网上有现成的工具 示例&#xff…

一篇搞懂HTTP协议

本文转自 :flyhero 码上实战《一个HTTP打趴80%面试者》 HTTP协议简介 HTTP(超文本传输协议)是应用层上的一种客户端/服务端模型的通信协议,它由请求和响应构成,且是无状态的。(暂不介绍HTTP2) 协议&…

Jackson高级操作————节点树

引言 继《Jackson快速入门》基础篇之后的树模型相关操作。 节点树模型 ObjectMapper构建JsonNode节点树,类似于DOM解析器的XML。 Testpublic void testJsonTree() throws JsonProcessingException, IOException {String jsonString "{\"name\":\…

Jackson高级操作————流式API与JsonGenerator、JsonParser

引言 继《Jackson快速入门》之后的高级相关操作。 Jackson提供了一种对于性能要求应用程序操作json更加高效的方式——流式API,这种方式开销小,性能高,因此,如果应用程序或者程序逻辑对于性能有一定要求,可以使用这种…

Mysql高级教程思维导图

Mysql高级教程思维导图 1、思维导图总览: 2、MySQL架构介绍: 2.1、MySQL简介 2.2、MySQL Linux版安装 2.3、MySQL配置文件 2.4、MySQL逻辑架构介绍 2.5、MySQL存储引擎 2.6、MySQL的用户和权限管理 2.7、MySQL一些杂项配置 ’ 3、索引优化分析 3.1、性…

Eclipse(STS 4)闪退导致EGit插件异常

引言 到目前为止,STS已经闪退过三次了。 问题很棘手,我需要冷静。 首先出现了一个问题就是,EGit无法commit。 第二个问题是切换分支报错。 闪退重启后EGit无法提交代码 观察闪退出现的时机,一般出现在我 commit 代码的时候&…

spring源码分析第二天------spring系统概述以及IOC实现原理

1、Spring5 概述 Spring 是一个开源的轻量级 Java SE(Java 标准版本)/Java EE(Java 企业版本)开发应用框架, 其目的是用于简化企业级应用程序开发。 Spring 框架除了帮我们管理对象及其依赖关系,还提供像通…