大家好,我是陈哈哈,虎年第一篇,祝虎年大吉,拿个高薪!相信大家和我一样,
都有一个大厂梦
,作为一名资深Java选手,深知面试重要性,接下来我准备用100天时间,基于Java岗面试中的高频面试题,以每日3题
的形式,带你过一遍热门面试题及恰如其分的解答。 一路走来,随着问题加深,发现不会的也愈来愈多。但底气着实足了不少,相信不少朋友和我一样,日积月累才是最有效的学习方式!想起高三时一个同学的座右铭:只有沉下去,才能浮上来。
共勉(juan)。
车票
- 面试题1:String str与new String与有什么区别?
- 面试题2:为什么 String 在 Java 中是不可变(final)的?
- 面试题3:为什么说 char[] 比 Java 中的 String 更适合存储密码?
- 每日小结
本栏目Java开发岗高频面试题主要出自以下各技术栈:Java基础知识
、集合容器
、并发编程
、JVM
、Spring全家桶
、MyBatis等ORMapping框架
、MySQL数据库
、Redis缓存
、RabbitMQ消息队列
、Linux操作技巧
等。
面试题1:String str与new String与有什么区别?
要说明这两种字符创建方式的区别,首先要理解什么是常量池,常量池(constant pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量
。
在常量池中存储字符串常量的内存空间,即字符串常量池,当需要使用字符串时,先去字符串池中查看该字符串是否已经存在,如果存在,则可以直接使用,如果不存在,初始化,并将该字符串放入字符串常量池中。
常量池的位置
- JDK1.7以前,常量池(包含字符串常量池)位于方法区
- JDK1.7及以后,字符串常量池移入堆
常量池的作用
- 节省内存空间:所有常量池中相同的常量具有同样的内存地址
- 加快运行速率
1、String直接赋值(String str = “abc”;):
创建一个或不创建对象
。如果“abc”在字符串池中不存在,会在java字符串池中创建一个String对象(“abc”),然后str指向这个内存地址,无论以后用这种方式创建多少个值为“abc”的字符串对象,始终只有一个内存地址被分配;如果“abc” 在字符串池中存在, str直接指向这个内存地址。
String str1 = "abc";
String str2 = "abc";
String str3 = "abc";
System.out.println(str1 == str2); // true
System.out.println(str1 == str3); // true
2、使用new String创建字符串(String str = new String(“abc”);):
创建一个或两个对象
。因为用到new关键字,肯定会在堆中创建一个String对象,如果字符池中已经存在”abc”,则不会在字符串池中创建一个String对象,如果不存在,则会在字符串常量池中也创建一个对象。
String str1 = new String("abc");
String str2 = new String("abc");
String str3 = new String("abc");
System.out.println(str1 == str2);//false
System.out.println(str1 == str3);//false
3、String拼接字符串:
除了直接使用等号
赋值,有时也会用到字符串拼接,字符串拼接又分为变量拼接和已知字符串拼接。
只要拼接内容存在变量,那么该拼接后的新变量就是在堆内存中新建的一个对象实体。
代码语言:javascript复制String str = "abc";
String str1 = "abcd";
String str2 = str "d"; // 拼接字符串,此时会在堆中新建一个abcd的对象,因为str2编译之前是未知的
String str3 = "abc" "d"; // 拼接之后str3还是abcd,所以还是会指向字符串常量池的内存地址
System.out.println(str1==str2); // false
System.out.println(str1==str3); // true
概括为以下几点:
- 针对
String str="abc"
和String str=new String("abc");
产生的对象,前者1或0,后者2或1,先看字符串常量池,如果字符串常量池中没有,都在常量池中创建一个,如果有,前者直接引用,后者在堆内存中还需创建一个“abc”实例对象。 - 对于基础类型的变量和常量:
变量和引用存储在栈中,常量存储在常量池中
。 - 为了提升jvm(JAVA虚拟机)性能和减少内存开销,避免字符的重复创建 项目中还是不要使用new String去创建字符串,最好使用String直接赋值。
- 关于equals()和 “= =” 对于String类简单来说,equals()就是比较两字符串的内容是否相等,如果相等返回true;而 “= =” 是比较两字符串的地址是否相同,也就是是否是同一个字符串的引用,如果是返回true。
面试题2:为什么 String 在 Java 中是不可变(final)的?
我们先来看一下String类在源码中的成员变量;在JDK1.7中,String类的主要成员变量主要为以下两个:
代码语言:javascript复制public final class String implements java.io.Serializable, Comparable<string>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0</string>
由以上的代码可以看出, 在 Java 中,String 类其实就是对字符数组char []的封装
。在JDK7中,只有一个 value 变量,也就是 value 中的所有字符都是属于 String 这个对象的。除此之外还有一个 hash 成员变量,是该 String 对象的哈希值的缓存,这个成员变量也和本文的讨论无关。在Java中,数组也是对象。 所以 value 也只是一个引用,它指向一个真正的数组对象。其实执行了String s = “ABCabc”; 这句代码之后,真正的内存布局应该是这样的:
字符串在 Java 中是不可变的,因为 String 对象缓存在字符串常量池中。由于缓存的字符串在多个客户之间共享,因此始终存在风险,其中一个客户的操作会影响所有其他客户
。例如,如果一段代码将 String “Test” 的值更改为 “TEST”,则所有其他客户也将看到该值。由于 String 对象的缓存性能是很重要的一方面,因此通过使 String 类不可变来避免这种风险。
同时,String 是 final 的,因此没有人可以通过扩展和覆盖行为来破坏 String 类的不变性、缓存、散列值的计算等
。
String 类不可变的另一个原因可能是由于 HashMap,由于把字符串作为 HashMap 键很受欢迎。对于键值来说,重要的是它们是不可变的,以便用它们检索存储在 HashMap 中的值对象。
由于 HashMap 的工作原理是散列,因此需要具有相同的值才能正常运行。如果在插入后修改了 String 的内容,可变的 String将在插入和检索时生成两个不同的哈希码,可能会丢失 Map 中的值对象。
String 作为数据类型,传输对象和中间人角色的重要性和流行性也使这个问题在 Java 面试中很常见。
为什么 String 在 Java 中是不可变的是 Java 中最常被问到的字符串访问问题之一,它首先讨论了什么是 String,Java 中的 String 如何与 C 和 C 中的 String 不同,然后转向在Java中什么是不可变对象,不可变对象有什么好处,为什么要使用它们以及应该使用哪些场景。
这个问题有时也会问:“为什么 String 在 Java 中是 final 的”。我认为以下几点解释了为什么 String 类在 Java 中是不可变的或 final 的:
- 1、想象字符串池没有使字符串不可变的话,是站不住脚的,因为在字符串池的情况下,一个字符串对象/文字,例如 “Test” 已被许多参考变量引用,因此如果其中任何一个更改了值,其他参数将自动受到影响,如下:
String A="Test";
String B="Test";
现在字符串 B 调用 “Test”.toUpperCase, 将同一个对象改为“TEST”,所以 A 也是 “TEST”,这肯定是不行的。
- 2、字符串已被广泛用作许多 Java 类的参数,例如,为了打开网络连接,你可以将主机名和端口号作为字符串传递,你可以将数据库 URL 作为字符串传递, 以打开数据库连接,你可以通过将文件名作为参数传递给 File I/O 类来打开 Java 中的任何文件。如果 String 不是不可变的,这将导致严重的安全威胁,我的意思是有人可以访问他有权授权的任何文件,然后可以故意或意外地更改文件名并获得对该文件的访问权限。由于不变性,你无需担心这种威胁。这个原因也说明了,为什么 String 在 Java 中是最终的,通过使 java.lang.String final,Java设计者确保没有人覆盖 String 类的任何行为。
- 3、
由于 String 是不可变的,它可以安全地共享许多线程,这对于多线程编程非常重要
,并且避免了 Java 中的同步问题,不变性也使得String 实例在 Java 中是线程安全的,这意味着你不需要从外部同步 String 操作。关于 String 的另一个要点是由截取字符串 SubString 引起的内存泄漏,这不是与线程相关的问题,但也是需要注意的。 - 4、为什么 String 在 Java 中是不可变的另一个原因是允许 String 缓存其哈希码,Java 中的不可变 String 缓存其哈希码,并且不会在每次调用 String 的 hashcode 方法时重新计算,这使得它在 Java 中的 HashMap 中使用的 HashMap 键非常快。简而言之,因为 String 是不可变的,所以没有人可以在创建后更改其内容,这
保证了 String 的 hashCode 在多次调用时是相同的
。 - 5、String 不可变的绝对最重要的原因是它被类加载机制使用,因此具有深刻和基本的安全考虑。如果 String 是可变的,加载“java.io.Writer” 的请求可能已被更改为加载 “mil.vogoon.DiskErasingWriter”. 安全性和字符串池是使字符串不可变的主要原因。
面试题3:为什么说 char[] 比 Java 中的 String 更适合存储密码?
这个问题也可以定义为封装Char[]的String类相比Char数组,对密码存储会有哪些不同?
,这是最近在 Java 面试中向我的一位朋友询问的问题。任何与 String 相关的问题都必须对字符串的特殊属性有一些线索,比如不变性。在这里,我们将探讨为什么你应该使用char[]存储密码而不是String的一些原因。
1、由于字符串在 Java 中是不可变的,如果你将密码存储为纯文本,它将在内存中可用,直到垃圾收集器清除它. 并且为了可重用性,会存在 String 在字符串池中, 它很可能会保留在内存中持续很长时间,从而构成安全威胁
。
由于任何有权访问内存转储的人都可以以明文形式找到密码,这是另一个原因,你应该始终使用加密密码而不是纯文本。由于字符串是不可变的,所以不能更改字符串的内容,因为任何更改都会产生新的字符串,而如果你使用char[],你就可以将所有元素设置为空白或零。因此,在字符数组中存储密码可以明显降低窃取密码的安全风险。
2、Java 本身建议使用 JPasswordField 的 getPassword 方法,该方法返回一个 char[] 和不推荐使用的getTex()方法,该方法以明文形式返回密码,由于安全原因。应遵循 Java 团队的建议, 坚持标准而不是反对它。
3、使用 String 时,总是存在在日志文件或控制台中打印纯文本的风险,但如果使用 Array,则不会打印数组的内容而是打印其内存位置。虽然不是一个真正的原因,但仍然有道理。
代码语言:javascript复制String strPassword =“Unknown”; char [] charPassword = new char [] {'U','n','k','w','o','n'};
System.out.println(“字符密码:” strPassword);
System.out.println(“字符密码:” charPassword);
输出:
代码语言:javascript复制字符串密码:Unknown
字符密码:[C @110b053
我还建议使用散列或加密的密码而不是纯文本,并在验证完成后立即从内存中清除它。因此,在Java中,用字符数组用存储密码比字符串是更好的选择。虽然仅使用char[]还不够,还你需要擦除内容才能更安全。
每日小结
今天我们复习了Java基础知识中常考的两个老油条问题,你做到心中有数了么?对了,如果你的朋友也在准备面试,请将这个系列扔给他,如果他认真对待,肯定会感谢你的!!
好了,今天就到这里,学废了的同学,记得在评论区留言:打卡。
,给同学们以激励。
参考资料
https://v3.processon.com/view/5ef1a2917d9c0844202f7746 https://blog.csdn.net/qq_45737068/article/details/107149922 https://www.cnblogs.com/weirdo-lenovo/p/11418871.html