远景一面:

1.一个对象从创建到销毁的过程讲讲?

用户创建了一个Student对象,运行时JVM首先会去方法区寻找该对象的类型信息,没有则使用类加载器classloader将Student.class字节码文件加载至内存中的方法区,并将Student类的类型信息存放至方法区。
接着JVM在堆中为新的Student实例分配内存空间,这个实例持有着指向方法区的Student类型信息的引用,引用指的是类型信息在方法区中的内存地址。
在此运行的JVM进程中,会首先起一个线程跑该用户程序,而创建线程的同时也创建了一个虚拟机栈,虚拟机栈用来跟踪线程运行中的一系列方法调用的过程,每调用一个方法就会创建并往栈中压入一个栈帧,栈帧用来存储方法的参数,局部变量和运算过程的临时数据。上面程序中的stu是对Student的引用,就存放于栈中,并持有指向堆中Student实例的内存地址。
JVM根据stu引用持有的堆中对象的内存地址,定位到堆中的Student实例,由于堆中实例持有指向方法区的Student类型信息的引用,从而获得add()方法的字节码信息,接着执行add()方法包含的指令。
将stu指向null
JVM GC
————————————————
版权声明:本文为CSDN博主「大哥惯过谁」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_41772761/article/details/104190440

2.java集合了解吗?讲讲?

3.线程安全是什么样的,怎么解决?

美团一面:

1.优惠券项目问用户下单后怎么知道自己下单成功了?

三个算法题一个都没做出来(有点慌)

  1. 交换链表中两个节点的位置(看我没做出来,降低难度下一题)
  2. 反转链表(这都没做出来,next绕昏了)
  3. 该道题考察list排序,Integer转Double,
  4. Interger和int之间可以自动拆箱和装箱

    1. Integer a=1;
    2. int b=a;
    3. Integer c=b;
    4. 自动拆箱和装箱
    5. 然后
    6. Double d=Double.parseDouble(String.valueOf(b));
    7. 就可以
    8. 或者
    9. double dd=b;
    10. Double d=dd;
    11. 我面试硬是没弄出来
  5. list排序使用Collections.sort(list),问底层用的什么排序算法,时间复杂度是多少;

1.交换两个节点

  1. /**
  2. * 本算法是美团面试出的第一个题:交换链表中的任意两个节点的位置
  3. * 我能想到的就是对于两个节点,都保存pre,cur,next指针
  4. * 比如m就对应pre1,cur1,next1,
  5. * 比如n就对应pre2,cur2,next2,
  6. * 然后当前cur.val不等于m或者n的时候就将pre赋值为cur,
  7. * 当cur.val等于m的时候就将pre1赋值为pre,next1赋值为cur.next,cur1赋值为cur
  8. * 当cur.val等于n的时候就将pre2赋值为pre,next2赋值为cur.next,cur2赋值为cur
  9. * 最后再对这六个结点进行重连即可
  10. * 注意如果m对应头结点,则pre1为null,所以要额外处理
  11. *
  12. * 我在leetcode上看到的方法是找到两个节点,然后将两个节点的值进行交换??还能这样?
  13. * @param head
  14. * @param m
  15. * @param n
  16. * @return
  17. */
  18. public static ListNode exchageNode(ListNode head,int m,int n){
  19. ListNode front=new ListNode(-1);
  20. front.next=head;
  21. ListNode cur=head;
  22. ListNode pre=null;
  23. ListNode pre1=null;
  24. ListNode pre2=null;
  25. ListNode cur1=null;
  26. ListNode cur2=null;
  27. ListNode next1=null;
  28. ListNode next2=null;
  29. while(cur!=null){
  30. ListNode temp=cur.next;
  31. if(cur.val==m){
  32. next1=cur.next;
  33. pre1=pre;
  34. cur1=cur;
  35. }
  36. if(cur.val==n){
  37. next2=cur.next;
  38. pre2=pre;
  39. cur2=cur;
  40. break;
  41. }
  42. pre=cur;
  43. cur=temp;
  44. }
  45. //pre1==null考虑当m为头结点时的情况,此时pre1不会被更新
  46. if(pre1==null){
  47. front.next=cur2;
  48. }else{
  49. pre1.next=cur2;
  50. }
  51. cur2.next=next1;
  52. pre2.next=cur1;
  53. cur1.next=next2;
  54. return front.next;
  55. }

2.反转链表

  1. /**
  2. * 本方法采用头插法进行链表的反转
  3. * 使用头插法的时候一定要注意不要在while之前就写front.next=head
  4. * 美团面试的时候就是因为写了这个导致我没做出来
  5. * @param head
  6. * @return
  7. */
  8. public static ListNode reverse1(ListNode head){
  9. ListNode front=new ListNode(-1);
  10. while(head!=null){
  11. ListNode nex=head.next;
  12. ListNode tmp=front.next;
  13. front.next=head;
  14. head.next=tmp;
  15. head=nex;
  16. }
  17. return front.next;
  18. }
  19. /**
  20. * 本方法使用两个指针来解决,pre表示已反转链表的最后一个结点,cur表示当前遍历到的结点
  21. * 举个例子:
  22. * 遍历到第一个结点时,pre为null,我们将第一个结点的next指向pre,然后将第一个结点赋值为pre,此时pre就表示第一个结点,其next指向为null
  23. * 当遍历到第二个结点的时候,我们将其的next指向变成pre,而由于当前的pre表示第一个结点,说明此时第二个结点的next指向变成了第一个结点,
  24. * 然后我们将第二个结点赋值为pre,此时pre就表示第二个结点,其next指向为第一个结点,之后以此类推。
  25. * @param head
  26. */
  27. public static ListNode reverse2(ListNode head){
  28. ListNode pre=null;
  29. ListNode cur=head;//为什么不直接用head?可用可不用!head在之后并没有被重新赋值,所以head始终都表示第一个结点
  30. while(cur!=null){
  31. ListNode nex=cur.next;
  32. cur.next=pre;
  33. pre=cur; //此时pre和cur指向同一块区域,注意pre原先指向的地址内容不会变化
  34. cur=nex;
  35. }
  36. return pre;
  37. }