一、Java基础
1、基础知识
1.1重载和重写的区别
重载:发生在同一个类中,方法名必须相同,参数类型不同,个数不同,顺序不同时,方法返回值和访问修饰符可以不同,发生在编译时。
重写:发生在父子类中,方法名,参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类。访问修饰符范围大于等于父类,如果父类方法访问修饰符为private则子类就不能重写该方法。
1.2 String和StringBuffer,StringBuilder的区别是什么tring为什么是不可变的/strong>
可变性:
简单的来说:String类中使用final关键字字符数组保存字符串,private final char value[] ,所以String对象时不可变的。而StringBuilder和StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串 char value[] 但是没有用fianl修饰,所以这两种对象都是可变的。
StringBuilder 与 StringBuffer 的构造方法都是调用父类构造方法也就是
AbstractStringBuilder 实现的.
线程安全性
String 中的对象是不可变的,也就可以理解为常量,线程安全。
AbstractStringBuilder 是 StringBuilder 与 StringBuffer 的公共父类,定义
了一些字符串的基本操作,如 expandCapacity.append.insert.indexOf 等公
共 方法。StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以
是线程安全的。StringBuilder 并没有对
方法进行加同步锁,所以是非线程安全的。
性能
每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然
后将指针指向新的 String 对象。
StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新
的对象并改变对象引用。相同情况下使用 StirngBuilder 相比使用
StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的
风险。
对于三者使用的总结:
- 操作少量的数据 = String
- 单线程操作字符串缓冲区下操作大量数据 = StringBuilder
- 多线程操作字符串缓冲区下操作大量数据 = StringBuffer
1.3 自动装箱与拆箱
装箱:将基本类型用它们对应的引用类型包装起来;
拆箱:将包装类型转换为基本数据类型;
1.4 == 与 equals
== : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不
是同一个对象。(基本数据类型比较的是值,引用数据类型比较的是内存
地址)
equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情
况:
情况 1:类没有覆盖 equals() 方法。则通过 equals() 比较该类的两个对
象时,等价于通过“==”比较这两个对象。
情况 2:类覆盖了 equals() 方法。一般,我们都覆盖 equals() 方法来两
个对象的内容相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)。
说明:
String 中的 equals 方法是被重写过的,因为 object 的 equals 方法是
比较的对象的内存地址,而 String 的 equals 方法比较的是对象的值。
当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的
值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池
中重新创建一个 String 对象。
1.5 关于 final 关键字的一些总结
final 关键字主要用在三个地方:变量、方法、类。
- 对于一个 final 变量,如果是基本数据类型的变量,则其数值一旦在初始
化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。 - 当用 final 修饰一个类时,表明这个类不能被继承。final 类中的所有成
员方法都会被隐式地指定为 final 方法。
可查看堆空间大小分配(年轻代.年老代.持久代分配)
提供即时的垃圾回收功能
垃圾监控(长时间监控回收情况)
对象引用情况查看
有了堆信息查看方面的功能,我们一般可以顺利解决以下问题:
- 年老代年轻代大小划分是否合理
- 内存泄漏
- -XX:MaxPermSize=16m
- 换用 JDK。比如 JRocket
堆栈溢出
异常:java.lang.StackOverflowError
说明:这个就不多说了,一般就是递归没返回,或者循环调用造成
线程堆栈满
异常:Fatal: Stack size too small
说明:java 中一个线程的空间大小是有限制的。JDK5.0 以后这个值是 1M。
与这个线程相关的数据将会保存在其中。但是当线程空间满了以后,将会出现上
面异常。
解决:增加线程栈大小。-Xss2m。但这个配置无法解决根本问题,还要看
代码部分是否有造成泄漏的部分。
系统内存被占满
异常:java.lang.OutOfMemoryError: unable to create new native thread
说明:这个异常是由于操作系统没有足够的资源来产生这个线程造成的。系
统创建线程时,除了要在 Java 堆中分配内存外,操作系统本身也需要分配资源
来创建线程。因此,当线程数量大到一定程度以后,堆中或许还有空间,但是操
作系统分配不出资源来了,就出现这个异常了。
分配给 Java 虚拟机的内存愈多,系统剩余的资源就越少,因此,当系统内
存固定时,分配给 Java 虚拟机的内存越多,那么,系统总共能够产生的线程也
就越少,两者成反比的关系。同时,可以通过修改-Xss 来减少分配给单个线程
的空间,也可以增加系统总共内生产的线程数。
解决: - 重新设计系统减少线程数量。
Dump 线程详细信息:查看线程内部运行情况
死锁检查
热点分析
这是最典型的内存泄漏方式,简单说就是所有堆空间都被无法回收的垃圾对
象占满,虚拟机无法再在分配新空间。
如上图所示,这是非常典型的内存泄漏的垃圾回收情况图。所有峰值部分都
是一次垃圾回收点,所有谷底部分表示是一次垃圾回收后剩余的内存。连接所有
谷底的点,可以发现一条由底到高的线,这说明,随时间的推移,系统的堆空间
被不断占满,最终会占满整个堆空间。因此可以初步认为系统内部可能有内存泄
漏。(上面的图仅供示例,在实际情况下收集数据的时间需要更长,比如几个小
时或者几天)
解决:这种方式解决起来也比较容易,一般就是根据垃圾回收前后情况对比,同时根据对象引用情况(常见的集合对象引用)分析,基本都可以找到泄漏点。
持久代被占满
异常:java.lang.OutOfMemoryError: PermGen space
说明:Perm 空间被占满。无法为新的 class 分配存储空间而引发的异常。
这个异常以前是没有的,但是在 Java 反射大量使用的今天这个异常比较常见了。
主要原因就是大量动态反射生成的类不断被加载,最终导致 Perm 区被占满。
更可怕的是,不同的 classLoader 即便使用了相同的类,但是都会对其
进行加载,相当于同一个东西,如果有 N 个 classLoader 那么他将会被加载 N
次。因此,某些情况下,这个问题基本视为无解。当然,存在大量 classLoader
和大量反射类的情况其实也不多。
解决:
2. 什么时候回收/strong>
引用计数法
可达性分析
2.1 引用计数法
给对象添加一个引用计数器,每当有一个地方引用它时,计数器加一。反
之每当一个引用失效时,计数器减一。当计数器为 0 时,则表示对象不被引用。
举个例子:
但是,引用计数法不能解决对象之间的循环引用,见下例
2.2 可达性分析
设立若干根对象(GC Root),每个对象都是一个子节点,当一个对象找
不到根时,就认为该对象不可达。
3.2 复制算法
将内存分为两块,每次只使用一块。当这一块内存满了,就将还存活的对象
复制到另一块上,并且严格按照内存地址排列,然后把已使用的那块内存统一回
收。
优点是:能够得到连续的内存空间
缺点是:浪费了一半内存

补充:java finalize()方法:
在被 GC 回收前,可以做一些操作,比如释放资源。有点像析构函数,但是
一个对象只能调用一次 finalize()方法。
2. Java 集合框架
2.1 ArrayList 与 LinkedList 异同
1. 是否保证线程安全: ArrayList 和 LinkedList 都是不同步的,也就是不
保证线程安全;
2. 底层数据结构: ArrayList 底层使用的是 Object 数组;LinkedList 底
层使用的是双向链表数据结构(JDK1.6 之 前为循环链表,JDK1.7 取消了循环。
注意双向链表和双向循环链表的区别:);
3. 插入和删除是否受元素位置的影响: ① ArrayList 采用数组存储,所
以插入和删除元素的时间复杂度受元素位置的影响。 比如:执行 add(E e) 方法的时候, ArrayList 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是 O(1)。但是如果要在指定位置 i 插入和删除元素的话( add(int index, E element) )时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。 ② LinkedList 采用链表存储,所以插入,删除元素时间复杂度不受元素位置的影响,都是近似 O(1)而数组为近似 O(n)。
4. 是否支持快速随机访问: LinkedList 不支持高效的随机元素访问,而
ArrayList 支持。快速随机访问就是通过元素的序 快速获取元素对象(对应于
get(int index) 方法)。
5. 内存空间占用: ArrayList 的空 间浪费主要体现在在 list 列表的结尾会
预留一定的容量空间,而 LinkedList 的空间花费则体现在它的每一个元素都需
要消耗比 ArrayList 更多的空间(因为要存放直接后继和直接前驱以及数据)。
补充内容:RandomAccess 接口
查看源码我们发现实际上 RandomAccess 接口中什么都没有定义。所以,
在我看来 RandomAccess 接口不过是一个标识罢了。标识什么标识实现这
个接口的类具有随机访问功能。
在 binarySearch()方法中,它要判断传入的 list 是否 RamdomAccess
的实例,如果是,调用
indexedBinarySearch()方法,如果不是,那么调用 iteratorBinarySearch()方法
public static T声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!