布局优化
减少布局层次,单一可以用
LinearLayout解决的页面不用RelativeLayout,如果布局复杂,则用RelativeLayout更好,毕竟比起布局嵌套,这种会好些<include>标签使用,主要用于布局重用需要注意的是:此标签内,如果外面指定了
id里面跟布局也定义id以外部定义为准。同时如果里面定义了 类似于android:layout_width="wrap_content"的属性,需配套android:layout_height="wrap_content"同时使用。<merge>标签一般和<include>标签配合使用。一般如果当前根布局例如是:LinearLayout,include的布局也是LinearLayout,那么include的根布局就可以使用<merge>标签代替LinearLayout.<ViewStub>标签,意义为按需加载,轻量级,宽和高都是0,在实际开发中,有很多布局在正常情况下不会显示,比如网络异常的布局,这个时候没有必要在布局加载时就加载进来,通过<ViewStub>就可以在需要用到的时候再加载,提高程序初始化的性能。
绘制优化
在
onDraw()中不要创建新的局部对象,这是因为onDraw()可能会频繁的调用,这样就会在一瞬间产生大量的临时对象,这不仅占用过多的内存而且会导致系统频繁的gc, 降低程序的执行效率; 另外也不能做一些耗时的任务。内存泄漏优化
静态变量导致的内存泄漏
例如:
1
2
3
4
5
6
7
8
9private static Context mContext;
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_combine);
mContext = this;
}单例模式导致的内存泄漏
属性动画导致的内存泄漏
Android 3.0 之后,提供了属性动画,如果开启无限循环动画,隐藏后,未调用
animator,cancel()来停止动画,也会发生内存泄漏,因为这个时候Activity的View被动画所持有,而View又持有Activity, 最终Activity无法释放。0-
响应速度优化和ANR日志
响应速度优化的核心是避免在主线程做耗时操作,
Activity5s 未做出响应,Service10s未做出响应,就会出现 ANR ,查看\data\anr目录下的trace,txt文件分析产生 ANR 的原因线程优化
线程优化一般都是采用线程池,避免程序中存在大量的 Thread, 线程池可重用内部的线程,从而避免线程的创建和销毁所带来的性能开销。
高效使用RxJava
Question
场景一:在页面保存时,需要对页面所填写的一些列信息进行判断,确定正确无误后方可保存。
例如:页面上中需要填写:姓名,性别,职业,年薪,住址等信息。
按照常规做法,我们有可能会这么写:
1 | if(Util.checkempty(mUserName)){//判断用户名是否为空 |
而用 Rxjava.combineLatest
1 | //构建被观察者 |
场景二:搜索输入,避免多次调用搜索接口,搜索优化
1 | Observable.create(new Observable.OnSubscribe<String>() { |
RxJava
1. 变换
map()它是一对一的
1 | Observable.just("image/logo.png")//输入类型String,图片地址 |
flatMap()多对多转换
假如有这样一个场景,我们需要打印每个学生所修的课程,学生是一个集合,课程也是一个集合,如果按照map() 的做法如下:
这里我选用我项目里随意一个bean 对象,意思不变
1 | UserInfo[] userInfos = new UserInfo[3]; |
有种情况就是假如我不想要循环遍历呢,而是直接拿到 AppInfo 对象,显然 map() 是无法实现的,map() 我们前面也说过是 一对一的转换,而现在是一对多,那此时就应该用 flatMap().
使用flatMap() 的话,如下:
1 | Observable.from(userInfos) |
上述是用 flatMap() 编写的,去除了 for 循环,flatMap() 和 map() 有一个相同点:它们都是把传入的参数转化之后返回另一个对象。但需要注意的是,和map() 不同的是,flatMap() 中返回的是 Observable 对象。
flatMap() 原理如下:
- 使用传入的事件对象创建一个
Observable对象; - 并不发送这个
Observable对象,而是将它激活,于是它开始发送事件; - 每一个创建出来的
Observable发送的事件,都被汇入同一个Observable,而这个Observale负责将这些事件统一交给Subscriber的回调方法。
时间复杂度
衡量代码的好坏,包括两个非常重要的指标:
- 运行时间
- 占用空间
由于运行环境和输入规模的影响,代码的绝对执行时间是无法估计的,但是我们却可以预估代码的基本执行次数。我可以通俗的理解为:通过执行次数可以大概推算这个时间复杂度。
示例:
一个有序排列好的数组
1
int[] arry = {-5,-1,0,5,9,11,13,15,22,35,46},输入一个x,int x = 31
在数组中找出和为
x的两个数,例如: 9 +22 = 31。要求时间复杂度为O(n).
好,当我们看到这个问题的时候,我们认为这很容易嘛(先不考虑时间复杂度),于是写了如下代码:
第一种写法:
1 |
|
根据我们上面对时间复杂度的了解,我们知道改算法的时间复杂度是 O(n^2), 是最低的一种,可以说,那么我们如何把时间复杂度设置为O(n) ,线性复杂度,其实就是需要一遍for 循环;
第二种算法:
1 | int[] array = {-5,-1,0,5,9,11,13,15,22,35,46}; |
这是一种典型以空间换时间的解法。
总结
常见的时间复杂度有如下:
- T(n) = O(n)
- T(n) = O(logn)
- T(n) = O(1)
- T(n) = O(n²)
这四种时间复杂度谁用时更长,更节省时间呢?
O(1) < O(logn) < O(n) < O(n²)
java生产者消费者模式
Producter.java 生产者:
1 | /** |
Consumer.java
1 | public class Consumer extends Thread { |
main.java:
1 | public static void main(String args[]){ |
Android知识点三
1. RecycleView 如何设置分割线的?
它是读取系统自带的属性
R.attr.listDivider属性,来设置分割线的,支持横向和纵向。
2. RecycleView 分割线的样式如何修改?
第一种办法:因为
R.attr.listDivider定义在系统样式中,那么我们可以重写这个属性修改,如下:在
styles.xml中添加该属性:1
2
3
4
5<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
....
<item name="android:listDivider">@drawable/drawable_bg</item>
</style>其中
drawable_bg为自定义分割线的drawable.第二种办法:可以通过自带的
setDrawable()改变,如下:1
2
3
4DividerItemDecoration itemDecoration = new DividerItemDecoration(this,DividerItemDecoration.VERTICAL);
Drawable drawable = getResources().getDrawable(R.drawable.drawable_bg);
itemDecoration.setDrawable(drawable);
recyclerView.addItemDecoration(itemDecoration);3. RecycleView 缓存
缓存涉及三个类,分别是:
RecyclerRecycledViewPoolViewCachedExtension
Recycler: 用于管理已经废弃或者与RecyclerView分离的ViewHoldermChangedScrap: 与RecyclerView分离的ViewHolder列表mAttachedScrap: 未与RecyclerView分离的ViewHolder列表mCachedViews:ViewHolder缓存列表
RecycledViewPool:ViewHolder缓存池ViewCachedExtension: 开发者可以控制的ViewHolder缓存的帮助类
获取缓存里的 ViewHolder 的关键方法是: tryGetViewHolderForPositionByDeadline 如下:
1 | @Nullable |
总结缓存的机制如下:
- 1.从
mChangedScrap或mAttachScrap中获取 - 2.从
mCacheViews - 3.从
mRecycledViewPool缓存池中获取
4. RecycleView 刷新闪屏
一般刷新:
1 | adapter.notifyItemChanged(0); 参数:position |
实际上会全部刷新,局部刷新应使用:
1 | adapter.notifyItemChanged(0,"q"); 参数: position,payload |
并且在 adapter 中重写方法: `onBindViewHolder(RecyclerView.ViewHolder holder, int position, List payloads)方法`
payloads 的 size 始终为1.
1 | @Override |
5. RecycleView 和 listView
//https://zhuanlan.zhihu.com/p/23339185
缓存层级不同
recycleView 比 listView 多两级缓存,支持开发者自定义缓存处理,支持多个recycleView共用同一个 recycleViewPool(缓存池)
listView 缓存(两级缓存):
| 是否需要回调createView | 是否需要回调bindVIew | 生命周期 | 备注 | |
|---|---|---|---|---|
| mActiveViews | 否 | 否 | onLayout函数周期内 | 用于屏幕内itemView快速重用 |
| mScrapViews | 否 | 是 | 与mAdapter一致,当Adapter被更换时,mScrapViews即被清空 |
RecycrleView 缓存( 四级缓存):
| 是否回调createView | 回调bindVIew | 生命周期 | 备注 | |
|---|---|---|---|---|
| mAttachedScrap | 否 | 否 | onLayout | 用于屏幕内itemView快速重用 |
| mCacheViews | 否 | 否 | 与mAdapter一致,当Adapter被更换时,mCacheVIews被缓存到mRecyclerPool | 默认缓存大小是2 |
| mViewCacheExtension | 用户自定义实现 | |||
| mRecyclerPool | 否 | 是 | 与自身生命周期一致,不再引用时被释放 | 默认大小是5 |
ListView 和 RecyclerView 缓存机制:
mActiveViews和mAttachedScrap功能相似,意义在于快速重用屏幕上可见的列表项itemView,而不需要重新createView和bindViewmScrapView和mCachedViews+mRecyclerViewPool功能相似,意义在于缓存离开屏幕的itemView,目的是让即将进入屏幕的itemView重用RecycleView的优势在于mCacheVIews的使用,可以做到屏幕外的列表项itemView进入屏幕内时也无须bindView快速重用;mRecycleViewPool可以供多个recycleView共同使用,在特定场景下,如viewpager加多个列表页有优势。
6. RecycleView 的回收复用机制的内部实现都是哪个类完成的?
RecycleView 的回收复用机制都是由内部类
Recycler类,核心方法:tryGetViewHolderForPositionByDeadline()方法中完成的。
随笔源码记录:
复用机制
1 | public View getViewForPosition(int position) { |
回收机制
也是 Recycler 类中的,核心方法:
recycleViewHolderInternal(holder)
1 | public void recycleView(View view) { |
回收的逻辑:由 LayoutManager 来遍历移出屏幕的卡位,然后对每个卡位进行回收操作,回收时,都是把 viewHolder 放在 mCachedViews 里面,如果mCachedViews 满了,那就在mCachedViews 里拿一个ViewHolder 扔到 ViewPool 缓存里,然后mCachedViews 就可以空出位置来放新回收的ViewHolder 了。
mCachedViews里存放的ViewHolder只有原本位置的卡位才能复用参考链接:https://blog.csdn.net/xJ032w2j4cCjhOW8s8/article/details/79946425
===================================================================================
updateViewCacheSize()
1 | void updateViewCacheSize() { |
在第一次向上滑动
7. RecycleView 性能优化
7.1 数据处理和视图加载适度分离
什么意思呢?就是我们异步从服务端拉取数据,拿到数据之后就把数据丢给
ViewHolder进行处理,当然有些数据需要单独处理,那么其实这些处理逻辑也应该异步执行,这样的话就可以让adapter在 notifyDatasetChanged 之后,ViewHolder 就可以简单处理视图与数据的绑定工作。
7.2 数据刷新
如果是个别数据发生改变,调用局部刷新的方法
7.3 布局优化
减少 GPU 过度绘制;
7.4 杂七杂八
- 升级 RecycleView 版本到 25.1.0 以上使用 Prefetch 功能,开启这个功能之后对页面复杂度的敏感性降低,在高复杂度情况下,Prefetch 可以显著提高页面流畅度。Android 5.0 以后引入
Render Thread提升动画流畅性。- 如果item 高度固定的话,可以使用
RecyclerView.setHasFixSize(true)来避免requestLayout浪费资源;- 共用 RecycleViewPool ;
- 通过 getExtraLayoutSpace 来增加 RecyclerView 预留的额外空间,默认是一页数据的数量
8. 自定义属性获取完属性后为什么调用TypeArray.recycle()?
1 | /** |
1 | public class Resources{ |
看上面的那个大概意思就是从一个同步池子里获取到的,默认大小是5. 平常我们获取的属性是通过:
1 | TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.xx); |
context 的一个静态方法,点进去看看:
1 | //最终到了这里 |
SynchronizedPool
1 | /** |
维护一个同步栈结构的对象池,避免重复频繁创建属性值 TypeArray 对象,使用时记得调用 Recycle() 方法将不用的对象返回至对象池来达到重用的目的,如果不调用 recycle() 方法将会随着 Activity的每一次创建而创建,因此系统频繁创建 array, 对内存和性能是一个不小的开销,如果不使用池,每次都让 GC 来回收,很可能就会造成 OOM.
9. 自定义属性
1 | //自定义属性一般我们会在values-attr.xml 中定义,如下: |
两种方式有什么区别呢?
第一种方式会把有关这个控件的属性包裹起来,达到一个分组的概念,不零散,属性的使用范围更加明确,
第一种方式获取属性值:
1 | TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.customerView); |
第二种方式获取属性值:
1 | int[] array = {R.attr.AloneAttr}; |
Android知识点总结二
1. Canvas.save() 跟 Canvas.restore()的调用时机
save: 用来保存
Canvas的状态。save之后,可以调用Canvas的平移,放缩,旋转等操作。
restore: 用来恢复canvas之前保存的状态。防止save后对Canvas执行的操作对后续的绘制有影响。
2. Android P(9.0) 的新特性
- 支持WiFi室内定位
- 适配刘海屏
- 通知栏改进:可以显示对话,附加照片和表情等
- 多摄像头api
- 对于非
http连接,将直接拦截,推荐使用https
Android 8.0
PHONE 权限组新增加两个权限
- ANSWER_PHONE_CALLS:允许您的应用通过编程方式接听呼入电话。要在您的应用中处理呼入电话,您可以使用 assceptRingingCall() 函数
- READ_PHONE_NUMBERS: 权限允许您的应用读取设备中存储的电话号码
通知适配
为了更好的管理通知的提醒,不想一些不重要的通知打扰用户,新增加用户渠道,用户可根据渠道屏蔽一些不重要的通知
安装apk
在 AndroidManifest 文件中添加未知来源应用的权限:
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>这样系统会自动询问用户完成授权。也可以通过
canRequestPackageInstalls查询是否有此权限,如果没有的话就使用Settings.ACTION_MANAGE_UNKNOW_APP_SOURCE引导用户安装未知应用权限去授权。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18if(Build,VERSION>SDK_INT >= 26){
Boolean isInstall = getPackageManager().canRequestPackageInstalls();//26以上
if(isInstall){
//有此权限,安装应用
}else{
//跳转到 安装未知应用 权限界面,引导用户开启权限
Uri selfPackageUri = Uri.parse("package:" + this.getPackageName());
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, selfPackageUri);
startActivityForResult(intent, REQUEST_CODE_UNKNOWN_APP)
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_UNKNOWN_APP) {
//如果权限开启,则开始安装应用
}
}静态广播无法正常接受
Android N(7.0) 的新特性
- 私有文件外部将不能访问,通过
FileProvider可解决 SharedPreferences进程间通信模式闪退,SecurityException
Android 6.0
运行时权限的申请
https的推荐
3. APK 内容
assets存放需要打包到APK中的静态文件lib目录native库res目录 存放应用程序的资源- META-INF 存放应用程序签名和证书的目录
AndroidManifest.xmlclasses.dexresources.arsc资源配置文件
4. Binder 机制的优点
Linux中使用的IPC通信机制有:pipe(管道),signal(信号量),Socket
socket:是一个通用接口,传输效率低,开销比较大;- 管道和消息队列:采用存储转发方式,所以至少需要拷贝2次数据,效率低,共享内存虽然在传输时没有拷贝数据,但其控制机制复杂,而
Binder更好传输性能。- 安全性更高,可以建立私有通道,有身份标识(UID/PID)
- 另外一个优点就是,通过
binder可以很方便的调用server端的方法,犹如调用本地方法一样。
5. Gradle 的生命周期
1.初始化
初始化主要是读取
settings.gragle文件,用于确定哪些项目参与构建,并创建Project实例2.配置
配置阶段主要是为每个
build.gradle文件配置project对象3.执行
主要根据
gradle命令和传入参数创建并执行任务
随笔记录Android
1. Android中数据存储的方式
FIleSharedPreferenceSqlite- 网络
ContentProviderFileProvider: 继承于ContentProvider的子类,可以用于解决Android7.0中禁止我们的应用对外部公开file://的问题。
2. SharedPreference 是进程同步的吗
sharedPreference 默认不是线程同步的,可以设置模式为:
MODE_MULTI_PROCESS做到进程同步,系统默认也是有缓存的,有很多问题,在Android N(7.0)以上废弃不能使用了,会抛出异常。推荐使用ContentProvider
sharedPreference四种模式:
- MODE_PRIVATE
- MODE_MULTI_PROCESS
- MODE_WORLD_READABLE
- MODE_WORLD_WRITEABLE
3.Shareferences commit 和 apply 的区别
在很早的sdk当中,一般都使用
commit方法,同步,直接写入磁盘,并且有返回结果boolean类型,而apply是异步, 先写入内存,然后异步写入磁盘。 如果操作频繁的话,apply的性能优于commit.
在阿里巴巴开发手册中也记录到:
SharedPreference 提 交 数 据 时 , 尽 量 使 用 Editor#apply()
,而非Editor#commit()。一般来讲,仅当需要确定提交结果,并据此有后续操作时,才使用 Editor#commit()。
4. View 的 measureSpec 由谁决定的
- View的
MeasureSpec由这个父控件的MeasureSpec和自身的LayoutParams决定- 顶级
DecorView由窗口尺寸和自身的LayoutParams共同确定
5.ACTION_CANCEL 事件
一般来说,如果一个子视图接受了父视图分发给它的
ACTION_DOWN事件,那么与ACTION_DOWN事件相关的事件都要分发给这个子视图,但是如果父视图希望拦截其中的一些事件,不再继续转发事件给这个子视图的话,那么就需要给子视图一个ACTION_CANCEL事件。
6. View的invalidate postInvalidate requestLayout区别
invalidate会调用onDraw进行重绘,只能在主线程postIncalidate可以在其它线程,如子线程requestLayout会调用onLayout和onMeasure,不一定会调用onDraw
7. View 的生命周期
Creation创建(从xml中加载或者layout文件中定义加载)onFinishInflate()从xml中加载完成
Layout布局onMeasure()onLayout()
Drawing绘制onDraw()
Event processing事件处理Focus聚焦onFocusChanged()onWindowFocusChanged()
Attaching附上onAttachingToWindow()onDetachedFromWindow()onVisibiltyChanged()onWindowVisibiltyChanged()
java基础二
1. HashMap
1 | public class HashMap<K,V> extends AbstractMap<K,V> |
未完待续….仍需要查阅大量资料学习….
2.Java 内存分配
- 栈区:方法区内的局部变量,对象的引用等 优点:速度快,缺点:大小和生命周期必须是确定的。
- 堆区:new出来的对象,垃圾回收器回收的是堆区的内存
- 方法区(静态区):静态变量,常量,保存类信息
3.垃圾
什么是垃圾?要通过科学的方法才能判断是否是为垃圾,有两种方法
- 引用计数法:可能会导致内部循环
- 可达性分析:是否可达
借助一个图快速理解 引用计数法:

代码:
1 | /**循环问题*/ |
再看下这个 可达性分析 :

ObjD 和 ObjE 虽然内部存在引用,但是定点不可达,所以也是垃圾回收的对象。
4.垃圾回收的方法
标记-清除:减少停顿时间,但会造成内存碎片
标记-整理:可以解决内存碎片,但是会增加停顿时间
复制清除:从一个地方拷贝到另一个地方,适合有大量回收的场景,比如:新生代回收
- 优点:效率高于标记清除,活着的对象是整齐排列的,没有内存碎片
- 缺点: 浪费空间,毕竟如果按照1:1比例划分空间的话,那么将会有50%的空间被浪费。不过在jvm中,新生代空间并不是按照1:1来划分的,而是按照8:1:1的比例分为一个
eden区 和两个survivor(survivor0,survivor1)区,然后一个eden区,两个survivor区。大部分对象在Eden区中生成。回收时先将eden区存活对象复制到一个survicor0区,然后清空eden区,当这个survivor0区也存放满了时,则将eden区和survivor0区存活对象复制到另一个survivor1区,然后清空eden区和survivor0区,此时survivor0区是空的,然后将survivor0区和survivor1区交换,即保持survivor1区为空,这样的过程也叫做Minor GC,每进行Minor GC一次,存活着的对象的年龄就会+1,当存活着的对象的年龄到达15岁时,就会被送进年老代。 当然,当整个survivor1区不足以存放Eden和survivor0的存活对象时,也会将存活对象直接放到年老代。若是年老代也满了就会触发一次Full GC,也就是新生代,老年代都进行回收。
分代收集:把内存区域分成不同代,根据年代不同采取不同的策略;
- 新生代:存放新创建的对象,采用复制回收方法
- 年老代:这些对象垃圾回收的频率较低,采取标记整理法
- 永久代:存放Java本身的一些数据,当类不再使用时,也会被回收
手稿:

5. Full GC 触发的条件
- 调用
System.gc时,系统建议执行Full GC但是不必然执行 - 年老代或者永久代空间不足
6. 线程调度
wait(): object 方法,必须在同步代码块中使用,使当前线程处于等待状态,释放锁notify(): 和wait联合使用,通知一个线程,具体通知哪个由jvm 决定,使用不当可能发生死锁notifyAll(): 和wait方法联合使用,通知所有线程,具体哪个线程获得运行权jvm决定sleep(): 睡眠状态
7.线程同步
Synchronzied修饰ThreadLocal: 每个线程都有一个局部变量的副本,互不干扰。一种以空间换时间的方式。- 线程安全的容器和方法,可以实现线程同步,如:
Collections.SynchronizedList()将List转为线程同步;用ConurrentHashMap实现hashmap线程同步。
volatile修饰的变量不会缓存在寄存器中,每次使用都会从主存器中读取;保证可见性,不保证原子性
Java基础知识
1. 如何实现对象克隆?
有两种方式:
实现
Cloneable接口并重写clone方法(浅拷贝);实现
Serializable,通过对象的序列化和反序列化实现真正的深度克隆(深拷贝),代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28public class MyUtil {
private MyUtil() {
throw new AssertionError();
}
public static <T> T clone(T obj) throws Exception {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bout);
oos.writeObject(obj);
ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bin);
return (T) ois.readObject();
// 说明:调用ByteArrayInputStream或ByteArrayOutputStream对象的close方法没有任何意义
// 这两个基于内存的流只要垃圾回收器清理对象就能够释放资源,这一点不同于对外部资源(如文件流)的释放
}
}注意:基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型界定,可以检查出要克隆对象是否支持序列化,这项检查是编译器完成的,不是运行时抛出异常,这种方案明显优于使用object类的clone方法克隆对象。让问题在编译的时候暴漏出来总是优于把问题留在运行时。
2. 内部类访问局部变量的时候,为什么加final?
内部类和局部变量的生命周期不同,方法结束后局部变量的生命周期就结束了,而内部类只要有引用就不结束,内部类生命周期>=局部变量
Java会在编译时在内部类的构造方法里边,将局部变量以参数形式传递给内部类
而如果局部变量发生改变,内部类不知情的场景,所以要加
fianl,保证引用不可改变注意:在Java8中,可以不适用final关键字,但是如果我们修改了局部变量还是会发生错误,从而保证局部变量的引用不变。
3. transient 关键字
如果使用
transient修饰变量,当对象存储时,它的值不需要维持。换句话来说就是用transient修饰的变量不参与序列化的过程。
4. String 和 StringBuild,StringBuffer的区别
String是只读字符串,创建完成之后是不能修改,如果修改也是创建一个新对象,回收旧对象的过程,所以执行效率比较低,StringBuild和StringBuffer可以改变,StringBuild线程不安全,但是效率比较高,StringBuffter线程安全效率快慢:
Stringbuilder>StringBuffer>String
5. String 为什么设计成不可变
- 安全性
- 本身是
final类不可修改,不可变极为安全 - String 常被用来作为
HashMap的key如果可变会引来安全问题,例如两个key相同
- 本身是
- 效率高
- 通过字符串池可以节省很多空间
- 每一个
String对应一个hashcode,再次使用不用重新计算
6. java 中的四种引用及应用场景
- 强引用
- 弱引用—-引用到达,不可达就会被回收,即便是内存充足,可用于图片
Bitmap缓存,当不再使用Bitmap时,就会被回收 - 软引用—–内存不足时被回收,也可用于
Bitmap回收,当内存不足时,可回收 - 虚引用—-它指向的对象回收时,它本身会被加入到引用队列中,这样我们就知道它指向的对象何时被销毁