以面试官的角度来解答面试题!原来我去面大厂是怎样被挂的?

1. Java基础相关笔记

  1. Java的char是两个字节,如何存utf-8字符?

    面试官视角:这道题想考察什么?
            1\. 是否熟悉Java char和字符串(初级)
            2\. 是否了解字符的映射和存储细节(中级)
            3\. 是否能触类旁通,横向对比其它语言(高级)
    题目结论:
        初级:Java char不存utf-8的字节,而是utf-16,占两个字节;utf-8是1-6个字节存储;Unicode字符集(码点,人类认知层面的字符映射成整数,字符集不是编码)
        中级:Java char中存储的是utf-16编码。Unicode通用字符集占两个字节,'中',占一个char两个字节,byte[] bytes = '中'.getBytes("utf-16")--> 结果为:fe ff 4e 2d(fe ff为字节序)
        高级:
            1\. 令人迷惑的字符串长度:字符串长度 != 字符数(比如:表情emoji,一个字符数,占两个长度)
            2\. 触类旁通:
                1). Java9中对拉丁(Latin)字符优化:例如若字符串中是ascii码字符,只需要7个byte一个字节表示存储,而使用utf-16存储明显是有压力的。Java9若发现整个字符串中只有ascii码字符,则会使用byte来存,不使用char存储,这样就会节省一半字符,此时字符串长度 也!= 字符数。
                2). Java(String emoji = "emoji表情",长度为2) == Python(>=3.3,emoji = u"emoji表情",长度为1)
    
  2. Java String可以有多长?

    面试官视角:这道题想考察什么?
        1\. 字符串有多长是指字符数还是字节数(初级)
        2\. 字符串有几种存在形式(中级)
        3\. 字符串的不同形式受到何种限制(高级)
    题目结论:
        初级:分为两种形式写在代码中的字面量和文件中的字符串,即存在于栈内存中和堆内存中
        中级:
            栈内存中:字面量中,受限于Java代码编译完成的字节码文件CONSTANT_Utf8_info结构中的u2 length。u2代表两个字节,即长度最大为16位二进制表示,因此最大长度为65535。CONSTANT_Utf8_info运行时,会被加载到Java虚拟机方法区的常量池中,若常量池很小,字符串太大,会出现异常,常量池一般不会连65535都无法存储。
        高级:
            1\. Java编译器的bug:实战过程中发现输入65535个Latin字符,编译无法通过,而65534则可以。Java不会和c语言中一样,在字符串中加一个'\0'。Java编译器(Javac)中判断字符串长度时使用的是 <65535 号,而非 <=65535,因此65535无法编译通过。kotlin中是没有问题的
            2\. String中若为中文字符,比如String longString = "烫烫烫...烫烫烫"; 烫占3个字节,理论上最长为 65535/3 个。实战输入65535/3个烫,发现是可以编译通过的,原因:Java编译器对于中文字符这样,需要Utf8编码这种,没办法在编译时直接知道到底需要占用多少字节,只能先通过utf-8编码,然后再来查看占多少字节。此写法判断长度时使用 >65535 ,因此此处是正确的。
            3\. 总结:Latin字符,受Javac限制,最多65534个;非Latin字符最终对应字节个数差距较大,最多字节数为65535个;若运行时方法区设置较小(比如:嵌入式设备),也会受方法区大小限制。
            4\. 堆内存中:new String(bytes); 受String内部value[] 数组,此数组受虚拟机指令 newarray [int],因此数组理论上最大个数为 Integer.MAX_VALUE。有一些虚拟机可能保留一下头信息,因此实际上最大个数小于 Integer.MAX_VALUE。即堆中String理论上最长为Integer.MAX_VALUE,实际受虚拟机限制小于Integer.MAX_VALUE,并且若堆内存较小也受堆内存大小限制。    
    
  3. Java匿名内部类有哪些限制?

    面试官视角:这道题想考察什么?
        1\. 考察匿名内部类的概念和用法(初级)
        2\. 考察语言规范以及语言的横向对比等(中级)
        3\. 作为考察内存泄漏的切入点(高级)
    题目结论:
        初级:没有人类认知意义上的名字;匿名内部类的实际名字:包名+外部类+$N,N是匿名内部类的顺序(即在外部类中所有匿名内部类从上到下排第几)
        中级:
            匿名内部类的继承结构:匿名内部类必然有其父类或者父接口,在初始化匿名内部类new InnerClass(){ ... },InnerClass类实际上就是此匿名内部类的父类
            匿名内部类只能继承一个父类或者实现一个接口,不能同时即继承父类由实现接口;kotlin中是可以的
        高级:
            匿名内部类的构造方法:编译器生成,匿名内部类可能会有外部类的引用,可能会导致内存泄漏。
                匿名内部类实际参数列表:
                    1\. 外部类实例(定义在非静态域内)
                    2\. 父类的外部对象(父类非静态,即父类是定义在一个类中,外部对象是指父类的外部类实例)
                    3\. 父类的构造方法参数(父类若由构造方法且参数列表不为空)
                    4\. 外部捕获的变量(方法体内有引用外部的final变量)
            匿名内部类参数列表所知:
                1\. 父类定义在是非静态作用域内(即父类是定义在一个类中),会引用父类的外部类实例
                2\. 如果定义在非静态作用域内(非静态方法内),会引用外部类实例(非静态方法内所在类的实例)
                3\. 只能捕获外部作用域的final变量
                4\. Java8:创建时只有单一方法的接口可以用Lambda转换(SAM类型),只能是接口且接口中只有一个方法
    
  4. Java 方法分派

  5. Java 泛型的实现机制是怎样的?

  6. Android的onActivityResult使用起来非常麻烦,为什么不设计成回调?

2. 线程安全(高并发)笔记

  1. 如何停止一个线程?

  2. 如何写出线程安全的程序?

  3. ConcurrentHashMap(简写:CHM)如何支持并发访问(线程安全)?

  4. AtomicReference和AtomicReferenceFieldUpdater有何异同?

  5. 如何在Android中写出优雅的异步代码?

面试官视角:这道题想考察什么?
    1\. 是否熟练编写异步和同步代码(中级)
    2\. 是否熟悉回调地狱(中级)
    3\. 是否能够熟练使用RxJava(中级)
    4\. 是否对Kotlin协程有了解(高级)
    5\. 是否具备编写良好代码的意识和能力(高级)
题目剖析:
    1\. 什么是异步?
    2\. 如何定义优雅?
题目结论:
    什么是异步?
        同步:阻塞,通俗的讲,就是代码必定按照编写的顺序执行。
        异步:通俗的讲,就是代码并不是按照编写的顺序执行。例如:线程(实现异步的手段之一)、Handler(post、postDelay、sendMessage...)、AsyncTask、回调(onClick)
	异步的目的:提高CPU利用率(异步过程中程序若是CPU密集型的,异步或者高并发往往会降低CPU利用率,因为切换线程时会有一些开销;若是IO密集型的,则可以提供CPU利用率)、提升GUI程序的响应速度(GUI程序切换到IO线程时,往往是为了提高CPU利用率)、异步不一定快(若运行的是算法,多线程会让CPU利用率增高,若线程很多,切线程本身也会让CPU吃不消)
    回调地狱:太多层的嵌套,整体代码形成一个倒的三角形,非常难以阅读。
	RxJava(将异步逻辑扁平化):一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序的库。
	    RxJava基本用法:查看代码示例,具体可参考:com.android.baselibrary.RxJavaActivity
        RxJava异常处理:onErrorReturn
        RxJava取消处理:RxLifecycle或者AutoDispose
        RxJava代码示例:
	    @SuppressLint("CheckResult")
        private void makeRxJava() {
            // 问:有一些异常无法映射成一个结果,在RxJava使用过程中无法捕获?
            // 答:则可以做一个全局的异常捕获,并且日志上报,但是此异常若为很验证的异常,则抛出
            RxJavaPlugins.setErrorHandler(e -> {
                // OnErrorNotImplementedException此异常里真实的异常为e.getCause(),若不提取,则会包含大量无用信息
                report(e instanceof OnErrorNotImplementedException ? e.getCause() : e);
                // 致命的错误抛出
                Exceptions.throwIfFatal(e);
            });
            String[] strings = new String[]{"111", "222", "333", "444", "555"};
            Observable.fromArray(strings)
                    .map(Integer::getInteger)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    //.compose(bindUntilEvent(ActivityEvent.DESTROY)) // RxLifecycle:防止内存泄漏,自动取消处理。需要继承自 RxActivity 或 RxFragment 等
                    .onErrorReturn(t -> throwableToResponse())
                    // AutoDispose:防止内存泄漏,自动取消处理。使用AutoDispose时需要当前Activity实现LifecycleOwner接口,而AppCompatActivity是默认实现了该接口
                    //.as(RxLifecycleUtils.bindLifecycle(this)) // as1 规范性,封装AutoDispose工具类
                    //.as(AutoDispose.autoDisposable(ViewScopeProvider.from(button))) // as2 监听View状态自动取消订阅,即根据button按钮与Window分离时onDetachedFromWindow取消处理
                    .as(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(this, Lifecycle.Event.ON_DESTROY))) // as3 根据声明周期取消处理
                    .subscribe(this::onNextResponse);
        }

        private void report(Throwable throwable) {
            // 日志上报处理
        }

        private Integer throwableToResponse() {
            // 返回异常数字,自定义
            return -1000;
        }

        private void onNextResponse(Integer integer) {

        }

3. JNI 编程的细节笔记

  1. CPU架构适配需要注意哪些问题?

  2. Java Native方法与Native函数是怎么绑定的?

面试官视角:这道题想考察什么?
    1\. 是否有过Native开发经验(中级)
    2\. 是否面对知识善于完善背后的原因(高级)
题目剖析:
    1\. 静态绑定:通过命名规则映射
    2\. 动态绑定:通过JNI函数注册
代码示例:
package com.jni.study.native;
public class NativeStudy{
    public static native void callNativeStatic();
}
// javah生成native方法的.h头文件
extern "C" JNIEXPORT void JNICALL
Java_com_jni_study_native_NativeStudy_callNativeStatic(JNIEnv*,jclass)
题目结论:
    一星:静态绑定:包名的.变成_ + 类名_ + 方法名(JNIEnv*,jclass/jobject)
    二星:.h头文件说明
        extern "C":告诉编译器,编译该Native函数时一定要按照C的规则保留这个名字,不能混淆这个函数的名字(比如:C++混编)
        JNIEXPORT:为编译器设置可见属性,强制在符号表中显示,优化so库时可以隐藏不需要公开的符号INVISIBLE,而此处不可隐藏需要设置为DEFAULT
        JNICALL:部分平台上需要(比如:mips、windows),告诉编译器函数调用的惯例是什么,比如:参数入栈以及返回清理等等
    三星:动态绑定
        1\. 获取Class FindClass(className)
        2\. 注册方法 jboolean isJNIErr= env->RegisterNatives(class, methods, methodsLength) < 0;
        3\. 动态绑定可以在任何时刻触发
        4\. 动态绑定之前根据静态规则查找Native函数
        5\. 动态绑定可以在绑定后的任意时刻取消
        6\. 动态绑定和静态绑定对比:
                                    动态绑定            静态绑定
            Native函数名             无要求             按照固定规则编写且采用C的名称修饰规则
            Native函数可见性          无要求             可见
            动态更换                  可以              不可以
            调用性能                  无需查找           有额外查找开销
            开发体验                  几乎无副作用       重构代码时较为繁琐
            Android Studio支持       不能自动关联跳转    自动关联 JNI函数可跳转
  1. JNI如何实现数据传递?

  2. 如何全局捕获Native异常?

  3. 只有C、C++可以编写JNI的Native库吗?

面试官视角:这道题想考察什么?
    1\. 是否对JNI函数绑定的原理有深入认识(高级)
    2\. 是否对底层开发有丰富的经验(高级)
题目剖析:
    1\. Native程序与Java关联的本质是什么?
    2\. 举例如何用其它语言编写符合JNI命名规则的符号
题目结论:
    一星(按照14题):JNI对Native函数的要求
        1\. 静态绑定:
            1\. 符号表可见
            2\. 命名符合Java Native方法的 包名_类名_方法名
            3\. 符合名按照C语言的规则修饰
        2\. 动态绑定:
            1\. 函数本身无要求
            2\. JNI可识别入口函数如 JNI_OnLoad 进行注册即可
    二星:可选的Native语言(理论上)
        * Golang
        * Rust
        * Kotlin Native
        * Scala Native
        * ...
    三星:以Kotlin Native为例讲述如何开发

暂且分享这16题,由于部分答案过长影响阅读,小编已经整理成了一个PDF文档,关注我,可以私信我【大厂面试】领取文档

当然,这段时间大家学习的时间比较多,看完文章后也可以学习我收集的这份1612页的面试指南,满满的都是干货,希望对大家有帮助!

发布了113 篇原创文章 · 获赞 11 · 访问量 9471

猜你喜欢

转载自blog.csdn.net/Aerfa789/article/details/104319845