您的位置:首页 > 移动开发 > Android开发

Java+Android笔试知识点(一)

2018-02-27 17:19 120 查看
1. [Android中的进程优先级](#android中的进程优先级)
2. [DVM是什么?](#dvm是什么)
3. [Dalvik虚拟机实例和Linux进程的关系](#dalvik虚拟机实例和linux进程的关系)
4. [ArrayList 和LinkedList的内部实现](#arraylist-和linkedlist的内部实现)
5. [Vector 和 ArrayList的区别](#vector-和-arraylist的区别)
6. [ArrayList、LinkedList、Vector的区别](#arraylist-linkedlist-vector的区别)
7. [i++的使用](#i的使用)
8. [三目运算符](#三目运算符)
9. [& 和 &&、| 和 ||](#和-和)
10. [异常](#异常)
11. [IO操作](#io操作)
12. [为什么要进行序列化?](#为什么要进行序列化)
13. [为什么有的对象不能进行序列化?](#为什么有的对象不能进行序列化)
14. [Java垃圾回收机制](#java垃圾回收机制)
15. [Java是如何管理内存的?](#java是如何管理内存的)
16. [内存泄漏](#内存泄漏)
17. [内存溢出](#内存溢出)
18. [ANR形成的原因](#anr形成的原因)
19. [符号引用](#符号引用)
20. [直接引用](#直接引用)
21. [双亲委派模型](#双亲委派模型)
22. [双亲委派模型的工作过程](#双亲委派模型的工作过程)
23. [JDK7和JDK8的内存模型变化](#jdk7和jdk8的内存模型变化)
24. [boolean变量的大小不定](#boolean变量的大小不定)
25. [void也是Java的基本类型之一](#void也是java的基本类型之一)
26. [Java中switch的参数](#java中switch的参数)
27. [equals()与“==”的区别](#equals与的区别)
28. [Object有哪些公用方法](#object有哪些公用方法)
29. [hashcode的作用](#hashcode的作用)
30. [String、StringBuffer与StringBuilder的区别](#string-stringbuffer与stringbuilder的区别)


Android中的进程优先级

前台进程: 是Android系统中最重要的进程,是与用户正在交互的进程,包含以下几种情况:

a、托管用户正在交互的Activity(已调用Activity的onResume()方法)

b、托管某个Service,后者绑定到用户正在交互的Activity

c、托管正在“前台”运行的Service(服务已调用startForeground())

d、托管正执行一个生命周期回调的Service(onCreate()、onStart()或onDestroy())(正在执行(声明中)生命周期中的回调函数)

e、托管正执行其onReceive()方法的BroadcastReceiver

通常,在任意给定时间前台进程都为数不多。Android系统在多个前台进程同时运行时,可能会出现资源不足的情况,此时会清除部分前台进程,保证主要的用户界面能够及时响应。此时,设备往往已达到内存分页状态。

可见进程: 指部分程序界面能被用户看见,却不在前台与用户交互,不响应界面事件的进程

a、托管不在前台、但仍对用户可见的Activity(已调用其onPause()方法)。例如,如果前台Activity启

动了一个对话框,允许在其后显示上一Activity,则有可能会发生这种情况

b、托管绑定到可见(或前台)Activity的Service

可见进程被视为是极其重要的进程,除非为了为维持所有前台进程同时运行而必须终止,否则系统不会终止这些进程

服务进程: 正在运行已使用startService()方法启动了的服务且不属于上述两个更高类别进程的进程。

a、没有用户界面

b、在后台长期运行

尽管服务进程与用户所见内容没有直接关联,但是它们通常在执行一些用户关心的操作(例如,后台播放音乐或从网络下载数据)。因此,除非内存不足以维持所有前台进程和可见进程同时运行,否则系统会让服务进程保存运行状态。

后台进程: 不包含任何已经启动的服务,而且没有任何用户可见的Activity的进程(已调用Activity的onStop()方法)。

这些进程对用户体验没有直接影响,系统可能随时终止它们,以回收内存供前台进程、可见进程或服务进程使用。通常会有很多后台进程在运行,因此它们会保存在LRU列表中,以确保包含用户最近查看的Activity的进程最后一个被终止。如果某个Activity正确实现了生命周期方法,并保存了其当前状态,则终止其进程不会对用户体验产生明显影响,因为当用户导航回该Activity时,Activity会恢复其所有可见状态。

空进程: 不包含任何活跃应用组件的进程

保留这种进程的唯一目的是用作缓存,以缩短下次在其中运行组件所需的启动时间。为使总体系统资源在进程缓存和底层内核缓存之间保持平衡,空进程在系统资源紧张时会被首先清除,但为了提高程序的启动速度,Android会将空进程保存在系统内存用,在用户重新启动该程序时,空进程会被重新使用。

DVM是什么?

DVM指dalivk的虚拟机,每一个Android应用程序都在它自己的进程中运行,都拥有一个独立的Dalvik虚拟机实例,而每一个DVM都是在Linux中的一个进程,所以可以说Android DVM的进程和Linux的进程是同一个概念。

Dalvik虚拟机实例和Linux进程的关系

每一个Android应用程序都在它自己的进程中运行,都拥有一个独立的Dalvik虚拟机实例,每一个DVM都是在Linux中的一个进程。

Android运行时由两部分组成:Android核心库集合Dalvik虚拟机。其中核心库集提供了Java预研核心库所能使用的绝大部分功能,而虚拟机则负责运行Android应用程序。

每个Android应用程序都运行在单独的Dalvik虚拟机内(即每个Android应用程序对应一个Dalvik进程),Dalvik专门对阵同时搞笑地进行多个虚拟机进行优化,因此Android系统可以方便地实现对应用程序进行隔离。

ArrayList 和LinkedList的内部实现

ArrayList 内部实现是可变长数组,增加删除数据是通过移动数据实现的;

LinkedList 使用了循环双向链表数据结构。

Vector 和 ArrayList的区别

Vector 和 ArrayList 一样,也是通过数组实现的,

不同的是它支持线程的同步,可以避免多线程写引起的不一致,因此访问的速度就不如ArrayList。

ArrayList在内存不足时默认是扩展50%+1个,Vector默认扩展是一倍。

Vector提供index(obj, start)接口,ArrayList没有。

ArrayList、LinkedList、Vector的区别

相同点:三者都实现了List接口

不同点:

LinkedList还实现了Queue接口

ArrayList和Vector底层是由数组实现,而LinkedList是由双向链表实现

根据第2点,ArrayList和Vector具有更好的随机索引读取性能,LinkedList具有更好的插入删除性能

ArrayList和LinkedList都不是线程安全的,Vector是线程安全的,因此Vector在性能上比较差

i++的使用

我们知道 i++是在使用完 i 的值后才会自增的,但是是在使用完之后马上自增,还是语句结束的时候呢?

int  x, y = 0;
y = x++ + ++x;


这样的表达式,结果会是多少呢?答案是2。为什么呢,因为 x++的值交给 + 运算符后,就会进行自身+1的操作,到 ++x 时,此时的 x 已经由0变为1了,再进行++x自增就变为2。所以结果就是 y = 0 + 2 = 2。

三目运算符

嵌套三目运算符的计算。

例子:

int i = 42;
String s = (i < 40) ? "life" : (i > 50) ? "universe" : "everything";


三目运算符的结合顺序为自右向左,所以先从右边的三目运算符开始计算,得出结果后再将结果放入左侧的三目运算符进行计算。即右边的结果为”everything”,左边的也为”everything”。则答案即是”everything”。

& 和 &&、| 和 ||

单字符的为位运算符,双字符的为短路逻辑运算符

对于:& – > 不管怎样,都会执行”&”符号左右两边的程序

对于:&& – > 只有当符号”&&”左边程序为真(true)后,才会执行符号”&&”右边的程序。

异常

Throwable是异常的顶级父类。直接子类有Error和Exception。所有可抛出(throw)的异常都必须从Throwable派生而来。

IO操作

Java的IO操作中有面向字节(Byte)和面向字符(Charater)两种方式。

* 面向字节的操作以8位为单位对二进制的数据进行操作,对数据不进行转换,这些类都是InputStream和OutputStream的子类。
* 面向字符的操作以字符为单位对数据进行操作,在读的时候将二进制数据转为字符,在写的时候将字符转为二进制数据,这些类都是Reader和Writer的子类。


总结一下,即以InputStream(输入)/OutputStream(输出)为后缀的是字节流;以Reader(输入)/Writer(输出)为后缀的是字符流。

下面是Java中IO流的类图结构。



为什么要进行序列化?

Java的对象序列化将那些实现了Serializeable接口的对象转换成一个字节序列,并能够在以后将这个字节序列完全恢复为原来的对象。这一过程甚至可通过网络进行,这意味着序列化机制能够弥补不同操
ff80
作系统之间的差异。

利用对象序列化,可以实现轻量级持久性(lightweight persistence)。“持久性”意味着一个对象的生存周期并不取决于程序是否正在执行,它可以生存于程序的调用之间。通过一个序列化对象写入磁盘,然后在重新调用程序时恢复该对象,就能够实现持久化的效果。之所以称其为“轻量级”,是因为不能用某种“persistence”关键字来简单地定义一个对象,并让系统自动维护其他细节问题。相反,对象必须在程序中显式地序列化(serialize)和反序列化还原(deserialize)。

对象序列化的概念加入到语言中是为了支持两种主要特性。一是Java的远程方法调用(Remote Method Invocation,RMI),它使存活于其他计算机上的对象使用起来就像是存活于本地上一样。当向远程对象发送消息时,需要通过对象序列化来传输参数和返回值。再者,对Java Beans来说,对象的序列化也是必须的。使用一个Bean时,一般情况下是在设计阶段对它的状态信息进行配置。这种状态信息必须保存下来,并在程序启动时进行后期恢复,这种具体工作就是由对象序列化完成的。

相关注意事项:

* 当一个父类实现序列化,子类自动实现序列化,不需要显示实现Serializeable接口
* 当一个对象的实例变量引用其他对象,序列化该对象时也会把引用对象进行序列化
* 并非所有对象都可以序列化。


为什么有的对象不能进行序列化?

* 安全方面的原因,比如一个对象拥有private,public等field,对于一个要传输的对象,比如写到文件,或者进行rmi传输 等等,在序列化进行传输的过程中,这个对象的private等域是不受保护的。
* 资源分配方面的原因,比如socket,thread类,如果可以序列化,进行传输或者保存,也无法对他们进行重新的资源分 配,而且,也是没有必要这样实现


Java垃圾回收机制

gc即垃圾收集机制,是指JVM用于释放那些不再使用的对象所占用的内存。Java语言并不要求JVM有gc,也没有规定gc如何工作。不过常用的JVM都有gc,而且大多数gc都是用类似的算法管理内存和执行手机操作。

Java的垃圾回收机制是为所有的Java应用进程服务的,而不是为某个特定的进程服务的。因此,任何一个进程都不能命令垃圾回收机制做什么、怎么做或做什么。在JVM垃圾收集器手机一个对象之前,一般要求程序调用适当的方法释放资源,但在没有明确释放资源的情况下,Java提供了默认机制终止化该对象来释放资源,这个方法就是finalize()。它的原型为protected void finalize() throws Throwable。在finalize()方法返回之后,对象消失,垃圾收集开始执行。原型中的throws Throwable表示它可以抛出任何类型的异常。

垃圾回收时应注意几点:

* 不要试图去假定垃圾收集发生的事件,这一切都是未知的。
* Java中提供了一些和垃圾收集打交道的类,而且提供了一种强行执行的垃圾收集的方法——调用System.gc(),但这同样是一个不确定的方法。Java中并不保证每次调用该方法就一定能够启动垃圾收集,它只不过会向JVM发出遮掩一个申请,到底是否真正执行垃圾收集,一切都是未知数。
* 挑选适合自己的垃圾收集器。一般来说,如果系统没有特殊和苛刻的性能要求,可以此阿勇JVM的默认选项。否则可以考虑使用针对性的垃圾收集器。
* 避免内存泄漏。
* 尽早释放无用的对象的引用。


Java是如何管理内存的?

Java程序在运行时,需要在内存中的分配空间。为了提高运算效率,就对数据进行了不同空间的划分,因为每一片区域都有特定的处理数据方式和内存管理方式。

Java的内存管理就是对象的分配和释放问题。在Java中,程序员需要通过关键字new为每个对象申请内存空间,理论上所有的对象都在堆(Heap)上分配空间。另外,对象的释放是由gc完成的,这种收支两条线的方法确实简化了程序员的工作。但同时,也加重了JVM的工作,这也是Java程序运行速度较慢的原因之一。因为gc为了能够正确释放对象,必须监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等,gc都需要进行监控。

Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干不同的数据区域,这些区域都有各自的用途以及创建和销毁的时间。

在java中,内存管理由JVM完全负责,java中的“垃圾回收器”负责自动回收无用对象占据的内存资源,这样可以大大减少程序猿在内存管理上花费的时间,可以更集中于业务逻辑和具体功能实现;但这并不是说java有了垃圾回收器程序猿就可以高枕无忧,将内存管理抛之脑外了!一方面,实际上java中还存在垃圾回收器没法回收以某种“特殊方式”分配的内存的情况(内存泄漏);另一方面,java的垃圾回收是不能保证一定发生的,除非JVM面临内存耗尽的情况。所以java中部分对象内存还是需要程序猿手动进行释放,合理地对部分对象进行管理可以减少内存占用与资源消耗。

内存泄漏

程序向系统申请分配内存空间后,在使用完毕后未释放内存。结果导致一直占据该内存单元,我们和程序都无法再使用该内存单元,直到程序结束,这就是内存泄漏。

原因:有持有对象的强引用,且没有及时释放,进而造成内存单元一直被占用,从而导致内存溢出。

在Android中,导致内存泄漏的陷阱不外乎两种:

* 全局进程(process-global)的static变量;
* 活在Activity生命周期之外的线程,没有清空对Activity的强引用。


内存溢出

程序向系统申请的内存空间超出了系统所能分配的内存(包括动态扩展)的上限。比如内存只能分配一个int类型,我却要塞给他一个long类型,系统就出现OOM(Out of Memory)。

导致OutOfMemoryError异常的常见原因有以下几种:

1. 启动参数中内存值设定过小
2. 内存中加载的数据量过于庞大,如一次从数据库取出过多数据
3. 集合类中有对象的引用,使用结束后未清空,使得JVM没有进行回收(堆内存溢出)(内存泄漏)
4. 代码中存在死循环或循环过多产生大量重复的对象实体(栈溢出)
5. 加载的类数量过多,超出永久代或元空间设置的阈值(永久代溢出)
6. 启动线程过多,OS无内存可分配创建新线程
7. native heap也即将耗尽时,请求的总内存大于可用物理内存
8. 申请的数组大小过大,超过平台限制
9. 占用内存过多,当可用虚拟虚拟内存(包括交换空间)消耗到让整个操作系统面临风险时,就会被“优先”杀死
10. 使用的第三方库中的BUG


大量的内存泄漏会导致内存溢出(OOM)。

对应的解决方案

1. 增加堆空间的大小。在JVM的启动配置中增加如下配置:
java -Xmx1073741824 com.mycompany.MyClass
java -Xmx1048576k com.mycompany.MyClass
java -Xmx1024m com.mycompany.MyClass
java -Xmx1g com.mycompany.MyClass

2. 调整堆空间大小;尽量避免一次性请求过多的数据,分页分多次获取

3. 在对象使用完毕后,确保集合中没有持有其引用

4. 排查代码逻辑,检查代码中是否有死循环或错误的递归调用

5. 增大堆空间大小;

6. 使用线程池等方法,减少线程的重复和大量创建

7. 增加交换空间,升级及其以包含更多内存;优化应用程序以减少其内存占用

8. 检查代码,是否能够减少数组的大小,或者将数据分成更小的数据块,然后分批处理;由于Java数组是由int索引的,因此,挡在平台中使用标准数据结构时,数组不能超过2^31-1个元素(事实上,编译时就会出错:error:integer number too large)。

9. 升级机器内存;调整OOM Killer配置;水平扩展应用;将内存的负载分摊到若干小实例上.

10. 引进第三方库时需要经过严格审查,对可能存在的问题进行排查


其他一些注意事项:

1. 我们的程序里不可避免大量使用字符串处理,避免使用String,改用StringBuilder或StringBuffer
2. 尽量少用静态变量,因为静态变量是全局的
3. 避免集中创建对象尤其是大对象,JVM会突然需要大量内存,这时必然会触发GC优化系统内存环境
4. 不要在经常调用的方法中创建对象,尤其是忌讳在循环中创建对象。可以适当的使用hashtable,vector 创建一组对象容器,然后从容器中去取那些对象,而不用每次new之后又丢弃


另外有一些很好用的辅助工具:

* 使用Leakcanary排查内存泄漏
* 用好Debuggers, profilers, heap dump analyzers等工具,可以让你的程序最大程度的避免内存泄漏问题。
* 结合Android DDMS分析造成大量内存消耗的位置


ANR形成的原因

定义

在Android上,如果你的应用程序有一段时间响应不够灵敏,系统会向用户显示一个对话框,这个对话框称作应用程序无响应(ANR:Application Not Responding)对话框。用户可以选择“等待”而让程序继续运行,也可以选择“强制关闭”。

出现ANR的时间限制

默认情况下,在android中Activity的最长执行时间是5秒BroadcastReceiver的最长执行时间则是10秒Service服务的最长执行时间是20秒(后台Service为200秒)。超出就会提示应用程序无响应(ANR:Application Not Responding)对话框。

导致ANR的原因

简单的总结有以下两点:

1. 主线程执行了耗时操作,比如数据库操作或网络编程

2. 其他进程(就是其他程序)占用CPU导致本进程得不到CPU时间片,比如其他进程的频繁读写操作可能会导致这个问题。

细分的话,导致ANR的原因有如下几点:

1. 耗时的网络访问

2. 大量的数据读写

3. 数据库操作

4. 硬件操作(比如camera)

5. 调用thread的join()方法、sleep()方法、wait()方法或者等待线程锁的时候

6. service binder的数量达到上限

7. system server中发生WatchDog ANR

8. service忙导致超时无响应

9. 其他线程持有锁,导致主线程等待超时

10. 其它线程终止或崩溃导致主线程一直等待

解决方法

如何避免ANR的发生呢或者说ANR的解决办法是什么呢?

1. 避免在主线程执行耗时操作,所有耗时操作应新开一个子线程完成,然后再在主线程更新UI。

2. BroadcastReceiver要执行耗时操作时应启动一个service,将耗时操作交给service来完成。

3. 避免在Intent Receiver里启动一个Activity,因为它会创建一个新的画面,并从当前用户正在运行的程序上抢夺焦点。如果你的应用程序在响应Intent 广 播时需要向用户展示什么,你应该使用Notification Manager来实现。

符号引用

符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。符号引用于虚拟机实现的内存布局无关,引用的目标并不一定已经加载到了内存中。

直接引用

可以使直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。直接引用是与虚拟机实现的内存布局相关的,同一个符号引用在不同虚拟机实例上翻译出来的直接引用一般不会相同。如果有了直接引用,那说明引用的目标必定已经存在于内存之中了。

在类加载的连接过程中的解析,是虚拟机将常量池内的符号引用(就是各种class中所关联的其他类的名字,需要调用的方法,字段名字等)替换为直接引用(对应在内存中的的目标地址)的过程。

双亲委派模型

表示类加载器之间的加载顺序从顶而下的层次关系,加载器之间的父子关系一般都是通过组合来实现,而不是继承。可以防止内存中出现多份同样的字节码,并确保加载顺序

双亲委派模型的工作过程

在loadClass函数中,首先会判断该类是否被加载过,如果已经加载过则进行下一步——解析,否则进行加载;如果一个类加载器收到了加载的请求,先不尝试加载这个类,而是将这个请求委托给父类加载器去完成,这样依次向上传递,直到顶层的启动类加载器中。只有当父类加载器反馈自己无法完成这个加载请求时(它的搜索范围中没有找到所需的类),子类加载器才会尝试自己去加载。

JDK7和JDK8的内存模型变化

JDK7中把String常量池从永久代中移出,转移到了堆中,并通过intern()方法来保证不再堆中重复创建一个对象

JDK7开始使用G1替代CMS收集器

JDK8使用元空间来替代原来的方法区,并且提供了字符串去重功能,也就是G1收集器可以识别出堆中哪些重复出现的字符串并让它们指向同一个内部char[]数组,而不是在堆中存在多份拷贝

boolean变量的大小不定

一个是boolean的大小并非是确定的,boolean变量单独声明的时候被当做int变量处理,其大小为32bit;当boolean变量声明数组的时候,每个boolean当做byte变量处理,大小为8bit

void也是Java的基本类型之一

void也是Java的基本类型之一,其对应的包装类是Void,不过其包装类是不可实例化的。

Java中switch的参数

在Java中,switch后面的括号里放的参数类型只能是int类型,虽然说放入char,byte,short类型也不会报错,但其实是因为char,byte和short类型可以自己转化(宽化)为int类型,实际上最后操作的还是int类型。

补充:除了int类型外还支持枚举类型。另外在jdk1.7中有了新的特性,支持String类型

equals()与“==”的区别

首先在Java中,“==”,对于基本数据类型,比较的是它们的值是否相等;对于引用类型则比较两个对象是否地址相同,是否为同一引用。

equals()与“==”在Object类中的默认实现并没有区别。

但在我们使用String类,Integer类等等一些类,是重写了equals方法,才使得equals和“==不同”。

@Override public boolean equals(Object other) {
if (other == this) {
return true;
}
if (other instanceof String) {
String s = (String)other;
int count = this.count;
if (s.count != count) {
return false;
}
// TODO: we want to avoid many boundchecks in the loop below
// for long Strings until we have array equality intrinsic.
// Bad benchmarks just push .equals without first getting a
// hashCode hit (unlike real world use in a Hashtable). Filter
// out these long strings here. When we get the array equality
// intrinsic then remove this use of hashCode.
if (hashCode() != s.hashCode()) {
return false;
}
for (int i = 0; i < count; ++i) {
if (charAt(i) != s.charAt(i)) {
return false;
}
}
return true;
} else {
return false;
}
}


从上面方法我们可以看出,首先String比较传入的Object对象是否和当前String为同一引用,是则返回true。如果不是,则判断传入的Object对象是否String对象及其子类实例,如果不是则返回false。再然后如果传入的参数是String的实例,则比较两个String的内容是否完全相同。

Object有哪些公用方法



Object中的public方法如图所示,简单介绍下每个方法:

1. Object(),默认构造函数,略

2. getClass(),native和final方法,返回此对象的运行时类

3. hashCode(),native方法,返回此对象的哈希码数值

4. equals(Object),详见上一个问题

5. toString(),返回一个代表该对象的字符串,此处返回:“类名@16进制哈希码”

6. notify(),native和final方法,唤醒在此对象监视器上等待的单个线程

7. notifyAll(),native和final方法,唤醒在此对象监视器等待上的所有线程

8. wait(),该方法有3个重载,作用是让当前线程处于等待状态,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或超过指定时间,或其他线程中断当前线程。

hashcode的作用

在hashCode()方法的介绍中有这些注释(翻译后的):

只要在Java应用程序的执行过程中多次调用同一个对象,hashCode方法必须始终返回相同的整数,前提是在对象的equals比较中没有使用的信息被修改。 从应用程序的一次执行到同一应用程序的另一次执行,此整数不必保持一致。

如果两个对象按照equals(Object)方法相等,那么在两个对象的每一个上调用hashCode方法必须产生相同的整数结果。

如果两个对象根据equals(java.lang.Object)方法不相等,则不要求对两个对象中的每个对象调用hashCode方法都必须产生不同的整数结果。 但是,程序员应该知道,为不相等的对象生成不同的整数结果可以提高散列表的性能。

可以总结出以下几点:

hashCode的存在主要用于查找的快捷性,如HashMap、HashTable等,hashCode是用来在散列存储结构中确定对象的存储地址

两个通过equals()方法相等的对象返回的hashCode是相同的

如果对象的equals()方法被重写,那么对象的hashCode也尽量重写,避免违反上述的第2点

两个不同的对象hashCode不一定不同,但如果它们的hashCode不同,会使哈希表的性能更好(减少冲突)

String、StringBuffer与StringBuilder的区别

String:

String即字符串,底层由char数组实现,代码如下:

/** The value is used for character storage. */
private final char value[];


官方对String解释如下:

Strings are constant; their values cannot be changed after they are created.

综上我们可以得知String是不变的常量,String类中每一个看起来会修改String的方法,实际上都创建了一个全新的String对象。当我们为一个String变量赋值的时候,其实是新创建了一个String对象把地址赋给他。

除此以外,值得注意的是String是一个final类,这意味着它不能被继承。

StringBuffer和StringBuilder:

由于String是常量,当我们处理长度变化较多的String时效率低下,因此官方提供了StringBuffer和StringBuilder两个类。

两个类有共同的父类,AbstractStringBuilder,方法实现差不多,底层同样使用char数组存储数据,但是StringBuffer是线程安全的,而StringBuilder不是线程安全的。因此StringBuilder的效率更高。

浅析Java内存管理机制

关于一些基础的Java问题的解答(一)

关于一些基础的Java问题的解答(二)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: