android底层架构图

说下冷启动与热启动是什么,区别,如何优化,使用场景等。

app冷启动: 当应用启动时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用, 这个启动方式就叫做冷启动(后台不存在该应用进程)。冷启动因为系统会重新创建一个新的进程分配给它,所以会先创建和初始化Application类,再创建和初始化MainActivity类(包括一系列的测量、布局、绘制),最后显示在界面上。

app热启动: 当应用已经被打开, 但是被按下返回键、Home键等按键时回到桌面或者是其他程序的时候,再重新打开该app时, 这个方式叫做热启动(后台已经存在该应用进程)。热启动因为会从已有的进程中来启动,所以热启动就不会走Application这步了,而是直接走MainActivity(包括一系列的测量、布局、绘制),所以热启动的过程只需要创建和初始化一个MainActivity就行了,而不必创建和初始化Application

冷启动的流程当点击app的启动图标时,安卓系统会从Zygote进程中fork创建出一个新的进程分配给该应用,之后会依次创建和初始化Application类、创建MainActivity类、加载主题样式Theme中的windowBackground等属性设置给MainActivity以及配置Activity层级上的一些属性、再inflate布局、当onCreate/onStart/onResume方法都走完了后最后才进行contentView的measure/layout/draw显示在界面上

冷启动的生命周期简要流程:Application构造方法 –> attachBaseContext()–>onCreate –>Activity构造方法 –> onCreate() –> 配置主体中的背景等操作 –>onStart() –> onResume() –> 测量、布局、绘制显示

冷启动的优化主要是视觉上的优化,解决白屏问题,提高用户体验,所以通过上面冷启动的过程。能做的优化如下:

  1. 减少 onCreate()方法的工作量

  2. 不要让 Application 参与业务的操作

  3. 不要在 Application 进行耗时操作

  4. 不要以静态变量的方式在 Application 保存数据

  5. 减少布局的复杂度和层级

  6. 减少主线程耗时

为什么冷启动会有白屏黑屏问题?原因在于加载主题样式Theme中的windowBackground等属性设置给MainActivity发生在inflate布局当onCreate/onStart/onResume方法之前,而windowBackground背景被设置成了白色或者黑色,所以我们进入app的第一个界面的时候会造成先白屏或黑屏一下再进入界面。解决思路如下

1.给他设置 windowBackground 背景跟启动页的背景相同,如果你的启动页是张图片那么可以直接给 windowBackground 这个属性设置该图片那么就不会有一闪的效果了

Android 新能优化:

一般从下面几个方面入手

  • 业务/功能

  • 符合逻辑的交互

  • 优秀的性能

1 线程

主线程太忙则需要注意了,主线程关键职责是处理用户交互,在屏幕上绘制像素,并进行加载显示相关的数据,所以特别需要避免任何主线程的事情,这样应用程序才能保持对用户操作的即时响应。总结起来,主线程主要做以下几个方面工作:

  • UI 生命周期控制

  • 系统事件处理

  • 消息处理

  • 界面布局

  • 界面绘制

  • 界面刷新

除此之外,应该尽量避免将其他处理放在主线程中,特别复杂的数据计算和网络请求等。

优化建议:

那布局优化有哪些方法呢,主要通过减少层级、减少测量和绘制时间、提高复用性三个方面入手。总结如下:

  • 减少层级。合理使用 RelativeLayout 和 LinerLayout,合理使用Merge。

  • 提高显示速度。使用 ViewStub,它是一个看不见的、不占布局位置、占用资源非常小的视图对象。

  • 布局复用。可以通过 标签来提高复用。

  • 尽可能少用wrap_content。wrap_content 会增加布局 measure 时计算成本,在已知宽高为固定值时,不用wrap_content 。

  • 删除控件中无用的属性。

2,避免过度绘制

过度绘制是指在屏幕上的某个像素在同一帧的时间内被绘制了多次。在多层次重叠的 UI 结构中,如果不可见的 UI 也在做绘制的操作,就会导致某些像素区域被绘制了多次,从而浪费了多余的 CPU 以及 GPU 资源。

如何避免过度绘制呢,如下:

  • 布局上的优化。移除 XML 中非必须的背景,移除 Window 默认的背景、按需显示占位背景图片

  • 自定义View优化。使用 canvas.clipRect()来帮助系统识别那些可见的区域,只有在这个区域内才会被绘制。

3,启动优化

通过对启动速度的监控,发现影响启动速度的问题所在,优化启动逻辑,提高应用的启动速度。启动主要完成三件事:UI 布局、绘制和数据准备。因此启动速度优化就是需要优化这三个过程:

  • UI 布局。应用一般都有闪屏页,优化闪屏页的 UI 布局,可以通过 Profile GPU Rendering 检测丢帧情况。

  • 启动加载逻辑优化。可以采用分布加载、异步加载、延期加载策略来提高应用启动速度。

  • 数据准备。数据初始化分析,加载数据可以考虑用线程初始化等策略。

4,合理的刷新机制

在应用开发过程中,因为数据的变化,需要刷新页面来展示新的数据,但频繁刷新会增加资源开销,并且可能导致卡顿发生,因此,需要一个合理的刷新机制来提高整体的 UI 流畅度。合理的刷新需要注意以下几点:

  • 尽量减少刷新次数。

  • 尽量避免后台有高的 CPU 线程运行。

  • 缩小刷新区域。

5,其他

在实现动画效果时,需要根据不同场景选择合适的动画框架来实现。有些情况下,可以用硬件加速方式来提供流畅度。

ANR

ANR即Application Not Responding应用无响应,一般在ANR的时候会弹出一个应用无响应对话框。

一般情况下应用无响应的时候会产生一个日志文件,位于/data/anr/文件夹下面,trace文件是Android Davik虚拟机在收到异常终止信号时产生的,最常见的一个触发条件就是Android应用中产生了FC(force close)

产生的原因:

1 Activity的UI在5秒内没有响应输入事件(例如,按键按下,屏幕触摸)–主要类型

2 BroadcastReceiver在10秒内没有执行完毕

3 Service在特定时间内(20秒内)无法处理完成–小概率类型

我们需要注意的:

应该避免在主线程做太多耗时的操作,网络请求不用说了,Android4.0以后就禁止在主线程成执行请求了,除此之外就是要注意如下几个方面:

  • 主线程频繁进行IO操作,比如读写文件或者数据库;

  • 硬件操作如进行调用照相机或者录音等操作;

  • 多线程操作的死锁,导致主线程等待超时;

  • 主线程操作调用join()方法、sleep()方法或者wait()方法;

  • system server中发生WatchDog ANR;

  • service binder的数量达到上限。

如何避免ANR

  • 避免在主线程进行复杂耗时的操作,特别是文件读取或者数据库操作;

  • 避免频繁实时更新UI;

  • BroadCastReceiver 要进行复杂操作的的时候,可以在onReceive()方法中启动一个Service来处理;

  • 避免在IntentReceiver里启动一个Activity,因为它会创建一个新的画面,并从当前用户正在运行的程序上抢夺焦点。如果你的应用程序在响应Intent广 播时需要向用户展示什么,你应该使用Notification Manager来实现。

  • 在设计及代码编写阶段避免出现出现同步/死锁或者错误处理不恰当等情况。

ANR的原因

1.耗时的网络访问2.大量的数据读写3.数据库操作4.硬件操作(比如camera)5.调用thread的join()方法、sleep()方法、wait()方法或者等待线程锁的时候6.service binder的数量达到上限7.system server中发生WatchDog ANR8.service忙导致超时无响应9.其他线程持有锁,导致主线程等待超时10.其它线程终止或崩溃导致主线程一直等待

android中导致内存泄漏的主要几个点

  • 使用单例模式

  • 使用匿名内部类

  • 使用异步事件处理机制Handler

  • 使用静态变量

  • 资源未关闭

  • 设置监听 EventBus ,广播没有关闭

  • 使用AsyncTask

  • 使用Bitmap

Android 内存管理

Android是一个基于Linux实现的操作系统。但对于Linux内核来说,Android也仅仅只是一个运行在内核之上的应用程序,与其他运行在内核之上的应用程序没有任何区别。所以Android需要一套机制管理运行在Linux进程中的APK应用程序。Android内存管理包含两部分,一部分是Framework对内存的管理,一部分是Linux内核对内存管理,这两部分共同决定应用程序的生命周期

android 事件分发机制

基本会遵从 Activity => ViewGroup => View 的顺序进行事件分发,然后通过调用 onTouchEvent() 方法进行事件的处理。我们在项目中一般会对 MotionEvent.ACTION_DOWN,MotionEvent.ACTION_UP,MotionEvent.ACTION_MOVE,MotionEvent.ACTION_CANCEL 分情况进行操作。

1 activity的生命周期

onCreate-->表示activity被创建这是生命周期的第一个方法,主要可以做一些初始化工作,比如加载布局界面,初始化activity的数据

onStart-->表示activity正在被创建这时Activity已经可见了,但是还是没有出现在前台,还无法和用户交互.

onResume-->表示Activity已经可见了,并且出现在前台并开始活动

onPause-->表示Activity正在停止,正常情况下,紧接着onStop就会被调用。在特殊情况下,如果这个时候迅速地再回到当前Activity,那么onResume会被调用.

onStop-->表示Activity即将停止,可以做一些稍微重量级的回收工作,同样不能太耗时。

onDestory-->表示Activity即将被销毁,这是Activity生命周期中的最后一个回调,在这里,我们可以做一些回收工作和最终的资源释放。

2 onStart()和onResume()/onPause()和onStop()的区别?

onStart() 时activity出于可见,但是由于没有出现在前台无法与用户交互onresume()activity可见可以交互

onpause()可见不可点击(比如弹出对话框)onStop 不可见

3 Activity A启动另一个Activity B会回调哪些方法?如果Activity B是完全透明呢?如果启动的是一个Dialog呢?

启动另一个Activity B的话,A会回调onPause()、onStop()、onDestroy()方法。

如果Activity B是完全透明的,A会回调onPause()。

如果启动一个Dialog,如果是当前Activity弹出的dialog则不会执行Activity任何生命周期中的方法,只有其它Activity弹出了Dialog或者拦住了当前的Activity才会执行onPause() 。

A启动B:A的onPause() -->B的onCreate()-->onStart()-->onResume()-->A的onStop()

A启动B,B完全透明:A的onPause() -->B的onCreate()-->onStart()-->onResume()

A界面启动一个dialog:同上。

back键:

onPause -->onStop -->onDestory;

4 谈谈onSaveInstanceState()方法?何时会调用?

当某个Activity变得容易被系统销毁时,该Activity的onSaveInstanceState函数就会被执行,除非该Activity是被用户主动销毁的,如当用户按back键时。

1.当用户按下Home键时;

2.长按Home键,选择运行其他的程序时;

3.按下电源键(关闭屏幕显示)时;

4.从Activity A启动一个新的Activity时;

5.屏幕方向切换时,如从横屏切换到竖屏;

6.电话打入等情况发生时;

5 onSaveInstanceState()与onPause()的区别?

onSaveInstanceState()只会出现在Activity被异常终止的情况下,正常情况下不会别调用,而onPause()会。

6 如何避免配置改变时Activity重建?

在清单文件下每个activity注册时写上

android:configChanges=“XXX”

比如横竖屏切换:android:configChanges=“orientation”

7 优先级低的Activity在内存不足被回收后怎样做可以恢复到销毁前状态?

低优先级的Activity在内存不足被回收后重新启动会引发Activity重建,会调用onRestoreInstanceState方法,并将onSaveInstanceState方法保存的Bunble对象作为参数传到onRestoreInstanceState 和 onCreate方法中。因此可通过onRestoreInstanceState(Bundle savedInstanceState)和onCreate((Bundle savedInstanceState)来判断Activity是否被重建,并取出数据进行恢复。在onCreate中需要判断savedInstanceState是否为空。

8 如何避免优先级低的Activity在内存不足被回收

1.当app处于后台被系统回收时,app的进程被杀死了,Activity 也被回收了,而app的task和activity栈以及相应的intent和数据会被系统保存起来。当app被切回前台时,系统会恢复task和activity栈以及相应的intent和数据。

2.不要在Application类和全局单例类中存放数据,会导致app无法正确恢复状态。运行时的临时数据应存放在SharedPreference、临时文件或数据库中

3 Activity之间传数据应该用系统提供的intent机制。

9 如何启动其他应用的Activity?

在保证有权限访问的情况下,通过隐式Intent进行目标Activity的IntentFilter匹配

10 说下Activity的四种启动模式?

standard标准模式、默认模式:每次启动一个Activity都会创建一个新的实例,并放入栈顶位置。

(standard模式下,会不断地新建activity实例,都放入同一个task中)

singleTop栈顶复用模式:如果启动的Activity已经位于任务栈的栈顶,就不会重新创建实例,而是调用onNewIntent(intent)方法。反之创建新的实例加入栈中。

singleTask栈内复用模式:只要该Activity在一个任务栈中存在,就不会重新创建新的实例。并把栈中在其之上的其他Activity Destroy掉,调用onNewIntent(intent)方法。如果不存在,创建新的实例并入栈。

singleInstance单实例模式:Activity只能单独位于一个任务栈中,并且这个任务栈只存在这一个实例。

11 谈谈singleTop和singleTask的区别以及应用场景

singleTop允许同个Activity在栈中可以有多个实例,即可以重复创建;为防止快速点击时多次startActivity,可以将目标Activity设置为singleTop。

singleTask同个Activity在栈中只有一个实例,即不存在重复创建;常用于主页和登陆页

12 onNewIntent()调用时机?

启动模式为singleTop或singleTask的Activity会出现回调onNewIntent()的情况

13 了解哪些Activity启动模式的标记位?

Intent.FLAG_ACTIVITY_NEW_TASK:使用一个新的Task来启动Activity

Intent.FLAG_ACTIVITY_SINGLE_TOP:类似singleTop

Intent.FLAG_ACTIVITY_CLEAR_TOP:类似singleTask

Intent.FLAG_ACTIVITY_NO_HISTORY:使用这种模式启动Activity,当该Activity启动其他Activity后,该Activity就消失了,不会保留在Task栈中。

14 Activity的启动过程?

(两种方式启动一个Activity,一是在Launcher中点击应用的图标;二是在应用内部,通过函数调用启动Activity)

(手机桌面也是一个App,每一个应用的icon都罗列在Launcher上,点击icon触发onItemClick事件)

Activity划分为两种类型,根Activity和子Activity。

根Activity:以快捷图标的形式显示在Launcher中,它的启动过程代表了一个Android应用程序的启动过程。

子Activity:由根Activity或其他字Activity启动,他们有可能与启动他们的Activity运行在同一个进程中,也可能运行在不同的进程中。

因此,接下来我们将分析根Activity的启动流程,其他两种启动流程类似

根Activity的启动流程

子Activity在进程内的启动流程

子Activity在新进程的启动流程

根Activity的启动流程

我们以Launcher组件启动新的应用程序的MainActivity为例。其过程大概分为以下6步:

(1) Launcher组件向ActivityManagerService发送一个启动MainActivity的进程间通信请求

(2) ActivityManagerService首先将要启动的MainActivity信息保存起来,然后再向Launcher发送一个进入终止状态的进程间通信请求

(3) Launcher组件进入终止状态后就会向ActivityManagerService发送一个已经进入终止状态的进程间通信请求,以便ActivityManagerService可以继续执行MainActivity的启动

(4) ActivityManagerService发现用于运行MainActivity的应用程序进程不存在,因此会先启动一个新的应用程序进程

(5) 新的应用程序进程启动后就会向ActivityManagerService发送一个启动完成的进程间通信请求,以便ActivityManagerService可以继续执行MainActivity的启动

(6) ActivityManagerService将第二步保存 起来的MainActivity信息发送第四步创建的新的应用程序进程,以便将MainActivity启动起来

当APP启动后,打开某一界面,然后点击手机HOME键,使应用程序退到后台;当再次打开App时,如何保证App当前显示页面还是刚才退出时的页面?

答:在 Manifest.xml 中 application 标签内 设置启动模式 为 singleInstance ,即可。

android:launchMode="singleInstance"

从第一页跳转到第二页,然后再从第二页跳回第一页后,如何保证第一页面还是跳转前的状态?

在第一页写 Intent 跳转时,不要销毁此页;且 在 Manifest.xml 里 给第一页的 <activity>标签 加 launchMode 为

singleTask 或  SingleInstance;

android:launchMode="singleTask"

android:launchMode="singleInstance"

Android应用程序窗口(Activity)的窗口对象(Window)的创建过程分析

步骤一:Activity.attach

步骤二:PolicyManager.makeNewWindow

步骤三:Policy.makeNewWindow

步骤四:new PhoneWindow

步骤五:Window.setCallback

步骤六:Window.setSoftInputMode

步骤七:Window.setWindowManager

步骤八:mContext.getSystemService(Context.WINDOW_SERVICE);

步骤九: ((WindowManagerImpl)wm).createLocalWindowManager(this);

16 谈一谈Fragment的生命周期?

Fragment生命周期方法:onAttach() -> onCreate() -> onCreateView() -> onActivityCreated() -> onStart() -> onResume() -> onPause() -> onStop() -> onDestroyView() -> onDestroy() -> onDetach()

onAttach() 当Fragment和Activity建立关联时调用

onCreateView() 当Fragment创建视图时调用

onActivityCreated() 当与Fragment相关联的Activity完成onCreate()之后调用

onDestroyView() 在Fragment中的布局被移除时调用

onDetach() 当Fragment和Activity解除关联时调用

17 Activity和Fragment的异同?

相同点:它们都可包含布局、可有自己的生命周期,Fragment可看似迷你活动。

不同点:Fragment是依附在Activity上的,多了些和宿主Activity相关的生命周期方法,如onAttach()、onCreateView()、onActivityCreated()、onDestroyView()、onDetach();另外,Fragment的生命周期方法是由宿主Activity而不是操作系统调用的。

Activity 与 Fragment 通信方式总结

Handler

广播

EventBus

接口回调

Bundle和setArguments(bundle)

18 Activity和Fragment的关系?

它可作为Activity界面的组成部分,可在Activity运行中实现动态地加入、移除和交换。

一个Activity中可同时出现多个Fragment,一个Fragment也可在多个Activity中使用。

Activity的FragmentManager负责调用队列中Fragment的生命周期方法,只要Fragment的状态与Activity的状态保持了同步,宿主Activity的FragmentManager便会继续调用其他生命周期方法以继续保持Fragment与Activity的状态一致。

19 何时会考虑使用Fragment?

用两个Fragment封装两个界面模块,这样只使一套代码就能适配两种设备,达到两种界面效果

单一场景切换时使用Fragment更轻量化,如ViewPager和Fragment搭配使用

Service

20 谈一谈Service的生命周期?

onCreate():服务第一次被创建时调用

onStartCommand():服务启动时调用

onBind():服务被绑定时调用

onUnBind():服务被解绑时调用

onDestroy():服务停止时调用

21 Service的两种启动方式?区别在哪?

第一种:startService()方法可以启动一个Service,并回调服务中的onStartCommand()方法。如果该服务之前还没创建,那么回调的顺序是onCreate() -> onStartCommand();如果服务是开启状态,在次调用startService()不会回调onCreate()方法。服务启动了之后会一直保持运行状,直到 stopService() 或 stopSelf() 方法被调用,服务停止并回调onDestroy()(无论调用多少次startService()方法,只需调用一次stopService()或stopSelf()方法,服务就会停止了)。

第二种:bindService()方法可以绑定一个Service,并回调服务中的onBind()方法。如果该服务之前还没创建,那么回调的顺序是onCreate() -> onBind();如果服务是开启状态,在次调用startService()不会回调onCreate()方法。调用方可以获取到onBind()方法里返回的IBinder对象的实例,从而实现和服务进行通信。只要调用方和服务之间的连接没有断开,服务就会一直保持运行状态,直到调用了 unbindService()方法服务会停止,回调顺序onUnBind() -> onDestroy()。

22 一个Activty先start一个Service后,再bind时会回调什么方法?此时如何做才能回调Service的onDestroy()方法?

这两种启动方法并不冲突,startService()启动Service之后,再bindService()绑定,此时只会回调onBind()方法

需要同时调用 stopService()和 unbindService()方法才能让服务销毁掉。

23 Service如何和Activity进行通信?

通过bindService()可以实现Activity调用Service中的方法。

通过广播实现Service向Activity发送消息。

24 用过哪些系统Service?

  • WINDOW_SERVICE 管理打开的窗口程序

  • LAYOUT_INFLATER_SERVICE 取得XML里定义的View

  • ACTIVITY_SERVICE 管理应用程序的系统状态

  • POWER_SERVICE 电源服务

  • ALARM_SERVICE 闹钟服务

  • NOTIFICATION_SERVICE 状态栏服务

  • KEYAUARD_SERVICE 键盘锁服务

25 是否能在Service进行耗时操作?如果非要可以怎么做?

  • Service默认并不会运行在子线程中,也不运行在一个独立的进程中,它同样执行在主线程中(UI线程)。

  • 手动打开一个子线程,否则有可能出现主线程被阻塞(ANR)的情况。

26 前台服务是什么?和普通服务的不同?如何去开启一个前台服务?

  • 前台服务的服务状态可以被用户看到。它和普通服务最大的区别是,前者会一直有一个正在运行的图标在系统的状态栏显示,下拉状态栏后可以看到更加详细的信息,且当系统内存不足服务被杀死时,通知会被移除。

  • 创建一个Notification实例,调用startForeground()方法,不需要NotificationManager将通知显示出来。

27 是否了解ActivityManagerService,谈谈它发挥什么作用?

  • ActivityManagerService是Android中最核心的服务 , 主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作。

28 如何保证Service不被杀死?

  • 在Service的onStartCommand()中设置flages值为START_STICKY,使得Service被杀死后尝试再次启动Service

  • 提升Service优先级,比如设置为一个前台服务

  • 在Activity的onDestroy()通过发送广播,并在广播接收器的onReceive()中启动Service。

BroadcastReceiver

29 广播有几种形式?什么特点?

  • 普通广播:一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎都会在同一时刻接收到这条广播消息,因此它们接收的先后是随机的。

  • 有序广播:一种同步执行的广播,在广播发出之后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递,所以此时的广播接收器是有先后顺序的,且优先级(priority)高的广播接收器会先收到广播消息。有序广播可以被接收器截断使得后面的接收器无法收到它。

  • 本地广播:发出的广播只能够在应用程序的内部进行传递,并且广播接收器也只能接收本应用程序发出的广播。

  • 粘性广播:这种广播会一直滞留,当有匹配该广播的接收器被注册后,该接收器就会收到此条广播。

30 广播的两种注册形式?区别在哪?

  • 静态注册:广播接收器即便程序未启动也能接收到广播。

  • 动态注册:广播接收器必须要在程序启动之后才能接收到广播。当用来注册广播的Activity关掉后,广播也就失效了

unregisterReceiver是关闭广播的

ContentProvider & 数据存储

31 ContentProvider了解多少?

ContentProvider可以让不同应用程序之间进行数据共享,它还可以选择只对哪一部分数据进行共享,从而保证程序中的隐私数据不会有泄漏风险。

32 Android中提供哪些数据持久存储的方法?

  • File 文件存储:Context类中提供了openFileInput()和openFileOutput()方法来打开数据文件里的文件IO流。

  • SharedPreferences存储:一种轻型的数据存储方式,常用来存储一些简单的配置信息,基于XML文件存储key-value键值对数据。

  • SQLite数据库存储:一款轻量级的关系型数据库,它的运算速度非常快,占用资源很少,在存储大量复杂的关系型数据的时可以使用。

  • ContentProvider:四大组件之一,用于数据的存储和共享,不仅可以让不同应用程序之间进行数据共享,还可以选择只对哪一部分数据进行共享,可保证程序中的隐私数据不会有泄漏风险。

33 Java中的I/O流读写怎么做?

Context类中提供了openFileInput()和openFileOutput()方法来打开数据文件里的文件IO流。

bufferedReader.readLine()读

bufferedWriter.write(text)写

34 SharePreferences适用情形?使用中需要注意什么?

  • SharePreferences是一种轻型的数据存储方式,适用于存储一些简单的配置信息,如int、string、boolean、float和long。由于系统对SharedPreferences的读/写有一定的缓存策略,即在内存中有一份该文件的缓存,因此在多进程模式下,其读/写会变得不可靠,甚至丢失数据。

SharedPreferences 是线程安全的吗?它的 commit 和 apply 方法有什么区别?

SharedPreferences 是线程安全的 进程不安全的, commit 是同步写入,apply是异步写入。

RecyclerView与ListView缓存的区别?

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

ListView和RecyclerView最大的区别在于数据源改变时的缓存的处理逻辑,ListView是”一锅端”,将所有的mActiveViews都移入了二级缓存mScrapViews,而RecyclerView则是更加灵活地对每个View修改标志位,区分是否重新bindView。

35 了解SQLite中的事务处理吗?是如何做的?

  • SQLite在做CRDU操作时都默认开启了事务,然后把SQL语句翻译成对应的SQLiteStatement并调用其相应的CRUD方法,此时整个操作还是在rollback journal这个临时文件上进行,只有操作顺利完成才会更新.db数据库,否则会被回滚。

36 使用SQLite做批量操作有什么好的方法吗?

  • 使用SQLiteDatabase的beginTransaction()方法开启一个事务,将批量操作SQL语句转化成SQLiteStatement并进行批量操作,结束后endTransaction()

37 如果现在要删除SQLite中表的一个字段如何做?

  • SQLite数据库只允许增加表字段而不允许修改和删除表字段,只能采取复制表思想,即创建一个新表保留原表想要的字段、再将原表删除。

38 使用SQLite时会有哪些优化操作?

  • 使用事务做批量操作。

  • 及时关闭Cursor,避免内存泄漏

  • 耗时操作异步化:数据库的操作属于本地IO,通常比较耗时,建议将这些耗时操作放入异步线程中处理

  • ContentValues的容量调整:ContentValues内部采用HashMap来存储Key-Value数据,ContentValues初始容量为8,扩容时翻倍。因此建议对ContentValues填入的内容进行估量,设置合理的初始化容量,减少不必要的内部扩容操作

  • 使用索引加快检索速度:对于查询操作量级较大、业务对查询要求较高的推荐使用索引

IPC

IPC简介

a.IPC(Inter-Process Communication,跨进程通信):指两个进程之间进行数据交换的过程。

b.任何一个操作系统都有对应的IPC机制。

Windows:通过剪切板、管道、油槽等进行进程间通讯。

Linux:通过命名空间、共享内容、信号量等进行进程间通讯。

Android:没有完全继承Linux,比如,其独具特色的通讯方式有Binder、Socket等等。

c.IPC的使用场景:

由于某些原因,应用自身需要采用多进程模式来实现。可能原因有:

某些模块因特殊原因要运行在单独进程中;

为加大一个应用可使用的内存,需通过多进程来获取多份内存空间。

当前应用需要向其它应用获取数据。

d.Android的进程架构:每一个Android进程都是独立的,且都由两部分组成,一部分是用户空间,另一部分是内核空间,如下图:

如此设计的优点:

稳定性、安全性高:每一个Android进程都拥有自己独立的虚拟地址空间,一方面可以限制其他进程访问自己的虚拟地址空间;另一方面,当一个进程崩溃时不至于“火烧连营”。

便于复用与管理:内核共享有助于系统维护和并发操作、节省空间。

Android中进程和线程的关系?区别?

进程:是系统进行资源分配的独立单元

线程:cpu的调度单位

进程是程序在某个数据集合上的一次运行活动;线程是进程中的一个执行路径。(进程可以创建多个线程)

为何需要进行IPC?多进程通信可能会出现什么问题?

在Android系统中一个应用默认只有一个进程,每个进程都有自己独立的资源和内存空间,其它进程不能任意访问当前进程的内存和资源,系统给每个进程分配的内存会有限制。如果一个进程占用内存超过了这个内存限制,就会报OOM的问题,很多涉及到大图片的频繁操作或者需要读取一大段数据在内存中使用时,很容易报OOM的问题,为了彻底地解决应用内存的问题,Android引入了多进程的概念,它允许在同一个应用内,为了分担主进程的压力,将占用内存的某些页面单独开一个进程,比如Flash、视频播放页面,频繁绘制的页面等。Android多进程使用很简单,只需要在AndroidManifest.xml的声明四大组件的标签中增加”android:process”属性即可,process分私有进程和全局进程,以“:”号开头的属于私有进程,其他应用组件不可以和他跑在同一个进程中;不以“:”号开头的属于全局进程,其他应用可以通过ShareUID的方式和他跑在同一个进程中;

既然使用到了多进程,就不可避免地需要使用进程间通信了。

但是多进程模式出现以下问题:

1、静态成员和单例模式完全失效

2、线程同步机制完全失效

3、SharedPreferences的可靠性下降

4、Application多次创建

因此为了避免这些问题Android中有多种IPC机制,如AIDL,Messenger,Socket,ContentProvider,但是这些机制底层全部都是用了Binder机制来实现的

Android中IPC的几种方式详细分析与优缺点分析

1 使用Bundle   ----> 用于android四大组件间的进程间通信

2 使用文件共享  ---->用于单线程读写

3 使用Messenger   ---->用于可存放在message中的数据的传递

4 .AIDL android 接口定义语言  ---->主要用于调用远程服务的方法的情况 还可以注册接口

5 ContentProvider方式  实现对另一个应用进程开放provider数据的查询

6 Socket方法实现Ipc   这种方式也可以实现 但是不常用

什么是序列化?Serializable接口和Parcelable接口的区别?为何推荐使用后者?

所谓的序列化指的是把对象转换成字节序列的过程,也可以称之为对象流,可以保存到文件中,也可以用来网络传输数据。

反序列化既是相反的过程,可以从我们的文件中把对象流(字节序列)读出来,转换为对象供我们使用。

Serializable接口(是JavaSE本身就支持的)

Parcelable接口(是Android特有功能,效率比实现Serializable接口高效,可用于Intent数据传递,也可以用于进程间通信(IPC))。实现Serializable接口非常简单,声明一下就可以了,而实现Parcelable接口稍微复杂一些,但效率更高,推荐用这种方法提高性能。

binder是什么

Binder机制

Binder是Android系统中的一种IPC进程间通信结构。

Binder的整个设计是C/S结构,客户端进程通过获取服务端进程的代理,并通过向这个代理接口方法中读写数据来完成进程间的数据通信。

Android之所以选择Binder,有2个方面的原因。

1是安全,每个进程都会被Android系统分配UID和PID,不像传统的在数据里加入UID,这就让那些恶意进程无法直接和其他进程通信,进程间通信的安全性得到提升。

2是高效,像Socket之类的IPC每次数据拷贝都需要2次,而Binder只要1次,在手机这种资源紧张的情况下很重要。

1.客户端获取服务端的代理对象(proxy)。我们需要明确的是客户端进程并不能直接操作服务端中的方法,如果要操作服务端中的方法,那么有一个可行的解决方法就是在客户端建立一个服务端进程的代理对象,这个代理对象具备和服务端进程一样的功能,要访问服务端进程中的某个方法,只需要访问代理对象中对应的方法即可;

2.客户端通过调用代理对象向服务端发送请求。

4.服务端进程处理客户端发过来的请求,处理完之后通过Binder驱动返回处理结果给客户端的服务端代理对象;

5.代理对象将请求结果进一步返回给客户端进程。

通过以上5个步骤,就完成了一次Binder通信。

Binder机制的组成

Android中为何新增Binder来作为主要的IPC方式?

技术点:Binder机制

思路:回答Binder优点

参考回答:Binder机制有什么几条优点:

1. 传输效率高、可操作性强:传输效率主要影响因素是内存拷贝的次数,拷贝次数越少,传输速率越高。从Android进程架构角度分析:对于消息队列、Socket和管道来说,数据先从发送方的缓存区拷贝到内核开辟的缓存区中,再从内核缓存区拷贝到接收方的缓存区,一共两次拷贝,如图:

而对于Binder来说,数据从发送方的缓存区拷贝到内核的缓存区,而接收方的缓存区与内核的缓存区是映射到同一块物理地址的,节省了一次数据拷贝的过程,如图:

由于共享内存操作复杂,综合来看,Binder的传输效率是最好的。

2. 实现C/S架构方便:Linux的众IPC方式除了Socket以外都不是基于C/S架构,而Socket主要用于网络间的通信且传输效率较低。Binder基于C/S架构 ,Server端与Client端相对独立,稳定性较好。

3. 安全性高:传统Linux IPC的接收方无法获得对方进程可靠的UID/PID,从而无法鉴别对方身份;而Binder机制为每个进程分配了UID/PID且在Binder通信时会根据UID/PID进行有效性检测。

使用Binder进行数据传输的具体过程?

技术点:Binder机制

思路:通过AIDL实现方式解释Binder数据传输的具体过程

参考回答:服务端中的Service给与其绑定的客户端提供Binder对象,客户端通过AIDL接口中的asInterface()将这个Binder对象转换为代理Proxy,并通过它发起RPC请求。客户端发起请求时会挂起当前线程,并将参数写入data然后调用transact(),RPC请求会通过系统底层封装后由服务端的onTransact()处理,并将结果写入reply,最后返回调用结果并唤醒客户端线程。

Binder框架中ServiceManager的作用?

技术点:Binder机制

思路:从Binder框架出发讨论每个元素的作用

参考回答:在Binder框架定义了四个角色:Server,Client,ServiceManager和Binder驱动。其中Server、Client、ServiceManager运行于用户空间,Binder驱动运行于内核空间。关系如图:

Server&Client:服务器&客户端。在Binder驱动和Service Manager提供的基础设施上,进行Client-Server之间的通信。

ServiceManager服务的管理者,将Binder名字转换为Client中对该Binder的引用,使得Client可以通过Binder名字获得Server中Binder实体的引用。流程如图:

Binder驱动:

与硬件设备没有关系,其工作方式与设备驱动程序是一样的,工作于内核态。

提供open()、mmap()、poll()、ioctl() 等标准文件操作。

以字符驱动设备中的misc设备注册在设备目录/dev下,用户通过/dev/binder访问该它。

负责进程之间binder通信的建立,传递,计数管理以及数据的传递交互等底层支持。

驱动和应用程序之间定义了一套接口协议,主要功能由ioctl() 接口实现,由于ioctl()灵活、方便且能够一次调用实现先写后读以满足同步交互,因此不必分别调用write()和read()接口。

其代码位于linux目录的drivers/misc/binder.c中。

Android中有哪些基于Binder的IPC方式?简单对比下?

技术点:IPC方式

思路:分析每种IPC方式的优缺点和使用场景的差异

参考回答

是否了解AIDL?原理是什么?如何优化多模块都使用AIDL的情况?

技术点:AIDL

参考回答:

工作原理:每个业务模块创建自己的AIDL接口并实现此接口,然后向服务端提供自己的唯一标识和其对应的Binder对象。服务端只需要一个Service,服务器提供一个queryBinder接口,它会根据业务模块的特征来返回相应的Binder对像,不同的业务模块拿到所需的Binder对象后就可进行远程方法的调用了。

流程如图:

AIDL接口:继承IInterface。

Stub类:Binder的实现类,服务端通过这个类来提供服务。

Proxy类:服务器的本地代理,客户端通过这个类调用服务器的方法。

asInterface():客户端调用,将服务端的返回的Binder对象,转换成客户端所需要的AIDL接口类型对象。返回对象。

asBinder():根据当前调用情况返回代理Proxy的Binder对象。

onTransact():运行服务端的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理。

transact():运行在客户端,当客户端发起远程请求的同时将当前线程挂起。之后调用服务端的onTransact()直到远程请求返回,当前线程才继续执行。

若客户端和服务端位于同一进程,则直接返回Stub对象本身;

否则,返回的是系统封装后的Stub.proxy对象。

AIDL(Android Interface Definition Language,Android接口定义语言):如果在一个进程中要调用另一个进程中对象的方法,可使用AIDL生成可序列化的参数,AIDL会生成一个服务端对象的代理类,通过它客户端实现间接调用服务端对象的方法。

AIDL的本质是系统提供了一套可快速实现Binder的工具。关键类和方法:当有多个业务模块都需要AIDL来进行IPC,此时需要为每个模块创建特定的aidl文件,那么相应的Service就会很多。必然会出现系统资源耗费严重、应用过度重量级的问题。解决办法是建立Binder连接池,即将每个业务模块的Binder请求统一转发到一个远程Service中去执行,从而避免重复创建Service。

View

MotionEvent是什么?包含几种事件?什么条件下会产生?

  • ACTION_DOWN:第一个手指触摸屏幕

  • ACTION_MOVE:手指在屏幕上移动

  • ACTION_UP:最后一个手指离开屏幕

  • ACTION_CANCEL:手势被取消,不再接受后续事件;从当前控件转移到外层控件时会触发

  • ACTION_OUTSIDE:标志着用户触碰到了正常的UI边界

  • ACTION_POINTER_DOWN:出现一个新的触摸点

  • ACTION_POINTER_UP:非最后一个手指抬起

scrollTo()和scrollBy()的区别?

  • scrollBy内部调用了scrollTo,它是基于当前位置的相对滑动;而scrollTo是绝对滑动

  • 两者都只能对view内容进行滑动,而不能使view本身滑动。

Scroller中最重要的两个方法是什么?主要目的是?

  • 在MotionEvent.ACTION_UP事件触发时调用startScroll()方法,该方法并没有进行实际的滑动操作,而是记录滑动相关量

  • 马上调用invalidate/postInvalidate()方法,请求View重绘,导致View.draw方法被执行

  • 紧接着会调用View.computeScroll()方法,此方法是空实现,需要自己处理逻辑。具体逻辑是:先判断computeScrollOffset(),若为true(表示滚动未结束),则执行scrollTo()方法,它会再次调用postInvalidate(),如此反复执行,直到返回值为false。

View 绘制的步骤?

1.测量——onMeasure():决定View的大小

2.布局——onLayout():决定View在ViewGroup中的位置

3.绘制——onDraw():如何绘制这个View。

自定义View的分类

  • 继承View

  • 继承ViewGroup

  • 继承系统控件(Button,LinearLayout…)

谈一谈View的事件分发机制?

  • 事件传递顺序:Activity(Window) -> ViewGroup -> View

  • dispatchTouchEvent:进行事件的分发(传递)。返回值是 boolean 类型,受当前onTouchEvent和下级view的dispatchTouchEvent影响

  • onInterceptTouchEvent:对事件进行拦截。该方法只在ViewGroup中有,View(不包含 ViewGroup)是没有的。一旦拦截,则执行ViewGroup的onTouchEvent,在ViewGroup中处理事件,而不接着分发给View。且只调用一次,所以后面的事件都会交给ViewGroup处理。

  • onTouchEvent:进行事件处理。

如何解决View的滑动冲突?

  • 外部拦截法:指点击事件都先经过父容器的拦截处理,如果父容器需要此事件就拦截,否则就不拦截。onInterceptTouchEvent方法

  • 内部拦截法:指父容器不拦截任何事件,而将所有的事件都传递给子容器,如果子容器需要此事件就直接消耗,否则就交由父容器进行处理。子View的dispatchTouchEvent方法并设置requestDisallowInterceptTouchEvent方法,父View需要重写onInterceptTouchEvent方法

谈一谈View的工作原理?

  • View工作流程简单来说就是,先measure测量,用于确定View的测量宽高,再 layout布局,用于确定View的最终宽高和四个顶点的位置,最后 draw绘制,用于将View 绘制到屏幕上

MeasureSpec是什么?有什么作用?

UNSPECIFIED:父容器不对View有任何限制,要多大有多大。常用于系统内部。

EXACTLY(精确模式):父视图为子视图指定一个确切的尺寸SpecSize。对应LyaoutParams中的match_parent或具体数值。

AT_MOST(最大模式):父容器为子视图指定一个最大尺寸SpecSize,View的大小不能大于这个值。对应LayoutParams中的wrap_content。

作用:通过宽测量值widthMeasureSpec和高测量值heightMeasureSpec决定View的大小

getWidth()和getMeasuredWidth()的区别?

getMeasuredWidth():只要一执行完 setMeasuredDimension() 方法,就有值了,并且不再改变。

getWidth():必须执行完 onMeasure() 才有值,可能发生改变。

如果 onLayout 没有对子 View 实际显示的宽高进行修改,那么 getWidth() 的值 == getMeasuredWidth() 的值。

onLayout() 和Layout()的区别?

onLayout() ViewGroup中子View的布局方法,layout()是子View布局的方法

View 里面的 onSavedInstanceState和onRestoreInstanceState的作用?

View和Activity一样的,每个View都有onSavedInstanceState和onRestoreInstanceState这两个方法,可用于保存和恢复view的状态。

自定义View/ViewGroup需要注意什么?

  • 设置View支持wrap_content

  • 设置View支持padding

  • 尽量不要在View中使用Handler

  • View中有线程或动画需要及时停止

  • 处理滑动嵌套

onTouch()、onTouchEvent()和onClick()关系?

优先度onTouch()>onTouchEvent()>onClick()。因此onTouchListener的onTouch()方法会先触发;如果onTouch()返回false才会接着触发onTouchEvent(),同样的,内置诸如onClick()事件的实现等等都基于onTouchEvent();如果onTouch()返回true,这些事件将不会被触发。

SurfaceView和View的区别?

  • View需要在UI线程对画面进行刷新,而SurfaceView可在子线程进行页面的刷新

  • View适用于主动更新的情况,而SurfaceView适用于被动更新,如频繁刷新,这是因为如果使用View频繁刷新会阻塞主线程,导致界面卡顿

  • SurfaceView在底层已实现双缓冲机制,而View没有,因此SurfaceView更适用于需要频繁刷新、刷新时数据处理量很大的页面

invalidate()和postInvalidate()的区别?

  • invalidate()与postInvalidate()都用于刷新View,主要区别是invalidate()在主线程中调用,若在子线程中使用需要配合handler;而postInvalidate()可在子线程中直接调用。

补充

requestLayout

View重新调用一次layout过程

invalidate

View重新调用一次draw过程

forceLayout

标识View在下一次重绘,需要重新调用layout过程。

postInvalidate

这个方法与invalidate方法的作用是一样的,都是使View树重绘,但两者的使用条件不同,postInvalidate是在非UI线程中调用,invalidate则是在UI线程中调用。

Animation

Android中有哪几种类型的动画?

(分为view动画和属性动画 view动画有补间动画和逐帧动画,属性动画有value动画和object动画)

  • ViewAnimation补间动画:容易设置和能满足许多应用程序的需要。AlphaAnimation(透明度动画)、RotateAnimation(旋转动画)、ScaleAnimation(缩放动画)、TranslateAnimation(平移动画)四种类型的补间动画。并且View动画框架还提供了动画集合类(AnimationSet),通过动画集合类可以将多个补间动画以组合的形式显示出来,不能真正的改变view的位置。

  • PropertyAnimation属性动画:这种动画可以设置给任何Object,包括那些还没有渲染到屏幕上的对象。这种动画是可扩展的,可以让你自定义任何类型和属性的动画。对该类对象进行动画操作,真正改变了对象的属性。

  • DrawableAnimation:专门用来一个一个的显示Drawable的resources,就像放幻灯片一样。

帧动画在使用时需要注意什么?

  • 使用祯动画要注意不能使用尺寸过大的图片,否则容易造成OOM

View动画和属性动画的区别?

  • View动画:通过不断图形变换实现动画效果,不能真正的改变view的位置;只能作用在View上。

  • 属性动画:通过动态改变对象的属性实现动画效果,真正改变View的位置;能作用在任何对象上。

View动画为何不能真正改变View的位置?而属性动画为何可以?

  • View动画改变的只是View的显示,而没有改变View的响应区域;而属性动画会通过反射技术来获取和执行属性的get、set方法,从而改变了对象位置的属性值。

属性动画插值器和估值器的作用?

  • 插值器(Interpolator):根据时间流逝的百分比计算出当前属性值改变的百分比。确定了动画效果变化的模式,如匀速变化、加速变化等等。

  • 类型估值器(TypeEvaluator):根据当前属性改变的百分比计算出改变后的属性值。针对于属性动画,View动画不需要类型估值器。

Drawable

了解哪些Drawable?适用场景?

  • BitmapDrawable 表示一张图片

  • NinePatchDrawable 可自动地根据所需的宽/高对图片进行相应的缩放并保证不失真(表示一张.9格式的图片)

  • ShapeDrawable 表示纯色、有渐变效果的基础几何图形(矩形,圆形,线条等)

  • LayerDrawable 可通过将不同的Drawable放置在不同的层上面从而达到一种叠加后的效果

  • StateListDrawable 表示一个Drawable的集合且每个Drawable对应着View的一种状态

  • TransitionDrawable LayerDrawable的子类,实现两层 Drawable之间的淡入淡出效果。

  • InsetDrawable 表示把一个Drawable嵌入到另外一个Drawable的内部,并在四周留一些间距。

  • ScaleDrawable 表示将Drawable缩放到一定比例。

  • ClipDrawable 表示裁剪一个Drawable。

mipmap系列中xxxhdpi、xxhdpi、xhdpi、hdpi、mdpi和ldpi存在怎样的关系?

  • 表示不同密度的图片资源,像素从高到低依次排序为xxxhdpi>xxhdpi>xhdpi>hdpi>mdpi>ldpi,根据手机的dpi不同加载不同密度的图片

dp、dpi、px的区别?

  • px:像素,如分辨率1920x1080表示高为1920个像素、宽为1080个像素

  • dpi:每英寸的像素点

  • dp:密度无关像素,是个相对值

res目录和assets目录的区别?

  • res/raw中的文件会被映射到R.java文件中,访问时可直接使用资源ID,不可以有目录结构

  • assets文件夹下的文件不会被映射到R.java中,访问时需要AssetManager类,可以创建子文件夹

Window

Activity、View、Window三者之间的关系?

Activity像一个工匠(控制单元),Window像窗户(承载模型),View像窗花(显示视图)LayoutInflater像剪刀,Xml配置像窗花图纸。

1:Activity构造的时候会初始化一个Window,准确的说是PhoneWindow。

2:这个PhoneWindow有一个“ViewRoot”,这个“ViewRoot”是一个View或者说ViewGroup,是最初始的根视图。

3:“ViewRoot”通过addView方法来一个个的添加View。比如TextView,Button等

4:这些View的事件监听,是由WindowManagerService来接受消息,并且回调Activity函数。比如onClickListener,onKeyDown等。

Window有哪几种类型?

应用 Window、子 Window 和系统 Window。应用类 Window 对应一个 Acitivity,子 Window 不能单独存在,需要依附在特定的父 Window 中,比如常见的一些 Dialog 就是一个子 Window。系统 Window是需要声明权限才能创建的 Window,比如 Toast 和系统状态栏都是系统 Window。

Activity创建和Dialog创建过程的异同?

说一下2个的生命周期

Dialog的生命周期一共用以下6个方法:

onCreate(),show(),onStart() ,cancel(),onDismiss(),Stop() 。

Dialog仅在在第一次启动时候会执行onCreate()方法(之后无论该Dialog执行Dismiss(),cancel(),stop(),Dialog都不会再执行onCreate()方法)。

show() 和 onStart()在每次Dialog显示时都会 依次 执行。

onDismiss() 和 stop() 在每次Dialog消失的时候都会 依次 执行。

cancel() 是在点击BACK按钮或者Dialog外部时触发,然后依次执行onDismiss() 和 stop()。

Handler

什么是handler

Handler主要用于异步消息的处理:当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外一个部分在消息队列中逐一将消息取出,然后对消息进行处理,也就是发送消息和接收消息不是同步的处理。 这种机制通常用来处理相对耗时比较长的操作。

handler通过发送和处理message和Runnable对象来关联相应线程的MessageQueue

(二)handler的作用

1.可以让对应的message和Runabble在未来的某个时间点进行相应处理

2.让自己想要处理得耗时操作放在子线程,让更新UI的操作放在主线程

三、怎么使用Handler

(1)使用post(runnable)方法:(其内部其实也是调用sendMessageDelay()方法)

(A)创建一个Handler;

(B)建立runnable;

(C)把runnable Post出去;

(2)通过sendMessage方法:

(A)创建一个Handler,实现handleMessage()方法

(B)创建一个Message,通过new或者使用Message.Obtain()方法直接从MessageQueue中拿到message对象;

(C)调用handler.sendMessage()方法

  • 在非UI线程借助Handler.post(Runnable)更新UI

public class MainActivity extends AppCompatActivity {    private Handler handler = new Handler();    private TextView textView;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        textView = (TextView) findViewById(R.id.textView);        new Thread(){            @Override             public void run() {                handler.post(new Runnable() {                    @Override                    public void run() {                        textView.setText("Hello Handler");                    }                });            }        }.start();    }
}
  • 借助Handler.postDelay(Runnable, DelayTime)定时执行相关动作

public class MainActivity extends AppCompatActivity {    private Handler handler = new Handler();    private MyRunnable runnable = new MyRunnable();    private class MyRunnable implements Runnable{        @Override        public void run() {            // 执行动作            handler.postDelayed(runnable, 1000);        }}    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        handler.postDelayed(runnable, 1000);    }
}
  • 自定义Handler和Message实现自己的操作处理

public class MainActivity extends AppCompatActivity {    private Handler handler = new Handler(){        @Override        public void handleMessage(Message msg) {            switch (msg.what){   // 判断消息类型                // 根据消息类型对消息进行处理                // 可以接收 msg.arg1 , msg.arg2 , msg.obj            }}    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        new Thread(){            @Override            public void run() {                Message message = new Message();                                // 设置message传递参数                message.what = 0;                message.arg1 = 0;                message.arg2 = 0;                message.obj = null;                // 发送消息                handler.sendMessage(message);                // 或                // message.setTarget(handler);                // message.sendToTarget();            }        }.start();    }
}

ps:Handler消息拦截(不接收消息)

public class MainActivity extends AppCompatActivity {    private Handler handler = new Handler(new Callback(){@Override        public boolean handleMessage(Message msg) {            return true;     // 设置true拦截消息}   }){        @Override        public void handleMessage(Message msg) {            switch (msg.what){   // 判断消息类型                // 根据消息类型对消息进行处理                // 可以接收 msg.arg1 , msg.arg2 , msg.obj            }}    };
}

四、为什么Android要设计只能通过Handler机制更新UI?

  • 最根本的目的就是解决多线程并发问题

假设如果在一个Activity当中,有多个线程去更新UI,并且都没有加锁机制,那么会产生什么样的问题?(更新界面错乱)如果对更新UI的操作都进行加锁处理,又会产生什么样的问题?(性能下降)

  • 基于对以上目的的考虑,android给我们提供了一套更新UI的机制,我们只需遵循这样的机制,无需考虑多线程问题。

五、Handler的原理是什么?(实现机制)

  • Handler封装了消息的发送:内部会跟Looper关联

  • Looper(消息封装的载体):内部包含一个消息队列(MessageQueue),所有Handler发送的消息都会走向这个消息队列;Looper.Looper方法是一个死循环,不断的从MessageQueue取消息,如果有消息就处理消息,没有消息就阻塞

  • MessageQueue(消息队列):可以添加消息,并处理消息

handler引起的内存泄漏:

(1)原因:handler非静态内部类持有外部类的匿名引用,导致外部类无法被回收;

(2)解决办法:handler内部持有外部Activity的弱引用,并把handler改为静态内部类,然后在onDestroy方法中调用handler的removeCallBack()方法

Handler,Looper,Message Queue三者之间的关系

- Handler封装了消息的发送,也负责接收消。内部会跟Looper关联。

- Looper 消息封装的载,内部包含了MessageQueue,负责从MessageQueue取出消息,然后交给Handler处理

- MessageQueue 就是一个消息队列,负责存储消息,有消息过来就存储起来,Looper会循环的从MessageQueue读取消息。

Handler的post方法发送的是同步消息吗?可以发送异步消息吗

1 用户层面发送的都是同步消息

2 不能发送异步消息

3 异步消息只能由系统发送。

Android系统为什么不允许在子线程中访问UI?

Android的UI控件并不是线程安全的,如果多线程中并发访问可能导致UI控件处于不可预期的状态.

那为什么系统不对UI控件的访问加上锁机制呢?

缺点有两个:首先,加上锁机制会让UI访问的逻辑变得复杂;其次锁机制会降低UI访问的效率,因为锁机制会阻塞某些线程的执行.

鉴于这两个缺点,最简单且高效的方法及时采用单线程模型来处理UI操作,对于开发者来说也不是很麻烦,只是需要通过Handler切换一下UI访问的执行线程即可.

一个Thread可以有几个Looper?几个Handler?

一个线程有一个Looper 它是一个ThreadLocal ,Looper使一个线程变成Looper线程 ,可以有多个Handler

一个Thread只能有一个Looper,可以有多个Handler。

Looper有一个MessageQueue,可以处理来自多个Handler的Message;MessageQueue有一组待处理的Message,这些Message可来自不同的Handler;Message中记录了负责发送和处理消息的Handler;Handler中有Looper和MessageQueue;

如何将一个Thread线程变成Looper线程?Looper线程有哪些特点?

通过Looper.prepare()可将一个Thread线程转换成Looper线程。Looper线程和普通Thread不同,它通过MessageQueue来存放消息和事件、Looper.loop()进行消息轮询。

可以在子线程直接new一个Handler吗?那该怎么做?

可以,不同于主线程直接new一个Handler,由于子线程的Looper需要手动去创建,在创建Handler时需要多一些方法。

Message可以如何创建?哪种效果更好,为什么?

创建Message对象的几种方式:

  • Message msg = new Message();

  • Message msg = Message.obtain();

  • Message msg = handler1.obtainMessage();

后两种方法都是从整个Messge池中返回一个新的Message实例,能有效避免重复Message创建对象,因此更鼓励这种方式创建Message

ThreadLocal有什么作用?

ThreadLocal类可实现线程本地存储的功能,把共享数据的可见范围限制在同一个线程之内,无须同步就能保证线程之间不出现数据争用的问题,这里可理解为ThreadLocal帮助Handler找到本线程的Looper。

  • 底层数据结构:每个线程的Thread对象中都有一个ThreadLocalMap对象,它存储了一组以ThreadLocal.threadLocalHashCode为key、以本地线程变量为value的键值对,而ThreadLocal对象就是当前线程的ThreadLocalMap的访问入口,也就包含了一个独一无二的threadLocalHashCode值,通过这个值就可以在线程键值值对中找回对应的本地线程变量。

主线程中Looper的轮询死循环为何没有阻塞主线程?

因为 Android 的是由事件驱动的,looper.loop() 不断地接收事件、处理事件,每一个点击触摸或者 Activity 的生命周期都是运行在 Looper.loop() 的控制之下,一旦退出消息循环,应用也就停止了。只能说是对某个消息的处理阻塞了Looper.loop()。

使用Hanlder的postDealy()后消息队列会发生什么变化?

post delay的Message并不是先等待一定时间再放入到MessageQueue中,而是直接进入并阻塞当前线程,然后将其delay的时间和队头的进行比较,按照触发时间进行排序,如果触发时间更近则放入队头,保证队头的时间最小、队尾的时间最大。此时,如果队头的Message正是被delay的,则将当前线程堵塞一段时间,直到等待足够时间再唤醒执行该Message,否则唤醒后直接执行。

Looper中 quit() 和 quitSafely() 的区别

  • 当 Looper的 quit() 或 quitSafely() 被调用时,就会调用 MessageQueue 的 quit() 方法,分别对应的是 removeAllMessagesLocked() 和 removeAllFutureMessagesLocked() 方法。

  • 区别在于,quit() 会清空所有消息(无论延时或非延时),quitSafely() 只清空延时消息,最终 next 方法都是返回 null。

  • 注意:无论调用哪个方法,当 Looper 退出后,Handler 发送的消息都会失败。这时候再通过 Handler 调用 sendMessage 或 post 等方法发送消息时均返回 false,表示消息没有成功放入消息队列 MessageQueue 中,因为消息队列已经退出了。

Handler 引起的内存泄露以及解决方法

所创建的 Handler 不是静态内部类,它会隐匿持有 Activity 的引用。当 Activity 要回收的话,而 Handler 内部有可能在做耗时操作,所以 Handler 所持有的 Activity 的引用不能被释放。最终导致 Activity 没有被回收,停留在队列内存当中,造成内存泄露。

总结:静态内部类持有外部类的匿名引用,导致外部Activty 无法释放。

解决方法:

  • 将Handler 设置为静态内部类。

  • 在 Activity 的生命周期 onDestroy() 方法中,调用 handler.removeCallbacks() 方法。

  • handler 内部持有外部 activity 的弱引用。

Android中有哪些方便线程切换的类?

对Handler进一步的封装的几个类:

  • AsyncTask:底层封装了线程池和Handler,便于执行后台任务以及在子线程中进行UI操作。

  • HandlerThread:一种具有消息循环的线程,其内部可使用Handler。

  • IntentService:是一种异步、会自动停止的服务,内部采用HandlerThread。

AsyncTask相比Handler有什么优点?不足呢?

AsyncTask:创建异步任务更简单,过程可控。在使用多个异步操作并且需要进行 UI 变更时,变得复杂。

Handler:结构清晰,功能明确。对于多个后台任务时,清晰简单。在单个后台任务时,显得代码过多,结构复杂。多任务同时执行时不易精确控制线程。

子线程有哪些更新UI的方法?

主线程中定义 Handler,子线程通过 handler 发送消息,主线程 Handler 的 handleMessage 更新UI。

View.post(Runnable r) 。

用 Activity 对象的 runOnUiThread 方法。

使用AsyncTask需要注意什么?

  • AsyncTask 是一个抽象的泛型类,它提供了 Params、Progress 和 Result 这三个泛型参数,如果 AsyncTask 不需要传递具体的参数,那么三个参数可以用 Void 来代替。

  • AsyncTask提供了 4 个核心方法,除了doInBackground在线程池中执行其余在主线程中执行。

AsyncTask中使用的线程池大小?

在AsyncTask内部实现有两个线程池:

  • SerialExecutor:用于任务的排队,默认是串行的线程池,在3.0以前核心线程数为5、线程池大小为128,而3.0以后变为同一时间只能处理一个任务

  • THREAD_POOL_EXECUTOR:用于真正执行任务。

HandlerThread有什么特点?

HandlerThread是一个线程类,它继承自Thread。与普通Thread不同,HandlerThread具有消息循环的效果,这是因为它内部HandlerThread.run()方法中有Looper,能通过Looper.prepare()来创建消息队列,并通过Looper.loop()来开启消息循环。

在子线程中如何使用HandlerThread

  • 实例化一个HandlerThread对象,参数是该线程的名称;

  • 通过 HandlerThread.start()开启线程;

  • 实例化一个Handler并传入HandlerThread中的looper对象,使得与HandlerThread绑定;

  • 利用Handler即可执行异步任务;

  • 当不需要HandlerThread时,通过HandlerThread.quit()/quitSafely()方法来终止线程的执行。

IntentService的特点?

不同于线程,IntentService是服务,优先级比线程高,更不容易被系统杀死,因此较适合执行一些高优先级的后台任务;不同于普通Service,IntentService可自动创建子线程来执行任务,且任务执行完毕后自动退出。

为何不用bindService方式创建IntentService?

IntentService的工作原理是,在IntentService的onCreate()里会创建一个HandlerThread,并利用其内部的Looper实例化一个ServiceHandler对象;而这个ServiceHandler用于处理消息的handleMessage()方法会去调用IntentService的onHandleIntent(),这也是为什么可在该方法中处理后台任务的逻辑;当有Intent任务请求时会把Intent封装到Message,然后ServiceHandler会把消息发送出,而发送消息是在onStartCommand()完成的,只能通过startService()才可走该生命周期方法,因此不能通过bindService创建IntentService。

线程池的好处、原理、类型?

  • (1)线程池的好处:

    • 重用线程池中的线程,避免线程的创建和销毁带来的性能消耗;

    • 有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致阻塞现象;

    • 进行线程管理,提供定时/循环间隔执行等功能

  • (2)线程池的分类:

    • FixThreadPool:线程数量固定的线程池,所有线程都是核心线程,当线程空闲时不会被回收;能快速响应外界请求。

    • CachedThreadPool:线程数量不定的线程池(最大线程数为Integer.MAX_VALUE),只有非核心线程,空闲线程有超时机制,超时回收;适合于执行大量的耗时较少的任务

    • ScheduledThreadPool:核心线程数量固定,非核心线程数量不定;可进行定时任务和固定周期的任务。

    • SingleThreadExecutor:只有一个核心线程,可确保所有的任务都在同一个线程中按顺序执行;好处是无需处理线程同步问题。

  • (3)线程池的原理:实际上通过ThreadPoolExecutor并通过一系列参数来配置各种各样的线程池,具体的参数有:

    • corePoolSize核心线程数:一般会在线程中一直存活

    • maximumPoolSize最大线程数:当活动线程数达到这个数值后,后续的任务将会被阻塞

    • keepAliveTime非核心线程超时时间:超过这个时长,闲置的非核心线程就会被回收

    • unit:用于指定keepAliveTime参数的时间单位

    • workQueue任务队列:通过线程池的execute()方法提交的Runnable对象会存储在这个参数中。

    • threadFactory:线程工厂,可创建新线程

    • handler:在线程池无法执行新任务时进行调度

ThreadPoolExecutor的工作策略?

  • 若程池中的线程数量未达到核心线程数,则会直接启动一个核心线程执行任务。

  • 若线程池中的线程数量已达到或者超过核心线程数量,则任务会被插入到任务列表等待执行。

    • 若任务无法插入到任务列表中,往往由于任务列表已满,此时如果

      • 线程数量未达到线程池最大线程数,则会启动一个非核心线程执行任务;

      • 线程数量已达到线程池规定的最大值,则拒绝执行此任务,ThreadPoolExecutor会调用RejectedExecutionHandler的rejectedExecution方法来通知调用者。

Bitmap 加载图片的时候需要注意什么?

( BitmapRegionDecoder加载大图 )

注意内存空间消耗会导致程序崩溃问题

导致该问题的出现,一般由以下几方面原因导致:

引动设备一般存储空间非常有限。当然不同设备分配给应用的内存空间是不同的。但相对不但提高的设备分辨率而言,内存的分配仍然是相对紧张的。

Bitmap对象常常占用大量的内存空间,比如:对于2592*1936的设备,如果采用ARGB_8888的格式加载图像,内存占用将达到19MB空间。

在Anroid App中经常用到ListView,ViewPager等控件,这些控件常会包含较大数量的图片资源。

1 高效地加载大图片。

BitmapFactory类提供了一些加载图片的方法:decodeByteArray(), decodeFile(), decodeResource(), 等等。

为了避免占用较大内存,经常使用BitmapFactory.Options 类,设置inJustDecodeBounds属性为true。

2 不要在主线程处理图片。

注意两点:(1). 为了保证使用的资源能被回收,建议使用WeakReference, 以应用内存内存紧张时,回收部分资源,保证程序进程不被杀死。

(2). 避免异步任务的长时间耗时操作,在任务执行结束后,及时释放资源

3 管理Bitmap内存。

在Android开发中,加载一个图片到界面很容易,但如果一次加载大量图片就复杂多了。在很多情况下(比如:ListView,GridView或ViewPager),能够滚动的组件需要加载的图片几乎是无限多的。

有些组件的child view在不显示时会回收,并循环使用,如果没有任何对bitmap的持久引用的话,垃圾回收器会释放你加载的bitmap。这没什么问题,但当这些图片再次显示的时候,要想避免重复处理这些图片,从而达到加载流畅的效果,就要使用内存缓存和本地缓存了,这些缓存可以让你快速加载处理过的图片。

3.1 内存缓存

内存缓存以牺牲内存的代价,带来快速的图片访问。LruCache类(API Level 4之前可以使用Support Library)非常适合图片缓存任务,在一个LinkedHashMap中保存着对Bitmap的强引用,当缓存数量超过容器容量时,删除最近最少使用的成员(LRU)。

LRU算法的原理?

(核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”)。

(LRU全称是Least Recently Used,即最近最久未使用的意思)

其核心思想是使用一个Map来保存数据并使用双向链表来维持顺序——它是将插入的每一条记录都包装成一个节点,每个节点包含两个其他节点的引用,一个指向前一个节点,另一个指向后一个节点,(如下图所示:)            

Android中缓存更新策略?

  1. 内存缓存(LruCache)

2.磁盘缓存(文件缓存)——DiskLruCache分析

3 ASimpleCache

内存泄漏和内存溢出的区别?

1、内存泄漏memory leak :是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄漏似乎不会有大的影响,但内存泄漏堆积后的后果就是内存溢出。

2、内存溢出 out of memory :指程序申请内存时,没有足够的内存供申请者使用,或者说,给了你一块存储int类型数据的存储空间,但是你却存储long类型的数据,那么结果就是内存不够用,此时就会报错OOM,即所谓的内存溢出。

3、二者的关系

内存泄漏的堆积最终会导致内存溢出

内存溢出就是你要的内存空间超过了系统实际分配给你的空间,此时系统相当于没法满足你的需求,就会报内存溢出的错误。

内存泄漏是指你向系统申请分配内存进行使用(new),可是使用完了以后却不归还(delete),结果你申请到的那块内存你自己也不能再访问(也许你把它的地址给弄丢了),而系统也不能再次将它分配给需要的程序。就相当于你租了个带钥匙的柜子,你存完东西之后把柜子锁上之后,把钥匙丢了或者没有将钥匙还回去,那么结果就是这个柜子将无法供给任何人使用,也无法被垃圾回收器回收,因为找不到他的任何信息。

内存溢出:一个盘子用尽各种方法只能装4个果子,你装了5个,结果掉倒地上不能吃了。这就是溢出。比方说栈,栈满时再做进栈必定产生空间溢出,叫上溢,栈空时再做退栈也产生空间溢出,称为下溢。就是分配的内存不足以放下数据项序列,称为内存溢出。说白了就是我承受不了那么多,那我就报错,

如何阻止内存泄漏?

以下是一些阻止内存泄漏的快速动手技巧。

(1)注意集合类,例如HashMap,ArrayList,等等。因为它们是内存泄漏经常发生的地方。当它们被声明为静态时,它们的生命周期就同应用程序的生命周期一般长。

(2)注意事件监听器和回调,如果一个监听器已经注册,但是当这个类不再被使用时却未被注销,就会发生内存泄漏。

(3)“如果一个类管理它自己的内存,程序员应该对内存泄漏保持警惕,很多时候当一个对象的成员变量指向其他对象时,不再使用时需要被置为null。

谈谈MVC、MVP和MVVM,好在哪里,不好在哪里?

MVC把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller),关系如下图所示:

模型(Model):数据库相关的操作、文件的访问和数据结构等。

视图(View):专注于显示,如Web前端(HTML/CSS/Java Script)

控制器(Controller):连接模型和视图,如把视图的请求发送给模型或把数据返回给视图等

总结:MVC的实现了视图和模型的分离,避免了视图和模型糅合在一起,当视图改变的时候只要业务逻辑没变不需要改变模型;但是它有一个缺点缺点是因为MVC中的控制器并不能直接更新视图,所以MVC并不能实现视图和模型的完全分离,视图依然依赖模型的数据(数据结构)来显示,也就是说视图依赖模型。

MVP是针对MVC的缺点而进行了改进,它把软件系统分为三个基本部分:模型(Model)、视图(View)和展示器(Presenter),关系如下图所示:

模型(Model):数据库相关的操作、文件的访问和数据结构等。

视图(View):专注于显示,如Web前端(HTML/CSS/Java Script)

展示器(Presenter):连接模型和视图,处理视图的请求并根据模型更新视图。

总结:

MVP用展示器代替了控制器,而展示器是可以直接更新视图,所以MVP中展示器可以处理视图的请求并递送到模型又可以根据模型的变化更新视图,实现了视图和模型的完全分离。

由于对视图的渲染放在了Presenter中,所以视图和Presenter的交互会过于频繁。如果Presenter过多地渲染了视图,往往会使得它与特定的视图的联系过于紧密。一旦视图需要变更,那么Presenter也需要变更了。

MVVM

MVVM是MVP更进一步的发展,把软件系统分为三个基本部分:模型(Model)、视图(View)和视图模型(ViewModel),关系如下图所示:

模型(Model):数据库相关的操作、文件的访问和数据结构等。

视图(View):专注于显示,如Web前端(HTML/CSS/Java Script)

视图模型(ViewModel):连接模型和视图,视图模型和视图是双休绑定的。

总结:

MVVM用视图模型代替了MVP中的展示器,视图模型和视图实现了双向绑定,当视图发生变化的时候视图模型也会发生改变,当视图模型变化的时候视图也随之变化。

java

java内存管理机制

一般来说,内存管理主要包括内存分配和内存回收两个部分

分配 :内存的分配是由程序完成的,程序员需要通过关键字new 为每个对象申请内存空间 (基本类型除外),所有的对象都在堆 (Heap)中分配空间。

释放 :对象的释放是由垃圾回收机制决定和执行的,这样做确实简化了程序员的工作。但同时,它也加重了JVM的工作。因为,GC为了能够正确释放对象,GC必须监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等,GC都需要进行监控。

在java中,内存管理由JVM完全负责,java中的“垃圾回收器”负责自动回收无用对象占据的内存资源

什么叫java的内存泄露

在Java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是可达的,即在有向图中,存在通路可以与其相连(也就是说仍存在该内存对象的引用);其次,这些对象是无用的,即程序以后不会再使用这些对象。如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄漏,这些对象不会被GC所回收,然而它却占用内存。

注解

注解的本质就是一个继承了 Annotation 接口的接口

什么是注解

注解也叫元数据,例如我们常见的@Override和@Deprecated,注解是JDK1.5版本开始引入的一个特性,用于对代码进行说明,可以对包、类、接口、字段、方法参数、局部变量等进行注解

一般常用的注解可以分为三类:

  1. 一类是Java自带的标准注解,包括@Override(标明重写某个方法)、@Deprecated(标明某个类或方法过时)和@SuppressWarnings(标明要忽略的警告),使用这些注解后编译器就会进行检查。

  2. 一类为元注解,元注解是用于定义注解的注解,包括@Retention(标明注解被保留的阶段)、@Target(标明注解使用的范围)、@Inherited(标明注解可继承)、@Documented(标明是否生成javadoc文档)

  3. 一类为自定义注解,可以根据自己的需求定义注解

注解的用途

在看注解的用途之前,有必要简单的介绍下XML和注解区别,

注解:是一种分散式的元数据,与源代码紧绑定。

xml:是一种集中式的元数据,与源代码无绑定

当然网上存在各种XML与注解的辩论哪个更好,这里不作评论和介绍,主要介绍一下注解的主要用途:

  1. 生成文档,通过代码里标识的元数据生成javadoc文档。

  2. 编译检查,通过代码里标识的元数据让编译器在编译期间进行检查验证。

  3. 编译时动态处理,编译时通过代码里标识的元数据动态处理,例如动态生成代码。

  4. 运行时动态处理,运行时通过代码里标识的元数据动态处理,例如使用反射注入实例

ClassLoader双亲委派机制的工作流程:

1. 当前ClassLoader首先从自己已经加载的类中查询是否此类已经加载,如果已经加载则直接返回原来已经加载的类。

每个类加载器都有自己的加载缓存,当一个类被加载了以后就会放入缓存,等下次加载的时候就可以直接返回了。

2.  当前classLoader的缓存中没有找到被加载的类的时候,委托父类加载器去加载,父类加载器采用同样的策略,首先查看自己的缓存,然后委托父类的父类去加载,一直到bootstrp ClassLoader.

3.  当所有的父类加载器都没有加载的时候,再由当前的类加载器加载,并将其放入它自己的缓存中,以便下次有加载请求的时候直接返回。

面向对象编程的四大特性及其含义?

封装:是一种隐藏信息的特性。找到变化并且把它封装起来,你就可以在不影响其它部分的情况下修改或扩展被封装的变化部分,这是所有设计模式的基础,就是封装变化,因此封装的作用,就解决了程序的可扩展性。

抽象:在使用面向对象的方法设计一个软件系统时,首先就要区分出现实世界中的事物所述的类型,分析它们拥有哪些性质与功能,再将它们抽象为在计算机虚拟世界中才有意义的实体——类,在程序运行时,由类创建出对象,用对象之间的相互合作关系来模拟真实世界中事物的相互关联。

继承:子类继承父类,可以继承父类的方法及属性,实现了多态以及代码的重用,因此也解决了系统的重用性和扩展性,但是继承破坏了封装,因为他是对子类开放的,修改父类会导致所有子类的改变,因此继承一定程度上又破坏了系统的可扩展性,所以继承需要慎用,只有明确的IS-A关系才能使用,同时继承在在程序开发过程中重构得到的,而不是程序设计之初就使用继承,很多面向对象开发者滥用继承,结果造成后期的代码解决不了需求的变化了。因此优先使用组合,而不是继承,是面向对象开发中一个重要的经验。

多态:接口的多种不同的实现方式即为多态。接口是对行为的抽象,刚才在封装提到,找到变化部分并封装起来,但是封装起来后,怎么适应接下来的变化?这正是接口的作用,接口的主要目的是为不相关的类提供通用的处理服务,我们可以想象一下。比如鸟会飞,但是超人也会飞,通过飞这个接口,我们可以让鸟和超人,都实现这个接口,这就实现了系统的可维护性,可扩展性。

String、StringBuffer和StringBuilder的区别?

String:不可变类,对 String 的任何改变都会生成新的对象,内部使用 char[] 实现,存储格式是 UTF-16

StringBuilder:内容可变,继承自 AbstractStringBuilder,线程不安全

StringBuffer:内容可变,继承自 AbstractStringBuilder,几乎所有成员方法都使用 synchronized 修饰,线程安全

------------------扩展---------

String类的内容一旦声明则不可改变,而StringBuffer类与StringBuilder类声明的内容可以改变

StringBuffer类中提供的方法都是同步方法,属于安全的线程操作,而StringBuilder类中方法属于异步方法,属于非线程安全的操作。

三者在执行速度方面的比较:StringBuilder >  StringBuffer  >  String

1.如果要操作少量的数据用 = String

2.单线程操作字符串缓冲区 下操作大量数据 = StringBuilder

3.多线程操作字符串缓冲区 下操作大量数据 = StringBuffer

String a=""和String a=new String("")的的关系和异同?

a 指向常量池引用,假如 “” 在常量池中已存在,那么 a 不会创建新的对象

new String 必定会创建一个新的对象,因此 a != b,但 a.equals(b) 为 true

String a="" 创建一个对象

String a=new String("") 代表在堆内存中,创建了一个字符串对象,变量a指向该对象,而该对象又指向在常量池中的字符串常量。创建了两个对象,一个是在常量池中,一个是在堆内存中,常量池的为"aa";堆内存中为new String();

Object的equal()和==的区别?

==操作比较的是两个变量的值是否相等,对于引用型变量表示的是两个变量在堆中存储的地址是否相同,即栈中的内容是否相同。

equals操作表示的两个变量是否是对同一个对象的引用,即堆中的内容是否相同。

==比较的是2个对象的地址,而equals比较的是2个对象的内容。

装箱、拆箱什么含义?

装箱就是  自动将基本数据类型转换为包装器类型;拆箱就是  自动将包装器类型转换为基本数据类型。

int和Integer的区别

1、Integer是int的包装类,int则是java的一种基本数据类型

2、Integer变量必须实例化后才能使用,而int变量不需要

3、Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值

4、Integer的默认值是null,int的默认值是0

堆和栈的区别

最主要的区别就是栈内存用来存储局部变量和方法调用。

而堆内存用来存储Java中的对象。无论是成员变量,局部变量,还是类变量,它们指向的对象都存储在堆内存中

1.堆内存用来存放由new创建的对象和数组。

2.栈内存用来存放方法或者局部变量等

3.堆是先进先出,后进后出

4.栈是后进先出,先进后出。

29、遇见过哪些运行时异常?异常处理机制知道哪些?

常见异常 使用场合

IllegalArgumentException 参数(非 null)使用不正确

IllegalStateException 对象的状态不正确

NullPointerException 禁止使用 null 的情况下对象值为 null

IndexOutOfBoundsException 下标参数值越界

ConcurrentModificationException 禁止并发修改的情况下,检测到对象的并发修改

UnsupportedOperationException 对象不支持用户请求的方法

异常处理机制:

可抛出类型 Throwable有 两个子类——Error 和 Exception

a) 其中 Error 代表编译时错误和系统错误,例如内存溢出、线程死锁等,程序本身无法克服和恢复

b) Exception 是可以被抛出的基本类型,程序本身能够克服和恢复,是开发者需要关心的

Exception 同样可以分为两类——不受检查的异常(运行时异常)和受检查的异常

a) 运行时异常代表的是编程错误,自动被 Java 虚拟机抛出,所以不必在异常说明中列出来

b) 其它异常称为受检查的异常,在编译器就变强制检查

异常链、异常查找顺序、子类父类、吞噬异常等可以自行网上查阅相关资料

error和exception的区别

Error 类和Exception 类的父类都是 Throwable 类,他们的区别是:

Error 类一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢等。对于这类错

误的导致的应用程序中断,仅靠程序本身无法恢复和和预防,遇到这样的错误,建议让程序终止。

Exception 类表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。

Exception 类又分为运行时异常(Runtime Exception)和受检查的异常(Checked Exception ),运行时异

常;ArithmaticException,IllegalArgumentException,编译能通过,但是一运行就终止了,程序不会处理运行时异常,

出现这类异常,程序会终止。而受检查的异常,要么用try。。。catch捕获,要么用 throws 字句声明抛出,交给它

的父类处理,否则编译不会通过

String是基本数据类型吗?可以被继承吗?

String是引用类型,底层用 char数组实现的。因为 String 是final 类,在 java 中被final 修饰的类不能被继承,

因此 String当然不可以被继承。

Java 是值传递还是引用传递?

都是值传递,引用存储的是对象的地址,重新赋值会修改引用指向的地址,但原来的对象不会被改变

什么是反射,有什么作用和应用?

反射可以在运行时打开和检查 class 文件,从而获取类的某些信息

应用:Dagger2 等依赖注入框架

什么是内部类?有什么作用?静态内部类和非静态内部类的区别?

内部类分为静态内部类和非静态内部类,非静态内部类又可以分为局部类、匿名类、普通(成员)内部类

非静态内部类的创建依赖于外围类,拥有一个隐式地指向外部类的引用,因此可以访问外围对象的所有成员,除了基本数据类型和 String 类型的 static final 变量外,不能存在 static 成员(包括变量、方法、内部类)

静态内部类的创建不依赖于外围类,不能访问外围对象的非静态成员,可以拥有 static 成员

作用:

解决了多重继承的问题

可以让多个内部类以不同的方式实现同一个接口,或继承同一个类

内部类可以有多个实例,每个实例都有自己的状态信息,并且与其外围类对象地信息相互独立

成员内部类能够提供更好的封装,除了该外围类,其它类都不能访问

为什么匿名内部类中使用局部变量要用 final 修饰?

这是由 Java 的闭包实现机制决定的,闭包可以简单地认为是:

一个依赖于外部环境自由变量的函数

这个函数能够访问外部环境里的自由变量

Java 到处都是闭包,比如类的成员方法、内部类等,都是闭包。但 Java 编译器对闭包的支持不完整,它会偷偷地把外部局部变量复制一个副本到闭包里面,即 Java 编译器实现的只是 capture-by-value,并没有实现 capture-by-reference,而只有后者才能保持匿名内部类和外部环境局部变量保持同步。既然内外不能同步,Java 就干脆一刀切,不允许改变外部的局部变量。

static 修饰的方法可以被子类重写吗?为什么?

不行,子类如果实现了和父类名称和参数相同的 staitc 方法,只会隐藏父类的实现(隐藏的意思是:当这个 static 方法可见性为 protected、public 时,在子类访问这个方法,只会调用到子类自己的)。因为 static 修饰的方法是属于类本身的,而多态这一特性是针对单个对象而言的。

4、final、finally、finalize 分别表示什么含义?

final:修饰对象时,引用不可变;修饰基本数据类型时,值不可变;修饰类时,不可被继承;修饰方法时,不能被复写

finally:和 try-catch 配合使用,除非 JVM 终止,否则一定会被调用,常用于释放资源。但如果 try-catch 中执行了 return,则 finally 无法修改返回值

finalize:对象在回收时会被调用,只会被调用一次

重写和重载的区别?

重写:子类重写父类的方法,是实现多态的关键

重载:方法名相同,参数个数或类型不同

谈谈对面向过程编程、面向对象编程还有面向切面编程的理解?

// TODO 如果有更好的理解,欢迎指出

面向过程编程:将问题划分为多个步骤,一步一步实现以解决问题

面向对象编程: 把一个问题分解成多个对象,然后使用这些对象互相配合以解决问题。

面向切换编程:通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,可以在运行时(或者编译期、类加载时)动态地将代码切入到类的指定方法的指定位置上。

抽象类能否实例化,理论依据是什么?

不能,抽象类是不完整的,某些方法可能只有声明,而没有定义(实现),调用这些方法会出现未知的结果

6、抽象类跟接口的区别,接口的默认修饰符?

个人理解,抽象类就像是一个模板,实现了子类共有的内容,剩下的部分交给子类自定义自己的实现;接口就像是一套规范,任何类都可以去按要求去实现这套规范。

即抽象类对类抽象,接口对行为抽象。

具体的区别如下:

方法实现。抽象类可以有已实现的方法;Java 8 之后接口也可以有默认方法和静态方法了

继承。子类用 extends 继承抽象类,只能继承一个,必须实现所有抽象方法,implements 实现接口,可以实现多个,必须实现所有已声明方法

构造器。抽象类可以有构造方法,接口不可以

抽象类除了不能实例化之外,和普通的类没有区别,接口则是完全不同的类型

访问修饰符。抽象类有 public、private 修饰符,接口默认方法都是 public 的(Java 9 可以有私有辅助方法),成员变量都是 public static final 的

main 方法。抽象类可以有 main 方法,而且能够运行,接口不可以

速度。抽象类速度比接口稍快,因为接口需要去寻找类中实现的方法

添加新方法。抽象类可以添加新方法并给它默认的实现而子类不需要修改,接口则不行(Java 8 之后可以有默认方法)

Object有哪些公用方法?

Object是所有类的父类,任何类都默认继承Object。

clone ,equals,hashcode,getClass,wait,notify,notifyAll,toString.

Java集合框架中有哪些类?都有什么特点

Collection:代表一组对象,每一个对象都是它的子元素。

Set:不包含重复元素,。

List:有顺序的集合,并且可以包含重复元素,其中含有arraylist和linkedlist。

Map:可以把键(key)映射到值(value)的对象,键不能重复。

集合、数组、泛型的关系,并比较

三种其实都是集合,因为他们都继承并实现了ICollection

集合可放任意类型的元素,会自动增大,取出时要做类型转换

泛型集合只能放定义类型的元素,会自动增大,取出时不用做类型转换

数组只能放定义类型的元素,不会自动增大,取出时不用做类型转换

ArrayList内部用什么实现的?

ArrayList 内部是用Object[]实现的。

ArrayList和LinkList的区别?

ArrayList和LinkedList都是实现了List接口的容器类,用于存储一系列的对象引用。他们都可以对元素的增删改查进行操作。

对于ArrayList,它在集合的末尾删除或添加元素所用的时间是一致的,但是在列表中间的部分添加或删除时所用时间就会大大增加。但是它在根据索引查找元素的时候速度很快。

对于LinkedList则相反,它在插入、删除集合中任何位置的元素所花费的时间都是一样的,但是它根据索引查询一个元素的时候却比较慢。

ArrayList和LinkedList的大致区别:

1.ArrayList是实现了基于动态数组的数据结构,LinkedList是基于链表结构。

2.对于随机访问的get和set方法,ArrayList要优于LinkedList,因为LinkedList要移动指针。

3.对于新增和删除操作add和remove,LinkedList比较占优势,因为ArrayList要移动数据。

ArrayList和Vector的区别?

Vector的方法都是同步的(Synchronized),是线程安全的(thread-safe),而ArrayList的方法不是,由于线程的同步必然要影响性能,因此,ArrayList的性能比Vector好。

2) 当Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而ArrayList只增加50%的大小,这样,ArrayList就有利于节约内存空间。

Hashtable & HashMap

Hashtable和HashMap它们的性能方面的比较类似 Vector和ArrayList,比如Hashtable的方法是同步的,而HashMap的不是。

HashSet和TreeSet的区别?

HashSet

不能保证元素的排列顺序,顺序有可能发生变化

 不是同步的

 集合元素可以是null,但只能放入一个null

TreeSet类

TreeSet是SortedSet接口的唯一实现类,TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序 和定制排序,其中自然排序为默认的排序方式。向TreeSet中加入的应该是同一个类的对象。

TreeSet判断两个对象不相等的方式是两个对象通过equals方法返回false,或者通过CompareTo方法比较没有返回0

如何解决Hash冲突?

1, 开放定址法

2, 再哈希法

3, 链地址法

4, 建立公共溢出区

HashMap的原理

HashMap的数据结构为数组+链表,以key,value的形式存值,通过调用put与get方法来存值与取值。

它内部维护了一个Entry数组,得到key的hashCode值将其移位按位与运算,然后再通过跟数组的长度-1作逻辑与运算得到一个index值来确定数据存储在Entry数组当中的位置,通过链表来解决hash冲突问题。当发生碰撞了,对象将会储存在链表的下一个节点中。

get方法调用

1.当调用get方法时会调用hash函数,这个hash函数会将key的hashCode值返回,返回的hashCode与Entry数组长度-1进行逻辑与运算得到一个index值,用这个index值来确定数据存储在Entry数组当中的位置

2.通过循环来遍历索引位置对应的链表,初始值为数据存储在Entry数组当中的位置,循环条件为Entry对象不为null,改变循环条件为Entry对象的下一个节点

3.如果hash函数得到的hash值与Entry对象当中key的hash值相等,并且Entry对象当中的key值与get方法传进来的key值equals相同则返回该Entry对象的value值,否则返回null

put方法调用

1.调用hash函数得到key的HashCode值

2.通过HashCode值与数组长度-1逻辑与运算得到一个index值

3.遍历索引位置对应的链表,如果Entry对象的hash值与hash函数得到的hash值相等,并且该Entry对象的key值与put方法传过来的key值相等则,将该Entry对象的value值赋给一个变量,将该Entry对象的value值重新设置为put方法传过来的value值。将旧的value返回。

4.添加Entry对象到相应的索引位置

HashMap为什么线程不安全

个人觉得HashMap在并发时可能出现的问题主要是两方面,首先如果多个线程同时使用put方法添加元素,而且假设正好存在两个put的key发生了碰撞(hash值一样),那么根据HashMap的实现,这两个key会添加到数组的同一个位置,这样最终就会发生其中一个线程的put的数据被覆盖。第二就是如果多个线程同时检测到元素个数超过数组大小*loadFactor,这样就会发生多个线程同时对Node数组进行扩容,都在重新计算元素位置以及复制数据,但是最终只有一个线程扩容后的数组会赋给table,也就是说其他线程的都会丢失,并且各自线程put的数据也丢失。

如何线程安全的使用HashMap

三种方式:

  • Hashtable

  • ConcurrentHashMap

  • Synchronized Map

HashMap,LinkedHashMap,TreeMap的有序性

HashMap是无序的,如果希望元素保持输入的顺序,可以使用 LinkedHashMap 替代。

LinkedHashMap是有序的

LinkedHashMap 继承自 HashMap,具有高效性,同时在 HashMap 的基础上,又在内部增加了一个链表,用以存放元素的顺序

TreeMap 则提供了一种完全不同的 Map 实现。从功能上讲,TreeMap 有着比 HashMap 更为强大的功能,它实现了 SortedMap 接口,这意味着它可以对元素进行排序。TreeMap 的性能略微低于 HashMap。如果在开发中需要对元素进行排序,那么使用 HashMap 便无法实现这种功能,使用 TreeMap 的迭代输出将会以元素顺序进行。LinkedHashMap 是基于元素进入集合的顺序或者被访问的先后顺序排序,TreeMap 则是基于元素的固有顺序 (由 Comparator 或者 Comparable 确定)。

LinkedHashMap 是根据元素增加或者访问的先后顺序进行排序,而 TreeMap 则根据元素的 Key 进行排序。

hashcode()的作用,与equal()有什么区别?

hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。

hashCode()方法和equal()方法的作用其实一样,在Java里都是用来对比两个对象是否相等一致

1、hashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode是用来在散列存储结构中确定对象的存储地址的;

2、如果两个对象相同,就是适用于equals(java.lang.Object) 方法,那么这两个对象的hashCode一定要相同;

3、如果对象的equals方法被重写,那么对象的hashCode也尽量重写,并且产生hashCode使用的对象,一定要和equals方法中使用的一致,否则就会违反上面提到的第2点;

4、两个对象的hashCode相同,并不一定表示两个对象就相同,也就是不一定适用于equals(java.lang.Object) 方法,只能够说明这两个对象在散列存储结构中,如Hashtable,他们“存放在同一个篮子里”。

线程种类

java线程一共分成两种,用户线程和守护线程.

默认就是用户线程,那么什么是守护线程?

当A线程去启动B线程的时候,如果当A线程死掉的时候,如果B是守护线程,则B也跟着死掉,反之B继续自己的业务逻辑。

Android中常见的4种线程池

1  FixThreadPool(一堆人排队上公厕)

2 SingleThreadPool(公厕里只有一个坑位)

3 CachedThreadPool(一堆人去一家很大的咖啡馆喝咖啡)

4 ScheduledThreadPool(4个里面唯一一个有延迟执行和周期重复执行的线程池)

线程创建和销毁的方法?

创建

1.继承Thread类,并复写run方法,创建该类对象,调用start方法开启线程。 2.实现Runnable接口,复写run方法,创建Thread类对象,将Runnable子类对象传递给Thread类对象。调用start方法开启线程。 3.创建FutureTask对象,创建Callable子类对象,复写call(相当于run)方法,将其传递给FutureTask对象(相当于一个Runnable)。 创建Thread类对象,将FutureTask对象传递给Thread对象。调用start方法开启线程。这种方式可以获得线程执行完之后的返回值。

销毁的3中方法:

(1)设置退出标志,使线程正常退出,也就是当run()方法完成后线程终止

(2)使用interrupt()方法中断线程

(3)使用stop方法强行终止线程(不推荐使用,Thread.stop, Thread.suspend, Thread.resume 和

Runtime.runFinalizersOnExit 这些终止线程运行的方法已经被废弃,使用它们是极端不安全的!)

线程有哪些状态?

线程从创建、运行到结束总是处于下面五个状态之一:新建状态、就绪状态、运行状态、阻塞状态及死亡状态。

什么是线程安全?保障线程安全有哪些手段?

如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,

就是线程安全的。

方法有

竞争与原子操作、同步与锁、可重入、过度优化。

ReentrantLock和synchronized的区别?

这两种同步方式有很多相似之处,它们都是加锁方式同步,而且都是阻塞式的同步,也就是说当如果一个线程获得了对象锁,进入了同步块,其他访问该同步块的线程都必须阻塞在同步块外面等待,而进行线程阻塞和唤醒的代价是比较高的(操作系统需要在用户态与内核态之间来回切换,代价很高,不过可以通过对锁优化进行改善)。

功能区别:

这两种方式最大区别就是对于Synchronized来说,它是java语言的关键字,是原生语法层面的互斥,需要jvm实现。而ReentrantLock它是JDK 1.5之后提供的API层面的互斥锁,需要lock()和unlock()方法配合try/finally语句块来完成

便利性:很明显Synchronized的使用比较方便简洁,并且由编译器去保证锁的加锁和释放,而ReenTrantLock需要手工声明来加锁和释放锁,为了避免忘记手工释放锁造成死锁,所以最好在finally中声明释放锁。

锁的细粒度和灵活度:很明显ReenTrantLock优于Synchronized

性能的区别:

在Synchronized优化以前,synchronized的性能是比ReenTrantLock差很多的,但是自从Synchronized引入了偏向锁,轻量级锁(自旋锁)后,两者的性能就差不多了,在两种方法都可用的情况下,官方甚至建议使用synchronized,其实synchronized的优化我感觉就借鉴了ReenTrantLock中的CAS技术。都是试图在用户态就把加锁问题解决,避免进入内核态的线程阻塞。

线程的sleep()方法和yield()方法有什么区别?

sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会;

线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态;

sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常;

sleep()方法比yield()方法(跟操作系统CPU调度相关)具有更好的可移植性。

谈谈对wait和notify关键字的理解?

使用wait()、notify()和notifyAll()时需要首先对调用对象加锁

调用wait()方法后,线程状态会从RUNNING变为WAITING,并将当线程加入到lock对象的等待队列中

调用notify()或者notifyAll()方法后,等待在lock对象的等待队列的线程不会马上从wait()方法返回,必须要等到调用notify()或者notifyAll()方法的线程将lock锁释放,等待线程才有机会从等待队列返回。这里只是有机会,因为锁释放后,等待线程会出现竞争,只有竞争到该锁的线程才会从wait()方法返回,其他的线程只能继续等待

notify()方法将等待队列中的一个线程移到lock对象的同步队列,notifyAll()方法则是将等待队列中所有线程移到lock对象的同步队列,被移动的线程的状态由WAITING变为BLOCKED

wait()方法上等待锁,可以通过wait(long timeout)设置等待的超时时间

线程之间是如何通信的?

答:

当线程间是可以共享资源时,线程间通信是协调它们的重要的手段。Object类中wait()\notify()\notifyAll()方法可以用于线程间通信关于资源的锁的状态。点击这里有更多关于线程wait, notify和notifyAll.

为什么线程通信的方法wait(), notify()和notifyAll()被定义在Object类里?

答:

Java的每个对象中都有一个锁(monitor,也可以成为监视器) 并且wait(),notify()等方法用于等待对象的锁或者通知其他线程对象的监视器可用。在Java的线程中并没有可供任何对象使用的锁和同步器。这就是为什么这些方法是Object类的一部分,这样Java的每一个类都有用于线程间通信的基本方法

Java中notify 和 notifyAll有什么区别?

notify()&notifyall()的共同点:均能唤醒正在等待的线程,并且均是最后只有一个线程获取资源对象的锁。

不同点:notify() 只能唤醒一个线程,而notifyall()能够唤醒所有的线程,当线程被唤醒以后所有被唤醒的线程竞争获取资源对象的锁,其中只有一个能够得到对象锁,执行代码。

注意:wait()方法并不是在等待资源的锁,而是在等待被唤醒(notify()),一旦被唤醒后,被唤醒的线程就具备了资源锁(因为无需竞争),直至再次执行wait()方法或者synchronized代码块执行完毕。

为什么wait(), notify()和notifyAll()必须在同步方法或者同步块中被调用?

答:

当一个线程需要调用对象的wait()方法的时候,这个线程必须拥有该对象的锁,接着它就会释放这个对象锁并进入等待状态直到其他线程调用这个对象上的notify()方法。同样的,当一个线程需要调用对象的notify()方法时,它会释放这个对象的锁,以便其他在等待的线程就可以得到这个对象锁。由于所有的这些方法都需要线程持有对象的锁,这样就只能通过同步来实现,所以他们只能在同步方法或者同步块中被调用。

Java多线程中的死锁

死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。这是一个严重的问题,因为死锁会让你的程序挂起无法完成任务,死锁的发生必须满足以下四个条件:

  • 互斥条件:一个资源每次只能被一个进程使用。

  • 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。

  • 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。

  • 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

避免死锁最简单的方法就是阻止循环等待条件,将系统中所有的资源设置标志位、排序,规定所有的进程申请资源必须以一定的顺序(升序或降序)做操作来避免死锁。

Java中活锁和死锁有什么区别?

活锁和死锁类似,不同之处在于处于活锁的线程或进程的状态是不断改变的,活锁可以认为是一种特殊的饥饿。一个现实的活锁例子是两个人在狭小的走廊碰到,两个人都试着避让对方好让彼此通过,但是因为避让的方向都一样导致最后谁都不能通过走廊。简单的说就是,活锁和死锁的主要区别是前者进程的状态可以改变但是却不能继续执行。

为什么Thread类的sleep()和yield()方法是静态的?

答:

Thread类的sleep()和yield()方法将在当前正在执行的线程上运行。所以在其他处于等待状态的线程上调用这些方法是没有意义的。这就是为什么这些方法是静态的。它们可以在当前正在执行的线程中工作,并避免程序员错误的认为可以在其他非运行线程调用这些方法。

如何确保线程安全?

答:

在Java中可以有很多方法来保证线程安全——同步,使用原子类(atomic concurrent classes),实现并发锁,使用volatile关键字,使用不变类和线程安全类。

6) Thread 类中的start() 和 run() 方法有什么区别?

 start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果不一样。当你调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程

同步和非同步、阻塞和非阻塞的概念

1.同步与异步

同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)

所谓同步,就是在发出一个*调用*时,在没有得到结果之前,该*调用*就不返回。但是一旦调用返回,就得到返回值了。

换句话说,就是由*调用者*主动等待这个*调用*的结果。

而异步则是相反,*调用*在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在*调用*发出后,*被调用者*通过状态、通知来通知调用者,或通过回调函数处理这个调用。

2. 阻塞与非阻塞

阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.

阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。

非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。

Thread的join()有什么作用?

Thread中,join()方法的作用是调用线程等待该线程完成后,才能继续用下运行。

.什么导致线程阻塞?

线程在运行的过程中因为某些原因而发生阻塞,阻塞状态的线程的特点是:该线程放弃CPU的使用,暂停运行,只有等到导致阻塞的原因消除之后才回复运行。或者是被其他的线程中断,该线程也会退出阻塞状态,同时抛出InterruptedException。

导致阻塞的原因有很多种,大致分为三种来讨论,分别是一般线程中的阻塞,Socket客户端的阻塞,Socket服务器端的阻塞

volatile和synchronized的区别

volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。

volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的

volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性

volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。

volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化

类锁,对象锁,私有锁

1. 类锁:在代码中的方法上加了static和synchronized的锁,或者synchronized(xxx.class)的代码段

2.对象锁:在代码中的方法上加了synchronized的锁,或者synchronized(this)的代码段

3.私有锁:在类内部声明一个私有属性如private Object lock,在需要加锁的代码段synchronized(lock)

对象锁:假设我有一个类ClassA,其中有一个方法synchronized methodA(),那么当这个方法被调用的时候你获得就是对象锁。

举例:ClassA a = new ClassA(); ClassA b = new ClassA(); 那么如果你在a这对象上调用了methodA,不会影响b这个对象,也就是说对于b这个对象,他也可以调用methodA,因为这是两对象,所以说对象锁是针对对象的

类锁,其实没有所谓的类锁,因为类锁实际上就是这个类的对象的对象锁

举例:我有一个类ClassA,其中有一个方法synchronized static methodA(),注意这个方法是静态的了,那就是说这个类的所有的对象都公用一个这个方法了,那如果你在这个类的某个对象上调用了这个方法,那么其他的对象如果想要用这个方法就得等着锁被释放,所以感觉就好像这个类被锁住了一样。

TCP和UDP编程区别

1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接

2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付

Tcp通过校验和,重传控制,序号标识,滑动窗口、确认应答实现可靠传输。如丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。

3、UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。

4.每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信

5、TCP对系统资源要求较多,UDP对系统资源要求较少。

-----------------------

TCP编程的服务器端一般步骤是:

  1、创建一个socket,用函数socket();

  2、设置socket属性,用函数setsockopt(); * 可选

  3、绑定IP地址、端口等信息到socket上,用函数bind();

  4、开启监听,用函数listen();

  5、接收客户端上来的连接,用函数accept();

  6、收发数据,用函数send()和recv(),或者read()和write();

  7、关闭网络连接;

  8、关闭监听;

  TCP编程的客户端一般步骤是:

  1、创建一个socket,用函数socket();

  2、设置socket属性,用函数setsockopt();* 可选

  3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选

  4、设置要连接的对方的IP地址和端口等属性;

  5、连接服务器,用函数connect();

  6、收发数据,用函数send()和recv(),或者read()和write();

  7、关闭网络连接;

TCP的流量控制和拥塞控制

一、提要

1、TCP的流量控制和拥塞控制看上去是两个比较相近的概念,容易产生混淆。但事实上,他们在期望的目标和使用的方法是完全不同的。

2、流量控制解决的是发送方和接收方速率不匹配的问题,发送方发送过快接收方就来不及接收和处理。采用的机制是滑动窗口的机制。

3、拥塞控制解决的是避免网络资源被耗尽的问题,通过大家自律的采取避让的措施,来避免网络有限资源被耗尽。想想BT,利用UDP协议暴力地消耗网络资源,以达到自己更快下载速度的目的。

二、流量控制

流量控制是通过滑动窗口来实现的。 滑动窗口分为发送端窗口和接收端窗口。

窗口有大小限制,窗口大小是接收端用来告诉发送端目前接收端能接收的最大字节数。窗口的大小在TCP协议头里,大小为16位。然而在TCP协议的可选项里,还可以定义窗口的比例因子,因此实际的窗口大小可以超过64KB。窗口的含义实际上就是接收缓冲区的大小。

发送窗口维护了发送端发送的已被接收端ACK的序号,以及已经发送的最大序号,这样就可以知道还能发送多少的新数据。

接收窗口维护了已经ACK的序号,以及所有接收到的包序号。

窗口大小在特定的一次连接通信过程中,大小是不变的。而滑动窗口是一种机制,滑动窗口的大小在发送端代表的是可发送的数据大小,在接收端代表的是可接收的数据大小,它们是动态变化的。

三、拥塞控制

拥塞控制是通过拥塞窗口来实现的。

拥塞控制一般包括慢启动、拥塞避免两个阶段。

慢启动阶段是从1开始指数增长到限定大小的过程。

拥塞避免阶段时超过限定大小之后线性增加的过程,以及发现丢包后将拥塞窗口改为1,并把限定大小减半的过程。

TCP为什么是三次握手,为什么不是两次或者四次 && TCP四次挥手

我们要知道TCP是全双工的,即客户端在给服务器端发送信息的同时,服务器端也可以给客户端发送信息。而半双工的意思是A可以给B发,B也可以给A发,但是A在给B发的时候,B不能给A发,即不同时,为半双工。 单工为只能A给B发,B不能给A发; 或者是只能B给A发,不能A给B发。

  我们假设A和B是通信的双方。我理解的握手实际上就是通信,发一次信息就是进行一次握手。

  • 第一次握手: A给B打电话说,你可以听到我说话吗?

  • 第二次握手: B收到了A的信息,然后对A说: 我可以听得到你说话啊,你能听得到我说话吗?

  • 第三次握手: A收到了B的信息,然后说可以的,我要给你发信息啦!

  在三次握手之后,A和B都能确定这么一件事: 我说的话,你能听到; 你说的话,我也能听到。 这样,就可以开始正常通信了。

  注意: HTTP是基于TCP协议的,所以每次都是客户端发送请求,服务器应答,但是TCP还可以给其他应用层提供服务,即可能A、B在建立链接之后,谁都可能先开始通信。

    

  如果两次,那么B无法确定B的信息A是否能收到,所以如果B先说话,可能后面的A都收不到,会出现问题 。

  如果四次,那么就造成了浪费,因为在三次结束之后,就已经可以保证A可以给B发信息,A可以收到B的信息; B可以给A发信息,B可以收到A的信息。

  

 那么三次握手在正式情况下都做了什么呢?

第一次握手:

客户端发送一个TCP的SYN标志位置1的包指明客户打算连接的服务器的端口,以及初始序号X,保存在包头的序列号(Sequence Number)字段里

第二次握手 :

服务器发回确认包(ACK)应答。即SYN标志位和ACK标志位均为1同时,将确认序号(Acknowledgement Number)设置为客户的I S N加1以.即X+1。

第三次握手:

客户端再次发送确认包(ACK) SYN标志位为0,ACK标志位为1.并且把服务器发来ACK的序号字段+1,放在确定字段中发送给对方.并且在数据段放写ISN的+1

即SYN就是询问: 你能听得到吗?  ACK就是回到: 我能听得到啊。

TCP四次挥手的过程:

四次挥手:

A:“喂,我不说了 (FIN)。”A->FIN_WAIT1

B:“我知道了(ACK)。等下,上一句还没说完。Balabala…..(传输数据)”B->CLOSE_WAIT | A->FIN_WAIT2

B:”好了,说完了,我也不说了(FIN)。”B->LAST_ACK

A:”我知道了(ACK)。”A->TIME_WAIT | B->CLOSED

A等待2MSL,保证B收到了消息,否则重说一次”我知道了”,A->CLOSED

这样,通过四次挥手,可以把该说的话都说完,并且A和B都知道自己没话说了,对方也没花说了,然后就挂掉电话(断开链接)了 。

HTTP和TCP的区别?

TCP是传输层协议,定义数据传输和连接方式的规范。握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。

HTTP 超文本传送协议(Hypertext Transfer Protocol )是应用层协议,定义的是传输数据的内容的规范。

HTTP协议中的数据是利用TCP协议传输的,特点是客户端发送的每次请求都需要服务器回送响应,它是TCP协议族中的一种,默认使用 TCP 80端口。

好比网络是路,TCP是跑在路上的车,HTTP是车上的人。每个网站内容不一样,就像车上的每个人有不同的故事一样

HTTP和HTTPS的基本概念

  HTTP:是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准(TCP),用于从WWW服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高效,使网络传输减少。

  HTTPS:是以安全为目标的HTTP通道,简单讲是HTTP的安全版,即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。

  HTTPS协议的主要作用可以分为两种:一种是建立一个信息安全通道,来保证数据传输的安全;另一种就是确认网站的真实性。

二、HTTP与HTTPS有什么区别?

  HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(Secure Sockets Layer)协议用于对HTTP协议传输的数据进行加密,从而就诞生了HTTPS。简单来说,HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。

 HTTPS和HTTP的区别主要如下:

  1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。

  2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。

  3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。

  4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

三、HTTPS的工作原理

  我们都知道HTTPS能够加密信息,以免敏感信息被第三方获取,所以很多银行网站或电子邮箱等等安全级别较高的服务都会采用HTTPS协议。

 客户端在使用HTTPS方式与Web服务器通信时有以下几个步骤,如图所示。

  (1)客户使用https的URL访问Web服务器,要求与Web服务器建立SSL连接。

  (2)Web服务器收到客户端请求后,会将网站的证书信息(证书中包含公钥)传送一份给客户端。

  (3)客户端的浏览器与Web服务器开始协商SSL连接的安全等级,也就是信息加密的等级。

  (4)客户端的浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,并传送给网站。

  (5)Web服务器利用自己的私钥解密出会话密钥。

  (6)Web服务器利用会话密钥加密与客户端之间的通信。

四、HTTPS的优点

  尽管HTTPS并非绝对安全,掌握根证书的机构、掌握加密算法的组织同样可以进行中间人形式的攻击,但HTTPS仍是现行架构下最安全的解决方案,主要有以下几个好处:

  (1)使用HTTPS协议可认证用户和服务器,确保数据发送到正确的客户机和服务器;

  (2)HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全,可防止数据在传输过程中不被窃取、改变,确保数据的完整性。

  (3)HTTPS是现行架构下最安全的解决方案,虽然不是绝对安全,但它大幅增加了中间人攻击的成本。

  (4)谷歌曾在2014年8月份调整搜索引擎算法,并称“比起同等HTTP网站,采用HTTPS加密的网站在搜索结果中的排名将会更高”。

五、HTTPS的缺点

  虽然说HTTPS有很大的优势,但其相对来说,还是存在不足之处的:

  (1)HTTPS协议握手阶段比较费时,会使页面的加载时间延长近50%,增加10%到20%的耗电;

  (2)HTTPS连接缓存不如HTTP高效,会增加数据开销和功耗,甚至已有的安全措施也会因此而受到影响;

  (3)SSL证书需要钱,功能越强大的证书费用越高,个人网站、小网站没有必要一般不会用。

    (4)SSL证书通常需要绑定IP,不能在同一IP上绑定多个域名,IPv4资源不可能支撑这个消耗。

  (5)HTTPS协议的加密范围也比较有限,在黑客攻击、拒绝服务攻击、服务器劫持等方面几乎起不到什么作用。最关键的,SSL证书的信用链体系并不安全,特别是在某些国家可以控制CA根证书的情况下,中间人攻击一样可行。

六、http切换到HTTPS

  如果需要将网站从http切换到https到底该如何实现呢?

这里需要将页面中所有的链接,例如js,css,图片等等链接都由http改为https。例如:http://www.baidu.com改为https://www.baidu.com

  BTW,这里虽然将http切换为了https,还是建议保留http。所以我们在切换的时候可以做http和https的兼容,具体实现方式是,去掉页面链接中的http头部,这样可以自动匹配http头和https头。例如:将http://www.baidu.com改为//www.baidu.com。然后当用户从http的入口进入访问页面时,页面就是http,如果用户是从https的入口进入访问页面,页面即使https的。

socket和http的区别:

Http协议:简单的对象访问协议,对应于应用层。Http协议是基于TCP链接的。

tcp协议:对应于传输层

ip协议:对应与网络层

TCP/IP是传输层协议,主要解决数据如何在网络中传输;而Http是应用层协议,主要解决如何包装数据。

Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。

Http连接:http连接就是所谓的短连接,及客户端向服务器发送一次请求,服务器端相应后连接即会断掉。

socket连接:socket连接及时所谓的长连接,理论上客户端和服务端一旦建立连接,则不会主动断掉;但是由于各种环境因素可能会是连接断开,比如说:服务器端或客户端主机down了,网络故障,或者两者之间长时间没有数据传输,网络防火墙可能会断开该链接已释放网络资源。所以当一个socket连接中没有数据的传输,那么为了位置连续的连接需要发送心跳消息,具体心跳消息格式是开发者自己定义的。

短连接

连接->传输数据->关闭连接

HTTP是无状态的,浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束就中断连接。

也可以这样说:短连接是指SOCKET连接后发送后接收完数据后马上断开连接。

长连接

连接->传输数据->保持连接 -> 传输数据-> 。。。 ->关闭连接。

长连接指建立SOCKET连接后不管是否使用都保持连接,但安全性较差

什么时候用长连接,短连接?

长连接多用于操作频繁,点对点的通讯,而且连接数不能太多情况,。每个TCP连接都需要三步握手,这需要时间,如果每个操作都是先连接,再操作的话那么处理速度会降低很多,所以每个操作完后都不断开,次处理时直接发送数据包就OK了,不用建立TCP连接。例如:数据库的连接用长连接, 如果用短连接频繁的通信会造成socket错误,而且频繁的socket 创建也是对资源的浪费。

而像WEB网站的http服务一般都用短链接,因为长连接对于服务端来说会耗费一定的资源,而像WEB网站这么频繁的成千上万甚至上亿客户端的连接用短连接会更省一些资源,如果用长连接,而且同时有成千上万的用户,如果每个用户都占用一个连接的话,那可想而知吧。所以并发量大,但每个用户无需频繁操作情况下需用短连好

Socket是什么

Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

主机 A 的应用程序要能和主机 B 的应用程序通信,必须通过 Socket 建立连接,而建立 Socket 连接必须需要底层 TCP/IP 协议来建立 TCP 连接。建立 TCP 连接需要底层 IP 协议来寻址网络中的主机。我们知道网络层使用的 IP 协议可以帮助我们根据 IP 地址来找到目标主机,但是一台主机上可能运行着多个应用程序,如何才能与指定的应用程序通信就要通过 TCP 或 UPD 的地址也就是端口号来指定。这样就可以通过一个 Socket 实例唯一代表一个主机上的一个应用程序的通信链路了。

建立通信链路

当客户端要与服务端通信,客户端首先要创建一个 Socket 实例,操作系统将为这个 Socket 实例分配一个没有被使用的本地端口号,并创建一个包含本地和远程地址和端口号的套接字数据结构,这个数据结构将一直保存在系统中直到这个连接关闭。在创建 Socket 实例的构造函数正确返回之前,将要进行 TCP 的三次握手协议,TCP 握手协议完成后,Socket 实例对象将创建完成,否则将抛出 IOException 错误。

与之对应的服务端将创建一个 ServerSocket 实例,ServerSocket 创建比较简单只要指定的端口号没有被占用,一般实例创建都会成功,同时操作系统也会为 ServerSocket 实例创建一个底层数据结构,这个数据结构中包含指定监听的端口号和包含监听地址的通配符,通常情况下都是“*”即监听所有地址。之后当调用 accept() 方法时,将进入阻塞状态,等待客户端的请求。当一个新的请求到来时,将为这个连接创建一个新的套接字数据结构,该套接字数据的信息包含的地址和端口信息正是请求源地址和端口。这个新创建的数据结构将会关联到 ServerSocket 实例的一个未完成的连接数据结构列表中,注意这时服务端与之对应的 Socket 实例并没有完成创建,而要等到与客户端的三次握手完成后,这个服务端的 Socket 实例才会返回,并将这个 Socket 实例对应的数据结构从未完成列表中移到已完成列表中。所以 ServerSocket 所关联的列表中每个数据结构,都代表与一个客户端的建立的 TCP 连接。

怎么理解数据结构?

1、数据的逻辑结构

数据的逻辑结构可以看做是从具体问题中抽象出来的数学模型,仅仅描述数据项之间的逻辑关系(理解到这里就可以了),即具体问题中各个数据项之间的前后关系。与数据在计算机中的存储位置无关,独立于计算机之外。

2、数据的存储结构:

是数据的逻辑结构在计算机中的实现,是具体的,与具体的计算机语言有关。通常有两种不同的存储方式:顺序存储和链式存储。

3、算法

数据的运算定义在数据的逻辑结构上,每种逻辑结构都有一个运算的集合。最常用的检索、插入、删除、更新、排序等运算实际上只是在抽象的数据上所施加的一系列抽象的操作。

 所谓抽象的操作,是指我们只知道这些操作是”做什么”,而无须考虑”如何做”。只有确定了存储结构之后,才考虑如何具体实现这些运算。

二叉查找树(BST)具备什么特性呢?

1.左子树上所有结点的值均小于或等于它的根结点的值。

2.右子树上所有结点的值均大于或等于它的根结点的值。

3.左、右子树也分别为二叉排序树。

红黑树

R-B Tree,全称是Red-Black Tree,又称为“红黑树”,它一种特殊的二叉查找树。红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red)或黑(Black)。

红黑树的特性:

(1)每个节点或者是黑色,或者是红色。

(2)根节点是黑色。

(3)每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]

(4)如果一个节点是红色的,则它的子节点必须是黑色的。

(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。

注意:

(01) 特性(3)中的叶子节点,是只为空(NIL或null)的节点。

(02) 特性(5),确保没有一条路径会比其他路径长出俩倍。因而,红黑树是相对是接近平衡的二叉树。

Jvm、Dalvik和Art的区别

JVM Java虚拟机

JVM(Java Virtual Machine)是一种软件实现,执行像物理程序的机器。JVM并是不专为Java所实现运行的,只要其他编程语言的编译器能生成Java字节码,那这个语言也能实现在JVM上运行。因此,JVM通过执行Java bytecode可以使java代码在不改变的情况下在各种硬件之上。

Dalvik 虚拟机

Dalvik虚拟机是基于apache的java虚拟机,并被改进以适应低内存,低处理器速度的移动设备环境。Dalvik虚拟机依赖于Linux内核,实现进程隔离与线程调试管理,安全和异常管理,垃圾回收等重要功能。

Art虚拟机

即Android Runtime,Android 4.4发布了一个ART运行时,准备用来替换掉之前一直使用的Dalvik虚拟机。

ART 的机制与 Dalvik 不同。在Dalvik下,应用每次运行的时候,字节码都需要通过即时编译器(just in time ,JIT)转换为机器码,这会拖慢应用的运行效率,而在ART 环境中,应用在第一次安装的时候,字节码就会预先编译成机器码,使其成为真正的本地应用。这个过程叫做预编译(AOT,Ahead-Of-Time)。这样的话,应用的启动(首次)和执行都会变得更加快速。

Dalvik与Art的区别:

  1. Dalvik每次都要编译再运行,Art只会首次启动编译

  2. Art占用空间比Dalvik大(原生代码占用的存储空间更大),就是用“空间换时间”

  3. Art减少编译,减少了CPU使用频率,使用明显改善电池续航

  4. Art应用启动更快、运行更快、体验更流畅、触感反馈更及时

2020年Andorid很详细的基础面试题相关推荐

  1. 部队文职计算机试题,2020军队文职人员考试:计算机基础模拟试题(1)

    [导语]2020年军队文职备考进行中,为助力各位考生做好2020年军队文职招聘考试准备,山东中公教育小编为大家整理了考试题库:2020军队文职人员考试:计算机基础模拟试题(1),供大家学习和参考. 1 ...

  2. 【渝粤教育】 国家开放大学2020年春季 3962金融营销基础 参考试题

    试卷代号:3983 座位号口口 2 0 2 0年春季学期期末统一考试 品牌管理 试题 2020年7月 一.单项选择题(在各题的备选答案中,只有1项是正确的,请将正确答案的序号填写在题中的括号内,每个小 ...

  3. 【渝粤教育】 国家开放大学2020年春季 2716动物营养基础 参考试题

    试卷代号:2721 座位号 2 0 2 0年春季学期期末统一考试 乡镇行政管理试题 2020年7月 一.单项选择题(每小题1分,共10分,每小题只有一项答案正确,请将正确答案的序号填在括号内) 1. ...

  4. [转载] Java基础知识面试题(2020最新版)

    参考链接: Java中的异常类型与示例 文章目录 Java概述何为编程什么是Javajdk1.5之后的三大版本JVM.JRE和JDK的关系什么是跨平台性?原理是什么Java语言有哪些特点什么是字节码? ...

  5. 超详细的Java面试题总结(四 )之JavaWeb基础知识总结

    系列文章请查看: 超详细的Java面试题总结(一)之Java基础知识篇 超详细的Java面试题总结(二)之Java基础知识篇 超详细的Java面试题总结(三)之Java集合篇常见问题 超详细的Java ...

  6. 很详细的Nginx配置说明

    这篇文章主要为大家分享了一篇很详细的Nginx配置说明,主要内容包括Nginx常用功能.Nginx配置文件结构,想要了解Nginx配置的朋友不要错过,参考一下 Nginx是lgor Sysoev为俄罗 ...

  7. Ello讲述Haar人脸检测:易懂、很详细、值得 ...

     Ello讲述Haar人脸检测:易懂.很详细.值得 ...                                                                      ...

  8. linux入门_Linux超详细0基础入门篇(一)

    首先要感谢大康老师对我在Linux操作系统上的教导. 今天来讲一下用途广泛的Linux的基础入门教程 仅仅是做入门使用,如果想更加深入的学习那就需要自己做探索了. 本次例子使用的是kali linux ...

  9. python教程很详细_Python编程入门教程:从入门到高级,非常详细

    本文的资料和内容是我下载的,觉得非常有用,于是转过来大家瞧瞧: 这里给初学Python的朋友提供一些建议和指导吧.大神请无视, 俗话说:授人以鱼不如授人以渔.所以我这里只是阐述学习过程,并不会直接详细 ...

最新文章

  1. 记录Mask RCNN调整预测网格 font大小
  2. Clumpify:能使 Fastq 压缩文件再缩小 30% 并加速后续分析流程
  3. Python能让你上天?带你挖掘隐藏彩蛋~(附代码)
  4. jquery extend函数
  5. 【机器学习】自己手写实现线性回归,梯度下降 原理
  6. 网页mp3提取器_用Python写一个酷狗音乐下载器!
  7. python创建画布与子图_python实现在一个画布上画多个子图
  8. IAR执行到断点处不能单步运行解决方法
  9. scrapy简单爬虫
  10. Web API应用架构在Winform混合框架中的应用(5)--系统级别字典和公司级别字典并存的处理方式...
  11. tm4c123g c语言,Tm4c123GX(tiva)入门详细教程
  12. 一名前端 Leader 的转正述职记录
  13. 如何利用Matlab对指定条件下的excel单元格填充颜色
  14. 纽约大学Gary Marcus等撰文:人类思维对于AI的11个启示
  15. 内网主机通过公网域名解析访问内网服务器,存在什么问题,如何解决?
  16. 连米哈游都成了第二,这个一刀999的页游大王把老外吃透了。
  17. 全面吃透JAVA Stream流操作,让代码更加的优雅
  18. 熵为什么使用log?
  19. 学习笔记(2)——TransE算法(Translating Embedding)
  20. 提质信创•协同发展—— 麒麟信安云+操作系统交流会(武汉站)顺利举行

热门文章

  1. 英国内政部(Home Office)间谍机构(spy powers)假装它是Ofcom咨询中的一名私人公民1525445485395...
  2. 锐捷ruijie无线控制器AC登录说明
  3. 实验九 FBG 团队项目需求改进与系统设计
  4. 文旅夜游——让城市夜晚更加绚烂多彩
  5. 哪款骨传导耳机好用,好用的骨传导耳机汇总
  6. Flink 流数据处理
  7. 等保是强制的吗?企业不办等保有啥处罚?
  8. Adobe Illustrator Mac使用教程
  9. 区块链+数字经济,我们看到了什么
  10. 【C语言】猴子吃桃问题。猴子第1天摘下若干个桃子,当即吃了一半,还不过瘾,又多吃了一个。第2天早上又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。到第10天早上想……