写点什么

这些 Java 基础知识,诸佬们都还记得嘛 (学习,复习,面试都可)

作者:钟奕礼
  • 2022 年 9 月 28 日
    湖南
  • 本文字数:3503 字

    阅读完需:约 11 分钟

前言:本篇将记录几次面试中经常被问到的知识点以及对学习的知识点总结。


本篇文章记录的基础知识,适合在学 Java 的小白,也适合复习中,面试中的大佬朗朗。


如果文章有什么需要改进的地方还请大佬不吝赐教。


小编在此先感谢各位大佬啦~~


以下正文开始


文章目录双亲委派模型工作流程 StringTable 具有哪些特性常量池面试题什么情况下对象会进入老年代根据垃圾回收次数根据动态对象年龄判断老年代空间担保机制大对象会直接进入老年代 Java 中不同的引用类型零钱兑换算法题双亲委派模型工作流程如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此。因此所有的加载请求最终都应该传送到最顶层的启动类加载器中,只有当上一层类加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到这个类)时,下一层类加载器才会尝试自己去加载。


双亲委派模型的好处:


1.可以确保我们的程序安全。比如,我们自定义一个包名为 java.lang 的包,类名为 String 的类, 此时会报出"java.lang.String 中找不到类方法"的错误 ,这是因为在父类加载器中以及存在了 String 类,而父类加载器中的 String 类没有 main 方法,因此报错。这也是我们初学 java 时经常犯的一个小错误。


那么如何打破 JVM 双亲委派模型呢?


想要打破 JVM 双亲委派模型,需要我们自己自定义一个类加载器,然后重写其中的 loadClass 方法,使其不进行双亲委派流程就行了。


StringTable 具有哪些特性首先,在 JDK1.8 的版本中,StringTable 是位于 JVM 内存结构的堆中的。而堆中经常存储一些所以创建的对象,数组等。


那么 StringTable 具有哪些特性呢?


常量池中的字符串仅是符号,第一次用到时才变为对象


利用串池的机制,来避免重复创建字符串对象


字符串变量拼接的原理是 StringBuilder(1.8) 字符串常量拼接的原理是编译期优化


可以使用 intern 方法 ,主动将串池中还没有的字符串对象放入串池


在 jdk1.8 版本中会将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池,会把串池中的对象返回


在 jdk1.6 版本中会将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有会把此对象复制一份,放入串池,会把串池中的对象返回


常量池面试题常量池就是一张表,虚拟机指令根据这张表找到要执行的类名,方法名,参数类型,字面量等信息。


分享一道经典的关于常量池的面试题,后面有每一步的详细注释


public static void main(String[] args){


String s1 ="a";//加载到这一步会将"a"放入常量池中 String s2= "b";//加载到这一步会将"b"放入常量池中 String s3 ="a"+"b";// 在编译期间会进行优化对字面量运算得出结果,池中并没有"ab",因此将"ab"放入池中 String s4 = s1 + s2; // 这一步并不会直接将 s1 和 s2 相加,而是先加载 s1 和 s2 的值,此时拼接的原理是利用 StringBuilder,append()方法,newString("ab"),将此对象放入堆中 String s5 ="ab";//从字符串常量池中取对象,因此 s5 和 s3 是一样的 String s6 = s4.intern();//因为串池中有了字符串"ab",所以 s6 是和 s3,s5 一样的 System.out.println(s3==s4);// 结果为 false,s3 是串池中的,s4 是堆中的 System.out.println(s3== s5); // 结果为 trueSystem.out.println(s3 == s6); // 结果为 true


String x2 = new String("c")+ new String("d"); // 串池中没有 c 和 d,因此会在堆中创建对象,同时会把"c"和"d"丢到串池中,x2 也是通过 StringBuilder 的 append()方法拼接而成 ,new String("cd")x2.intern(); //常量池中没有"cd"对象,会将 x2 入池,同时 x2 也存在于堆中 String x1="cd";//直接从串池中取出"cd"System.out.print1n(x1 ==x2);// 结果为 true


什么情况下对象会进入老年代一:根据垃圾回收次数默认情况下,经历了 15 次垃圾回收,仍然没有被垃圾回收器回收的将会进入老年代,当然也可以通过指令设置次数:通过 JVM 参数“ -XX:MaxTenuringThreshold ”来设置次数;


二:根据动态对象年龄判断动态对象年龄判断: 虚拟机并不是要求对象的年龄必须达到了 MaxTenuringThreshold=15 才能晋升老年代 ;其实是 Survivor 区的对象年龄会从小到大进行累加,当累加到 X 年龄(某个年龄)时占用空间的总和大于 50%(可以使用-XX:TargetSurvivorRatio=?来设置保留多少空闲空间,默认值是 50),那么比 X 年龄大的对象都会晋升到老年代;


老年代空间担保机制 3.老年代空间担保机制:


(1)执行任何一次 MinorGC 之前,JVM 会先检查一下老年代可用内存空间,是否大于新生代所有对象的总大小,因为在极端情况下,可能新生代 Minor GC 之后,新生代所有对象都需要存活,那就会造成新生代所有对象全部要进入老年代;


(2)如果老年代的可用内存大于新生代所有对象总大小,此时就可以放心大胆的对新生代发起一次 Minor GC,因为 Minor GC 之后即使所有对象都存活,Survivor 区放不下了,也可以转移到老年代去;


(3)如果执行 Minor GC 之前,检测发现老年代的可用空间已经小于新生代的全部对象总大小,那么就会进行下一个判断,判断老年代的可用空间大小,是否大于之前每一次 Minor GC 后进入老年代的对象的平均大小,如果判断发现老年代的内存大小,大于之前每一次 Minor GC 后进入老年代的对象的平均大小,那么就是说可以冒险尝试一下 Minor GC,但是此时真的可能有风险,那就是 Minor GC 过后,剩余的存活对象的大小,大于 Survivor 空间的大小,也大于老年代可用空间的大小,老年代都放不下这些存活对象了,此时就会触发一次“Full GC”;所以老年代空间分配担保机制的目的?也是为了避免频繁进行 Full GC;


(4)如果 Full GC 之后,老年代还是没有足够的空间存放 Minor GC 过后的剩余存活对象,那么此时就会导致“OOM”内存溢出。


大对象会直接进入老年代大对象是指需要大量连续内存空间的 Java 对象,比如很长的字符串或者是很大的数组或者 List 集合,大对象在分配空间时,容易导致内存明明还有不少空间时就提前触发垃圾回收以获得足够的连续空间来存放它们,而当复制对象时,大对象又会引起高额的内存复制开销,为了避免新生代里出现那些大对象,然后屡次躲过 GC 而进行来回复制,此时 JVM 就直接把该大对象放入老年代,而不会经过新生代;


我们可以通过 JVM 参数“-XX:PretenureSizeThreshold”设置多大的对象直接进入老年代,该值为字节数,比如“1048576”字节就是 1MB,该参数表示如果创建一个大于这个大小的对象,比如一个超大的数组或者 List 集合,此时就直接把该大对象放入老年代,而不会经过新生代;-XX:PretenureSizeThreshold 参数只对 Serial 和 ParNew 两款新生代收集器有效,其他新生代垃圾收集器不支持该参数,如果必须使用此参数进行调优,可考虑 ParNew+CMS 的收集器组合。


Java 中不同的引用类型 Java 里有不同的引用类型,分别是强引用、软引用、弱引用和虚引用。


强引用:所有的 GC Roots 对象都不通过强引用引用该对象,该对象才能被垃圾回收,Object object =new Object();


软引用:SoftReference 内存充足时不回收,内存不足时则回收,可以配合引用队列来释放引用自身;


弱引用:WeakReference 不管内存是否充足,只要 GC 一运行就会回收该引用对象,可以配合引用队列来释放弱引用自身;


虚引用:PhantomReference 这个其实暂时忽略也行,因为很少用,它形同虚设,就像没有引用一样,其作用就是该引用对象被 GC 回收时候触发一个系统通知,或者触发进一步的处理;


零钱兑换算法题给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。


计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。


你可以认为每种硬币的数量是无限的。


示例 1:


输入:coins = [1, 2, 5], amount = 11


输出:3


解释:11 = 5 + 5 + 1


示例 2:


输入:coins = [2], amount = 3


输出:-1


示例 3:


输入:coins = [1], amount = 0


输出:0


class Solution {


public int coinChange(int[] coins, int amount) {
int max=amount+1; int [] dp=new int [max];//定义数组长度,从0开始需要+1 Arrays.fill(dp,max);//赋初值,让求的是最小值,将每一步用最大值填充 dp[0]=0;//下标为0的数值为0,因为下标为0没有数 for(int i=1;i<=amount;i++){
for(int j=0;j<coins.length;j++){
if(coins[j]<=i){
复制代码


//根据 coin 值与当前遍历 i 的大小判断,否则 dp[负数]不合题意 dp[i]=Math.min(dp[i],dp[i-coins[j]]+1);//转移方程,没多遍历一次,就会更新 dp[i]的值}}}return dp[amount]>amount ? -1 :dp[amount];//如果初始给定的值大于最终值,则返回-1,表示不成立}}


文章到这里就结束了,如果有什么疑问的地方请指出,诸佬们一起讨论


希望能和诸佬们一起努力,今后进入到心仪的公司


再次感谢各位小伙伴儿们的支持

用户头像

钟奕礼

关注

还未添加个人签名 2021.03.24 加入

还未添加个人简介

评论

发布
暂无评论
这些Java基础知识,诸佬们都还记得嘛(学习,复习,面试都可)_编程_钟奕礼_InfoQ写作社区