java相关:面试中关于Java你所需知道的的一切
一:架构方面
1、模块化组件化开发架构
一开始使用比较简单的架构 ,界面->逻辑层->工具包层,后面改为分业务模块->工具包,但是业务模块之间存在大量耦合。现在将业务模块再具体划分,比如购物流程模块,地址选择管理模块,消息模块。自己写的router,没有用注解,没有依赖注入,写了大量的映射和接口注册。借鉴阿里的Arouter[Alibaba-ARouter] 新版本解读与控制反转在移动端的应用
Arouter:自定义注解
采用arr方式解决重复依赖
采用新的网络请求框架Retrofit+OkHttp,新的图片及下载框架,okhttp,规范了接口定义以及数据加密等。
当然,我们还做了很多的新的尝试,RecycleView替换了ListView,状态栏Immersive Mode设计,组件化开发的尝试,App功能动态化配置,通用自定义控件设计以及国际化设配等。
ARouter的优势:
编译器处理注解产生映射文件,运行期加载映射文件实现路由
Bootstrapping、Extensibility以及Simple & Enough
编译期间:页面自动注册—注解&注解处理器
运行期间:动态加载—–分组管理,按需加载
全局拦截器
依赖注入
运行期动态修改路由
降级问题
2、hybrid架构
① NativeUI组件,header组件、消息类组件
② 通讯录、系统、设备信息读取接口
③ H5与Native的互相跳转,比如H5如何跳到一个Native页面,H5如何新开Webview做动画跳到另一个H5页面
4、数据请求,数据操作的接口,工具包之类
android中通信方式:
1、传统的JavascriptInterface,setJavaScriptEnabled呀小心xss攻击,4.2已修复
2、JSbridge,定义一套协议
3、代码重构的经验
首先讲述自己的工程架构演变,由普通依赖到模块化组件化,路由结构,通信原理,怎样解耦。然后问面试官对中小型公司自己这一套改进有没有什么建议,引用RXjava这类开源框架。提到自己对知识的渴望,然而在小公司人员不足和业务量不庞大的情况体验不到它的好处,特别想进大的平台加深自己对整个架构的优点的深入理解。
4、AOP OOP
面向切面(从左到右的关系,将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装到一个可重用模块,并将其名为“Aspect”)。Android中应用:网络请求,图片操作,数据解析,文件操作,Log,格式转换等共用型Util包。
面向对象:注重上下层,封装、继承和多态性等概念来建立一种对象层次结构,允许你定义从上到下的关系,但并不适合定义从左到右的关系
二:性能优化和内存优化具体经验
Runtime.getRuntime().maxMemory()最大可分配内存
Runtime.getRuntime().freeMemory()当前空闲内存
Runtime.getRuntime().totalMemory()当前占用内存
内存检测工具MemoMonitor,android device monitor
优化:
数据结构的优化(1、StringBuilder拼接;2、SparseArray(2个数组,int型的Key数组,value数组)、ArrayMap(hash值的Key数组,keyvalue成对的数组)(添加、删除、查找数据的时候都是先使用二分查找法得到相应的index)代替HashMap,HashMap初始就会创建容量为16的数组,而且每次都是以2倍的容量递增,SparseArray、ArrayMap;3、内存抖动;4、使用parcelable代替serializebal,serializebal会产生大量临时变量引起GC)
对象复用:列表布局里单Item的复用,避免在ondraw里进行对象的创建
避免内存泄露:内存泄漏会导致heap可用内存越来越少,这时当有一个稍大的内存占用,很可能会造成OOM,还有一点它会让GC频繁的触发造成内存抖动。造成内存泄漏的一些点:单例使用了context(使用applicationcontext),匿名内部类,handler,static变量,资源未关闭(io流,cursor使用了一定要及时关闭),asyntask,耗时操作可以考虑使用service
OOM优化:大部分是图片造成的。使用软引用弱引用;临死bitmap的及时回收;某些大内存分配时可以使用try/catch,即使失败了也不会崩溃;加载bitmap时注意(缩放比例、解码格式ARGB_4444、局部加载),LUR cache
三:用到的开源框架原理
okhttp原理:
首先使用建造者模式构建request,然后经过一系列拦截器(包括跟服务器桥接的响应BridgeInterapter,CacheInterapter,ConnectInterapter,Callserver),拦截器连成了一条链InterappetChain.底层用了okio连接,socket。异步请求做了一个线程池的封装。
多路复用机制(nextconnection从connection池里获取到的话,就复用,获取不到才create),重连机制(判断是否能onresponce,获取不到则recovery,循环)
支持HTTP2/SPDY黑科技
socket自动选择最好路线,并支持自动重连
拥有自动维护的socket连接池,减少握手次数
拥有队列线程池,轻松写并发
拥有Interceptors轻松处理请求与响应(比如透明GZIP压缩,LOGGING)
实现基于Headers的缓存策略
volley原理,
glide原理
glide .with(context):所有request请求是通过requestmanagerrechiver这个类把requestmanager绑定到activity或fragment类。创建了一个fragment把requestmanager传给fragmengt,与activity生命周期绑定
.load(url) : 创建DrawableTypeRequest,添加一系列条件初始化
.into里面将imageview与lifecycle生命周期关联了起来,里面的核心是一个引擎类Engine,里面有个Load,先从缓存中读取,Engine中包含LruCache缓存及一个当前正在使用的active资源Cache(弱引用),Cache优先级LruCache>activeCache,从LruCache取出使用了则会放到activeCache,缓存没有则通过线程池去获取图片,Engine在初始化时要传入两个ExecutorService,即会有两个线程池,一个用来从DiskCache获取resource,另一个用来从Source中获取(通常是下载)。先进入DiskCacheService中执行获取,如果没找到则进入SourceState,进到SourceService中执行下载。
Glide的Target:
负责图片加载的回调中
4.4以前是Bitmap复用必须长宽相等才可以复用
4.4及以后是Size>=所需就可以复用,只不过需要调用reconfigure来调整尺寸
Glide用AttributeStategy和SizeStrategy来实现两种策略
图片池在收到传来的Bitmap之后,通过长宽或者Size来从KeyPool中获取Key(对象复用到了极致,连Key都用到了Pool),然后再每个Key对应一个双向链表结构来存储。每个Key下可能有很多个待用Bitmap
取出后要减少图片池中记录的当前Size等,并对Bitmap进行eraseColor(Color.TRANSPAENT)操作确保可用
butterknife原理
四:Framework层常用模块原理
AMS和WMS原理
audioflinger原理,
整个音频系统的核心与难点,启到承上(为上层提供访问接口)启下(通过HAL来管理音频设备)的作用。
audiotrack原理,
播放声音,2种模式,AudioTrack中有MODE_STATIC和MODE_STREAM两种分类。STREAM的意思是由用户在应用程序通过write方式把数据一次一次得写到audiotrack中。这个和我们在socket中发送数据一样,应用层从某个地方获取数据,例如通过编解码得到PCM数据,然后write到audiotrack。
这种方式的坏处就是总是在JAVA层和Native层交互,效率损失较大。
而STATIC的意思是一开始创建的时候,就把音频数据放到一个固定的buffer,然后直接传给audiotrack,后续就不用一次次得write了。AudioTrack会自己播放这个buffer中的数据。
这种方法对于铃声等内存占用较小,延时要求较高的声音来说很适用。
vold框架原理,
1,有一个netlinkmanager接收来自Linux内核的uevent消息,例如sd卡的插拔会引起kernel向NM发送uevent消息,
2,netlinkmanager将这些消息转发给volumeManager,volumeManager做一些操作后将消息通过CommandListener发送给MountService.MountService收到消息后做进一步处理。比如volumeManager----disk insert-----MountService----Mount----Vold,表示挂载这个SD卡
4,CommandListener内部封装了一个socket用于跨进程通信,在vold进程中属于服务端,接收来自mountservice的控制命令,同时volumeManager和netlinkmanager又通过它发送消息给MountService
dhcp协议
binder原理:
传统·IPC·:需要做两次拷贝:用户空间->内核空间->用户空间
binder:由Binder驱动负责管理数据接收缓存,驱动会根据发送数据包的大小,使用最佳匹配算法从缓存池中找到一块大小合适的空间,将数据从发送缓存区复制过来,mmap()分配的内存是映射在接收方用户空间里的.为了实现用户空间到用户空间的拷贝,mmap()分配的内存除了映射进了接收方进程里,还映射进了内核空间。所以调用copy_from_user()将数据拷贝进内核空间也相当于拷贝进了接收方的用户空间,这就是Binder只需一次拷贝的‘秘密’。
五:android重要知识点
Android开发--如何减小Apk文件的大小
静态编译和动态编译
静态 include $(BUILD_STATIC_JAVA_LIBRARY)
动态 include $(BUILD_JAVA_LIBRARY)
a、可以在apk的Android.mk中去添加动态jar包的依赖,使用的时候jar包的类可以直接调用
b、apk编译的时候如果不在android.mk中做动态jar包的依赖的话,可以在使用的时候去动态寻找system/framework/SkyFramework的jar包,去load此jar包,在去找jar包中的 class;这种方法 apk中可以直接调用接口中自己定义的方法,编译时不需要去做android.mk的依赖。 实现的地方直接继承接口即可,打包到动态jar包中。
这样可以实现分离,接口的定义和实现做到分离,在开发阶段非常有用,不同部门直接可以并行进行。
网络编程,服务器端编程
安卓中Fragment应用,相比Activity的好处
Fragment可以使你能够将activity分离成多个可重用的组件,每个都有它自己的生命周期和UI。
Fragment可以轻松得创建动态灵活的UI设计,可以适应于不同的屏幕尺寸。从手机到平板电脑。
Fragment是一个独立的模块,紧紧地与activity绑定在一起。可以运行中动态地移除、加入、交换等。
Fragment提供一个新的方式让你在不同的安卓设备上统一你的UI。
Fragment 解决Activity间的切换不流畅,轻量切换。
Fragment 替代TabActivity做导航,性能更好。
Fragment 在4.2.版本中新增嵌套fragment使用方法,能够生成更好的界面效果。
Fragment做局部内容更新更方便,原来为了到达这一点要把多个布局放到一个activity里面,现在可以用多Fragment来代替,只有在需要的时候才加载Fragment,提高性能。
可以从startActivityForResult中接收到返回结果,但是View不能。
recyclerview的复用原理
热修复原理
android7.0原理--签名机制:
android 7.0中引入了APK Signature Scheme V2(Full APK Signature),而V1(jar Signature) 来自JDK;
V1:仅验证未解压的文件内容,这样APK 签署后可进行许多修改 ,可以移动甚至重新压缩文件;
V2:验证压缩文件的所有字节,而不是单个 ZIP 条目,因此,在签名后无法再更改(包括 zipalign)。正因如此,现在在编译过程中,我们将压缩、调整和签署合并成一步完成。好处显而易见,更安全而且新的签名可缩短在设备上进行验证的时间(不需要费时地解压缩然后验证),从而加快应用安装速度。如有任何自定义任务篡改 APK 文件或对其进行后处理(无论以任何方式),那么V2 签名会有作废的风险,从而导致您的 APK 与 Android 7.0 及更高版本不兼容。
六:android重要知识点
java类加载机制
类加载的过程包括了加载(查找并加载类的二进制数据)、验证(确保被加载的类的正确性:确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全)、准备(为类的静态变量分配内存,并将其初始化为默认值,public static int value = 3,这是设为0)、解析(把类中的符号引用转换为直接引用)、初始化(类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要对类变量进行初始化)五个阶段。
解析阶段则不一定,它在某些情况下可以在初始化阶段之后开始,这是为了支持Java语言的运行时绑定(也成为动态绑定或晚期绑定)
类加载器:双亲委派模型,如果存在父类加载器,就委派给父类加载器加载,如果不存在父类加载器,就检查是否是由启动类加载器加载的类,通过调用本地方法,如果父类加载器和启动类加载器都不能完成加载任务,才调用自身的加载功能。
意义:系统类防止内存中出现多份同样的字节码,-保证Java程序安全稳定运行
java编译过程
分析和输入到符号表
注解处理
语义分析和生成class字节码文件
GC
final用法,反射原理
反射:先讲反射机制,反射就是程序运行期间JVM会对任意一个类洞悉它的属性和方法,对任意一个对象都能够访问它的属性和方法。依靠此机制,可以动态的创建一个类的对象和调用对象的方法。
其次就是反射相关的API,只讲一些常用的,比如获取一个Class对象。Class.forName(完整类名)。通过Class对象获取类的构造方法,class.getConstructor。根据class对象获取类的方法,getMethod和getMethods。使用class对象创建一个对象,class.newInstance等。
最后可以说一下反射的优点和缺点,优点就是增加灵活性,可以在运行时动态获取对象实例。缺点是反射的效率很低,而且会破坏封装,通过反射可以访问类的私有方法,不安全。
注解原理
io,nio区别和原理,NIO中select的实现机制
Java IO是面向流的,这意味着我们需要每次从流中读取一个或多个字节,直到读取完所有字节;NIO是面向缓冲的,也就是说会把数据读取到一个缓冲区中,然后对缓冲区中的数据进行相应处理。
Java IO是阻塞IO,而NIO是非阻塞IO。
Java NIO中存在一个称为选择器(selector)的东西,它允许你把多个通道(channel)注册到一个选择器上,然后使用一个线程来监视这些通道:若这些通道里有某个准备好可以开始进行读或写操作了,则开始对相应的通道进行读写。而在等待某通道变为可读/写期间,请求对通道进行读写操作的线程可以去干别的事情。
多线程同步的几个方式及区别
1、synchronized (托管给JVM执行的),2、重入锁(retrandlock,读写ReentrantReadWriteLock)3、volatile关键字,阻塞队列(linkedblockingqueen),是java代码自己实现的,4、局部变量(ThreadLocal,提到Looper.prepare)5、使用同步容器(vector,hashtable,ConcurrentHashMap),6、CountDownLatch
乐观锁和悲观锁的区别
悲观锁,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
乐观锁,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。
synchronized是基于悲观锁,比较适用于大量线程频繁请求,lock是乐观锁,适用于不是很频繁的
ReentrantLock非中断锁(一直等待)和中断锁(lock.lockInterruptibly()),得到中断响应则中断,还提供公平锁及非公平锁。
stringbuffer和stringbuilder区别
stringbuffer是线程安全,适合多线程操作字符串,stringbuilder是非线程安全的,适合单线程大量操作字符串,在单线程中的性能比StringBuffer高
Java重写hashCode(),equals()函数的时机
虽然 a 与 b不是同一个对象,但是你希望无论使用 a 还是 b 都可以从map中取出相同的 value,也就是你会认为 a 与 b 的内容是相同的,就是当把他们作为索引值,或者在 set 中不会重复出现,这些情况适用于重写他们。重写equals一定要重写Hashcode,这是java规定的。如果不满足这个条件则map, set等的处理会出现错误的。
get()和post()区别
语义的区别,设计不同,get是为请求,post是提交改变服务器状态
1. GET使用URL或Cookie传参。而POST将数据放在BODY中。
2. GET的URL会有长度上的限制,则POST的数据则可以非常大。
3. POST比GET安全,因为数据在地址栏上不可见。
成员变量的类型
静态变量,实例变量,final修饰的常量
七:数据结构算法相关
如何判断一个链表中存在环
1遍历做标记,2:便利把指针反转,如果走到开始就有环,3:双指针 一个一次走一步,一个一次走两步
各排序、冒泡排序、二分查找思想原理
二叉树
问1000坛酒,里面有1坛毒酒,需要多少只老鼠才能判断出毒酒是哪坛?
都转为二进制 2的10次方为1024,10只老鼠
Linux中查看内存使用状况命令?查看网络状况?
cat/proc/meminfo free