字节跳动Android春招一面

字节跳动Android面试

  • 四大引用,软引用什么时候使用?

    强引用:具有强引用就不会被垃圾回收器回收,即使内存空间不足,抛出异常也不会回收;除非其在生命周期已经过了或者显示地将其设置为null;

    软引用:SoftReference,通过get来获取对象,在jvm内存不足的时候会被回收;用来实现内存敏感的高速缓存,如网页缓存,图片缓存等,一旦SoftReference保存了对一个java对象的软引用猴,在回收前,get方法提供的是该对象的强引用,回收之后,get返回null;

    弱引用:WeakReference,通过get获取对象,在gc的时候会被回收只与弱引用关联的对象;

    虚引用:与没有引用是一样的,需要和队列联合使用;

    • 补充:对象可及性判断:

    对于一个对象的可及性要先判断有几条路径可以到达,取最强的一条路径,而某一条路径是什么是由最弱的一个引用决定; 如一条路径是由一个软和一个强组成,这条路径就是软;有两条路径同时到达该对象,一条软一条弱,则该对象为软可及对象;

    • 利用软引用解决OOM问题

      当一个应用需要读取大量的本地图片,加载内存时,会造成内存溢出;

      这时使用一个HashMap来保存图片的路径与响应图片关联的软引用之间的映射关系,当内存不足时,jvm会自动回收这些缓存图片所占用的空间,有效避免了oom的问题;

     

  • Lrucache,是用什么数据结构实现的?

    Least Recently Used最近最少使用,原则:如果一个数据最近一段时间没有被访问,那么将来他被访问的可能性很小;

    实现是使用双向链表+hashmap,使用hashmap是为了提高查询速度,如果在map中可以查到,就去链表中去删除节点,然后插到头部,否则返回null;当Cache慢了就删掉最后一个节点;

     

  • 优先队列听说过吗,如何实现的?

    在优先队列中,元素被赋予优先级,当访问元素时,具有最高优先级的元素最先删除

    使用堆来实现

    • 堆是什么?

      堆是一种完全二叉树,最大堆孩子节点小于等于父亲节点,最小堆反之;

  • GC回收算法

    把一系列“GC Roots”作为起始点,从节点向下搜索,路径称为引用链,当一个对象到GC Roots没有任何引用链相连,即不可达时,则证明此对象时不可用的。

  • GCroots有哪些

    • 局部变量对象

    • 静态 static 字段对象

    • 常量 static final 字段对象

    • JNI (本地方法栈) 对象

  • 堆和栈的区别

    1. 堆内存: 用于存储java中的对象和数组,当我们new一个对象或者创建一个数组,就会在堆内存中开辟一块空间; 生存期不必告诉编译器,存取速度较慢; 堆是所有线程共享的一块公共区域,对象都在堆里创建,但为了提升效率,线程会从堆中拷贝一个缓存到自己的栈中;

    2. 栈内存: 主要用于执行程序,比如基本类型的变量和对象的引用变量; 栈数据数据大小和生存期是确定的,存取速度比堆快; 栈是一块和线程相关的内存区域,每个线程都有自己的栈内存,用于存储本地变量,方法参数和栈调用;

  • 注解有哪几种类型

    代码级别的说明,可以在编译,类加载,运行时被读取并执行相应处理;

  • java内存划分

    • 栈:基本数据类型,引用数据类型变量名;

    • 堆:引用数据类型变量值,对象/数组;

    • 全局代码区:保存所有方法的定义;

    • 全局数据区:保存static类型的属性;

      1.一个本地变量如果是原始类型,在线程栈上;

    如果是指向一个对象的引用,则引用存放在线程栈上,对象本身存放在堆上;

    方法中的本地变量存放在栈;

    2.解决内存可见性问题

    volatile

    synchronized

    final

  • Synchronize机制,当其他线程获取资源时会发生什么?

    • 一个变量在同一个时刻只允许一条线程对其进行lock操作,持有同一个锁的两个同步块只能串行的进入;

  • final关键字的作用

    • 修饰一个引用:引用为基本数据类型,则为常量,无法修改;

      • 引用为数据类型,如对象数组,则对象,数组本省可以修改,地址的引用不可修改;

      • 引用是类的成员变量,则必须当场赋值

    • 修饰一个方法

      • 无法被子类重写,但是可以被继承,除非是private类型则不可继承

    • 用来修饰类

      • 该类成为最终类,final类中的所有成员方法都会隐式的定义为final方法;

  • volatile

    volatile可以保证直接从主存中读取一个变量,如果这个变量被修改后总是会被写回到主存中去,在变量读取前从主存中刷新变量值实现可见性;

    保证了有序性(禁止指令的重排序);

    volatile不保证原子性,如果要实现原子性,需要使用lock和unlock来满足

    使用场景:用于标志位,用于单例模式的双检锁

  • 异步一般使用什么?AsyncTask内部机制怎么实现的?

  • Handler是在什么时候进行线程切换的?

  • Handler的PostDelay方法的原理?

    • postDelay的调用路径?

      • Handler.postDelayed(Runnable r, long delayMillis)

      • Handler.sendMessageDelayed(getPostMessage(r), delayMillis)

      • Handler.sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)

      • Handler.enqueueMessage(queue, msg, uptimeMillis)

      • MessageQueue.enqueueMessage(msg, uptimeMillis)

    • Handler没有自己处理Delay,而是交给了MessageQueue处理,然后Looper在调用next方法时,会判断头部的Message是否有延迟且延迟时间没有到,就调用nativePollOnce进行阻塞,这个方法的作用类似于object.wait(也就是说延迟时间的方法是myssageQUeue中的next方法)时间到了也就相当于

    • 新的疑问:如果postDelay了一个A,我再去post一个B,B会不会等A执行完了再执行?

      • 首先是postDelay一个10秒钟的Runnable A消息进队,MessageQUeue调用nativePollOnce阻塞,Looper阻塞;

      • post进来一个B,消息进队,这时A时间还未到,正在阻塞,就把B插到队列的头部,然后调用nativeWake方法唤醒线程;

      • MessageQueue.next方法被唤醒后,重新开始读取消息链表,第一个消息B无延时,直接返回给

  • RecyclerView与ListView的区别

    • ListView的两级缓存:

      • mActiveViews:用于屏幕内的ItemView快速重用;

      • mScrapViews:相等于mCachedViews+mRecyclerViewPool

    • RecyclerView的四级缓存

      • mAttachedScrap:用于屏幕内ItemView快速重用,无需createView和bindView;

      • mCacheViews:缓存屏幕外的2个ItemView,目的是让即将进入屏幕的itemView重用;

      • mViewCacheExtension:不直接使用,需要用户定制实现;

      • mRecyclerPool:支持所有RecyclerView公用同一个RecyclerViewPool;

    • 两者的缓存不同点

      • RecyclerView缓存ViewHolder,避免了每次createView时调用findViewById;ListView缓存View;

      • RecyclerView中mCacheViews获取缓存时,是通过匹配pos获取目标位置的缓存,这样在数据源数据不变的情况下,无需重新bindView的;ListView是从mScrapViews根据pos获取响应的缓存,但是并没有直接使用,重新getView

    • 局部刷新

      • RecyclerView提供了局部刷新的接口,通过局部刷新,就能避免调用许多无用的bindView.ListView和RecyclerView的全部在于数据源改变时的缓存的处理逻辑;ListView是将所有mActiveViews都移入了二级缓存mScrapViews,而RecyclerView则是更加灵活地对每个View修改标志位,区分是否重新bindView;

  • RecyclerView翻页效果如何实现的?

  • ViewPager中的懒加载

    • 第一种方法:Fragment可见时去请求数据

    • 在Fragment中重写setUserVisibleHint方法,当isVisibleHint为true时请求数据;

  • 内存泄露

    • 一个对象是可达的,但是无用的,无法被GC回收,然后占用内存;

    • 内存泄露的几种情况

      • 单例模式:

      • 静态变量导致的内存泄露

        • 静态变量的生命周期与程序一致

      • 非静态内部类导致的内存泄露

      • 未取消注册导致的内存泄露

      • 集合中的对象未清理造成内存泄露

      • 资源未关闭导致内存泄露

  • ANR

  • Okhttp的拦截器作用

    Okhttp一共有七个拦截器,使用了list存储起来,每一个拦截器分成三部分:

    1].预处理拦截器内容;

    2].通过proceed方法把请求交给下一个拦截器

    3].下一个拦截器处理完成并返回,后续处理工作;

    1. addInterceptor:这个是为开发者设置的,可以在所有拦截器处理之前进行更早的拦截处理,如一些公共参数,Header可以在这里添加;

    2. RetryAndFollowUpInterceptor:对连接进行一些初始化工作,以及请求失败的重试工作,重定向的后续请求工作;

    3. BridgeInterceptor:会为用户构建一个能够网络访问的请求,同时后续工作将请求回来的响应转化为用户可用的Response;

    4. CacheInterceptor:主要处理Cache相关处理,一句OkhttpClient对象的配置以及缓存策略对请求值进行缓存到本地;

    5. ConnectInterceptor:主要负责建立连接,会建立TCP连接或者TLS连接

    6. netWorkInterceptor:可以做一些网络调试;

    7. CallServerInterceptor:进行网络数据的请求和响应,通过socket读写数据;

  • Rxjava中的设计模式

    • 责任链模式,就是Intercepter的使用

  • Retrofit

    • 通过java接口以及注解来描述网络请求,用动态代理生成网络请求的request,然后通过client调用响应的网络框架,默认使用okhttp,并将返回的response通过converterFactory转换为相应的数据model,最后通过calladapter转换为其他数据方式如RxJava的Observable

      • 动态代理和静态代理的区别:

        • 动态代理是程序运行期间由jvm根据反射等机制动态生成,不存在代理类的字节码文件

        • 静态代理是成程序员创建或工具生成代理类的与那吗,字节码文件

    • Retrofit中的数据是怎么处理的

      • 数据交给了callAdapter以及converter去处理,callAdapter负责把OkhttpCall转为我们所需的Observable类型,converter负责把服务器返回的数据朱文内具体的实体类

    • 与Okhttp的联系

    • Rxjava

  • 二叉树的中序遍历(非递归)

```

public static List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        if (root == null) return list;
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        while (cur != null || !stack.isEmpty()) {
            if (cur != null){
                stack.push(cur);
                //看是否有左节点
                cur = cur.left;
            }else {
                //返回上一层
                cur = stack.pop();
                //只有每次左侧没有节点的时候才添加
                list.add(cur.val);
                cur = cur.right;
            }
        }
        return list;
    }

```

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值