Android 面试题整理

来源:互联网 发布:云服务百度 计算 编辑:程序博客网 时间:2024/06/08 19:17

Android 面试题整理(总结一些文章的面试题

感谢各位大神的文章和答案


Android部分

理解Android应用的进程

进程是一个动态的过程,每一个App都运行在一个独立的进程中,它拥有自己独立的内存和数据空间,进程的名字就是App的包名。默认情况下,同一个app的所有组件都是运行在相同进程的中的,当然,一个app中也允许有多个进程,只需要在AndroidManifest.xml里边给四大组件配置android:process属性,就可以让这些组件在指定的进程中运行,这些进程名字都是packageName:name这种,以区分是属于哪个App。

此外,您还可以设置android:process(即不以“:”开头,以符合包命名的格式字符串为它的值),使不同应用的组件在相同的进程中运行,但前提是这些应用共享相同的Linux用户ID并使用相同的证书进行签署.

小提示:当你有时候发现Application.oncreate方法执行了两遍,这时候你就需要去检查一下配置文件中是否给某个组件配置了Android:process属性。每个进程创建后,都会启动一个主线程(Looper接收消息),每个组件启动前都会先创建Application实例且一个进程只创建一次。

优先级

当系统内存不足的时候,Android系统会选择终止掉一部分进程,回收顺序如下:
(重要性从高到低)

前台进程
可见进程
服务进程
后台进程
空进程

如下是一些提高进程等级的方法,如:

1.进程要运行一些组件,不要成为空进程
2.运行一个service,并设置为前台运行模式
3.AndroidManifest.xml中配置persistent属性(persistent的App会被优先照顾,进程优先级设置为PERSISTENT_PROC_ADJ=-12)

关于第二点,示例代码:

 private void keepAlive() {        try {            Notification notification = new Notification();            notification.flags |= Notification.FLAG_NO_CLEAR;            notification.flags |= Notification.FLAG_ONGOING_EVENT;            startForeground(0, notification); // 设置为前台服务避免kill,Android4.3及以上需要设置id为0时通知栏才不显示该通知;        } catch (Throwable e) {            e.printStackTrace();        }    }

在service的onCrete方法里面调用keepAlive方法即可。

线程

线程是CPU调度的基本单元。一个应用启动后,至少会有3个线程,一个主线程和两个Binder线程。Zygote进程(APK所在的进程也是有Zygote进程fork出来的)还会产生一些Daemon线程如:ReferenceQueueDaemon、FinalizerDaemon、FinalizerWatchdogDaemon、HeapTaskDaemon

这里写图片描述

线程间可以共享资源,为了保存UI的更新不会混乱,所以更新UI控件时要求在主线程进行更新,即需要保证更新UI是线程安全的。有时还可以问问面试者,什么是线程安全(UI主线程不能被阻塞,多个线程同时操作相同代码会不会产生二义性等),貌似不知道这个概念的人也不少。

MVC、MVP、MVVM模式的概念

MVC

这里写图片描述
视图(View):用户界面
控制器(Controller):业务逻辑
模型(Model):数据保存
View 传送指令到 Controller
Controller 完成业务逻辑后,要求 Model 改变状态
Model 将新的数据发送到 View,用户得到反馈,所有通信都是单向的。

MVP

这里写图片描述
使用MVP时,Activity和Fragment变成了MVC模式中View层,Presenter相当于MVC模式中Controller层,处理业务逻辑。每一个Activity都有一个相应的presenter来处理数据进而获取model。
1、各部分之间的通信,都是双向的。
2、View 与 Model 不发生联系,都通过 Presenter 传递。
3、View 非常薄,不部署任何业务逻辑,称为”被动视图”(Passive View),即没有任何主动性,而 Presenter非常厚,所有逻辑都部署在那里。

MVVM

将 Presenter 改名为 ViewModel,基本上与 MVP 模式完全一致。唯一的区别是,它采用双向绑定(data-binding):View的变动,自动反映在 ViewModel,反之亦然。
这里写图片描述

如何导入外部数据库

把原数据库包括在项目源码的 res/raw

android系统下数据库应该存放在 /data/data/com..(package name)/ 目录下,所以我们需要做的是把已有的数据库传入那个目录下.操作方法是用FileInputStream读取原数据库,再用FileOutputStream把读取到的东西写入到那个目录.

本地广播和全局广播有什么差别

因广播数据在本应用范围内传播,不用担心隐私数据泄露的问题。 不用担心别的应用伪造广播,造成安全隐患。 相比在系统内发送全局广播,它更高效。

intentService作用是什么,AIDL解决了什么问题

生成一个默认的且与主线程互相独立的工作者线程来执行所有传送至onStartCommand() 方法的Intetnt。

生成一个工作队列来传送Intent对象给你的onHandleIntent()方法,同一时刻只传送一个Intent对象,这样一来,你就不必担心多线程的问题。在所有的请求(Intent)都被执行完以后会自动停止服务,所以,你不需要自己去调用stopSelf()方法来停止。

该服务提供了一个onBind()方法的默认实现,它返回null

提供了一个onStartCommand()方法的默认实现,它将Intent先传送至工作队列,然后从工作队列中每次取出一个传送至onHandleIntent()方法,在该方法中对Intent对相应的处理。

AIDL (Android Interface Definition Language) 是一种IDL 语言,用于生成可以在Android设备上两个进程之间进行进程间通信(interprocess communication, IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数。 AIDL IPC机制是面向接口的,像COM或Corba一样,但是更加轻量级。它是使用代理类在客户端和实现端传递数据。

Activity、Window、View三者的区别,fragment的特点

Activity是一个控制单元,Window是承载模型,View是显示视图,LayoutInflater像剪刀,Xml配置是图纸。

  1. 在activity中调用attach,创建一个window
  2. 创建window是其之类phonewindow,在attach中创建phonewindow
  3. 在activity中调用setContentView(),实际是调用了getWindow().setContentView()
  4. 调用phonewindow中的setContentView方法
  5. 创建parentView,作为ViewGroup的之类,实际是创建的DecorView作为FramLayout子类
  6. 将指定的布局文件进行填充通过布局填充器进行填充
  7. 调用到ViewGroup,调用ViewGroup的removeAllView(),先讲所有的view移除掉
  8. 添加新的view:addView()

    fragment的特点

  9. fragment可以作为activity界面的一部分组成出现

  10. 可以在一个activity中同时出现多个activity,并且一个fragment也可以在多个activity中使用
  11. 在activity运行过程中,可以添加移除或者替换fragment
  12. fragment可以相应自己的输入事件,并且有自己的生命周期,他们的生命周期会受宿主activity的生命周期影响

描述一次网络请求的流程

这里写图片描述

LanuchMode应用场景

standard,创建一个新的Activity。

singleTop,栈顶不是该类型的Activity,创建一个新的Activity。否则,onNewIntent。

singleTask,回退栈中没有该类型的Activity,创建Activity,否则,onNewIntent+ClearTop。

注意:

设置了”singleTask”启动模式的Activity,它在启动的时候,会先在系统中查找属性值affinity等于它的属性值taskAffinity的Task存在; 如果存在这样的Task,它就会在这个Task中启动,否则就会在新的任务栈中启动。因此, 如果我们想要设置了”singleTask”启动模式的Activity在新的任务中启动,就要为它设置一个独立的taskAffinity属性值。
如果设置了”singleTask”启动模式的Activity不是在新的任务中启动时,它会在已有的任务中查看是否已经存在相应的Activity实例, 如果存在,就会把位于这个Activity实例上面的Activity全部结束掉,即最终这个Activity 实例会位于任务的Stack顶端中。
在一个任务栈中只有一个”singleTask”启动模式的Activity存在。他的上面可以有其他的Activity。这点与singleInstance是有区别的。
singleInstance,回退栈中,只有这一个Activity,没有其他Activity。

singleTop适合接收通知启动的内容显示页面。

例如,某个新闻客户端的新闻内容页面,如果收到10个新闻推送,每次都打开一个新闻内容页面是很烦人的。

singleTask适合作为程序入口点。

例如浏览器的主界面。不管从多少个应用启动浏览器,只会启动主界面一次,其余情况都会走onNewIntent,并且会清空主界面上面的其他页面。

singleInstance应用场景:

闹铃的响铃界面。 你以前设置了一个闹铃:上午6点。在上午5点58分,你启动了闹铃设置界面,并按 Home 键回桌面;在上午5点59分时,你在微信和朋友聊天;在6点时,闹铃响了,并且弹出了一个对话框形式的 Activity(名为 AlarmAlertActivity) 提示你到6点了(这个 Activity 就是以 SingleInstance 加载模式打开的),你按返回键,回到的是微信的聊天界面,这是因为 AlarmAlertActivity 所在的 Task 的栈只有他一个元素, 因此退出之后这个 Task 的栈空了。如果是以 SingleTask 打开 AlarmAlertActivity,那么当闹铃响了的时候,按返回键应该进入闹铃设置界面。

多线程

  • Activity.runOnUiThread(Runnable)
  • View.post(Runnable),View.postDelay(Runnable,long)
  • Handler
  • AsyncTask

线程同步

http://www.itzhai.com/java-based-notebook-thread-synchronization-problem-solving-synchronization-problems-synchronized-block-synchronized-methods.html#read-more
http://www.juwends.com/tech/android/android-inter-thread-comm.html

什么情况导致内存泄漏

  1. 资源对象没关闭造成的内存泄漏
    描述: 资源性对象比如(Cursor,File文件等)往往都用了一些缓冲,我们在不使用的时候,应该及时关闭它们,以便它们的缓冲及时回收内存。它们的缓冲不仅存在于 java虚拟机内,还存在于java虚拟机外。如果我们仅仅是把它的引用设置为null,而不关闭它们,往往会造成内存泄漏。因为有些资源性对象,比如 SQLiteCursor(在析构函数finalize(),如果我们没有关闭它,它自己会调close()关闭),如果我们没有关闭它,系统在回收它时也会关闭它,但是这样的效率太低了。因此对于资源性对象在不使用的时候,应该调用它的close()函数,将其关闭掉,然后才置为null.在我们的程序退出时一定要确保我们的资源性对象已经关闭。 程序中经常会进行查询数据库的操作,但是经常会有使用完毕Cursor后没有关闭的情况。如果我们的查询结果集比较小,对内存的消耗不容易被发现,只有在常时间大量操作的情况下才会复现内存问题,这样就会给以后的测试和问题排查带来困难和风险。

  2. 构造Adapter时,没有使用缓存的convertView
    描述: 以构造ListView的BaseAdapter为例,在BaseAdapter中提供了方法: public View getView(int position, ViewconvertView, ViewGroup parent) 来向ListView提供每一个item所需要的view对象。初始时ListView会从BaseAdapter中根据当前的屏幕布局实例化一定数量的 view对象,同时ListView会将这些view对象缓存起来。当向上滚动ListView时,原先位于最上面的list item的view对象会被回收,然后被用来构造新出现的最下面的list item。这个构造过程就是由getView()方法完成的,getView()的第二个形参View convertView就是被缓存起来的list item的view对象(初始化时缓存中没有view对象则convertView是null)。由此可以看出,如果我们不去使用 convertView,而是每次都在getView()中重新实例化一个View对象的话,即浪费资源也浪费时间,也会使得内存占用越来越大。 ListView回收list item的view对象的过程可以查看: android.widget.AbsListView.java –> voidaddScrapView(View scrap) 方法。 示例代码:

public View getView(int position, ViewconvertView, ViewGroup parent) {View view = new Xxx(...); ... ... return view; } 

修正示例代码:

public View getView(int position, ViewconvertView, ViewGroup parent) {View view = null; if (convertView != null) { view = convertView; populate(view, getItem(position)); ... } else { view = new Xxx(...); ... } return view; } 

3.Bitmap对象不在使用时调用recycle()释放内存

描述: 有时我们会手工的操作Bitmap对象,如果一个Bitmap对象比较占内存,当它不在被使用的时候,可以调用Bitmap.recycle()方法回收此对象的像素所占用的内存,但这不是必须的,视情况而定。可以看一下代码中的注释:

/* •Free up the memory associated with thisbitmap’s pixels, and mark the •bitmap as “dead”, meaning itwill throw an exception if getPixels() or •setPixels() is called, and will drawnothing. This operation cannot be •reversed, so it should only be called ifyou are sure there are no •further uses for the bitmap. This is anadvanced call, and normally need •not be called, since the normal GCprocess will free up this memory when •there are no more references to thisbitmap. /

4.试着使用关于application的context来替代和activity相关的context
这是一个很隐晦的内存泄漏的情况。有一种简单的方法来避免context相关的内存泄漏。最显著地一个是避免context逃出他自己的范围之外。使用Application context。这个context的生存周期和你的应用的生存周期一样长,而不是取决于activity的生存周期。如果你想保持一个长期生存的对象,并且这个对象需要一个context,记得使用application对象。你可以通过调用 Context.getApplicationContext() or Activity.getApplication()来获得。更多的请看这篇文章如何避免 Android内存泄漏。

5.注册没取消造成的内存泄漏
一些Android程序可能引用我们的Anroid程序的对象(比如注册机制)。即使我们的Android程序已经结束了,但是别的引用程序仍然还有对我们的Android程序的某个对象的引用,泄漏的内存依然不能被垃圾回收。调用registerReceiver后未调用unregisterReceiver。 比如:假设我们希望在锁屏界面(LockScreen)中,监听系统中的电话服务以获取一些信息(如信号强度等),则可以在LockScreen中定义一个 PhoneStateListener的对象,同时将它注册到TelephonyManager服务中。对于LockScreen对象,当需要显示锁屏界面的时候就会创建一个LockScreen对象,而当锁屏界面消失的时候LockScreen对象就会被释放掉。 但是如果在释放 LockScreen对象的时候忘记取消我们之前注册的PhoneStateListener对象,则会导致LockScreen无法被垃圾回收。如果不断的使锁屏界面显示和消失,则最终会由于大量的LockScreen对象没有办法被回收而引起OutOfMemory,使得system_process 进程挂掉。 虽然有些系统程序,它本身好像是可以自动取消注册的(当然不及时),但是我们还是应该在我们的程序中明确的取消注册,程序结束时应该把所有的注册都取消掉。

6.集合中对象没清理造成的内存泄漏
我们通常把一些对象的引用加入到了集合中,当我们不需要该对象时,并没有把它的引用从集合中清理掉,这样这个集合就会越来越大。如果这个集合是static的话,那情况就更严重了。

ANR定位

  • 主线程被IO操作(从4.0之后网络IO不允许在主线程中) 阻塞。
  • 主线程存在耗时计算
  • 主线程中错误的操作,比如Thread.wait或者Thread.sleep等Android系统会监控程序的响应状况,一旦出现以上两种情况,则弹出ANR对话框
  • 应用在5秒内未响应用户的输入事件
  • BroadcastReceiver未在10秒内完成相关的处理
  • Service在特定的时间内无法处理完成20秒

ANR修正

  • 使用AsyncTask处理耗时IO操作
  • 使用Thread或者handlerThread时,调用Process.setThreadPriority设置优先级,否则已然会降低程序响应,因为默认thread的优先级和主线程相同
  • 使用Handler处理工作线程结果,而不是使用Thread.wait()或者Thread.sleep()来阻塞主线程
  • Activity的onCreate和onResume回调中尽量避免耗时的代码
  • BroadcastReceiver中onReceive代码也要尽量减少耗时,建议使用IntentService处理

什么情况导致OOM

http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0920/3478.html

  • 使用更轻量级的数据结构,避免OOM
  • 避免Android里面使用Enum,避免OOM
  • Bitmap对象的内存占用
  • 更大的图片
  • onDraw方法里面执行对象的创建
  • 使用StringBuilder来替代频繁的“+”,来避免OOM

Service与Activity之间通信的几种方式

  • 通过binder对象
  • 通过broadcast的形式

Android各个版本API的区别

http://blog.csdn.net/lijun952048910/article/details/7980562

Android代码中实现WAP方式联网

http://blog.csdn.net/asce1885/article/details/7844159

如何保证service在后台下不被kill

  1. onStartCommand方法,返回START_STICKY
    1.START_STICKY在运行onStartCommand后service进程被kill后,那将保留开始状态,但是不保留那些传入的intent。不久后service尝试再次创建,因为保留在开始状态,再创建service后将保证调用onStartCommand。如果没有传递任何命令给service,那么将获取到null的intent
    2.START_NOT_STICKY 在service被kill掉后,并且没有新的intent传递给它,service将移出开始状态,并且知道新的明显的方法(startservice)调用才重新创建。因为如果没有传递任何未决定的intent,那么service是不会启动,也就是期间onStartCommand不会接受任何null的intent
    3.START_REDELIVER_INTENT 在运行后service进程kill后,系统将会再次启动service,并传入最后一个intent给onStartCommand。直到调用stopSelf才停止传递intent。如果在被kill后还有未处理好的intent,那被kill后服务还是会自动启动。因此onstartCommand不会接收到任何null的intent。

  2. 提升service优先级
    在AndroidManifest.xml文件中对于intent-filter可以通过android:priority = “1000”这个属性设置最高优先级,1000是最高值,如果数字越小则优先级越低,同时适用于广播。

  3. 提升service进程优先级
    Android中的进程是托管的,当系统进程空间紧张的时候,会依照优先级自动进行进程的回收。Android将进程分为6个等级,它们按优先级顺序由高到低依次是:

    前台进程( FOREGROUND_APP)
    可视进程(VISIBLE_APP )
    次要服务进程(SECONDARY_SERVER )
    后台进程 (HIDDEN_APP)
    内容供应节点(CONTENT_PROVIDER)
    空进程(EMPTY_APP)
    当service运行在低内存的环境时,将会kill掉一些存在的进程。因此进程的优先级将会很重要,可以使 用startForeground 将service放到前台状态。这样在低内存时被kill的几率会低一些

  4. onDestroy方法里重启service
    service +broadcast 方式,就是当service走ondestory的时候,发送一个自定义的广播,当收到广播的时候,重新启动service

  5. Application加上Persistent属性

  6. 监听系统广播判断Service状态
    通过系统的一些广播,比如:手机重启、界面唤醒、应用状态改变等等监听并捕获到,然后判断我们的Service是否还存活,别忘记加权限啊

RequestLayout,onLayout,DrawChild区别与联系

requestlayout方法,会导致调用measure过程和layout过程。将会根据标志位判断是否需要ondraw
onLayout()方法(如果该View是ViewGroup对象,需要实现该方法,对每个子视图进行布局)
调用onDraw()方法绘制视图本身 (每个View都需要重载该方法,ViewGroup不需要实现该方法)
drawChild()去重新回调每个子视图的draw()方法

invalidate()和postInvalidate()区别

http://blog.csdn.net/mars2639/article/details/6650876
一个是主线程更新,一个是在子线程更新的

Android动画框架的实现原理

Animation框架定义了透明度,旋转,缩放和位移几种常见的动画,而且控制的是整个view,实现原理是每次绘制视图时View所在的viewgroup中的drawChild函数获取该View的Animation的transFormation值,然后调用cancas.concat(transformToApply.getMatrix()),通过矩阵运算完成动画桢,如果动画没有完成,继续调用invalidate函数,启动下次绘制来驱动动画,动画过程中的帧之间间隙时间是绘制函数所消耗的时间,可能会导致动画消耗比较多的cpu,最重要的是,动画改变的只是显示,并不能相应事件。

Android为每个应用程序分配的内存大小是多少

Android程序内存一般限制在16M,也有的是24M。

View刷新机制

由ViewRoot对象的performTraversals()方法调用draw方法发起绘制该view树,值得注意的是每次发起绘图时,并不会重新绘制每个View树的视图,而只会重新绘制那些“需要重绘”的视图,View类内容变量包含了一个标志位DRAWN,当该视图需要重绘时,就会为该View添加该标志位。
调用流程:
mView.draw()开始绘制,draw()方法实现的功能如下
1. 绘制该View的背景
2. 为显示渐变框做一些准备操作
3. 调用onDraw方法绘制视图本身
4. 调用dispatchDraw方法绘制子视图(如果该View类型不为ViewGroup,即不包含子视图,不需要重载该方法)值得说明的是,ViewGroup类已经为我们重写了dispatchDraw ()的功能实现,应用程序一般不需要重写该方法,但可以重载父类函数实现具体的功能。

LinearLayout和RelativeLayout性能对比

RelativeLayout会让子View调用2次onMeasure,LinearLayout 在有weight时,也会调用子View2次onMeasure
RelativeLayout的子View如果高度和RelativeLayout不同,则会引发效率问题,当子View很复杂时,这个问题会更加严重。如果可以,尽量使用padding代替margin。
在不影响层级深度的情况下,使用LinearLayout和FrameLayout而不是RelativeLayout。
最后再思考一下文章开头那个矛盾的问题,为什么Google给开发者默认新建了个RelativeLayout,而自己却在DecorView中用了个LinearLayout。因为DecorView的层级深度是已知而且固定的,上面一个标题栏,下面一个内容栏。采用RelativeLayout并不会降低层级深度,所以此时在根节点上用LinearLayout是效率最高的。而之所以给开发者默认新建了个RelativeLayout是希望开发者能采用尽量少的View层级来表达布局以实现性能最优,因为复杂的View嵌套对性能的影响会更大一些。

优化自定义view

为了加速你的view,对于频繁调用的方法,需要尽量减少不必要的代码。先从onDraw开始,需要特别注意不应该在这里做内存分配的事情,因为它会导致GC,从而导致卡顿。在初始化或者动画间隙期间做分配内存的动作。不要在动画正在执行的时候做内存分配的事情。

你还需要尽可能的减少onDraw被调用的次数,大多数时候导致onDraw都是因为调用了invalidate().因此请尽量减少调用invaildate()的次数。如果可能的话,尽量调用含有4个参数的invalidate()方法而不是没有参数的invalidate()。没有参数的invalidate会强制重绘整个view。

另外一个非常耗时的操作是请求layout。任何时候执行requestLayout(),会使得Android UI系统去遍历整个View的层级来计算出每一个view的大小。如果找到有冲突的值,它会需要重新计算好几次。另外需要尽量保持View的层级是扁平化的,这样对提高效率很有帮助。

如果你有一个复杂的UI,你应该考虑写一个自定义的ViewGroup来执行他的layout操作。与内置的view不同,自定义的view可以使得程序仅仅测量这一部分,这避免了遍历整个view的层级结构来计算大小

ContentProvider

http://blog.csdn.net/coder_pig/article/details/47858489

Android设计模式

http://blog.csdn.net/bboyfeiyu/article/details/44563871

Android属性动画特性

如果你的需求中只需要对View进行移动、缩放、旋转和淡入淡出操作,那么补间动画确实已经足够健全了。但是很显然,这些功能是不足以覆盖所有的场景的,一旦我们的需求超出了移动、缩放、旋转和淡入淡出这四种对View的操作,那么补间动画就不能再帮我们忙了,也就是说它在功能和可扩展方面都有相当大的局限性,那么下面我们就来看看补间动画所不能胜任的场景。

注意上面我在介绍补间动画的时候都有使用“对View进行操作”这样的描述,没错,补间动画是只能够作用在View上的。也就是说,我们可以对一个Button、TextView、甚至是LinearLayout、或者其它任何继承自View的组件进行动画操作,但是如果我们想要对一个非View的对象进行动画操作,抱歉,补间动画就帮不上忙了。可能有的朋友会感到不能理解,我怎么会需要对一个非View的对象进行动画操作呢?这里我举一个简单的例子,比如说我们有一个自定义的View,在这个View当中有一个Point对象用于管理坐标,然后在onDraw()方法当中就是根据这个Point对象的坐标值来进行绘制的。也就是说,如果我们可以对Point对象进行动画操作,那么整个自定义View的动画效果就有了。显然,补间动画是不具备这个功能的,这是它的第一个缺陷。

然后补间动画还有一个缺陷,就是它只能够实现移动、缩放、旋转和淡入淡出这四种动画操作,那如果我们希望可以对View的背景色进行动态地改变呢?很遗憾,我们只能靠自己去实现了。说白了,之前的补间动画机制就是使用硬编码的方式来完成的,功能限定死就是这些,基本上没有任何扩展性可言。

最后,补间动画还有一个致命的缺陷,就是它只是改变了View的显示效果而已,而不会真正去改变View的属性。什么意思呢?比如说,现在屏幕的左上角有一个按钮,然后我们通过补间动画将它移动到了屏幕的右下角,现在你可以去尝试点击一下这个按钮,点击事件是绝对不会触发的,因为实际上这个按钮还是停留在屏幕的左上角,只不过补间动画将这个按钮绘制到了屏幕的右下角而已。

java部分

内部类的作用

  1. 内部类可以用多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独立
    1. 在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或者继承同一个类
    2. 创建内部类对象的时刻并不依赖于外围类对象的创建
    3. 内部类并没有令人迷惑的“is-a”关系,他就是一个独立的实体
    4. 内部类提供了更好的封装,除了该外围类,其他类都不能访问

java排序算法

Java常用排序

java虚拟机的特性

java语言的一个重要特点就是与平台无关。而使用java虚拟机是实现这一特点的关键,一般的高级语言如果要在不同的平台上运行,至少需要编译成不同的目标代码。而引入java虚拟机后,java语言在不同平台上运行的时候不需要重新编译了。java语言使用模式java虚拟机屏蔽了与具体平台相关的信息,使得java语言编译程序只需要生成在java虚拟机上运行。java虚拟机在执行字节码的时候,把字节码解释成具体平台上的机器指令。

常见的排序算法时间复杂度

这里写图片描述

HashMap的实现原理

  1. HashMap概述: HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用NULL值和null键。不保证映射的顺序
  2. HashMap的数据结构:在java编程语言中,最基本的结构就是两种,一个是数组,一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的,HashMap也不例外。HashMap实际上是一个链表散列的数据结构,既数组和链表的结合体。
  3. 这里写图片描述
    HashMap底层就是一个数组结构,数组中的每一项又是一个链表。当新建一个HashMap的时候,就会初始化一个数组。

string-stringbuffer-stringbuilder区别

String 字符串常量

StringBuffer 字符串变量(线程安全)

StringBuilder 字符串变量(非线程安全)

简要的说, String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象, 因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用String ,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后,JVM 的 GC 就会开始工作,那速度是一定会相当慢的。

而如果是使用 StringBuffer 类则结果就不一样了,每次结果都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,再改变对象引用。所以在一般情况下我们推荐使用 StringBuffer ,特别是字符串对象经常改变的情况下。而在某些特别情况下, String 对象的字符串拼接其实是被 JVM 解释成了 StringBuffer 对象的拼接,所以这些时候 String 对象的速度并不会比 StringBuffer 对象慢,而特别是以下的字符串对象生成中, String 效率是远要比 StringBuffer 快的:

String S1 = “This is only a” + “ simple” + “ test”;

StringBuffer Sb = new StringBuilder(“This is only a”).append(“ simple”).append(“ test”); 你会很惊讶的发现,生成 String S1 对象的速度简直太快了,而这个时候 StringBuffer 居然速度上根本一点都不占优势。其实这是 JVM 的一个把戏,在 JVM 眼里,这个 String S1 = “This is only a” + “ simple” + “test”; 其实就是: String S1 = “This is only a simple test”; 所以当然不需要太多的时间了。但大家这里要注意的是,如果你的字符串是来自另外的 String 对象的话,速度就没那么快了,譬如: String S2 = “This is only a”; String S3 = “ simple”; String S4 = “ test”; String S1 = S2 +S3 + S4; 这时候 JVM 会规规矩矩的按照原来的方式去做

在大部分情况下 StringBuffer > String

StringBuffer

Java.lang.StringBuffer线程安全的可变字符序列。一个类似于 String 的字符串缓冲区,但不能修改。虽然在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容。

可将字符串缓冲区安全地用于多个线程。可以在必要时对这些方法进行同步,因此任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致。

StringBuffer 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。append 方法始终将这些字符添加到缓冲区的末端;而 insert 方法则在指定的点添加字符。

例如,如果 z 引用一个当前内容是“start”的字符串缓冲区对象,则此方法调用 z.append(“le”) 会使字符串缓冲区包含“startle”,而 z.insert(4, “le”) 将更改字符串缓冲区,使之包含“starlet”。

在大部分情况下 StringBuilder > StringBuffer

java.lang.StringBuilder

java.lang.StringBuilder一个可变的字符序列是5.0新增的。此类提供一个与 StringBuffer 兼容的 API,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。两者的方法基本相同

0 0
原创粉丝点击