1,Java中深拷贝与浅拷贝的区别?
- 浅拷贝:对基础数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝。
- 深拷贝:对基础数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。
2,Java GC原理?
- Java的垃圾回收(GC)是Java虚拟机JVM提供的一种自动管理内存的机制。它可以自动识别和回收应用程序不再使用的对象,从而防止内存泄露和溢出。
- Java GC的基本原理是:
- 1,标记:确定哪些对象正在使用中,哪些对象不再使用
- 2,清除:回收不再使用的对象所占用的内存
- 3,整理:整理回收后的空间,使得分配新对象时不再需要额外的内存空间分配工作
- Java的垃圾收集器可以分为几类:
- 1,串行垃圾收集器
- 2,并行垃圾收集器
- 3,CMS垃圾收集器
- 4,G1垃圾收集器
3,Java中有哪些锁?
- 分为乐观锁,悲观锁,不是真实存在的锁,而是一种设计思想
- 悲观锁:是一种悲观思想,它总是认为最坏的情况可能会出现,它认为数据很可能会被其他人所修改,所以悲观锁在持有数据的时候总会把资源或者数据锁住,这样其他线程想请求这个资源的时候就会阻塞,直到等到悲观锁把资源释放为止。
- 乐观锁:与悲观锁的思想相反,它总认为资源和数据不会被别人所修改,所以读取不会上锁。
4,ArrayList是线程安全的吗?
- 不是
5,知道CAS,原子性吗?
- CAS主要作用是不使用加锁就可以实现线程安全
- CAS算法又称为比较交换算法,是一种实现并发算法时常用到的技术
- CAS具体包含三个参数:当前内存值V,旧的预期值A,即将更新的值B,当且仅当预期值A和内存值V相同时,将内存值修改为B并返回true,否则什么都不做,并返回false。
6,Java对象的生命周期?
- 是指一个class文件加载到注销的整个过程,包括加载,连接,初始化,使用,卸载五个阶段。而对象作为类的一个实例,其生命周期主要受应用阶段,不可视阶段,不可达阶段,可收集阶段,终结阶段和释放阶段的影响。
7,Java有哪些数据结构?
- 数组Array,链表LinkedList,堆Heap,栈Stark,哈希Hash,树Tree,队列Queue,图Graph
8,Java有哪些容器?
- Java中的容器主要分为两大类:Collection和Map。Collection接口及子接口包括List,Set和Queue等,而Map接口则提供了键值对的存储方式。
- Collection接口及其子接口
- 1,List:有序的集合,允许存储重复的元素。常见实现类有:
- ArrayList:基于数组实现的动态数组,提供快速的随机访问和高效的增删操作
- LinkedList:基于双向链表实现的列表,具有高效的插入和删除操作,但随机访问效率较低
- Vector:与ArrayList类似,但它是线程安全的,适用于多线程环境,性能通常比ArrayList差
- Stack:栈是后进先出(LIFO)的数据结构,继承自Vector,也是线程安全的
- 2,Set:不允许存储重复元素的集合,元素是无序的,常见实现类有:
- HashSet:基于哈希表实现的集合,提供快速的查找和插入操作
- LinkedHashSet:具有HashSet的所有特性,同时维护了一个双向链表来记录元素的插入顺序,因此是有序的
- TreeSet:基于红黑树实现的集合,不允许重复元素,且元素是有序的
- 3,Queue:队列,用于存储一组元素,并支持在两端插入和删除元素。常见实现类:
- PriorityQueue:优先队列,元素按优先级排序
- ArrayDeque:双端队列,支持在两端插入和删除元素,基于数组实现,没有容量限制
- Map接口及其实现类
- 1,HashMap:基于哈希表实现的映射,允许使用null健和null值,不保证映射的顺序
- 2,LinkedHashMap:具有HashMap的所有特性,同时维护了一个双向链表来记录键值对的插入顺序,因此是有序的
- 3,TreeMap:基于红黑树实现的映射,键值对是有序的,可以根据元素的自然顺序或指定的排序规则进行排序
9,String,StringBuilder,StringBuffer的区别?
- String不可变字符串
- StringBuilder可变字符串,线程不安全,效率高,推荐使用
- StringBuffer可变字符串,线程安全,效率低,不推荐使用
10,什么是单例模式?
- 一种常见的设计模式,其核心思想是确保某个类只有一个实例,并提供一个全局访问点来获取这个实例
- 优点:由于只有一个实例,可以减少系统资源的消耗;提供一个全局访问点,方便管理;线程安全;
- 缺点:单例类的职责过重,不利于系统的扩展和维护;测试时难以模仿多个实例的环境;滥用单例模式,可能会内存泄露;
11,介绍一下饿汉式,懒汉式,双重锁?
- 饿汉式:简单,线程安全;缺点是实例对象是static的,在声明的时候就实例化了,浪费资源。
- 懒汉式:用到的时候才去实例化,在一定程度上节约了资源;缺点是getInstance方法是用synchronized修饰的,该方法是同步的,为了保证线程安全,但是导致每次调用该方法的时候都会被同步,这样会消耗不必要的资源。
- 双重锁:第一次getInstance方法时才去实例化,资源利用率高,效率高:缺点是偶然会失败。
12,手写双重锁单例
public class Singleton{
private volatile static Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if(null == instance){
synchronized(Singleton.class){
if(null == instance){
instance = new Singleton();
}
}
}
return instance;
}
}
13,简述一下HashMap原理?
- HashMap采用的是数组+链表的数据结构,数组是HashMap的主体,链表主要是为了解决哈希冲突而存在的。
- 如果定位的数组不含链表,那么执行查找,添加等操作很快,仅需要一次寻址即可
- 如果定位到的数组包含链表,对于添加操作时间复杂度为n,首先遍历链表,存在即覆盖,否则新增;对于查询操作来讲,仍需遍历链表,然后通过key对象的equals方法逐一比对查找,所以性能考虑,链表出现越少,性能会越好
- JDK1.8是链表+红黑树,增加一个阈值进行判断是否将链表转红黑树,HashEntry修改为Node,目的是解决Hash冲突造成的链表越来越长,查询慢的问题
14,HashMap调用Put的执行流程?
- 判断当前数组是否要初始化
- 如果key为空,则put一个空值进去
- 根据key计算出hashcode,根据hashcode定位出在桶内的位置
- 如果桶是链表,则需要遍历判断hashcode,判断key和原来的key是否相等,相等则进行覆盖,返回原来的值;
- 如果桶是空的,说明当前位置没有数据存入,新增一个Entry对象写入当前位置,当调用addEntry写入Entry时需要判断是否需要扩容,如果需要进行两倍扩容,并将当前的key重新hash并定位
- 而在createEntry中会将当前位置的桶传入到新建的桶中,如果当前桶有值就会在位置形成链表
15,HashMap调用Get的执行流程?
- 根据key计算出hashcode,并定位到桶内的位置
- 判断是不是链表,如果是则需要根据key遍历直到key及hashcode相等时就返回值,如果不是就根据key,key的hashcode是否相等来返回值
- 如果啥也没取到就返回null
16,多线程并发,如何保证线程安全?
- 1,使用锁机制:锁机制是一种用于控制多个线程对共享资源进行访问的机制,在Java中提供了两种锁机制,synchronized关键字和Lock接口。synchronized关键字是 Java中最基本的锁机制,它可以用来修饰方法或代码块,以实现对共享资源的互斥访问;而Lock接口是Java5中新增的一种锁机制,它提供了比synchronized更强大,更灵活的锁定机制,例如可重入锁,读写锁等
- 2,使用线程安全的容器:如HashTable,Vector,ConcurrentHashMap
- 3,使用本地变量:线程本地变量是一种特殊的变量,它只能被同一个线程访问。在Java中,线程本地变量可以通过ThreadLocal类来实现,每个ThreadLoacl对象都可以存储一个线程本地变量,而且每个线程都有自己的一份本地变量副本,因此不同的线程之间互不干扰
17,Synchronized修修方法和修饰代码块有什么区别?
- 主要是锁不同
- 修饰方法时:对于静态方法,是把class作为锁,对于非静态方法,是把this对象当成锁
- 修饰代码块时:是把任意对象作为锁,如果锁对象为空,会抛出空指针异常,但是修饰方法不会
- 在锁的作用区域上:修饰方法时是整个方法体,而修饰代码块时只是对应的代码块,后者更加灵活和细粒度
- 可以把修饰方法看作是修饰代码块的一种特殊形式,一种快捷方式
18,如何判断链表有环?
- 1,快慢指针法:这种方法设置两个指针,一个慢指针和一个快指针。慢指针从头节点开始移动,每次移动一个节点;快指针从另一个节点开始,移动两步后与慢指针相遇。如果快指针和慢指针相遇在同一个节点,则链表有环。这种方法的时间复杂度为O(n),其中n是链表的节点数
- 2,穷举法:这种方法遍历链表的每个节点,同时使用另一个指针来检测是否已经被访问过。如果检测到相同节点,说明链表有环。这种方法的时间复杂度为O(n^2),其中n是链表的节点数
- 3,标记法:这种方法在链表的每一个节点中增加一个标记,表示该节点是否已经被访问过。如果链表有环,某些节点会被访问两次,从而产生冲突。这种方法的时间复杂度为O(n),其中n是链表的节点数
- 4,利用哈希表法:这种方法首先将一个链表的节点按照地址进行排序,并建立哈希表。然后遍历另一个链表,检查每个节点的地址是否已经在哈希表中存在。如果存在,说明两个链表有共同的节点,从而证明链表有环。这种方法的时间复杂度为O(m+n),其中m和n分别是两个链表的节点数。
19,手写冒泡排序
public static void bubbleSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
// 交换 arr[j+1] 和 arr[j]
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
20,手写插入排序
public static void insertionSort(int[] arr) {
for (int i = 1; i < arr.length; i++) {
int key = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}
21,线程的创建方式?
- 1,继承Thread类,重写run方法
- 2,实现Runable接口,重写run方法
- 3,实现Callable接口,重写call方法
- 4,使用线程池
22,线程间通信的方式?
- 1,Handler
- 2,runOnUiThread
- 3,View.post(Runnable run)
- 4,AsyncTask
23,多进程的通信方式?
- 1,AIDL:功能强大,支持进程间一对多的实时并发通信,并可实现RPC(远程过程调用)
- 2,Messenger:支持一对多的串行实时通信,AIDL的简化版本
- 3,Bundle:四大组件的进程通信方式,只能传输Bundle支持的数据类型
- 4,ContenProvider:强大的数据源访问支持,主要支持CRUD操作,一对多的进程间数据共享,例如我们的应用访问系统的通讯录数据
- 5,BroadcastReceiver:广播,但只能单向通信,接收者只能被动的接收消息
- 6,Socket:通过网络传输数据
24,常用的Git指令
- git branch 查看本地所有分支
- git status 查看当前状态
- git commit 提交
- git branch -a 查看所有的分支
- git branch -r 查看本地所有分支
- git commit -am "init" 提交并且加注释
- git remote add origin git@192.168.1.119:ndshow
- git push origin master 将文件给推到服务器上
- git remote show origin 显示远程库origin里的资源
- git push origin master:develop
- git push origin master:hb-dev 将本地库与服务器上的库进行关联
- git checkout --track origin/dev 切换到远程dev分支
- git branch -D master develop 删除本地库develop
- git checkout -b dev 建立一个新的本地分支dev
- git merge origin/dev 将分支dev与当前分支进行合并
- git checkout dev 切换到本地dev分支
- git remote show 查看远程库
- git add .
- git rm 文件名(包括路径) 从git中删除指定文件
- git clone git://github.com/xxxxx.git 从服务器上将代码给拉下来
- git config --list 看所有用户
- git ls-files 看已经被提交的
- git rm [file name] 删除一个文件
- git commit -a 提交当前repos的所有的改变
- git add [file name] 添加一个文件到git index
- git commit -v 当你用-v参数的时候可以看commit的差异
- git commit -m "This is the message describing the commit" 添加commit信息
- git commit -a -a是代表add,把所有的change加到git index里然后再commit
- git commit -a -v 一般提交命令
- git log 看你commit的日志
- git diff 查看尚未暂存的更新
- git rm a.a 移除文件(从暂存区和工作区中删除)
- git rm --cached a.a 移除文件(只从暂存区中删除)
- git commit -m "remove" 移除文件(从Git中删除)
- git rm -f a.a 强行移除修改后文件(从暂存区和工作区中删除)
- git diff --cached 或 $ git diff --staged 查看尚未提交的更新
- git stash push 将文件给push到一个临时空间中
- git stash pop 将文件从临时空间pop下来
- git remote add origin git@github.com:username/Hello-World.git
- git push origin master 将本地项目给提交到服务器中
- git pull 本地与服务器端同步
- git push (远程仓库名) (分支名) 将本地分支推送到服务器上去。
- git push origin serverfix:awesomebranch
- git fetch 相当于是从远程获取最新版本到本地,不会自动merge
- git commit -a -m "log_message" (-a是提交所有改动,-m是加入log信息) 本地修改同步至服务器端 :
- git branch branch_0.1 master 从主分支master创建branch_0.1分支
- git branch -m branch_0.1 branch_1.0 将branch_0.1重命名为branch_1.0
- git checkout branch_1.0/master 切换到branch_1.0/master分支
25,常见的Linux指令?
- pwd:显示用户当前所在目录
- ls:列出文件
- cd:改变工作目录
- find:查找
- kill:删除执行中的程序或工作
- ps:正在运行的进程
- date:系统日期
- echo :打印
- ping
- mkdir:创建空目录
- rmdir:删除非空目录
- rm:删除
- mv:移动
- cp:复制
26,事件分发机制?
- 当一个事件发生,首先会将点击事件传递给Activity中,执行dispatchTouchEvent进行事件分发
- 经过Window,DecorView依次传递后,页面上的ViewGroup会接收到该事件
- ViewGroup如果消费了该事件,则分发结束;未消费则继续调用Activity的onTouchEvent方法处理事件。
- ViewGroup的事件分发机制从dispatchTouchEvent开始,接着调用onInterceptTouchEvent方法判断是否需要拦截事件;
- 如果需要拦截,则表示当前ViewGroup希望处理该事件或者不希望子View处理该事件,此时将直接调用onTouchEvent方法处理事件;
- onTouchEvent方法如果消费了该事件,则分发结束;未消费则调用Activity的onTouchEvent方法处理事件;
- 如果不需要拦截,则会遍历寻找被点击的子View,将该事件传递给子View的dispatchTouchEvent方法;
- 如果未找到子View,事件将会继续传递给ViewGroup的onTouchEvent方法,与事件被拦截的效果一致;
- View通过dispatchTouchEvent方法接收到从ViewGroup传递过来的事件后,直接调用onTouchEvnet方法处理事件;
- 如果消费了该事件,则分发结束;未消费则调用ViewGroup的onTouchEvent方法处理事件。
借用一张流程图表示整个过程
27,ViewGroup的事件分发?
- 事件传递顺序:Activity -> Window -> View
- 如果顶级的ViewGroup拦截事件即onInterceptTouchEvnet返回true的话,则事件会交给ViewGroup处理,
- 如果ViewGroup的onTouchListener被设置的话,则onTouch将会被调用,否则的话onTouchEvent将会被调用,也就是说:两者都设置的话,onTouch将会屏蔽掉onTouchEvent,在onTouchEvnet中,如果设置了onClickListener的话,那么onClick将会被调用;
- 如果顶级ViewGroup不拦截的话,那么事件将会被传递给它所在的点击事件的子View,这时候子View的disptachTouchEvent将会被调用。
28,View的事件分发?
- dispatchTouchEvent -> onTouch(setOnTouchListener) -> onTouchEvent -> onClick
- onTouch和onTouchEvent的区别:两者都是在dispatchTouchEvent中调用的,onTouch优先于onTouchEvent,如果onTouch返回true,那么onTouchEvent则不执行,及onClick也不执行。
29,LeakCanary检测原理?
- 通过监测对象的引用关系和生命周期,以及检查对象的引用链,来检测应用中的内存泄露问题,并提供详细的报告帮助开发者定位和解决这些问题
30,LeakCanary实现原理?
- LeakCanary是一个用于检测内存泄漏的开源库
- 1,监测对象的引用关系:会监测应用中所有的对象引用关系,包括Activity,Fragment,View等。它会跟踪对象的创建和销毁过程,以及对象之间的关联关系
- 2,监测对象的生命周期:会跟踪应用中所有对象的生命周期,并记录它们的创建和销毁过程。当一个对象被创建后,LeakCanary会标记它为“弱引用”,并在它销毁后将其标记为“无引用”
- 3,监测对象的引用链:当一个对象被标记为“无引用”时,LeakCanary会检查该对象是否仍然被其他对象引用。如果存在引用链,即一系列对象相互引用导致无法被垃圾回收器回收。 LeakCanary会认为发生了内存泄漏
- 4,检测到内存泄漏时处理:当LeakCanary检测到内存泄漏时,它会生成一个内存泄漏报告,并通过通知栏或日志输出的方式提示开发者。报告中包含了引起内存泄漏的对象,引用链以及相关的堆栈信息,帮助开发者定位和解决内存泄漏问题
31,Glide对Bitmap是怎么优化的?
- 根据目标控件的尺寸,对图片进行适当的下采样,剪裁,和变换,以减少内存占用,并确保加载过程中尽快完成。
- Glide中Bitmap的自动适配ImageView,Glide默认支持Bitmap内存复用,Bitmap解码。
32,Glide的缓存机制?
- 1,活动缓存:优先从活动缓存中查找资源,如果找到直接返回资源,当活动缓存中的资源释放时,将资源放入内存缓存,使用Map缓存资源。
- 2,内存缓存:当活动缓存中找不到可用资源,从内存缓存中查找,同时将内存缓存中的资源放入活动缓存,并从内存缓存中移除资源。内存缓存使用的是LruCache算法,这种算法会根据图片的使用频率来决定哪些图片应该被保留在缓存中,如果内存缓存中没有找到目标图片的缓存,则会继续寻找下一层级的缓存。
- 3,磁盘缓存:当内存缓存中也找不到可用资源,则从磁盘缓存中查找,同时将资源放入活动缓存。同样使用LruCache算法,不同的是,它会考虑文件的修改时间等因素来决定哪些文件应该被保留在磁盘缓存中。如果没有找到或者磁盘已满,继续尝试从网络加载。
- 4,网络缓存:用于在内存缓存和磁盘缓存都无法满足需要时,从网络上下载图片。网络缓存依赖OkHttp库,当网络请求成功完成时,新的图片会被添加到磁盘缓存和内存缓存中,供后续使用。
33,OkHttp基本实现原理?
- 主要是通过5个拦截器和3个双端队列(2个异步队列,1个同步队列)进行工作。内部实现通过一个责任链模式完成,将网络请求的各个阶段封装到各个链条中,实现了各层的解耦。
34,OkHttp的请求流程?
- 1,先创建OkHttpClient实例
- 2,构造Request实例,传入url等参数
- 3,通过前两步中的实例对象构造Call对象
- 4,发送请求,异步用enqueue,同步用execute
- 5,内部通过Dispatcher分发任务
- 6,五大默认拦截器完成整个请求过程
- 7,返回结果
35,OkHttp中Dispatcher的分发流程?
- 1,调用execute或enqueue加入任务
- 2,Dispatcher根据条件判断将当前任务加入队列runningAsyncCall(执行队列)还是readyAsyncCall(等待队列)
- 3,执行队列调用线程池执行任务
- 4,执行队列执行任务完成,根据条件获取等待队列中任务执行
36,OkHttp中的设计模式?
- 构建者模式:OkHttpClient,Request等各种对象的创建
- 工厂模式:在Call接口中,有一个内部工厂Factory接口
- 单例模式:Platform类
- 策略模式:在CacheInterceptor中,在响应数据中选择中使用了策略模式,选择缓存数据还是选择网络访问
- 责任链模式:拦截器的链式调用
- 享元模式:享元模式核心即池中复用,OkHttp复用TCP连接时用到的连接池,同时在异步请求中也用到了线程池
37,Retrofit功能特点?
- 将Http请求对象化,函数化,让接口的函数代表具体请求
- 利用注解的方式标记参数,将Http的请求方法,请求头,请求参数,请求体等都用注解的方式标记,使用起来非常方便
- 支持Multipart,以及文件上传
- 直接将Http的Response转换成对象,用户根据Response的具体内容,更换转换器,或者自己新建转换器
- 默认使用OkHttp开源库请求后台,也可以自定义具体请求方式,方便扩展
- 自带提供了异步处理Http请求的方式
38,Retrofit中的设计模式?
- 建造者模式,观察者模式,适配器模式,外观模式,动态代理,工厂模式,抽象工厂模式,策略模式,装饰模式
39,Retrofit.create做了哪些工作?
- 使用Proxy.newProxyInstance方法来创建Service实例。