java 核心知识点——基础知识
目录
数据类型
包装类与基本数据类型
为什么1000==1000为false,100==100为true。
new String("hello")创建了几个对象
String StringBuild StringBuff 区别
对象
Java对象的创建过程
深浅拷贝的区别
4种对象引用
一个空对象占多大内存
重写equals方法就一定要重写hashcode方法
其他
js与Java的区别
受检异常和非受检异常
fail-fast和fail-save区别
序列化和反序列化
spi
finally语句一定会执行吗
内存溢出和内存泄漏
基础知识-思维导图
数据类型
包装类与基本数据类型
为什么基础类型需要设计一个对应的封装类?
Java面向对象的语言,有时需要传递对象类型的数据,例如:ArrayList等集合。
隐藏实现细节对调用者更加友好,使用定义好的方法操作数据。
安全性更好,可以避免外部操作随意修改成员变量的值。
Integer与int的区别是什么?
Integer 是一个对象,用new创建,存储在堆内存 ,初始化null,可以在集合中使用ArrayList,混合计算时自动装箱和拆箱,具有方法和属性,例如,MAX_VALUE,Integer.valueOf(s)。
int 是基本数据类型,直接定义即可使用,存储在栈空间,初始值是0。
为什么1000==1000为false,100==100为true。
public static void main(String[] args) {Integer a = 100, b = 100, c = 1000, d = 1000;//给Integer 赋值的时候发生自动装箱调用Integer.valueOf方法,System.out.println("a==b " + (a==b));System.out.println("c==d " + (c==d));//从缓存里取出值等于100的Integer对象给a,b赋值,所以a,b是同一个对象// 1000不在缓存里,调用new Integer(i) 给c,d赋值,所以c,d不是同一个对象int e = a;//自动拆箱 调用intValue()Byte f = 1;//缓存范围-128~127Short g = 2;//缓存范围-128~127Long h = 3L;//缓存范围-128~127Character i = 'a';//缓存范围0~127}//注意到这里使用到了缓存public static Integer valueOf(int i) {if (i >= IntegerCache.low && i <= IntegerCache.high)return IntegerCache.cache[i + (-IntegerCache.low)];// 怎么计算出数组下标?i的范围在-128~127,// 这里加上一个最小值,就能计算出来下标的位置return new Integer(i);}
下面这这段代码初始化cache数组 这里high属性可以使用-XX:AutoBoxCacheMax=<size>设置 缓存的范围是-128~127 为什么使用这个范围,因为数据在这个范围内频繁使用,避免频繁创建对象消耗资源
private static class IntegerCache {static final int low = -128;static final int high;static final Integer cache[];static {// high value may be configured by propertyint h = 127;String integerCacheHighPropValue =VM.getSavedProperty("java.lang.Integer.IntegerCache.high");if (integerCacheHighPropValue != null) {try {int i = parseInt(integerCacheHighPropValue);i = Math.max(i, 127);// Maximum array size is Integer.MAX_VALUEh = Math.min(i, Integer.MAX_VALUE - (-low) -1);} catch( NumberFormatException nfe) {// If the property cannot be parsed into an int, ignore it.}}high = h;cache = new Integer[(high - low) + 1];int j = low;for(int k = 0; k < cache.length; k++)cache[k] = new Integer(j++);// range [-128, 127] must be interned (JLS7 5.1.7)assert IntegerCache.high >= 127;}private IntegerCache() {}}
new String("hello")创建了几个对象
1.如果hello这个字符串常量不存在,则创建2个对象,分别是hello字符串常量和new String这个实例对象。
2.如果hello 这个字符串常量存在,则只会创建String对象
vm检查字符串常量池里是否存在相同内容("hello")的String对象, 如果存在则直接返回该对象的引用,如果不存在,则在堆内存创建一个新的字符串对象,并且把他的引用保存到字符串常量池里。 new String 创建一个对象存放在堆上
为什么需要字符串常量池?提高性能和节省内存空间
字符串常量池在哪里? 堆
public static void main(String[] args) {//String s = "hello".intern();//如果池中包含等于此String对象的字符串,则返回池中的字符串,// 否则,将这个String对象添加到池中,并返回此String对象的引用String a = "abc";String b = new String("abc");String c = "abc";String d = "a" + "bc";//编译期间进行了优化,拼接结果是"abc"String temp = "bc";String e = "a" + temp;//2个对象相加,把结果存放在堆上String f = "a" + new String(temp);String g = a.intern();System.out.println(a == b);//falseSystem.out.println(a == c);//trueSystem.out.println(a == d);//trueSystem.out.println(a == e);//falseSystem.out.println(e == f);//falseSystem.out.println(a == g);//true}
String StringBuild StringBuff 区别
可变性 String 不可变类 StringBuild 和 StringBuff可变
线程安全性 String 不可变类 是线程安全的 存储在字符串常量池
StringBuff 每个方法都加了Synchronize 关键字 线程安全的 多线程环境下使用 存储在堆
StringBuild 线程不安全 单线程环境下使用 存储在堆
性能 String 性能最差,他是不可变类每次修改都会创建新的String对象
其次 StringBuff 有加同步锁
最好 StringBuild 没有加锁
对象
Java对象的创建过程
1.类加载检查
目标类的加载通过类加载器实现
目标类的初始化,对静态变量、成员变量,静态代码块进行初始化。
目标类初始化之后,就可以常量池中找到对应的类元信息。
2.分配内存
有2种方法,指针碰撞,需要内存空间连续(规整)。空闲列表,内存空间不连续,记录哪些内存块是可用的,分配的时找到一个大小合适的内存块,给目标对象使用,更新记录
3.初始化零值
目标对象里普通成员变量初始化为零值,例如,int类型 初始化0,保证对象里的属性不用初始化就可以使用
4.设置对象头
对象类的元信息,对象GC分代年龄,锁标记,hash码
5.执行init方法
初始化成员变量,执行构造块,构造方法,完成对象创建。
深浅拷贝的区别
1.克隆数据之后两者是否存在关联2.改变克隆对象的属性是否会影响到源对象。
浅拷贝,复制对象本身及其引用类型成员的引用,不复制引用指向的对象本身
值类型成员变量---复制值,引用类型成员变量---复制地址
常用API,实现Cloneable接口,Arrays--copyOf.
常用工具类,Spring--BeanUtils,commons--PropertyUtils
深拷贝,递归复制源对象引用类型成员的副本,新对象是完全独立于旧对象
值类型成员变量---复制值,引用类型成员变量---复制值
常用API,重写clone方法,序列化,工厂方法、拷贝构造方法
常用工具类,JSON工具类,Apache commons--SerializationUtils.clone
4种对象引用
强引用,只要引用关系存在,对象就不会被回收
软引用,相对强引用弱一些,非必须存活的对象,vm在内存溢出前对其进行回收。用来实现内存敏感的缓存,内存还有空闲空间时,就可以暂时保留,当内存空间不足时,就会清理掉
弱引用,不管内存是否够用,下一次GC一定回收,即使存在引用关系也会被清理
虚引用,等同没有引用,对象被回收时会收到通知,必须和引用队列一起使用
一个空对象占多大内存
对象的组成,
1.对象头,markword,64位vm8字节,用来存储hashcode,GC分代年龄,锁标记
类型指针,4(压缩)或8字节,
数组长度,数组对象才会有,4个字节
2.实例数据,对象中的字段信息
3.对齐填充,补充实现java对象大小的整数倍对齐,按照8字节或者8字节的倍数对齐,避免为共享问题
一个空对象在开启压缩指针的情况下,占用16=8(对象头)+4(类型指针)+4(对齐填充)字节。
一个空对象在关闭压缩指针的情况下,占用16=8(对象头)+8(类型指针)字节。
重写equals方法就一定要重写hashcode方法
1.Object规范要求根据equals比较相等的对象,他们的hash码方法产生相同的整数结果
2.只重写equals方法,逻辑上相等对象,他们hash码不相等,因为Object对象的hash码方法返回随机整数
3.hashMap.get方法返回null,hashmap有一项优化,将与每个项相关联的散列码缓存起来,如果散列码不匹配,也不必比较对象相等性
其他
js与Java的区别
JavaScript,动态语言,浏览器解释执行,函数式编程,函数是一等公民,lambda表达式、闭包。
Java,静态语言,源码编译然后在vm运行,类是一等公民,Java8开始支持函数式编程、lambda。
受检异常和非受检异常
受检异常,在编译时必须检查的异常,需要通过catch字句捕获,或者抛出异常。除了error和runtimeException之外的异常,提高可维护性。
非受检异常,不需要显式捕获,编程错误使用运行时异常。
设计异常的原因,提高程序的可读性、可靠性、可维护性
fail-fast和fail-save区别
他们都是多线程并发操作集合时失败处理机制。
fail-fast==快速失败,在遍历集合的时候,一旦发现集合被修改了,立即抛出并发修改异常,导致遍历失败。例如,hashmap,arraylist。
fail-save==失败安全,在这种机制下,出现集合元素修改不会抛出并发修改异常,例如,copyonwriteArraylist,在遍历时,不是直接在结合上访问,而是先复制集合内容,在复制的集合上进行遍历。
序列化和反序列化
序列化,解决网络通信中对象传输问题,把vm进程里一个对象,跨网络传输到另一个vm进程里,序列化把对象转化成字节流,以便实现存储和传输。保证通信双方对于对象的可识别性,把对象转化为通用解析格式--json、xml。
反序列化,根据文件或网络上的对象的字节流,解析字节流里对象信息和状态,创建一个新的对象。
选择何种序列化技术考虑的因素,序列化后数据的大小--影响传输性能,序列化耗费的时间,跨平台、跨语言,技术的成熟度。
spi
service-provide-interface,基于接口的动态扩展机制,定义一个标准接口,然后第三方可以实现这个接口,程序运行时,根据配置信息或上下文场景,发现加载实现类,实现功能的动态扩展。例如,数据库驱动==driver。这个机制的主要思想:将装配的控制权转移到程序之外,做到标准和实现解耦,提供动态的可插拔能力。缺点,不能根据需求加载需要的扩展实现,每次都会加载所有接口的实现类。加载一些不需要的实现类造成内存资源浪费。
finally语句一定会执行吗
通常情况下,无论是否触发异常,finally块中的代码一定会执行,例外情况,程序没有进入try块之前,异常终止,在try或catch块中,执行system.exit语句。
内存溢出和内存泄漏
内存溢出,vm可用内存不足以存放一个新创建的对象。
内存泄漏,对象使用完后,对象内存空间没有释放,空间一直被占用。代码每次执行都会留下被占用内存,不被释放。反复多次执行后,就会有大量对象用完没有释放,这些不能回收的对象导致内存溢出。