记一次android面试

Android实习生面试,有四轮技术面(最后一面为CTO面试,基本就是问一些情况),先记录下我记忆比较深的一些题,然后最后也是收到了offer,总体感受就是算法基础要牢固,尤其是Leetcode上面要多刷题,比如链表这些都是必考的,就算一些知识点没有答上来,只要算法过了就还有希望;

目录

目录

第一轮:

Handler的原理

Handler源码简介

Handler的正确写法:

第二轮:

ListView和RecyclerView的区别

hashMap的数据结构,是否线程安全,为什么线程不安全

http协议的了解,https为什么安全

SharePreferences的原理,apply和commit的区别

扔鸡蛋问题(LeetCode887)

第三轮

汉明重量

扔石头问题

第四面

时针秒针的夹角问题



第一轮:

Handler的原理

Handler的作用为将一个任务切换到某个指定的线程中去进行,Handler的运行需要底层的MessageQueue和Looper的支持;

MessageQueue为消息队列,在内存存储了一组消息,以队列的形式对外提供插入和删除的操作,其内部的存储结构其实不是队列,而是单链表;

Looper的作用是用来消息循环,Looper会以无限循环的形式去查询是否有新消息,如果有的话就处理新消息,否则就会一直等待着;

Looper中有一个ThreadLocal,其作用是可以在每个线程中存储数据,ThreadLocal可以在不同的线程中互不干扰地存储和提供数据,通过ThreadLocal可以轻松获取每个线程的Looper;

需要注意的是,线程是默认没有Looper的,当我们需要使用Handler的时候就必须为线程创建Looper,我们经常提到的主线程,就是ActivityThread,在创建时会初始化Looper,这就是主线程中默认可以使用Handler的原因;

下面两个是我自己顺便记录的知识点

Handler源码简介

Handler可以通过post方法将一个Runnable投递到Handler内部的Looper中去处理,也可以通过Handler的send方法发送一个消息,这个消息同样会在Looper中去处理,其实post方法也是通过send处理的;

当Handler的send被调用后,会调用MessageQueue的enqueueMessage将这个消息放入消息队列中,读取的方法为next,如果有西消息就会通知Looper,Looper收到消息开始处理,最后Looper交由Handler吹,即Handler的dispatchMessage方法会被调用;在dispatchMessage中最后会调用handlerMessage来处理消息

Looper.prepare()可为当前线程创建一个Looper,通过Looper.loop()来开启消息循环;

Looper的退出:quit会直接退出,quitSafely只是设定一个退出表示,然后把消息队列中的已有消息处理完毕后才完全地退出;在子线程中,如有手动创建了Looper,应该在最后调用quit来终止消息循环

Handler的正确写法:

 用静态内部类以及弱引用防止内存泄漏;

private static class InnerHandler extends Handler {
        WeakReference<DetailActivity> mWeakReference;

        public InnerHandler(DetailActivity detailActivity) {
            mWeakReference = new WeakReference<DetailActivity>(detailActivity);
        }

        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            if (mWeakReference.get() != null) {
                if (msg.what == 1) {
                    //todo
                }
            }
        }
    }
mInnerHandler = new InnerHandler(this);
        mInnerHandler.sendEmptyMessage(1);

 最后在onDestroy中

mInnerHandler.removeCallbacksAndMessages(null);

第二轮:

ListView和RecyclerView的区别

1.在使用效果上说:RecyclerView可以提供线性布局,网格布局,瀑布流布局三种,还可以控制横向和纵向滚动

2.使用方法:

ListView需要继承重写BaseAdapter;自定义ViewHolder和convertView一起完成复用优化工作;

RecyclerView继承重写RecyclerView.Adapter和RecyclerView.ViewHolder;设置布局管理器,控制布局效果;

RecyclerView提供了notifyItemChanged用于单个item的刷新;

RecyclerView提供了item的动画效果;

3.缓存机制:

RecyclerView比ListView多两级缓存,支持开发者自定义缓存处理逻辑,支持所有RecyclerView共用同一个RecyclerViewPool(缓存池);

1). RecyclerView缓存RecyclerView.ViewHolder,抽象可理解为:

View + ViewHolder(避免每次createView时调用findViewById) + flag(标识状态);

RecyclerView中mCacheViews(屏幕外)获取缓存时,是通过匹配pos获取目标位置的缓存,这样做的好处是,当数据源数据不变的情况下,无须重新bindView:

2). ListView缓存View。而同样是离屏缓存,ListView从mScrapViews根据pos获取相应的缓存,但是并没有直接使用,而是重新getView(即必定会重新bindView)

hashMap的数据结构,是否线程安全,为什么线程不安全

是一个Entry数组,每一个Entry包含一个key-value键值对;
Entry就是HashMap中的一个静态内部类;
简单来说,HashMap是由数组+链表组成的,数组时HashMap的主体,链表是为了哈希冲突而存在的,如果定位到的数组位置不含链表,那么查找,添加等操作很快,否则对于添加操作,时间复杂度为O(n)
扩容时,容量默认16,扩容2倍
java8比java7的基础上添加了红黑树这种数据结构

为什么HashMap不安全
1.put的时候导致的多线程数据不一致,比如有两个线程A和B,首先A希望插入一个key_value到HashMap中,首先计算要落到的桶的索引坐标,然后获取到该桶的链表头结点,此时线程A的时间片用完了,线程B执行,B将记录插到桶里面,假设两个索引一样,这时线程A在调用,就覆盖掉了线程B的记录,造成数据不一致;
 

http协议的了解,https为什么安全

  1. 基于请求和响应模式的无连接,无状态,应用层协议
  2. 简单快速:协议简单,通信速度快;
  3. 灵活:成功传输任意类型的数据对象,由Content-Type标记;
  4. 无连接:每次处理一个请求,处理完成即断开;
  5. 无状态:对事物处理没有记忆功能;
  6. http是应用层的协议,底层基于TCP/IP协议

   为什么安全: 

     应用层http和传输层tcp中间加多了一层TLS/SSL加密套件,https就是应用层将数据给到TLS/SSL,然后将数据加密后,再给到TCP进行传输;

SharePreferences的原理,apply和commit的区别

SharedPreferences的使用非常简单,能够轻松的存放数据和读取数据。SharedPreferences只能保存简单类型的数据,例如,String、int等。一般会将复杂类型的数据转换成Base64编码,然后将转换后的数据以字符串的形式保存在 XML文件中,再用SharedPreferences保存。

使用SharedPreferences保存key-value对的步骤如下:

  (1)使用Activity类的getSharedPreferences方法获得SharedPreferences对象,其中存储key-value的文件的名称由getSharedPreferences方法的第一个参数指定。

  (2)使用SharedPreferences接口的edit获得SharedPreferences.Editor对象。

  (3)通过SharedPreferences.Editor接口的putXxx方法保存key-value对。其中Xxx表示不同的数据类型。例如:字符串类型的value需要用putString方法。

  (4)通过SharedPreferences.Editor接口的commit方法保存key-value对。commit方法相当于数据库事务中的提交(commit)操作。

apply没有返回值,commit有返回值

在数据并发时commit效率低于apply,推荐使用apply

扔鸡蛋问题(LeetCode887)

你将获得 K 个鸡蛋,并可以使用一栋从 1 到 N  共有 N 层楼的建筑。

每个蛋的功能都是一样的,如果一个蛋碎了,你就不能再把它掉下去。

你知道存在楼层 F ,满足 0 <= F <= N 任何从高于 F 的楼层落下的鸡蛋都会碎,从 F 楼层或比它低的楼层落下的鸡蛋都不会破。

每次扔,你可以取一个鸡蛋(如果你有完整的鸡蛋)并把它从任一楼层 X 扔下(满足 1 <= X <= N)。

你的目标是确切地知道 F 的值是多少。

无论 F 的初始值如何,你确定 F 的值的最小扔的次数是多少?

题意:就是要找到你鸡蛋在哪一个楼层扔下去正好碎掉,要考虑最坏情况下的次数

题解:因为这个题限定了鸡蛋的个数,所以不能简单地用二分法来求解,这个题用到了动态规划,就是说比如你有两个鸡蛋,在x层扔的时候,这个时候的函数为dp(2,x),然后扔下去碎了,就意味着你剩下一个鸡蛋,且只需要验证x下面的所有楼层,即dp(1,x-1); 而如果没有碎,则意味着只需要验证x之上的楼层即可,即dp(2,N-x);

又因为这两个函数的单调性,要找到所求的值,需要找到他们的交点,即可用二分法来找交点两侧的亮点,并最后求最小值

class Solution {
    public int superEggDrop(int K, int N) {
        return dp(K, N);
    }

    Map<Integer, Integer> memo = new HashMap();

    public int dp(int K, int N) {
        if (!memo.containsKey(N * 100 + K)) {//判断之前是否计算过
            int res ;
            if (N == 0) { //在第0层楼
                res = 0;
            } else if (K == 1) { //只剩一个鸡蛋
                res = N;
            } else {//使用二分法找交点
                int low = 1, high = N;
                while (low + 1 < high) {
                    int x = (low + high) / 2;
                    int t1 = dp(K - 1, x - 1);//碎了,单调递增
                    int t2 = dp(K, N - x);//没碎,单调递减
                    //求两函数交点
                    if (t1 < t2) {
                        low = x;
                    } else if (t1 > t2) {
                        high = x;
                    } else {
                        low = high = x;
                    }
                }
                //退出循环说明在low和high在交点两侧
                res = 1 + Math.min(
                        Math.max(dp(K - 1, low - 1), dp(K, N - low)),
                        Math.max(dp(K - 1, high - 1), dp(K, N - high))
                );
            }
            memo.put(N * 100 + K, res);//防止重复计算
        }

        return memo.get(N * 100 + K);
    }
}

第三轮

汉明重量

编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量

public class Solution {
    // you need to treat n as an unsigned value
    public int hammingWeight(int n) {
        int res = 0;
        while (n != 0) {
            res++;
            n &= n-1; //把最后一个1变为0
        }
        return res;
    }
}

扔石头问题

你和你的朋友,两个人一起玩 Nim 游戏:

桌子上有一堆石头。
你们轮流进行自己的回合,你作为先手。
每一回合,轮到的人拿掉 1 - 3 块石头。
拿掉最后一块石头的人就是获胜者。
j假设你们每一步都是最优解。请编写一个函数,来判断你是否可以在给定石头数量为 n 的情况下赢得游戏。如果可以赢,返回 true;否则,返回 false 。

题解:这个题就是只要留下了4个,那么对方怎么拿,你都能赢,因此4的倍数就是是否能赢的判断条件;

class Solution {

    public boolean canWinNim(int n) {

        return n % 4 != 0;

    }

}

第四面

时针秒针的夹角问题

题意:比如00:00时时针秒针夹角0°,00:01时夹角为6°-0.5° = 5.5°,问什么时候夹角为6°

class Solution {
    public static void main(String[] args) {
        for (int i = 0; i <= 23; i++) {
            for (int j = 0; j <= 59; j++) {
                if (getDegree(i, j) == 6*10 || getDegree(i, j) == -6*10) {
                    System.out.println(i + " " + j);
                }
            }
        }
    }

    private static int getDegree(int hour, int minute) {
        int hourDegree = 0;
        int minDegree = 0;
        int res = 0;
        if (hour >= 12) hour -= 12;
        hourDegree += hour * 30 * 10;
        minDegree += minute * 6 * 10;
        hourDegree += minute * 0.5 * 10;
        res = hourDegree - minDegree;
        return res;
    }
}

猜你喜欢

转载自blog.csdn.net/qq873044564/article/details/109323144