Android面试知识点:JAVA

本文详细讲解了Java基础,如==、equals与hashCode的关系,基本数据类型、String与容器类的区别,内部类、抽象类和接口,线程与多线程、线程池,内存结构与引用类型,以及线程池、死锁和Java内存管理。涵盖了多线程编程、数据结构和并发控制的关键知识点。
摘要由CSDN通过智能技术生成

一、JAVA基础

1、JAVA 中 == 、equals和hashCode的区别

== : 基本数据类型比较的是值;引用数据类型比较的是内存地址
equals:在不重写的情况下比较的是内存地址;重写的情况下一般比较的是对象内容
hashCode:是Object类中一个方法,返回一个离散的int整数型。在集合类中操作中使用,为了提高查询速度。

  • equals与hashCode的关系
    1.如果两个对象equals,Java运行环境会认为他们的hashCode一定相等
    2.如果两个对象 不 equals,他们的hashCode有可能相等
    3.如果两个对象hashCode相等,他们不一定equals
    4.如果两个对象hashCode 不 相等,他们一定 不 equals

2、JAVA 8中基本数据类型

byte(1)、char(2)、short(2)、int(4)、long(8)、float(4)、double(8)、boolean(1或4)

3、String、StringBuffer、StringBuilder 区别

String:字符串常量,不适用于经常改变值的情况,每次改变相当于生成一个对象
StringBuffer:字符串变量,线程安全
StringBuilder:字符串变量,线程不安全,单线程下可用,效率略高于StringBuffer

4、内部类定义和种类

内部类可以访问外部类的属性和方法
种类有:成员内部类、局部内部类(嵌套在方法和作用域内)、匿名内部类(没有构造方法)、静态内部类(static修饰的内部类,不能使用任何外部类非static修饰的变量和方法,不依赖外部类)

5、抽象类和接口的区别

接口抽象类
多继承支持不支持
类型限制没有有,只能是引用类型
方法实现继承类中必须给出方法实现继承类中可以不给出
扩展性比较麻烦相对比较灵活
多层继承比较麻烦,需要借助虚函数比较灵活

6、final、finally、finalize区别

final:修饰类、成员变量和成员方法。类不可被继承,成员变量不可变,成员方法不可重写
finally:与try…catch…共同使用。确保无论是否出现异常都能被调用到
finalize:方法。垃圾回收之前会调用此方法,子类可以重写该方法实现对资源的回收

7、static 关键字

修饰变量可以通过类名.变量名直接引用,而不需要new出一个类来
修饰方法可以通过类名.方法名直接引用,而不需要new出一个类来
修饰类一般情况下来说是不可以修饰类的,如果static要修饰一个类,说明这个类是一个静态内部类
修饰代码块静态块里面的代码只执行一次,且只在初始化类的时候

8、break、continue、return区别

break完全中止循环
continue中止本次循环,接着开始下一次循环
return结束一个方法

9、JAVA 异常体系

所有异常类的基类是Throwable类,两大子类分别是Error和Exception。

Error:描述的是内部系统错误,例如Java虚拟机崩溃。这种情况仅凭程序自身是无法处理的,在程序中也不会对Error异常进行捕捉和抛出。
Exception:又分为RuntimeException(运行时异常)和CheckedException(检查时异常)

RuntimeException:程序运行过程中才可能发生的异常。一般为代码的逻辑错误。例如:类型错误转换,数组下标访问越界,空指针异常、找不到指定类等
CheckedException:编译期间可以检查到的异常,必须显式的进行处理(捕获或者抛出到上一层)。例如:IOException, FileNotFoundException等

10、JAVA 中实现多态的机制

方法的重写和重载是Java多态性的不同表现
重写:是父类与子类之间多态性的一种表现。
重载:是一个类中多态性的一种表现。

11、JAVA 反射

JAVA反射机制即在运行状态中对于任意一个类都能够知道这个类的所有属性和方法;对于任意一个对象都能够调用它的任意一个方法和属性。 从对象出发、通过反射(Class类)可以取得取得类的完整信息(类名 Class类型、所在包、具有的所有方法 Method[]类型、某个方法的完整信息(包括修饰符、返回值类型、异常、参数类型)、所有属性 Field[]、某个属性的完整信息、构造器 Constructors),调用类的属性或方法。

总结: 在运行过程中获得类、对象、方法的所有信息。

12、泛型

泛型即实现了参数化类型的概念,使代码可以应用于多种类型。

  • 泛型的边界:

extends的主要作用是设定类型通配符的上限;
super与extends是完全相反的,其定义的是通配符的下界

  • 泛型的局限性:
    1.基本类型无法作为类型参数

如: ArrayList< int >这样是非法的,只能ArrayList< Integer >注意:数组表示中int[]和Integer[]都是可以的

2.在泛型代码内部,无法获得任何有关泛型参数类型的信息

如:你传入的泛型参数为T,而在方法内部你无法使用T的任何方法,毕竟编译期它的类型还不确定如果你的泛型是< T extends BaseEntity >的话,你是可以使用BaseEntity方法的,但如果你没有任何继承,那你只能使用Object的方法

3.在能够使用泛型方法的时候,尽量避免使整个类泛化。粗细粒度需要控制好

  • 泛型通配符

1.常用的 T,E,K,V,?

表示不确定的 java 类型
T (type)表示具体的一个java类型
K V (key value)分别代表java键值中的Key Value
E (element)代表Element

2,上界通配符 < ? extends E>
用 extends 关键字声明,表示参数化的类型可能是所指定的类型,或者是此类型的子类。

3,下界通配符 < ? super E>
表示参数化的类型可能是所指定的类型,或者是此类型的父类型,直至 Object

二、JAVA数据结构

1、集合类与集合框架

在这里插入图片描述
Collection接口:集合框架的根接口。它是集合类框架中最具一般性的顶层接口。Java平台没有提供任何该接口的直接具体实现类,但是提供了具有各种不同特性的子接口
Set接口:不允许包含重复值的集合
List接口:可索引的集合,可以包含重复值。使用该接口时我们通过索引对元素进行精准的插入和查找

Queue接口:该集合适用于组织一个队列,队列中的元素按照优先级进行处理。除了继承自Collection接口的方法,该接口还提供了另外的插入、提取和检验方法。典型的队列是符合“先进先出”原则的,优先级队列是一种例外,它按照元素的优先级顺序排列元素。无论按照什么原则排序,队头元素总是首先被检出。每个Queue接口的实现类必须指定它的排序原则
Deque接口:与Queue的不同之处在于它是一个双端队列,在两端都能插入和移除元素,它继承并扩展了Queue接口
Map接口:提供了键值对(key/value)的映射关系的集合。关键字不能有重复值,每个关键字至多可映射一个值
SortedSet接口:以升序的原则维持着集合中的元素顺序。
SortedMap接口:以关键字升序的原则维持着集合中的元素顺序

2、HashMap和HashTable的区别

HashMap是非线程安全的,效率高一点、方法不是Synchronize的要提供外同步,有containsvalue和containsKey方法。
HashTable是线程安全的,不允许有null的键和值,效率稍低,方法是是Synchronize的。有contains方法方法。HashTable继承于Dictionary 类。

3、ArrayList和LinkedList的区别

ArrayList是基于数组实现的,ArrayList非线程安全。
LinkedList是基于双链表实现的,LinkedList非线程安全。

使用场景:
1.如果应用程序对各个索引位置的元素进行大量的存取或删除操作,ArrayList对象要远优于LinkedList对象;
2.如果应用程序主要是对列表进行循环,并且循环时候进行插入或者删除操作,LinkedList对象要远优于ArrayList对象

三、线程、多线程、线程池

1、开启线程的三种方式

  1. 继承Thread类
  2. 实现Runable接口
  3. 使用线程池

2、线程和进程的区别

  1. 进程是cpu资源分配的最小单位;线程是cpu调度的最小单位。
  2. 进程之间不能共享资源;线程共享所在进程的地址空间和其它资源。
  3. 一个进程内可拥有多个线程,进程可开启进程,也可开启线程。
  4. 一个线程只能属于一个进程,线程可直接使用同进程的资源,线程依赖于进程而存在。

3、run()和start()方法区别

start()方法:启动新创建的线程,而且start()内部调用了run()方法。
run()方法:只会是在原来的线程中调用,没有新的线程启动。

4、在Java中wait和sleep方法的不同

wait()方法:用于线程间通信,如果等待条件为真且其它线程被唤醒时它会释放锁;
sleep()方法:仅仅释放CPU资源或者让当前线程停止执行一段时间,但不会释放锁。

5、谈谈wait/notify关键字的理解

wait():等待对象的同步锁,需要获得该对象的同步锁才可以调用这个方法,否则编译可以通过,但运行时会收到一个异常:IllegalMonitorStateException。

调用任意对象的 wait() 方法导致该线程阻塞,该线程不可继续执行,并且该对象上的锁被释放。

notify():唤醒在等待该对象同步锁的线程(只唤醒一个,如果有多个在等待),注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。

调用任意对象的notify()方法则导致因调用该对象的 wait()方法而阻塞的线程中随机选择的一个解除阻塞(但要等到获得锁后才真正可执行)。

6、interrupt()关闭线程

  1. interrupt()的作用是中断本线程。
  2. 本线程中断自己是被允许的;其它线程调用本线程的interrupt()方法时,会通过checkAccess()检查权限。这有可能抛出SecurityException异常。
  3. 如果本线程是处于阻塞状态:调用线程的wait(), wait(long)或wait(long, int)会让它进入等待(阻塞)状态,或者调用线程的join(), join(long), join(long, int), sleep(long), sleep(long, int)也会让它进入阻塞状态。若线程在阻塞状态时,调用了它的interrupt()方法,那么它的“中断状态”会被清除并且会收到一个InterruptedException异常。例如,线程通过wait()进入阻塞状态,此时通过interrupt()中断该线程;调用interrupt()会立即将线程的中断标记设为“true”,但是由于线程处于阻塞状态,所以该“中断标记”会立即被清除为“false”,同时,会产生一个InterruptedException的异常。
  4. 如果线程被阻塞在一个Selector选择器中,那么通过interrupt()中断它时;线程的中断标记会被设置为true,并且它会立即从选择操作中返回。
  5. 如果不属于前面所说的情况,那么通过interrupt()中断线程时,它的中断标记会被设置为“true”。
  6. 中断一个“已终止的线程”不会产生任何操作。

7、java中同步的方法

  1. synchronized关键字修改的方法。
  2. synchronized关键字修饰的语句块。
  3. 使用特殊域变量(volatile)实现线程同步

8、synchronized用法及原理

修饰实例方法对方法所属对象进行加锁,是对象锁
修饰静态方法对类对象进行加锁,是类锁
修饰代码块对一段代码块进行加锁,是对象锁

Synchronized底层实现原理:Java 虚拟机中的同步(Synchronization)基于进入和退出管程(Monitor)对象实现,无论是显式同步(有明确的 monitorenter 和 monitorexit 指令,即同步代码块)还是隐式同步都是如此。在 Java 语言中,同步用的最多的地方可能是被 synchronized 修饰的同步方法。同步方法并不是由 monitorenter 和 monitorexit 指令来实现同步的,而是由方法调用指令读取运行时常量池中方法的 ACC_SYNCHRONIZED 标志来隐式实现的。

9、volatile用法及原理

volatile的用法:volatile通常被比喻成"轻量级的synchronized",也是Java并发编程中比较重要的一个关键字。和synchronized不同,volatile是一个变量修饰符,只能用来修饰变量。无法修饰方法及代码块等。

volatile的原理:为了提高处理器的执行速度,在处理器和内存之间增加了多级缓存来提升。但是由于引入了多级缓存,就存在缓存数据不一致问题。

但是,对于volatile变量,当对volatile变量进行写操作的时候,JVM会向处理器发送一条lock前缀的指令,将这个缓存中的变量回写到系统主存中。

但是就算写回到内存,如果其他处理器缓存的值还是旧的,再执行计算操作就会有问题,所以在多处理器下,为了保证各个处理器的缓存是一致的,就会实现缓存一致性协议

缓存一致性协议:每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,当处理器要对这个数据进行修改操作的时候,会强制重新从系统内存里把数据读到处理器缓存里。

所以,如果一个变量被volatile所修饰的话,在每次数据变化之后,其值都会被强制刷入主存。而其他处理器的缓存由于遵守了缓存一致性协议,也会把这个变量的值从主存加载到自己的缓存中。这就保证了一个volatile在并发编程中,其值在多个缓存中是可见的。

10、synchronized 和volatile 关键字的区别

synchronized 关键字和 volatile 关键字是两个互补的存在,而不是对立的存在!

  1. volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
  2. volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的
  3. volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性
  4. volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
  5. volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化
  6. volatile关键字主要用于解决变量在多个线程之间的可见性,而 synchronized 关键字解决的是多个线程之间访问资源的同步性。

11、lock原理

Lock和ReadWriteLock是顶层锁的接口
Lock代表实现类是ReentrantLock(可重入锁)
ReadWriteLock(读写锁)的代表实现类是ReentrantReadWriteLock

  • lock与synchronized比较

synchronized是java中的一个关键字,也就是说是Java语言内置的特性。

  1. Lock不是Java语言内置的,synchronized是Java语言的关键字,因此是内置特性。Lock是一个类,通过这个类可以实现同步访问;
  2. 采用synchronized不需要用户去手动释放锁,当synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁的占用;而Lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象。
  • synchronized 的局限性与Lock的优点

如果一个代码块被synchronized关键字修饰,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待直至占有锁的线程释放锁。

占有锁的线程释放锁一般会是以下三种情况之一:

  1. 占有锁的线程执行完了该代码块,然后释放对锁的占有;
  2. 占有锁线程执行发生异常,此时JVM会让线程自动释放锁;
  3. 占有锁线程进入WAITING状态从而释放锁,例如在该线程中调用wait()方法等。

下列三种情况:

  1. 在使用synchronized关键字的情形下,假如占有锁的线程由于要等待IO或者其他原因(比如调用sleep方法)被阻塞了,但是又没有释放锁,那么其他线程就只能一直等待,别无他法。这会极大影响程序执行效率。因此,就需要有一种机制可以不让等待的线程一直无期限地等待下去(比如只等待一定的时间 (解决方案:tryLock(long time, TimeUnit unit))或者能够响应中断(解决方案:lockInterruptibly())),这种情况可以通过 Lock 解决。

  2. 当多个线程读写文件时,读操作和写操作会发生冲突现象,写操作和写操作也会发生冲突现象,但是读操作和读操作不会发生冲突现象。但是如果采用synchronized关键字实现同步的话,就会导致一个问题,即当多个线程都只是进行读操作时,也只有一个线程在可以进行读操作,其他线程只能等待锁的释放而无法进行读操作。因此,需要一种机制来使得当多个线程都只是进行读操作时,线程之间不会发生冲突。同样地,Lock也可以解决这种情况 (解决方案:ReentrantReadWriteLock) 。

  3. 通过Lock得知线程有没有成功获取到锁 (解决方案:ReentrantLock) ,但这个是synchronized无法办到的。

上面提到的三种情形,我们都可以通过Lock来解决,但 synchronized 关键字却无能为力。事实上,Lock 是 java.util.concurrent.locks包 下的接口,Lock 实现提供了比 synchronized 关键字更广泛的锁操作,它能以更优雅的方式处理线程同步问题。

12、死锁的四个必要条件

  1. 互斥条件:一个资源每次只能被一个进程使用,即在一段时间内某 资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。
  2. 请求与保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源 已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
  3. 不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能 由获得该资源的进程自己来释放(只能是主动释放)。
  4. 循环等待条件: 若干进程间形成首尾相接循环等待资源的关系

这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。

13、线程池

  • 使用线程池的原因
    在android开发中经常会使用多线程异步来处理相关任务,而如果用传统的newThread来创建一个子线程进行处理,会造成一些严重的问题:
  1. 在任务众多的情况下,系统要为每一个任务创建一个线程,而任务执行完毕后会销毁每一个线程,所以会造成线程频繁地创建与销毁。

  2. 多个线程频繁地创建会占用大量的资源,并且在资源竞争的时候就容易出现问题,同时这么多的线程缺乏一个统一的管理,容易造成界面的卡顿。

  3. 多个线程频繁地销毁,会频繁地调用GC机制,这会使性能降低,又非常耗时。

总而言之:频繁地为每一个任务创建一个线程,缺乏统一管理,降低性能,并且容易出现问题。

  • ThreadPoolExecutor 创建基本线程池

创建线程池,主要是利用ThreadPoolExecutor这个类,而这个类有几种构造方法,其中参数最多的一种构造方法如下:

  public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        ...
    }

corePoolSize: 该线程池中核心线程的数量。

注意:线程池中存在核心线程与非核心线程,核心线程一旦创建会一直执行任务或等待任务到来,而非核心线程只在任务队列塞满任务时去执行多出的任务,并且非核心线程在等待一段时间后将会被回收,这个时间作为参数可调配,见下面的keepAliveTime参数。

maximumPoolSize:该线程池中最大线程数量。(区别于corePoolSize)

keepAliveTime:从字面上就可以理解,是非核心线程空闲时要等待下一个任务到来的时间,当任务很多,每个任务执行时间很短的情况下调大该值有助于提高线程利用率。注意:当allowCoreThreadTimeOut属性设为true时,该属性也可用于核心线程。

unit:上面时间属性的单位

workQueue:任务队列,后面详述。

threadFactory:线程工厂,可用于设置线程名字等等,一般无须设置该参数。

  • 线程池的最大值:一般设置为2N + 1
//参数初始化
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
//核心线程数量大小
private static final int corePoolSize = Math.max(2, Math.min(CPU_COUNT - 1, 4));
//线程池最大容纳线程数
private static final int maximumPoolSize = CPU_COUNT * 2 + 1;
//线程空闲后的存活时长
private static final int keepAliveTime = 30;

//任务过多后,存储任务的一个阻塞队列
BlockingQueue<Runnable>  workQueue = new SynchronousQueue<>();

//线程的创建工厂
ThreadFactory threadFactory = new ThreadFactory() {
    private final AtomicInteger mCount = new AtomicInteger(1);

    public Thread newThread(Runnable r) {
        return new Thread(r, "AdvacnedAsyncTask #" + mCount.getAndIncrement());
    }
};

//线程池任务满载后采取的任务拒绝策略
RejectedExecutionHandler rejectHandler = new ThreadPoolExecutor.DiscardOldestPolicy();

//线程池对象,创建线程
ThreadPoolExecutor mExecute = new ThreadPoolExecutor(
        corePoolSize, 
        maximumPoolSize,
        keepAliveTime,
        TimeUnit.SECONDS,
        workQueue,
        threadFactory, 
        rejectHandler
);

四、JAVA内存结构

补充

五、JAVA中的四种引用的区别以及使用场景

1、强引用(StrongReference)

强引用就是指在程序代码之中普遍存在的,比如下面这段代码中的object和str都是强引用:

Object object = new Object();
String str = "hello";

只要某个对象有强引用与之关联,JVM必定不会回收这个对象,即使在内存不足的情况下,JVM宁愿抛出OutOfMemory错误也不会回收这种对象。比如下面这段代码:

public class Main {
    public static void main(String[] args) {
        new Main().fun1();
    }
     
    public void fun1() {
        Object object = new Object();
        Object[] objArr = new Object[1000];
    }
}

当运行至Object[] objArr = new Object[1000];这句时,如果内存不足,JVM会抛出OOM错误也不会回收object指向的对象。不过要注意的是,当fun1运行完之后,object和objArr都已经不存在了,所以它们指向的对象都会被JVM回收。
如果想中断强引用和某个对象之间的关联,可以显示地将引用赋值为null,这样一来的话,JVM在合适的时间就会回收该对象。

2、软引用(SoftReference)

软引用是用来描述一些有用但并不是必需的对象,在Java中用java.lang.ref.SoftReference类来表示。对于软引用关联着的对象,只有在内存不足的时候JVM才会回收该对象。因此,这一点可以很好地用来解决OOM的问题,并且这个特性很适合用来实现缓存:比如网页缓存、图片缓存等。
软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被JVM回收,这个软引用就会被加入到与之关联的引用队列中。下面是一个使用示例:

import java.lang.ref.SoftReference;
 
public class Main {
    public static void main(String[] args) {
         
        SoftReference<String> sr = new SoftReference<String>(new String("hello"));
        System.out.println(sr.get());
    }
}

3、弱引用(WeakReference)

弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在java中,用java.lang.ref.WeakReference类来表示。下面是使用示例:

import java.lang.ref.WeakReference;
 
public class Main {
    public static void main(String[] args) {
     
        WeakReference<String> sr = new WeakReference<String>(new String("hello"));
         
        System.out.println(sr.get());
        System.gc();                //通知JVM的gc进行垃圾回收
        System.out.println(sr.get());
    }
}

第二个输出结果是null,这说明只要JVM进行垃圾回收,被弱引用关联的对象必定会被回收掉。不过要注意的是,这里所说的被弱引用关联的对象是指只有弱引用与之关联,如果存在强引用同时与之关联,则进行垃圾回收时也不会回收该对象(软引用也是如此)。
  弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被JVM回收,这个软引用就会被加入到与之关联的引用队列中。

4、虚引用(PhantomReference)

虚引用和前面的软引用、弱引用不同,它并不影响对象的生命周期。在java中用java.lang.ref.PhantomReference类表示。如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收。
  要注意的是,虚引用必须和引用队列关联使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会把这个虚引用加入到与之 关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
 
 
public class Main {
    public static void main(String[] args) {
        ReferenceQueue<String> queue = new ReferenceQueue<String>();
        PhantomReference<String> pr = new PhantomReference<String>(new String("hello"), queue);
        System.out.println(pr.get());
    }
}

对于强引用,我们平时在编写代码时经常会用到。而对于其他三种类型的引用,使用得最多的就是软引用和弱引用,这2种既有相似之处又有区别。它们都是用来描述非必需对象的,但是被软引用关联的对象只有在内存不足时才会被回收,而被弱引用关联的对象在JVM进行垃圾回收时总会被回收。
在SoftReference类中,有三个方法,两个构造方法和一个get方法(WekReference类似):
两个构造方法:

public SoftReference(T referent) {
    super(referent);
    this.timestamp = clock;
    }
 
public SoftReference(T referent, ReferenceQueue<? super T> q) {
    super(referent, q);
    this.timestamp = clock;
    }

get方法用来获取与软引用关联的对象的引用,如果该对象被回收了,则返回null。
在使用软引用和弱引用的时候,我们可以显示地通过System.gc()来通知JVM进行垃圾回收,但是要注意的是,虽然发出了通知,JVM不一定会立刻执行,也就是说这句是无法确保此时JVM一定会进行垃圾回收的。
特别注意:在程序设计中一般很少使用弱引用与虚引用,使用软用的情况较多,这是因为软引用可以加速JVM对垃圾内存的回收速度,可以维护系统的运行安全,防止内存溢出(OutOfMemory)等问题的产生。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值