远景一面:
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.优惠券项目问用户下单后怎么知道自己下单成功了?
三个算法题一个都没做出来(有点慌)
- 交换链表中两个节点的位置(看我没做出来,降低难度下一题)
- 反转链表(这都没做出来,next绕昏了)
- 该道题考察list排序,Integer转Double,
Interger和int之间可以自动拆箱和装箱
Integer a=1;int b=a;Integer c=b;自动拆箱和装箱然后Double d=Double.parseDouble(String.valueOf(b));就可以或者double dd=b;Double d=dd;我面试硬是没弄出来
list排序使用Collections.sort(list),问底层用的什么排序算法,时间复杂度是多少;
1.交换两个节点
/*** 本算法是美团面试出的第一个题:交换链表中的任意两个节点的位置* 我能想到的就是对于两个节点,都保存pre,cur,next指针* 比如m就对应pre1,cur1,next1,* 比如n就对应pre2,cur2,next2,* 然后当前cur.val不等于m或者n的时候就将pre赋值为cur,* 当cur.val等于m的时候就将pre1赋值为pre,next1赋值为cur.next,cur1赋值为cur* 当cur.val等于n的时候就将pre2赋值为pre,next2赋值为cur.next,cur2赋值为cur* 最后再对这六个结点进行重连即可* 注意如果m对应头结点,则pre1为null,所以要额外处理** 我在leetcode上看到的方法是找到两个节点,然后将两个节点的值进行交换??还能这样?* @param head* @param m* @param n* @return*/public static ListNode exchageNode(ListNode head,int m,int n){ListNode front=new ListNode(-1);front.next=head;ListNode cur=head;ListNode pre=null;ListNode pre1=null;ListNode pre2=null;ListNode cur1=null;ListNode cur2=null;ListNode next1=null;ListNode next2=null;while(cur!=null){ListNode temp=cur.next;if(cur.val==m){next1=cur.next;pre1=pre;cur1=cur;}if(cur.val==n){next2=cur.next;pre2=pre;cur2=cur;break;}pre=cur;cur=temp;}//pre1==null考虑当m为头结点时的情况,此时pre1不会被更新if(pre1==null){front.next=cur2;}else{pre1.next=cur2;}cur2.next=next1;pre2.next=cur1;cur1.next=next2;return front.next;}
2.反转链表
/*** 本方法采用头插法进行链表的反转* 使用头插法的时候一定要注意不要在while之前就写front.next=head* 美团面试的时候就是因为写了这个导致我没做出来* @param head* @return*/public static ListNode reverse1(ListNode head){ListNode front=new ListNode(-1);while(head!=null){ListNode nex=head.next;ListNode tmp=front.next;front.next=head;head.next=tmp;head=nex;}return front.next;}/*** 本方法使用两个指针来解决,pre表示已反转链表的最后一个结点,cur表示当前遍历到的结点* 举个例子:* 遍历到第一个结点时,pre为null,我们将第一个结点的next指向pre,然后将第一个结点赋值为pre,此时pre就表示第一个结点,其next指向为null* 当遍历到第二个结点的时候,我们将其的next指向变成pre,而由于当前的pre表示第一个结点,说明此时第二个结点的next指向变成了第一个结点,* 然后我们将第二个结点赋值为pre,此时pre就表示第二个结点,其next指向为第一个结点,之后以此类推。* @param head*/public static ListNode reverse2(ListNode head){ListNode pre=null;ListNode cur=head;//为什么不直接用head?可用可不用!head在之后并没有被重新赋值,所以head始终都表示第一个结点while(cur!=null){ListNode nex=cur.next;cur.next=pre;pre=cur; //此时pre和cur指向同一块区域,注意pre原先指向的地址内容不会变化cur=nex;}return pre;}
