相信很多同行小伙伴会因为许多原因想跳槽,不论是干得不开心还是想跳槽涨薪,在如此内卷的行业,我们都面临着“面试造火箭,上班拧螺丝”的局面,鉴于当前形势博主呕心沥血整理的干货满满的造火箭的技巧来了,本博主花费2个月时间,整理归纳java全生态知识体系常见面试题!总字数高达百万! 干货满满,每天更新,关注我,不迷路,用强大的归纳总结,全新全细致的讲解来留住各位猿友的关注,希望能够帮助各位猿友在应付面试笔试上!当然如有归纳总结错误之处请各位指出修正!如有侵权请联系博主QQ1062141499!
目录
1 两个对象的hashCode()相同,则equals()也一定为true,对吗?
2 HashCode的作用
2.1、HashCode的特性
2.2、HashCode作用
3 为什么要重载equals方法?
4 为什么重载hashCode方法?
5 为什么equals()相等,hashCode就一定要相等,而hashCode相等,却不要求equals相等?
6 为什么需要hashCode?
7 两个对象值相同(x.equals(y)==true),但却可有不同的hashCode,这句话对不对?
8 为什么重写equals方法时,必须重写hashcode方法
、
1 String 属于基础的数据类型吗?
2 Java中操作字符串都有哪些类?它们之间有什么区别?
3 String str="i"与 String str=new String("i")一样吗?
4 如何将字符串反转?
?????5 String 类的常用方法都有那些?
6 String s = "Hello";s = s + " world!";这两行代码执行后,原始的String对象中的内容到底变了没有?
7 是否可以继承String?
8 数组有没有length()方法?String有没length()方法
9 请说出下面程序的输出
10 判断字符串是否包含中文
11 为什么 String 在 Java 中是不可变的?
12 为什么char数组比Java中的String更适合存储密码?
13 String对象中的replace和replaceAll的区别?
14 GB2312编码的字符串如何转换为ISO-8859-1编码?
15 String s="a"+"b"+"c"+"d";创建了几个对象?
???????
1 两个对象的hashCode()相同,则equals()也一定为true,对吗?
不对,两个对象的hashCode()相同,equals()不一定为true。
代码示例:
Stringstr1="通话";
Stringstr2="重地";
System.out.println(String.format("str1:%d|str2:%d",str1.hashCode(),str2.hashCode()));
System.out.println(str1.equals(str2));
执行的结果:
str1:1179395|str2:1179395
false
代码解读:很显然“通话”和“重地”的hashCode()相同,然而equals()则为false,因为在散列表中,hashCode()相等即两个键值对的哈希值相等,然而哈希值相等,并不一定能得出键值对相等。
关于hashCode()和equals()是方法是有一些常规协定:
1、两个对象用equals()比较返回true,那么两个对象的hashCode()方法必须返回相同的结果。
2、两个对象用equals()比较返回false,不要求hashCode()方法也一定返回不同的值,但是最好返回不同值,以提高哈希表性能。
3、重写equals()方法,必须重写hashCode()方法,以保证equals()方法相等时两个对象hashcode()返回相同的值。
2 HashCode的作用
2.1、HashCode的特性
(1)HashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,HashCode经常用于确定对象的存储地址。
(2)如果两个对象相同,equals方法一定返回true,并且这两个对象的HashCode一定相同。
(3)两个对象的HashCode相同,并不一定表示两个对象就相同,即equals()不一定为true,只能够说明这两个对象在一个散列存储结构中。
(4)如果对象的equals方法被重写,那么对象的HashCode也尽量重写。
2.2、HashCode作用
Java中的集合有两类,一类是List,再有一类是Set。前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可重复。
equals方法可用于保证元素不重复,但如果每增加一个元素就检查一次,若集合中现在已经有1000个元素,那么第1001个元素加入集合时,就要调用1000次equals方法。这显然会大大降低效率。于是,Java采用了哈希表的原理。
哈希算法也称为散列算法,是将数据依特定算法直接指定到一个地址上。
这样一来,当集合要添加新的元素时,先调用这个元素的HashCode方法,就一下子能定位到它应该放置的物理位置上。
(1)如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了。
(2)如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了。
(3)不相同的话,也就是发生了Hashkey相同导致冲突的情况,那么就在这个Hashkey的地方产生一个链表,将所有产生相同HashCode的对象放到这个单链表上去,串在一起(很少出现)。
这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。
如何理解HashCode的作用:
从Object角度看,JVM每new一个Object,它都会将这个Object丢到一个Hash表中去,这样的话,下次做Object的比较或者取这个对象的时候(读取过程),它会根据对象的HashCode再从Hash表中取这个对象。这样做的目的是提高取对象的效率。若HashCode相同再去调用equals。
3 为什么要重载equals方法?
因为Object的equals方法默认是两个对象的引用的比较,意思就是指向同一内存,地址则相等,否则不相等;如果你现在需要利用对象里面的值来判断是否相等,则重载equals方法。
4 为什么重载hashCode方法?
一般的地方不需要重载hashCode,只有当类需要放在HashTable、HashMap、HashSet等等hash结构的集合时才会重载hashCode,那么为什么要重载hashCode呢?
如果你重写了equals,比如说是基于对象的内容实现的,而保留hashCode的实现不变,那么很可能某两个对象明明是“相等”,而hashCode却不一样。这样,当你用其中的一个作为键保存到hashMap、hashTable或hashSet中,再以“相等的”找另一个作为键值去查找他们的时候,则根本找不到。
5 为什么equals()相等,hashCode就一定要相等,而hashCode相等,却不要求equals相等?
1、因为是按照hashCode来访问小内存块,所以hashCode必须相等。
2、HashMap获取一个对象是比较key的hashCode相等和equals为true。
之所以hashCode相等,却可以equals不等,就比如ObjectA和ObjectB他们都有属性name,那么hashCode都以name计算,所以hashCode一样,但是两个对象属于不同类型,所以equals为false。
6 为什么需要hashCode?
1、通过hashCode可以很快的查到小内存块。
2、通过hashCode比较比equals方法快,当get时先比较hashCode,如果hashCode不同,直接返回false。
7 两个对象值相同(x.equals(y)==true),但却可有不同的hashCode,这句话对不对?
不对,如果两个对象x和y满足x.equals(y)==true,它们的哈希码(hashCode)应当相同。Java对于eqauls方法和hashCode方法是这样规定的:
(1)如果两个对象相同(equals方法返回true),那么它们的hashCode值一定要相同;
(2)如果两个对象的hashCode相同,它们并不一定相同。当然,你未必要按照要求去做,但是如果你违背了上述原则就会发现在使用容器时,相同的对象可以出现在Set集合中,同时增加新元素的效率会大大下降(对于使用哈希存储的系统,如果哈希码频繁的冲突将会造成存取性能急剧下降)。
关于equals和hashCode方法,很多Java程序员都知道,但很多人也就是仅仅知道而已,在JoshuaBloch的大作《EffectiveJava》(很多软件公司,《EffectiveJava》、《Java编程思想》以及《重构:改善既有代码质量》是Java程序员必看书籍,如果你还没看过,那就赶紧去买一本吧)中是这样介绍equals方法的。
首先equals方法必须满足自反性(x.equals(x)必须返回true)、对称性(x.equals(y)返回true时,y.equals(x)也必须返回true)、传递性(x.equals(y)和y.equals(z)都返回true时,x.equals(z)也必须返回true)和一致性(当和y引用的对象信息没有被修改时,多次调用x.equals(y)应该得到同样的返回值),而且对于任何非null值的引用x,x.equals(null)必须返回false。实现高质量的equals方法的诀窍包括:
1.使用==操作符检查"参数是否为这个对象的引用";
2.使用instanceof操作符检查"参数是否为正确的类型";
3.对于类中的关键属性,检查参数传入对象的属性是否与之相匹配;
4.编写完equals方法后,问自己它是否满足对称性、传递性、一致性;
5.重写equals时总是要重写hashCode;
6.不要将equals方法参数中的Object对象替换为其他的类型,在重写时不要忘掉@Override注解。
8 为什么重写equals方法时,必须重写hashcode方法
1.维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。
2.hashcode是用于散列数据的快速存取,如利用hash结构集合类来存储数据时,都是根据存储对象的hashcode值来进行判断是否相同的。所以会出现一种可能,当重写equals方法后,判断对象相等,但其hashcode却不一致,这种相等可以看作为逻辑相等。当使用hash集合类时,存放时会根据该类的hashcode方法,来确定其存放位置,如hashset,将无法过滤相同对象,因为不重写hashcode,会默认调用Object类的hashcode方法,计算出来的存 地址不一样,会导致能同时存放两个值相等的对象,产生混淆。
、
1 String 属于基础的数据类型吗?
String 不属于基础类型,基础类型有 8 种:byte、boolean、char、short、int、float、long、double,而 String 属于对象。
2 Java中操作字符串都有哪些类?它们之间有什么区别?
操作字符串的类有:String、StringBuffer、StringBuilder。
String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。
StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。
3 String str="i"与 String str=new String("i")一样吗?
不一样,因为内存的分配方式不一样。String str="i"的方式,Java 虚拟机会将其分配到常量池中;而 String str=new String("i") 则会被分到堆内存中。
String str="i";
String str2 = new String("i");
System.out.println(str==str2);//false
System.out.println(str.equals(str2));//true
4 如何将字符串反转?
使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。
示例代码:
// StringBuffer reverse
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("abcdefg");
System.out.println(stringBuffer.reverse()); // gfedcba
// StringBuilder reverse
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("abcdefg");
System.out.println(stringBuilder.reverse()); // gfedcba
?????5 String 类的常用方法都有那些?
- equals:字符串是否相同
- equalsIgnoreCase:忽略大小写后字符串是否相同
- compareTo:根据字符串中每个字符的Unicode编码进行比较
- compareToIgnoreCase:根据字符串中每个字符的Unicode编码进行忽略大小写比较
- indexOf:目标字符或字符串在源字符串中位置下标
- lastIndexOf:目标字符或字符串在源字符串中最后一次出现的位置下标
- valueOf:其他类型转字符串
- charAt:获取指定下标位置的字符
- codePointAt:指定下标的字符的Unicode编码
- concat:追加字符串到当前字符串
- isEmpty:字符串长度是否为0
- contains:是否包含目标字符串
- startsWith:是否以目标字符串开头
- endsWith:是否以目标字符串结束
- format:格式化字符串
- getBytes:获取字符串的字节数组
- getChars:获取字符串的指定长度字符数组
- toCharArray:获取字符串的字符数组
- join:以某字符串,连接某字符串数组
- length:字符串字符数
- matches:字符串是否匹配正则表达式
- replace:字符串替换
- replaceAll:带正则字符串替换
- replaceFirst:替换第一个出现的目标字符串
- split:以某正则表达式分割字符串
- substring:截取字符串
- toLowerCase:字符串转小写
- toUpperCase:字符串转大写
- trim:去字符串首尾空格
6 String s = "Hello";s = s + " world!";这两行代码执行后,原始的String对象中的内容到底变了没有?
没有。因为 String 被设计成不可变(immutable)类,所以它的所有对象都是不可变对象。在这段代码中,s 原先指向一个 String 对象,内容是 "Hello",然后我们对 s 进行了“+”操作,那么 s 所指向的那个对象是否发生了改变呢?答案是没有。这时,s 不指向原来那个对象了,而指向了另一个 String 对象,内容为"Hello world!",原来那个对象还存在于内存之中,只是 s 这个引用变量不再指向它了。
通过上面的说明,我们很容易导出另一个结论,如果经常对字符串进行各种各样的修改,或者说,不可预见的修,那么使用 String来代表字符串的话会引起很大的内存开销。因为 String对象建立之后不能再改变,所以对于每一个不同的字符串,都需要一个 String 对象来表示。这时,应该考虑使用 StringBuffer类,它允许修改,而不是每个不同的字符串都要生成一个新的对象。并且,这两种类的对象转换十分容易。同时,我们还可以知道,如果要使用内容相同的字符串,不必每次都 new 一个 String。例如我们要在构造器中对一个名叫 s 的 String 引用变量进行初始化,把它设置为初始值,应当这样做:
public class Demo {private String s;...s = "Initial Value";...
}
而非:
s = new String("Initial Value");
后者每次都会调用构造器,生成新对象,性能低下且内存开销大,并且没有意义,因为 String 对象不可改变,所以对于内容相同的字符串,只要一个 String 对象来表示就可以了。也就说,多次调用上面的构造器创建多个对象,他们的 String 类型属性 s 都指向同一个对象。
上面的结论还基于这样一个事实:对于字符串常量,如果内容相同,Java 认为它们代表同一个String对象。而用关键字 new 调用构造器,总是会创建一个新的对象,无论内容是否相同。 至于为什么要把 String 类设计成不可变类,是它的用途决定的。其实不只 String,很多Java标准类库中的类都是不可变的。在开发一个系统的时候,我们有时候也需要设计不可变类,来传递一组相关的值,这也是面向对象思想的体现。不可变类有一些优点,比如因为它的对象是只读的,所以多线程并发访问也不会有任何问题。当然也有一些缺点,比如每个不同的状态都要一个对象来代表,可能会造成性能上的问题。所以 Java 标准类库还提供了一个可变版本,即 StringBuffer。
7 是否可以继承String?
String类是final类,不可以被继承。
继承String本身就是一个错误的行为,对String类型最好的重用方式是关联关系(Has-A)和依赖关系(Use-A)而不是继承关系(Is-A)。
8 数组有没有length()方法?String有没length()方法
数组没有length()方法,而是有length的属性。String有length()方法。JavaScript中,获得字符串的长度是通过length属性得到的,这一点容易和Java混淆。
9 请说出下面程序的输出
String s1 = "Programming";
String s2 = new String("Programming");
String s3 = "Program";
String s4 = "ming";
String s5 = "Program" + "ming";
String s6 = s3 + s4;
System.out.println(s1 == s2); //false
System.out.println(s1 == s5); //true
System.out.println(s1 == s6); //false
System.out.println(s1 == s6.intern()); //true
System.out.println(s2 == s2.intern());//false
补充:解答上面的面试题需要知道如下两个知识点:
1.String对象的intern()方法会得到字符串对象在常量池中对应的版本的引用(如果常量池中有一个字符串与String对象的equals结果是true),如果常量池中没有对应的字符串,则该字符串将被添加到常量池中,然后返回常量池中字符串的引用;
2.字符串的+操作其本质是创建了StringBuilder对象进行append操作,然后将拼接后的StringBuilder对象用toString方法处理成String对象,这一点可以用javap-cStringEqualTest.class命令获得class文件对应的JVM字节码指令就可以看出来。
10 判断字符串是否包含中文
/*** 根据字符串长度判断字符串中是否包含中文* 由于中文字符占用的字节数是2或者3个,英文字符占用的字节数是1,因此转换成字节后,如果有中文字符,那么字节长度会大于字符串长度*/
public static boolean isContainChineseByLength(String str) {if (str.getBytes().length == str.length()) {return false;}return true;
}
/*** 根据正则判断字符串中是否包含中文*/
public static boolean isContainChineseByRegex(String str) {Pattern p = Pattern.compile("[\u4e00-\u9fa5]");Matcher m = p.matcher(str);if (m.find()) {return true;}return false;
}
11 为什么 String 在 Java 中是不可变的?
我最喜欢的Java面试问题,很棘手,但同时也非常有用。一些面试者也常问这个问题,为什么String在Java中是final的。
字符串在Java中是不可变的,因为String对象缓存在String池中。由于缓存的字符串在多个客户之间共享,因此始终存在风险,其中一个客户的操作会影响所有其他客户。例如,如果一段代码将String“Test”的值更改为“TEST”,则所有其他客户也将看到该值。由于String对象的缓存性能是很重要的一方面,因此通过使String类不可变来避免这种风险。
同时,String是final的,因此没有人可以通过扩展和覆盖行为来破坏String类的不变性、缓存、散列值的计算等。String类不可变的另一个原因可能是由于HashMap。
由于把字符串作为HashMap键很受欢迎。对于键值来说,重要的是它们是不可变的,以便用它们检索存储在HashMap中的值对象。
由于HashMap的工作原理是散列,因此需要具有相同的值才能正常运行。如果在插入后修改了String的内容,可变的String将在插入和检索时生成两个不同的哈希码,可能会丢失Map中的值对象。
如果你是印度板球迷,你可能能够与我的下一句话联系起来。字符串是Java的VVSLaxman,即非常特殊的类。我还没有看到一个没有使用String编写的Java程序。这就是为什么对String的充分理解对于Java开发人员来说非常重要。
String作为数据类型,传输对象和中间人角色的重要性和流行性也使这个问题在Java面试中很常见。
为什么String在Java中是不可变的是Java中最常被问到的字符串访问问题之一,它首先讨论了什么是String,Java中的String如何与C和C++中的String不同,然后转向在Java中什么是不可变对象,不可变对象有什么好处,为什么要使用它们以及应该使用哪些场景。
这个问题有时也会问:“为什么String在Java中是final的”。在类似的说明中,如果你正在准备Java面试,我建议你看看《Java程序员面试宝典(第4版)》,这是高级和中级Java程序员的优秀资源。它包含来自所有重要Java主题的问题,包括多线程,集合,GC,JVM内部以及Spring和Hibernate框架等。
正如我所说,这个问题可能有很多可能的答案,而String类的唯一设计者可以放心地回答它。我在JoshuaBloch的EffectiveJava书中期待一些线索,但他也没有提到它。我认为以下几点解释了为什么String类在Java中是不可变的或final的:
1)想象字符串池没有使字符串不可变,它根本不可能,因为在字符串池的情况下,一个字符串对象/文字,例如“Test”已被许多参考变量引用,因此如果其中任何一个更改了值,其他参数将自动受到影响,即假设
String A="Test";
String B="Test";
现在字符串B调用"Test".toUpperCase(),将同一个对象改为“TEST”,所以A也是“TEST”,这不是期望的结果。
下图显示了如何在堆内存和字符串池中创建字符串。
2)字符串已被广泛用作许多Java类的参数,例如,为了打开网络连接,你可以将主机名和端口号作为字符串传递,你可以将数据库URL作为字符串传递,以打开数据库连接,你可以通过将文件名作为参数传递给FileI/O类来打开Java中的任何文件。如果String不是不可变的,这将导致严重的安全威胁,我的意思是有人可以访问他有权授权的任何文件,然后可以故意或意外地更改文件名并获得对该文件的访问权限。由于不变性,你无需担心这种威胁。这个原因也说明了,为什么String在Java中是最终的,通过使java.lang.Stringfinal,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”.安全性和字符串池是使字符串不可变的主要原因。顺便说一句,上面的理由很好回答另一个Java面试问题:“为什么String在Java中是最终的”。要想是不可变的,你必须是最终的,这样你的子类不会破坏不变性。你怎么看?
12 为什么char数组比Java中的String更适合存储密码?
另一个基于String的棘手Java问题,相信我只有很少的Java程序员可以正确回答这个问题。这是一个真正艰难的核心Java面试问题,并且需要对String的扎实知识才能回答这个问题。
这是最近在Java面试中向我的一位朋友询问的问题。他正在接受技术主管职位的面试,并且有超过6年的经验。如果你还没有遇到过这种情况,那么字符数组和字符串可以用来存储文本数据,但是选择一个而不是另一个很难。但正如我的朋友所说,任何与String相关的问题都必须对字符串的特殊属性有一些线索,比如不变性,他用它来说服访提问的人。在这里,我们将探讨为什么你应该使用char[]存储密码而不是String的一些原因。
字符串:
1)由于字符串在Java中是不可变的,如果你将密码存储为纯文本,它将在内存中可用,直到垃圾收集器清除它.并且为了可重用性,会存在String在字符串池中,它很可能会保留在内存中持续很长时间,从而构成安全威胁。
由于任何有权访问内存转储的人都可以以明文形式找到密码,这是另一个原因,你应该始终使用加密密码而不是纯文本。由于字符串是不可变的,所以不能更改字符串的内容,因为任何更改都会产生新的字符串,而如果你使用char[],你就可以将所有元素设置为空白或零。因此,在字符数组中存储密码可以明显降低窃取密码的安全风险。
2)Java本身建议使用JPasswordField的getPassword()方法,该方法返回一个char[]和不推荐使用的getTex()方法,该方法以明文形式返回密码,由于安全原因。应遵循Java团队的建议,坚持标准而不是反对它。
3)使用String时,总是存在在日志文件或控制台中打印纯文本的风险,但如果使用Array,则不会打印数组的内容而是打印其内存位置。虽然不是一个真正的原因,但仍然有道理。
String strPassword =“Unknown”; char [] charPassword = new char [] {'U','n','k','w','o','n'}; System.out.println(“字符密码:”+ strPassword);System.out.println(“字符密码:”+ charPassword);
输出:
字符串密码:Unknown字符密码:[C @110b053
我还建议使用散列或加密的密码而不是纯文本,并在验证完成后立即从内存中清除它。因此,在Java中,用字符数组用存储密码比字符串是更好的选择。虽然仅使用char[]还不够,还你需要擦除内容才能更安全。
13 String对象中的replace和replaceAll的区别?
replace方法:支持字符和字符串的替换。
public String replace(char oldChar, char newChar)
public String replace(CharSequence target, CharSequence replacement)
replaceAll方法:基于正则表达式的字符串替换。
public String replaceAll(String regex, String replacement)
测试代码:
String str = "Hello Java. Java is a language.";
System.out.println(str.replace("Java.", "c++"));//打印 Hello c++ Java is a language.
System.out.println(str.replaceAll("Java.", "c++"));//打印 Hello c++ c++is a language.
???????‘
14 GB2312编码的字符串如何转换为ISO-8859-1编码?
String str = "B仔";
String strIso = new String(str.getBytes("GB2312"), "ISO-8859-1");
System.out.println(strIso);
???????15 String s="a"+"b"+"c"+"d";创建了几个对象?
1个
Java 编译器对字符串常量直接相加的表达式进行优化,不等到运行期去进行加法运算,在编译时就去掉了加号,直接将其编译成一个这些常量相连的结果。
所以 "a"+"b"+"c"+"d" 相当于直接定义一个 "abcd" 的字符串。