1.Java基础
1.1 为什么要设计封装类;
之所以要对基础数据类型设计一个封装类,是因为Java是一个面向对象的语言,对象是Java的基础操作单元,我们需要创建对象和使用对象,很多时候我们需要使用传递数据的时候也需要对象类型,例如HashMap、ArrayList、HashSet等数据类型,这类数据类型用于存储数据,因此封装类的作用有很多,此外,封装类可以使我们的数据更安全,避免外部操作随意修改成员变量的值,保证数据传递的安全性;也可以更好地隐藏细节,对使用者更为友好,只需要提供调用方法就可以完成对应的操作。
1.2 int和Integer的区别;
简单的分为三个方面:
1.Integer的初始值为null,int的初始值为0;
2.Integer的存储在堆内存,而int存储在栈内存;
3.Integer是对象类型,它封装了许多方法和属性( 例如Integet.parseInt() ),我们在使用的时候更方便;
1.2.1 为什么Integer中“100==100”为true,而“1000==1000”为false;
因为在Integer中取值时会采取装箱操作,会调用Integer.valueOf()方法,在值为-128~127时,因为这个范围内调用比较频繁,为了减少内存的消耗采用了享元模式,会在缓存中调用catch数组取值,而在此范围外会创建新的对象。
1.3 new String("hello")会创建几个对象
此处分为两种情况:1.如果hello这个字符串处于常量池中,则只会实例化一个new String()对象;
2.如果不存在于,则会创建两个对象,一个是hello这个字符串常量,另一个是new String()对象;
1.4 String、StringBuffer、StringBuilder的区别;
它们的区别主要有四个方面;
1.值的区别;
String的value值是由final定义的,因此它每次修改值的时候都会创建一个新的String类,而两外两个都是可变类;
2.线程安全方面;
String的值是不可变的,因此它是线性安全的;
StringBuffer类是线程安全的,因为它的每个操作方法都添加了synchronized关键字;
而StringBuilder类不是线程安全的,如果在多线程的情况下对字符串进行操作,建议使用StringBuffer类,在单线程的情况下使用StringBuilder;
3.数据存储方面;
String存储于常量池中,而StringBuffer和StringBuilder是存储在堆内存中。
4.性能方面;
String的性能是最低点,,因为它不可变,意味着在字符串拼接和修改时需要反复创建新的对象和分配新的内存;
StringBuffer要比String性能要高,因为它的可变性使得字符串可以直接被修改;
性能最高的是Stringbuilder因为其增加同步锁,其是无阻塞的。
最后,StringBuffer和StringBuilder类都继承自AbstractStringBuilder类中。
1.5 如何理解Java对象的创建过程;
1.类的加载;
在创建对象前,JVM首先需要加载该类,类加载主要由类加载器(ClassLoader)完成,确保每个泪痣被加载一次。
2.内存分配;
当类加载完成后,JVM会在堆区为对象分配内存空间。
3.构造函数执行;
当内存分配完成后,JVM会执行类的构造方法来初始化对象。构造函数主要的作用是初始化对象的状态。
4.返回对象引用;
当构造函数执行完毕后,JVM会将对象的引用(即对象的内存地址)返回给调用者。
Java提供了很多方法来进行对对象的创建,例如使用new关键字、通过反射API、使用克隆(Clone)方法等。
1.6 什么是深克隆和浅克隆;
1.6.1 浅克隆和深克隆的概念
1.浅克隆是指在复制一个对象时只会复制对象本身及其引用类型成员的引用,而不复制引用指向的对象本身,这意味着新的对象和原始对象共享相同的属性,一方数据发生变化,另一方的数据也会随之发生变化。
2.深克隆是指复制一个对象时,不是复制一个对象的地址,而是递归复制该对象所有引用类型的副本,这与浅克隆是相反的,一方数据发生变化,另一方的数据不会随之变化。
1.6.2 浅克隆和深克隆常用的API;
浅克隆:1.Spring类中的BeanUtils和commons的PropertyUtils;
2.实现Cloneable类;
3.Arrays的copyOf()方法;
深克隆:1.重写clone方法,每个对象都要实现Cloneable接口并重写Object类中的clone()方法;
2.序列化,必须实现Serializable接口;
3.Apache commons 工具包SerializationUtils.clone(T object);
4.通过JSON工具类实现深克隆;
5.通过构造方法实现深克隆,也就是手动new对象;
1.7 强引用、软引用、弱引用、虚引用有什么区别;
1.强引用:只要引用关系还在,对象就永远不会被回收(除非将对象引用强制赋值为null才会被回收);
2.软引用:非必需存活的对象,JVM会在内存溢出前对其进行回收(可以保证在使用缓存的同时不会耗尽内存);
3.弱引用:非必须存活的对象,无论内存是否够用,下次GC一定回收(如果只存在弱引用指向某个对象时,这个对象就会被回收);
4.虚引用:等同于没有引用,对象回收时会收到通知(虚引用必须和引用队列一起使用);
1.8 一个空的Object对象占用多大内存;
首先针对此问题我们需要先了解Java在JVM中的存储结构:
一 .对象头
1.Mark Word:用于存储对象自身运行时数据,在32位虚拟机长度为32bit,而在64位虚拟机则是64bit;
2.类指针:它是指指向对象所属类的元数据(Class对象)的指针,在32位系统中长度为4字节,而在64位系统中则是8字节,但如果开启了指针压缩则被压缩到4字节;
3.数组长度(仅数组对象有):如果对象是一个数组对象,则对象头需要包含一块用于记录数组长度的数据;
二.实例数据:实例数据部分存储的是对象真正的有效信息;
三.对齐填充:它并不是必然存在的,它的主要作用是确保对象的大小是8字节的整数倍;
从上述可知,一个空的Java对象在开启指针压缩的情况下会占用12字节的长度,但由于JVM会按照8字节的倍数进行填充,所以最后的长度为16字节;而在不开启指针压缩的情况下,默认为16字节,正好是8字节的整数倍因此不需要填充。
综上,一个空的 Java Object类对象会占用16字节的长度。
1.9 为什么重写equals()方法就一定要重写hashCode()方法;
首先来看一下equals()方法的源码:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
首先用==判断两个对象的内存地址,如果地址相同则会返回true;接下来会判断这两个字符串的值,如果完全相同就会返回true,否则返回false;
hashCode()方法会在散列集合会用到,在HashMap、HashTable在添加元素的时候需要判断值是否存在,这个时候使用equals()效率太慢所以一般是用对象的hashCode都值来进行取模运算,如果存在该HashCode值进行equals()方法比较,相同的话就不存储,不相同的话会散列其他地址。
如果只重写equals()方法,而不重写hashCode()方法,可能会导致两个对象在比较的时候可能会生成不同的hashCode值,就会导致这两个对象存储在Hash表中的不同位置,这样就会产生不可预料的错误。
1.10 Java和JavaScript的区别;
1.产生背景不同;
Java是由sun公司研发的,而JavaScript是由Netscape公司为了扩展浏览器的功能而开发的解释型语言;
2.对象设计不同;
Java是一种真正面向对象的语言;而JavaScript是一种脚本语言,是基于对象和事件驱动(Event-Driven) 的编程语言;
3.运行机制不同;
Java在运行前必须进行编译,而JavaScript是一种解释性编程语言,源码不需要进行编译直接由浏览器解释执行;
4.变量定义不同;
Java采用的是强类型变量检查,所有的变量在编译前必须事先声明;而JavaScript中的变量是弱类型的,JavaScript的解释器会在运行时自动判断数据类型;
1.10.1Java和JavaScript也有许多相同之处;
1.Java和JavaScript的语法都和C语言类似;
2.二者都是面向对象的,JavaScript在设计时参照了Java的命名规则;
1.11 什么是受检异常和非受检异常;
受检异常表示在编译的时候强制检查的异常,这种异常必须显式的通过try/catch包裹或者使用throws来抛出,否则程序无法正常进行编译;而非受检异常表示编译器不需要强制检查的异常,这种异常不需要显式的捕捉;
1.11.1 异常的种类
所有的异常类都继承自java.lang.Throwable类,它的直接子类有两个— Error和Exception。
Error用来表示程序底层或者硬件相关的错误,这种错误和程序本身无关,属于非受检异常;
Exception派生了RuntimeException和其他异常,其中钱一个属于运行时异常,也属于非受检异常。
而其它的异常都属于受检异常,就比如IOException、SQLException等。