前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java四种引用,Java堆和栈,热修复,ANR,设计模式

Java四种引用,Java堆和栈,热修复,ANR,设计模式

作者头像
陈宇明
发布2020-12-16 10:37:40
4360
发布2020-12-16 10:37:40
举报
文章被收录于专栏:设计模式
01
JAVA的四种引用以及应用场景

GC在收集一个对象的时候会判断是否有引用指向对象,在JAVA中的引用主要有四种:

强引用(Strong Reference)

强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。

软引用(Soft Reference)

如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。

下面举个例子,假如有一个应用需要读取大量的本地图片,如果每次读取图片都从硬盘读取,则会严重影响性能,但是如果全部加载到内存当中,又有可能造成内存溢出,此时使用软引用可以解决这个问题。

设计思路是:用一个HashMap来保存图片的路径和相应图片对象关联的软引用之间的映射关系,在内存不足时,JVM会自动回收这些缓存图片对象所占用的空间,从而有效地避免了内存溢出的问题。

软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。

弱引用(Weak Reference)

弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。

弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

虚引用(Phantom Reference)

“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。

虚引用主要用于检测对象是否已经从内存中删除,跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。

虚引用的唯一目的是当对象被回收时收到一个系统通知。

02

谈谈热修复的原理

我们知道Java虚拟机 —— JVM 是加载类的class文件的,而Android虚拟机——Dalvik/ART VM 是加载类的dex文件, 而他们加载类的时候都需要ClassLoader,ClassLoader有一个子类BaseDexClassLoader,而BaseDexClassLoader下有一个数组——DexPathList,是用来存放dex文件,当BaseDexClassLoader通过调用findClass方法时,实际上就是遍历数组,找到相应的dex文件,找到,则直接将它return。而热修复的解决方法就是将新的dex添加到该集合中,并且是在旧的dex的前面,所以就会优先被取出来并且return返回。

03

ANR异常的产生条件及解决方案

ANR是什么? ANR全称:Application Not Responding,也就是应用程序无响应. 简单来说,就是应用跑着跑着,突然duang,界面卡住了,无法响应用户的操作如触摸事件等.

原因 Android系统中,ActivityManagerService(简称AMS)和WindowManagerService(简称WMS)会检测App的响应时间. 如果App在特定时间无法相应屏幕触摸或键盘输入时间,或者特定事件没有处理完毕,就会出现ANR.

ANR的产生需要同时满足三个条件

  1. 主线程:只有应用程序进程的主线程(UI线程)响应超时才会产生ANR
  2. 超时时间:产生ANR的上下文不同,超时时间也不同,但只要超过这个时间上限没有响应就会产生ANR
  3. 输入事件/特定操作:输入事件是指按键、触屏等设备输入事件,特定操作是指BroadcastReceiver和Service的生命周期中的各个函数调用

解决方案: 总结为一句话,即不要在主线程(UI线程)里面做繁重的操作

04

Java中堆和栈有什么区别

堆与栈的区别很明显:

  1. 栈内存存储的是局部变量而堆内存存储的是实体;
  2. 栈内存的更新速度要快于堆内存,因为局部变量的生命周期很短;
  3. 栈内存存放的变量生命周期一旦结束就会被释放,而堆内存存放的实体会被垃圾回收机制不定时的回收。

堆、栈说明:

栈内存:栈内存首先是一片内存区域,存储的都是局部变量,凡是定义在方法中的都是局部变量(方法外的是全局变量),for循环内部定义的也是局部变量,是先加载函数才能进行局部变量的定义,所以方法先进栈,然后再定义变量,变量有自己的作用域,一旦离开作用域,变量就会被释放。栈内存的更新速度很快,因为局部变量的生命周期都很短。

堆内存:存储的是数组和对象(其实数组就是对象),凡是new建立的都是在堆中,堆中存放的都是实体(对象),实体用于封装数据,而且是封装多个(实体的多个属性),如果一个数据消失,这个实体也没有消失,还可以用,所以堆是不会随时释放的,但是栈不一样,栈里存放的都是单个变量,变量被释放了,那就没有了。堆里的实体虽然不会被释放,但是会被当成垃圾,Java有垃圾回收机制不定时的收取。

05

谈谈对生成器模式的理解

生成器模式:将一个复杂对象的构建与它的表示分离,使同样的构建过程可以创建不同的表示。

  • 何时使用
    1. 当系统准备为用户提供一个内部结构复杂的对象,而且在构造方法中编写创建该对象的代码无法满足用户需求时,就可以使用生成器模式老构造这样的对象。
    2. 当某些系统要求对象的构造过程必须独立于创建该对象的类时。
  • 优点
    1. 生成器模式将对象的构造过程封装在具体的生成器中,用户使用不同的具体生成器就可以得到该对象的不同表示。
    2. 生成器模式将对象的构造过程从创建该对象的类中分离出来,使用户无须了解该对象的具体组件。
    3. 可以更加精细有效的控制对象的构造过程。生成器将对象的构造过程分解成若干步骤,这就是程序可以更加精细,有效的控制整个对象的构造。
    4. 生成器模式将对象的构造过程与创建该对象类解耦,是对象的创建更加灵活有弹性。
    5. 当增加新的具体的生成器是,不必修改指挥者的代码,即该模式满足开-闭原则。

模式的重心在于分离构建算法和具体的构造实现,从而使构建算法可以重用。

比如我们要得到一个日期,可以有不同的格式,然后我们就使用不同的生成器来实现。

首先是这个类(产品):

代码语言:javascript
复制
//产品
public class MyDate {
 String date;
}

然后就是抽象生成器,描述生成器的行为:

代码语言:javascript
复制
//抽象生成器
public interface IDateBuilder {
 IDateBuilder buildDate(int y,int m,int d);
 String date();
}

接下来是具体生成器,一个以“-”分割年月日,另一个使用空格:

代码语言:javascript
复制
/具体生成器
public class DateBuilder1 implements IDateBuilder{
 private MyDate myDate;
 public DateBuilder1(MyDate myDate){
 this.myDate = myDate;
 }
 @Override
 public IDateBuilder buildDate(int y, int m, int d) {
 myDate.date = y+"-"+m+"-"+d;
 return this;
 }
 @Override
 public String date() {
 return myDate.date;
 }
}
代码语言:javascript
复制
//具体生成器
public class DateBuilder2 implements IDateBuilder{
 private MyDate myDate;
 public DateBuilder2(MyDate myDate){
 this.myDate = myDate;
 }
 @Override
 public IDateBuilder buildDate(int y, int m, int d) {
 myDate.date = y+" "+m+" "+d;
 return this;
 }
 @Override
 public String date() {
 return myDate.date;
 }
}

接下来是指挥官,向用户提供具体的生成器:

代码语言:javascript
复制
//指挥者
public class Derector {
 private IDateBuilder builder;
 public Derector(IDateBuilder builder){
 this.builder = builder;
 }
 public String getDate(int y,int m,int d){
 builder.buildDate(y, m, d);
 return builder.date();
 }
}

最后使用

代码语言:javascript
复制
public class MainGenerate {
 public static void main(String args[]){
 MyDate date = new MyDate();
 IDateBuilder builder;
 builder = new DateBuilder1(date).buildDate(2066, 3, 5);
 System.out.println(builder.date());
 builder = new DateBuilder2(date).buildDate(2066, 3, 5);
 System.out.println(builder.date());
 }
}

使用不同生成器,可以使原有产品表现得有点不一样。

06

结束语

如果你有好的答案可以提交至:

https://github.com/codeegginterviewgroup/CodeEggDailyInterview

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-08-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码个蛋 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • JAVA的四种引用以及应用场景
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档