介绍:
20道面试题=5 java + 3 Android + 1(计网+数据结构+操作系统选择式)+ 1 算法题
主要以我自己的回答方式总结,小伙伴们欢迎指教奥~
Java部分
1.你了解Java的类加载机制吗?
Java 的类加载过程可以分为 5 个阶段:载入、验证、准备、解析和初始化。这 5 个阶段一般是顺序发生的,但在动态绑定的情况下,解析阶段发生在初始化阶段之后。
1.首先是loading(载入),jvm在该阶段将字节码从不同数据源转化为二进制加载到内存中。
2.然后第二步是Verification(验证),jvm会在该阶段对二进制流进行验证,只有符合jvm规范的的才能被jvm正确执行,该阶段是保证 JVM 安全的重要屏障,
下面是一些主要的检查:
- 确保二进制字节流格式符合预期(比如说是否以 cafe bene 开头)。
- 是否所有方法都遵守访问控制关键字的限定。
- 方法调用的参数个数和类型是否正确。
- 确保变量在使用之前被正确初始化了。
- 检查变量是否被赋予恰当类型的值。
3.Preparation(准备) JVM 会在该阶段对类变量(也称为静态变量,static 关键字修饰的)分配内存并初始化(对应数据类型的默认初始值,如 0、0L、null、false 等)。
也就是说,假如有这样一段代码:
public String chenmo = "沉默";
public static String wanger = "王二";
public static final String cmower = "沉默王二";
chenmo 不会被分配内存,而 wanger 会;但 wanger 的初始值不是“王二”而是 null。
需要注意的是,static final 修饰的变量被称作为常量,和类变量不同。常量一旦赋值就不会改变了,所以 cmower 在准备阶段的值为“沉默王二”而不是 null。
4.Resolution(解析)
该阶段将常量池中的符号引用转化为直接引用。
符号引用以一组符号(任何形式的字面量,只要在使用时能够无歧义的定位到目标即可)来描述所引用的目标。
在编译时,Java 类并不知道所引用的类的实际地址,因此只能使用符号引用来代替。比如 com.Wanger 类引用了 com.Chenmo 类,编译时 Wanger 类并不知道 Chenmo 类的实际内存地址,因此只能使用符号 com.Chenmo。
直接引用通过对符号引用进行解析,找到引用的实际内存地址。
5.Initialization(初始化)
该阶段是类加载过程的最后一步。在准备阶段,类变量已经被赋过默认初始值,而在初始化阶段,类变量将被赋值为代码期望赋的值。换句话说,初始化阶段是执行类构造器方法的过程。
举个栗子
String cmower = new String("沉默王二");
上面这段代码使用了 new 关键字来实例化一个字符串对象,那么这时候,就会调用 String 类的构造方法对 cmower 进行实例化。
https://blog.csdn.net/qing_gee/article/details/95324392
2.Stringbuffer和Stringbuild区别
区别一:线程安全
StringBuffer:线程安全
StringBuilder:线程不安全。
因为 StringBuffer 的所有公开方法都是 synchronized 修饰的,而 StringBuilder 并没有 synchronized 修饰。
区别二:缓冲区
StringBuffer 每次获取 toString 都会直接使用缓存区的 toStringCache 值来构造一个字符串。
StringBuilder 则每次都需要复制一次字符数组,再构造一个字符串。
所以, StringBuffer 对缓存区优化,不过 StringBuffer 的这个toString 方法仍然是同步的。
区别三:性能
既然 StringBuffer 是线程安全的,它的所有公开方法都是同步的,
StringBuilder 是没有对方法加锁同步的,所以毫无疑问,StringBuilder 的性能要远大于 StringBuffer。
总结:StringBuffer 适用于用在多线程操作同一个 StringBuffer 的场景,如果是单线程场合 StringBuilder 更适合。
3.死锁是什么,怎么避免死锁?
线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行。当线程进入对象的synchronized代码块时,便占有了资源,直到它退出该代码块或者调用wait方法,才释放资源,在此期间,其他线程将不能进入该代码块。当线程互相持有对方所需要的资源时,会互相等待对方释放资源,如果线程都不主动释放所占有的资源,将产生死锁
当然死锁的产生是必须要满足一些特定条件的:
1.互斥条件:进程对于所分配到的资源具有排它性,即一个资源只能被一个进程占用,直到被该进程释放
2.请求和保持条件:一个进程因请求被占用资源而发生阻塞时,对已获得的资源保持不放。
3.不剥夺条件:任何一个资源在没被该进程释放之前,任何其他进程都无法对他剥夺占用
4.循环等待条件:当发生死锁时,所等待的进程必定会形成一个环路(类似于死循环),造成永久阻塞。
如何避免死锁
在有些情况下死锁是可以避免的。三种用于避免死锁的技术:
1.加锁顺序(线程按照一定的顺序加锁)
2.加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
3.死锁检测
怎么避免死锁?
4.Java有几种锁,synchronized与Lock有什么区别?
Synchronized放在静态方法和非静态方法上的锁对象分别是什么?
5.什么是Threadlocal?
ThreadLocal是一个解决线程并发问题的一个类,用于创建线程的本地变量,我们知道一个对象的所有线程会共享它的全局变量,所以这些变量不是线程安全的,我们可以使用同步技术。但是当我们不想使用同步的时候,我们可以选择ThreadLocal变量。
每个线程都会拥有他们自己的Thread变量,他们可以使用get/set方法去获取他们的默认值或者在线程内部改变他们的值。ThreadLocal实例通常是希望他们同线程状态关联起来是private static属性。
但是一个ThreadLocal只能保存一个,通过键值对(key-value)保存在ThreadLoalMap里面,并且各个线程的数据互不干扰。
ThreadLocal全攻略:使用实战,源码分析,内存泄露分析
Android部分
6.给我说说AsyncTask的原理
AsyncTask抽象类指定了三个泛型参数类型 , 如下:
} ```Params:开始异步任务执行时传入的参数类型,即doInBackground()方法中的参数类型; Progress:异步任务执行过程中,返回下载进度值的类型,即在 doInBackground中调用publishProgress()时传入的参数类型; Result:异步任务执行完成后,返回的结果类型,即doInBackground()方法的返回值类型; AsyncTask的回调方法 前面我们说过, AsyncTask对线程和handler进行了封装,那它的封装性体现在哪里呢?其实,AsyncTask的几个回调方法正是这种封装性的体现,使得我们感觉在子线程进行UI更新一样。一个基本的AsyncTask有如下几个回调方法: (1)onPreExecute(): 在执行后台下载操作之前调用, 运行在主线程中 ; (2)doInBackground():核心方法,执行后台下载操作的方法,必须实现的一个方法, 运行在子线程中; (3)onPostExecute():后台下载操作完成后调用,运行在主线程中;
AsyncTask主要是对异步任务和handler的封装,在处理异步任务时,AsyncTask内部使用了两个线程池,一个线程池 sDefaultExecutor是用来处理用户提交(执行AsyncTask的execute时)过来的异步任务,这个线程池中有一个Runnable异步任务队列 ArrayDeque mTasks ,把提交过来的异步任务放到这个队列中;另一个线程池 THREAD_POOL_EXECUTOR, 用来真正执行异步任务的。在处理handler时,自定义了一个InternalHandler,在publicProgress()和doInBackground()运行完成后,会通过这个handler往UI线程发送Message。
AsyncTask在使用中的一个特殊情况
我们在使用AsyncTask的时候,一般会在onPreExecute()和onPostExecute()中进行UI的更新,比如等待图片的显示、进度条的显示…当我们一个Activity中正在使用AsyncTask进行文件的下载时,如果此时屏幕发生了旋转,Activity会进行re-onCreate,又会创建一个AsyncTask进行文件的下载,这个正是我们前面将取消任务的时候谈到的第二个现象,我们只需要在onPause()中进行取消cancel()即可。但是这样仅仅是解决了发生等待的情况,因为Activity再次进入了onCreate()方法,还是会进行文件的下载,为了解决这个问题,一种方案是通过判断onCreate(Bundle savedInstanceState)方法参数中的savedInstanceState== null?来判断是哪种情况 只有savedInstanceState==null时才去创建新的AsyncTask。
AsyncTask和Handler的比较
AsyncTask:
优点:AsyncTask是一个轻量级的异步任务处理类,轻量级体现在,使用方便、代码简洁上,而且整个异步任务的过程可以通过cancel()进行控制;
缺点:不适用于处理长时间的异步任务,一般这个异步任务的过程最好控制在几秒以内,如果是长时间的异步任务就需要考虑多线程的控制问题;当处理多个异步任务时,UI更新变得困难。
Handler:
优点:代码结构清晰,容易处理多个异步任务;
缺点:当有多个异步任务时,由于要配合Thread或Runnable,代码可能会稍显冗余。
总之,AsyncTask不失为一个非常好用的异步任务处理类,只要不是频繁对大量UI进行更新,可以考虑使用;而Handler在处理大量UI更新时可以考虑使用。
Android异步任务AsyncTask的使用与原理分析
7.Binder机制的实现思想
Binder是一种跨进程通信的机制,在linux中,内存被分为用户空间和内核空间,而进程属于一个用户空间,进程之间是隔离的,用户空间与内核空间也是隔离的,用户空间需要通过系统调用,才能访问到内核空间,而binder是运行在内核空间的驱动程序,进程间通过dev/binder这个文件来建立通信通道。
实现的原理是,数据发送方通过copy_from_user,将数据拷贝到内核空间,然后binder驱动会创建一块物理内存,然后让内核空间和接收方的用户空间同时映射到这快物理内存里面去,这样的话,发送方通过copy_from_user把数据拷贝到内核空间后,这数据就相当于直接到了接收方的用户空间去了。
8.Service的生命周期
service的启动方式有两种,两种方式的生命周期也不一样
第一种是startService
生命周期是 onCreat–>onStartCommart-运行–>onDestroy
第二种是Bindervice,
生命周期是 onCreat–>onBInd–>运行–>onUnbind–>onDestroy
计算机网络
9.TCP和UDP的区别
1、TCP与UDP区别总结:
1、 TCP面向连接 (如打电话要先拨号建立连接); UDP是无连接 的,即发送数据之前不需要建立连接
2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
Tcp通过校验和,重传控制,序号标识,滑动窗口、确认应答实现可靠传输。如丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。
3、UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。
4.每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
5、TCP对系统资源要求较多,UDP对系统资源要求较少。
算法
10.逆置一个单链表
public class Nizhi {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
ListNode listNode = new ListNode(scanner.nextInt());
listNode.next=new ListNode(scanner.nextInt());
ListNode m= reverseList(listNode);
for (int i=0;i<2;i++){
System.out.println(m.val);
m=m.next;
}
}
static class ListNode{
public int val;
private String data;
private ListNode next;
public ListNode(int i) {
this.val=i;
}
}
public static ListNode reverseList(ListNode head) {
if(head==null)return head;
int[] a=new int[5001];
int k=0;
while(head!=null){
a[k]=head.val;
head=head.next;
k++;
}
ListNode b=new ListNode(a[k-1]);
head=b;
for(k=k-2;k>=0;k--){
b.next=new ListNode(a[k]);
b=b.next;
}
return head;
}
}
静待花开
转载:https://blog.csdn.net/qq_46526828/article/details/115626575