2.Java基础【Java面试第三季】
- 前言
- 推荐
- 2.Java基础
- 01_字符串常量Java内部加载-上
- 58同城的java字符串常量池
- 面试code
- 讲解
- intern()方法---源码+解释
- 02_字符串常量Java内部加载-下
- why
- OpenJDK8底层源码说明
- 递推步骤
- 总结
- 考查点
- 03_闲聊力扣算法第一题
- 字节跳动两数求和
- 题目说明
- 面试题
- 解法
- 04_TwoSum暴力解法
- 05_TwoSum优化解法
- 考察点
- 字节跳动手写LRU算法
- 最后
前言
2023-2-1 13:23:19
推荐
Java开发常见面试题详解(LockSupport,AQS,Spring循环依赖,Redis)
2.Java基础
01_字符串常量Java内部加载-上
58同城的java字符串常量池
面试code
package javase;
public class StringPool58Demo1 {
public static void main(String[] args) {
String str1=new StringBuilder("58").append("tongchen").toString();
System.out.println(str1.intern());
System.out.println(str1 == str1.intern());
System.out.println();
String str2=new StringBuilder("ja").append("va").toString();
System.out.println(str2);
System.out.println(str2.intern());
System.out.println(str2 == str2.intern());
}
}
讲解
intern()方法—源码+解释
由于运行时常量池是方法区的一部分,所以这两个区域的溢出测试可以放到一起进行。HotSpot从JDK 7开始逐步“去永久代”的计划,并在JDK 8中完全使用元空间来代替永久代的背景故事,在此我们就以测试代码来观察一下,使用"永久代"还是“元空间"来实现方法区,对程序有什么实际的影响。
String:intern()是一个本地方法,它的作用是如果字符串常量池中已经包含一个等于此String对象的字符串,则返回代表池中这个字符串的String对象的引用;否则,会将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用。在JDK 6或更早之前的HotSpot虚拟机中,常量池都是分配在永久代中,我们可以通过-XX:PermSize和-XX:MaxPermSize限制永久代的大小,即可间接限制其中常量池的容量。
package javase;
public class StringPool58Demo1 {
public static void main(String[] args) {
String str1=new StringBuilder("58").append("tongchen").toString();
System.out.println(str1.intern());
System.out.println(str1 == str1.intern());
System.out.println();
String str2=new StringBuilder("ja").append("va").toString();
System.out.println(str2);
System.out.println(str2.intern());
System.out.println(str2 == str2.intern());
}
}
输出结果:
58tongchen
58tongchen
true
java
java
false
02_字符串常量Java内部加载-下
why
- 按照代码结果,Java字符串答案为false,必然是两个不同的java,那另外一个java字符串如何加载进来的?
- 为什么
- 有一个初始化的Java字符串(JDK出娘胎自带的),在加载sun.misc.Version这个类的时候进入常量池。
OpenJDK8底层源码说明
递推步骤
- System代码解析 System -> initializeSystemClass() -> Version
System
package java.lang;
public final class System {
/* register the natives via the static initializer.
*
* VM will invoke the initializeSystemClass method to complete
* the initialization for this class separated from clinit.
* Note that to use properties set by the VM, see the constraints
* described in the initializeSystemClass method.
*/
private static native void registerNatives();
static {
registerNatives();
}
//本地方法registerNatives()将会调用initializeSystemClass()
private static void initializeSystemClass() {
...
sun.misc.Version.init();
...
}
...
}
Version
package sun.misc;
//反编译后的代码
public class Version {
private static final String launcher_name = "java";
...
}
测试Version的其他字符串
public class Version {
private static final java.lang.String launcher_name = "java";
private static final java.lang.String java_version = "1.8.0_60";
private static final java.lang.String java_runtime_name = "Java(TM) SE Runtime Environment";
private static final java.lang.String java_profile_name = "";
private static final java.lang.String java_runtime_version = "1.8.0_60-b27";
}
String str3 =new StringBuilder("1.8.0_60").toString();
String str4 =new StringBuilder("Java(TM) SE Runtime Environment").toString();
String str5 =new StringBuilder("").toString();
String str6 =new StringBuilder("1.8.0_60-b27").toString();
System.out.println(str3 == str3.intern()||str4 == str4.intern()
||str5==str5.intern()||str6==str6.intern());//false
- 类加载器和rt.jar----根加载器提前部署加载rt.jar
- OpenJDK8源码
- http://openjdk.java.net/
- openjdk8\jdk\src\share\classes\sun\misc
总结
《深入理解java虚拟机》----第二章 Java内存区域与内存溢出异常----2.4 实战:OutOfMemoryError异常----2.4.3 方法区和运行时常量池溢出----代码清单2-8 String.intern()返回引用的测试
这段代码在JDK 6中运行,会得到两个false,而在JDK 7中运行,会得到一个true和一个false。产生差异的原因是,在JDK 6中,intern()方法会把首次遇到的字符串实例复制到永久代的字符串常量池中存储,返回的也是永久代里面这个字符串实例的引用,而由StringBuilder创建的字符串对象实例在Java堆上,所以必然不可能是同一个引用,结果将返回false。
而JDK 7(以及部分其他虚拟机,例如JRockit)的intern()方法实现就不需要再拷贝字符串的实例到永久代了,既然字符串常量池已经移到Java堆中,那只需要在常量池里记录一下首次出现的实例引用即可,因此intern()返回的引用和由StringBuilder创建的那个字符串实例就是同一个。而对str2比较返回false,这是因为“java”这个字符串在执行StringBuilder.toString()之前就已经出现过了,字符串常量池中已经有它的引用,不符合intern()方法要求“首次遇到"”的原则,“计算机软件"这个字符串则是首次出现的,因此结果返回true。
sun.misc.Version类会在JDK类库的初始化过程中被加载并初始化,而在初始化时它需要对静态常量字段根据指定的常量值(ConstantValue〉做默认初始化,此时被sun.misc.Version.launcher静态常量字段所引用的"java"字符串字面量就被intern到HotSpot VM的字符串常量池——StringTable里了。
sun.misc.Version类会在JDK类库的初始化过程中被加载并初始化,而在初始化时它需要对静态常量字段根据指定的常量值(ConstantValue〉做默认初始化,此时被sun.misc.Version.launcher静态常量字段所引用的"java"字符串字面量就被intern到HotSpot VM的字符串常量池——StringTable里了。
考查点
- intern()方法,判断true/false?
- 《深入理解java虚拟机》书原题是否读过经典JVM书籍
03_闲聊力扣算法第一题
字节跳动两数求和
然后考了2个算法:
1给定一个数m,求大干该数的最小2的n次幂、返同n 这个我一分钟内就完成了。
2.leetCode的第i题
给定一个整数数组nums和一个目标值 target,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。示例:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
快手算法:
给定一个链表,.删除相邻相同的数据项。比如2 -〉2-〉3 返回 2-〉3。这个算法也没运行出来,但是之前题目打的比较好,一面过了。
给定一个链表,"其中元素只有0和1,删除0的节点,并返回链表。≤(后来面试官说这是AQS的一部分源码……)
题目说明
https://leetcode.cn/problems/two-sum/
面试题
package javase;
import java.util.HashMap;
/**
* 给定一个整数数组nums和一个目标值 target,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标。
* 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。示例:
* 输入:nums = [2,7,11,15], target = 9
* 输出:[0,1]
* 解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
*/
public class TwoSumDemo2 {
public int[] twoSum(int[] nums, int target) {
return null;
}
public static void main(String[] args) {
int[] nums=new int[]{2,7,11,15};
int target=9;
}
}
解法
- 暴力
04_TwoSum暴力解法
package javase;
import java.util.Arrays;
import java.util.HashMap;
/**
* 给定一个整数数组nums和一个目标值 target,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标。
* 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。示例:
* 输入:nums = [2,7,11,15], target = 9
* 输出:[0,1]
* 解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
*/
public class TwoSumDemo2 {
//遍历-->暴力破解
/**
* 通过双重循环遍历数组中所有元素的两两组合,
* 当出现符合的和的返回两个元素的下标
* @param nums
* @param target
* @return
*/
public static int[] twoSum1(int[] nums, int target) {
for (int i = 0; i < nums.length; i++) {
for (int j = i+1; j <nums.length-1 ; j++) {
if (target-nums[i]==nums[j]){
return new int[]{i,j};
}
}
}
return null;
}
public static void main(String[] args) {
int[] nums=new int[]{2,7,11,15};
int target=9;
System.out.println(Arrays.toString(twoSum1(nums, target)));
}
}
还有没有更优的解法
05_TwoSum优化解法
- 哈希
package javase;
import java.util.Arrays;
import java.util.HashMap;
/**
* 给定一个整数数组nums和一个目标值 target,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标。
* 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。示例:
* 输入:nums = [2,7,11,15], target = 9
* 输出:[0,1]
* 解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
*/
public class TwoSumDemo2 {
//遍历-->暴力破解
/**
* 通过双重循环遍历数组中所有元素的两两组合,
* 当出现符合的和的返回两个元素的下标
* @param nums
* @param target
* @return
*/
public static int[] twoSum1(int[] nums, int target) {
for (int i = 0; i < nums.length; i++) {
for (int j = i+1; j <nums.length-1 ; j++) {
if (target-nums[i]==nums[j]){
return new int[]{i,j};
}
}
}
return null;
}
public static int[] twoSum2(int[] nums, int target) {
HashMap<Integer,Integer> map=new HashMap();
for (int i = 0; i < nums.length; i++) {
if (map.containsKey(target-nums[i])){
return new int[]{map.get(target-nums[i]),i};
}
map.put(nums[i],i);
}
return null;
}
public static void main(String[] args) {
int[] nums=new int[]{2,7,11,15};
int target=9;
System.out.println(Arrays.toString(twoSum1(nums, target)));
System.out.println(Arrays.toString(twoSum2(nums, target)));
}
}
考察点
你都想来大厂了,算法居然从来没有刷过?呵呵机会偏爱有准备有实力的头脑,不是白说的…
字节跳动手写LRU算法
见Redis最后(请坚持到最后一章)
最后2023-2-1 14:56:19
这篇博客能写好的原因是:站在巨人的肩膀上
这篇博客要写好的目的是:做别人的肩膀
开源:为爱发电
学习:为我而行