布局优化
减少布局层次,单一可以用
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日志
响应速度优化的核心是避免在主线程做耗时操作,
Activity
5s 未做出响应,Service
10s未做出响应,就会出现 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 缓存
缓存涉及三个类,分别是:
Recycler
RecycledViewPool
ViewCachedExtension
Recycler
: 用于管理已经废弃或者与RecyclerView
分离的ViewHolder
mChangedScrap
: 与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
和bindView
mScrapView
和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.xml
classes.dex
resources.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中数据存储的方式
FIle
SharedPreference
Sqlite
- 网络
ContentProvider
FileProvider
: 继承于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
回收,当内存不足时,可回收 - 虚引用—-它指向的对象回收时,它本身会被加入到引用队列中,这样我们就知道它指向的对象何时被销毁