# ByteDance_Interview **Repository Path**: LixyAndroid/ByteDance_Interview ## Basic Information - **Project Name**: ByteDance_Interview - **Description**: 字节跳动视频面Android岗 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-03-04 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # ByteDance_Interview 字节跳动一面Android岗 ## 1,自我介绍一下 ## 2,然后就先聊聊java相关的,内存分配 内存模型 ### 程序计数器     程序计数器是一块较小的内存空间,它可以看做当前线程所执行的字节码的行号指示器。字节码解释器工作时通过改变这个计数器的值来选取下一条需要执行的字节码执行。分支,循环,跳转,异常处理,线程恢复等基础功能都需要依赖这个技术器来完成。     在任何一个确定的时刻,一个处理器只会执行一条线程中的指令。多线程通过线程轮流切换并分配处理器执行时间的方式来实现的。为了确保线程切换后能恢复到正确的执行位置,每个线程都需要有独立的程序计数器,各条线程之间计数互不影响,即线程私有内存。     如果线程正在执行的是一个Java方法,计数器记录的是正在执行的虚拟机字节码指令的地址。如果执行的是Native方法,这个计数器则为空。所以这是Java虚拟机规范唯一没有规定任何OutOfMemoryError情况的区域。 ### 虚拟机栈     线程私有每个方法执行时都会创建一个帧栈用于存放局部变量表,操作数栈,动态链接,方法出口等信息。每个方法从调用直到执行完成的过程,就对应着一个帧栈在虚拟机栈中入栈到出栈的过程。     局部变量表中存放编译期可知的各种基本类型本身,引用类型则存放指向对象起始地址的引用。局部变量表所需的内存空间在编译期间完成分配,每个方法在帧中分配的局部变量空间是完全确定的,在方法运行期不会改变。     当线程请求的栈深度大于虚拟机所允许的深度,会抛出StackOverflowError;当虚拟机栈可以动态扩展时,如果扩展时无法申请到足够的内存,则会抛出OutOfMemoryError异常。 ### 本地方法栈     为虚拟机中使用到的Native方法服务,基本功能和虚拟机栈相似。与虚拟机栈一样同样会抛出StackOverflowError和OutOfMemoryError异常。 ### 方法区     各个线程共享的内存区域,存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。当方法区无法满足内存分配需要的时候,将抛出OutOfMemoryError异常。     运行时常量池     属于方法区的一部分,用于存放编译期间生成的各种字面量的符号引用,这部分内容将在类加载后进入方法区的运行时常量池存放。在运行期间也可能有新的常量放入常量池中,例如通过String.intern()方法。 ### Java堆     Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动的时候,此内存区域存放对象实例。     Java堆可以细分为新生代和老年代。新生代中分为Eden,From Surivor空间,To Surivor空间等。Java堆中存在线程私有的空间,即为线程私有的分配缓冲区(TLAB)。Java虚拟机规范规定,Java堆可以出于物理上不连续的内存空间中,只要逻辑上连续即可。目前主流的虚拟机都是可以扩展的,通过-Xmx和-Xms控制,当堆无法扩展的时候,会发生OutOfMemoryError异常。 ### 直接内存     直接内存并不是虚拟机运行运行时数据区一块,也不再Java虚拟机规范中定义的内存区域。在JDK1.4中加入NIO,一种基于通道与缓冲区的I/O方式。使用Native函数库直接分配堆外内存。直接内存的分配虽然不会受到Java堆大小限制,但是会受到本机总内存的限制。默认情况下直接内存的大小与堆内存大小一致。当内存区域总和大于物理内存限制(包括物理和操作系统级的限制)从而会导致动态扩展时出现OutOfMemoryError异常。 ### 内存模型小结 系统的学习JVM,很多概念都很抽象,但是真是这些抽象的内容才能帮助我们以后更加深入的认识JVM。 ## 3,内存溢出 ### 基础 JAVA是在JVM所虚拟出的内存环境中运行的,内存分为三个区:堆、栈和方法区。 #### 栈(stack): 是简单的数据结构,程序运行时系统自动分配,使用完毕后自动释放。优点:速度快。 #### 堆(heap): 用于存放由new创建的对象和数组。在堆中分配的内存,一方面由java虚拟机自动垃圾回收器来管理,另一方面还需要程序员提供修养,防止内存泄露问题。 #### 方法区(method): 又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。 ### 概念 #### 内存溢出(Out of Memory): 系统会给每个APP分配内存也就是Heap Size值。当APP占用的内存加上我们申请的内存资源超过了Dalvik虚拟机的最大内存时就会抛出的Out Of Memory异常。 #### 内存泄漏(Memory Leak): 当一个对象不在使用了,本应该被垃圾回收器(JVM)回收。但是这个对象由于被其他正在使用的对象所持有,造成无法被回收的结果。内存泄漏最终会导致内存溢出。 ## 4,TCP和UDP 区别 UDP没有回答好,突然这些都知道,但是面试的时候没有回答上来 ,只回答了UDP,简单的,但是面试官 紧接着问,简单怎么理解 ### 面向连接的TCP “面向连接”就是在正式通信前必须要与对方建立起连接。比如你给别人打电话,必须等线路接通了、对方拿起话筒才能相互通话。 TCP(Transmission Control Protocol,传输控制协议)是基于连接的协议,也就是说,在正式收发数据前,必须和对方建立可靠的连接。一个TCP连接必须要经过三次“对话”才能建立起来,其中的过程非常复杂,我们这里只做简单、形象的介绍,你只要做到能够理解这个过程即可。 ### 面向非连接的UDP协议 “面向非连接”就是在正式通信前不必与对方先建立连接,不管对方状态就直接发送。这与现在风行的手机短信非常相似:你在发短信的时候,只需要输入对方手机号就OK了。 UDP(User Data Protocol,用户数据报协议)是与TCP相对应的协议。它是面向非连接的协议,它不与对方建立连接,而是直接就把数据包发送过去! UDP 适用于一次只传送少量数据、对可靠性要求不高的应用环境。比如,我们经常使用“ping”命令来测试两台主机之间TCP/IP通信是否正常,其实 “ping”命令的原理就是向对方主机发送UDP数据包,然后对方主机确认收到数据包,如果数据包是否到达的消息及时反馈回来,那么网络就是通的。例如, 在默认状态下,一次“ping”操作发送4个数据包。大家可以看到,发送的数据包数量是4包,收到的也是4包(因为对方主机收到后会发回一 个确认收到的数据包)。这充分说明了UDP协议是面向非连接的协议,没有建立连接的过程。正因为UDP协议没有连接的过程,所以它的通信效果高;但也正因为如此,它的可靠性不如TCP协议高。QQ就使用UDP发消息,因此有时会出现收不到消息的情况。 ## 5,抽象类和接口,接口回答的不好 1、概念不一样。接口是对动作的抽象,抽象类是对本质的抽象。 抽象类表示的是,这个对象是什么。接口表示的是,这个对象能做什么。比如,男人,女人,这两个类(如果是类的话……),他们的抽象类是人。说明,他们都是人。人可以吃东西,狗也可以吃东西,你可以把“吃东西”定义成一个接口,然后让这些类去实现它。 所以,在高级语言上,一个类只能继承一个类(抽象类)(正如人不可能同时是生物和非生物),但是可以实现多个接口(吃饭接口、走路接口)。 2、使用不一样:  a.抽象类 和 接口 都是用来抽象具体对象的. 但是接口的抽象级别最高 b.抽象类可以有具体的方法 和属性,  接口只能有抽象方法和不可变常量 c.抽象类主要用来抽象类别,接口主要用来抽象功能. d.抽象类中,且不包含任何实现,派生类必须覆盖它们。接口中所有方法都必须是未实现的。  e.接口是设计的结果 ,抽象类是重构的结果 3、使用方向:当你关注一个事物的本质的时候,用抽象类;当你关注一个操作的时候,用接口。 注意:抽象类的功能要远超过接口,但是,定义抽象类的代价高。因为高级语言来说(从实际设计上来说也是)每个类只能继承一个类。在这个类中,你必须继承或编写出其所有子类的所有共性。虽然接口在功能上会弱化许多,但是它只是针对一个动作的描述。而且你可以在一个类中同时实现多个接口。在设计阶段会降低难度的。 ## 6,Android的Touch事件分发机制,拦截,响应等这个基本没回答上来,刚开始以为要问handler的,本就没认真了解过 然后以这块知识没人深入了解过而gg。 很奇怪,问自定义view也可以呀,这个好歹可以说点(白眼)。 相关Touch事件的方法 1、public boolean dispatchTouchEvent(MotionEvent ev) —事件分发方法,分发Event所调用 2、public boolean onInterceptTouchEvent(MotionEvent ev)—事件拦截方法,拦截Event所调用 3、public boolean onTouchEvent(MotionEvent event) —事件响应方法,处理Event所调用 拥有上述事件的类 1、Activity类(Activity及其各种继承子类)     dispatchTouchEvent()、onTouchEvent() 2、ViewGroup类(LinearLayout、FrameLayout、ListView等.....)     dispatchTouchEvent()、onInterceptTouchEvent()、onTouchEvent() 3、View类(Button、TextView等.....)     dispatchTouchEvent()、onTouchEvent() ### PS:需要特别注意一点就是ViewGroup中额外拥有onInterceptTouchEvent()方法,其他两个方法为这三种类所共同拥有。 方法的简单用途解析 我们可以发现这三个方法的返回值都为boolean类型,其实它们就是通过返回值来决定下一步的传递处理方向。 1、dispatchTouchEvent() ——用来分发事件所用   该方法会将根元素的事件自上而下依次分发到内层子元素中,直到被终止或者到达最里层元素,该方法也是采用一种隧道方式来分发。在其中会调用onInterceptTouchEvent()和onTouchEvent(),一般不会去重写。   返回false则不拦截继续往下分发,如果返回true则拦截住该事件不在向下层元素分发,在dispatchTouchEvent()方法中默认返回false。 2、onInterceptTouchEvent() ——用来拦截事件所用   该方法在ViewGroup源代码中实现就是返回false不拦截事件,Touch事件就会往下传递给其子View。如果我们重写该方法并且将其返回true,该事件将会被拦截,并且被当前ViewGroup处理,调用该类的onTouchEvent()方法。 3、onTouchEvent() ——用来处理事件   返回true则表示该View能处理该事件,事件将终止向上传递(传递给其父View) 返回false表示不能处理,则把事件传递给其父View的onTouchEvent()方法来处理 ## 7,Android activity生命周期,onStop 和onPause的区别,生命周期也是想着说的,说了几个方法,没具体的说,然后就问onStop 和onPause的区别,模棱两可说了点,感觉不怎么对, ![lifecycle](https://github.com/xuyang-happy/ByteDance_Interview/blob/master/Activity/lifecycle.jpg) 关于activity的四个状态: running-poused-stopped-killed running->当前显示在屏幕的activity(位于任务栈的顶部),用户可见状态。 poused->依旧在用户可见状态,但是界面焦点已经失去,此Activity无法与用户进行交互。 stopped->用户看不到当前界面,也无法与用户进行交互 完全被覆盖. killed->当前界面被销毁,等待这系统被回收 oncreate()->onstart()->onResume()->onRestart()->onPouse()->onStop()->onDestory() ### 拓展:问题1:onStart和onResume、onPause以及onStop的实质区别? onStart和onStop是从Activity是否可见这个角度来回调的,而onResume和onPause是从Activity是否位于前台这个角度来回调的。 onstart表示Activity可见,但是还不能与用户进行交互,可以理解为Activity已经显示出来了,但是我们还看不见。 onStop表示Activity即将停止,此时可以做一些稍微重量级的回收工作,但不能太耗时,此时Activity已经变得不可见。 onResume表示此时Activity从后台切换到前台,可以与用户进行交互,于onstart相比,onStart和onResume都表示Activity可见,但onstart的时候Activity还在后台,而onResume时Activity从后台切换到前台。 onPause表示Activity正在停止,此时Activity切换到后台,不能与用户进行交互。不能再onPause中做重量级的操作。 从Activity的整个生命周期来看,onCreate和ondestory是配对的,分别标识着Activity的创建与销毁,并且只能有一次被调用。 从Activity的可见来说,onstart和onStop是配对的,随着用户的操作或者设备屏幕的点亮与熄灭,这两个方法可能被多次调用。 从Activit是否在前台来说,onResume和onPause是配对的,随着用户的操作或者设备屏幕的点亮与熄灭,这两个方法可能多次被调用。 #### 问题2:有两个Activity,分别为Activity A和Activity B,用A启动B ,B 的onResume和A的onPause哪个先执行? 追踪android的源码可知,在新的activity启动之前,栈顶的Activity需要先onPause后,新的Activity才能执行。因此在启动ActivityB之前先执行Activity A的onPause,然后启动Activity B 执行Activity B的onResume。 ## 8,然后开始写代码; 单例设计模式,线程安全的,写过几遍,很快速就写了出来 快速排序,磕磕绊绊也没写好,最后放弃了 两段代码已写出,在上面 # 写在最后: 这是第一次面试,对于一个只接触4个月互联网的萌新来说确实有点残酷了,(不过一般给面试管都说1年开发经验了) 也是自己努力不够。主要呈现的问题就是,一个问题问出来,也能说点,但是再深入一点就不知道了,深入不够。 还有就是刷题部分, 这个是刚开始刷一点,需要继续刷下去。下一步,明天是网易的当面的,更是紧张点。不过没关系,没有通过就继续上学了,也知道自己该怎么努力了。 两大点,java 基础还不行,Android 各方面知识还不完善,需要就是深入源码,自己要去分析。 再以后的项目中,一点要深入,每段代码怎么来的,为什么要这么写,一定弄要明白!!! # 续 ### 1,UI适配 ### 2,什么是广播,什么是广播接收者 ### 3,Activity四种启动方式 #### 1.默认启动模式standard: 该模式可以被设定,不在manifest设定时候,Activity的默认模式就是standard。在该模式下,启动的Activity会依照启动顺序被依次压入Task中 #### 2.栈顶复用模式singleTop: 在该模式下,如果栈顶Activity为我们要新建的Activity(目标Activity),那么就不会重复创建新的Activity。 #### 3.栈内复用模式singleTask: 与singleTop模式相似,只不过singleTop模式是只是针对栈顶的元素,而singleTask模式下,如果task栈内存在目标Activity实例,则: 1 将task内的对应Activity实例之上的所有Activity弹出栈。 2.将对应Activity置于栈顶,获得焦点。 #### 4.全局唯一模式singleInstance: 这是我们最后的一种启动模式,也是我们最恶心的一种模式:在该模式下,我们会为目标Activity分配一个新的affinity,并创建一个新的Task栈,将目标Activity放入新的Task,并让目标Activity获得焦点。新的Task有且只有这一个Activity实例。 如果已经创建过目标Activity实例,则不会创建新的Task,而是将以前创建过的Activity唤醒(对应Task设为Foreground状态) ### 4,set集合 Set不保存重复的元素,Set中最常被使用的是测试归属性,查找成为Setz中最重要的操作, ### 5,谈谈对java面向对象思想的理解 《Java编程思想》中提到“万物皆为对象”的概念。它将对象视为一种奇特的变量,它除了可以存储数据之外还可以对它自身进行操作。 它能够直接反映现实生活中的事物,例如人、车、小鸟等,将其表示为程序中的对象。每个对象都具有各自的状态特征(也可以称为属性)及行为特征(方法),java就是通过对象之间行为的交互来解决问题的。 面向对象就是把构成问题的事物分解成一个个对象,建立对象不是为了实现一个步骤,而是为了描述某个事物在解决问题中的行为。 类是面向对象中的一个很重要的概念,因为类是很多个具有相同属性和行为特征的对象所抽象出来的,对象是类的一个实例。 类具有三个特性:封装、继承和多态。 封装:核心思想就是“隐藏细节”、“数据安全”,将对象不需要让外界访问的成员变量和方法私有化,只提供符合开发者意愿的公有方法来访问这些数据和逻辑,保证了数据的安全和程序的稳定。 继承:子类可以继承父类的属性和方法,并对其进行拓展。 多态:同一种类型的对象执行同一个方法时可以表现出不同的行为特征。通过继承的上下转型、接口的回调以及方法的重写和重载可以实现多态。 #### 6,反转队列,树的遍历 #### 7,View的绘制流程 #### 8,kotlin 优点 #### 9,flutter相关 #### 10,JVM #### 11,OkHttp