java面试准备基础篇
1、Java中常用关键字和用途
synchronized:
加锁
transient
不参与序列化和反序列化
volatile
保证其他线程可见性,不保证原子性,禁止指令重排
2、hashCode(),equals()方法作用和区别
equals和hashCode方法都来源于Object类。equals比较两个对象得地址是否相等,hashCode是一个本地方法用native修饰。调用的是C或者C++的方法。
重写equals和hashCode方法。
- 如果两个对象的
hashCode
值相等,那这两个对象不一定相等(哈希碰撞)。 - 如果两个对象的
hashCode
值相等并且equals()
方法也返回true
,我们才认为这两个对象相等。 - 如果两个对象的
hashCode
值不相等,我们就可以直接认为这两个对象不相等。
总之,如果一个对象equals相等那hashCode一定相等,如果hashCode相等,equals不一定相等(哈希碰撞)
如果重写了一个类重写了equals方法,没有重写hashCode方法会使用hashSet或者HashMap等容器时有什么影响。
那么会出现equals方法相等但hashCode方法不一致的情况,从而导致存储了相等key的数据,与java设计冲突。
3、数据结构中的时间复杂度和空间复杂度。
算法效率分为两种,一种是时间复杂度,另一种是空间复杂度。
时间复杂度:在计算机科学中,算法的时间复杂度是一个函数,它定量描述了该算法的运行时间。一个算法执行所耗费的时间
空间复杂度:算法中”额外”开辟的内存空间
具体表现形式:大O渐进表示法。(计算时间复杂度和空间复杂度时,为了估算一个算法的耗时情况,不需要计算出精确的执行次数,只需要计算到一个大概的计算次数次数即可,我们使用大O渐近表示法O()->是一个函数渐近的数学符号)
具体规则:
- 常数1表示所有的加法常数 1000或者10000只保留1;
- 最后的大O函数只保留最高项,N^ 2+N+100只保留N^2;
- 若最高阶还有系数,去除系数,3N^ 2或者2N^ 2只保留N^2;
- 任意算法,如果不断/任意数字,最终等于1或者0,这个算法的时间复杂度就是O(logN)
时间复杂度:
最坏情况的时间复杂度:这个算法的最大运行时间
最好情况的时间复杂度:这个算法的最小运行时间
平均情况的时间复杂度:这个算法的平均运行时间
当前数组的个数为n,从头开始遍历,如果所需的元素在末尾就是最坏情况的时间复杂度O(n),如果所需的元素在第一个就是最好情况的时间复杂度O (1),如果所需的元素在中间就是O(n/2)
空间复杂度:
所谓空间复杂度指的是算法中”额外”开辟的内存空间
计算空间复杂度也使用大O渐近法
一般来说空间复杂度就看算法中有没有开辟”数组”空间
4.String,StringBuffer,StringBuilder的区别
String:对象不可(因为用final修饰)变属于常量,线程安全。
StringBuffer:继承AbstractStringBuilder,长度可变,方法用synchorized修饰,加了同步锁,线程安全。
StringBuilder:继承AbstractStringBuilder,长度可变,方法不加锁,线程不安全。
5:为什么String类不能被修改。
因为String类用final修饰,导致String类不能被继承,并且String类的值保存在一个final修饰的数组中,并且String类本身没有提供修改该的方法出来,所以这个数组无法被修改。
6:String类equals与==的区别。
==对于引用类型,比较的是两个对象的地址是否相同。
equals是Object的方法,String类重写了equals方法,比较的是两个对象的值是否相等。
字符串常量池 是 JVM 为了提升性能和减少内存消耗针对字符串(String 类)专门开辟的一块区域,主要目的是为了避免字符串的重复创建。
public static void testStringEquals(){ String str1 = new String("123"); String str2 = new String("123");
//将字符串"123"保存在常量池中 String str3 = "123"; String str4 = "123"; System.out.println(str1 == str2);//false System.out.println(str2 == str3);//false System.out.println(str3 == str4);//true System.out.println(str1.equals(str2));//true }
7:Java异常类
基类,Throwable,子类Exception,Error。 Exception包含了检查时异常和非检查异常(RuntimeException及其子类)
为什么不能在finally中使用return?
如果catch块中捕获了异常, 并且在catch块中将该异常throw给上级调用者进行处理, 但finally中return了, 那么catch块中的throw就失效了, 上级方法调用者是捕获不到异常的;
如果在finally里的return之前执行了其它return , 那么最终的返回值是finally中的return;
try不可单独使用,要么try catch,要么try finally,要么try catch finally
finally中的语句一定会执行么?
不一定,
8:@JsonFormat、@JsonField、@DateTimeFormat注解的作用和区别
@JsonFormat:@JsonFormat是jackson提供的一个注解,主要用来控制日期和日历类型的输出格式,它可以作用在字段上,也可以作用在getter方法上。当然你也可以为它指定时区
@JsonField:介绍@JSONField之前,介绍一下fastjson,fastjson是阿里的开源解析库,支持将Java Bean序列化为JSON字符串,也可以将JSON字符串反序列化为Java Bean,JSONField可以用在字段上或者getter/setter方法上,它的作用有很多,包括,日期格式化,指定不序列化的字段,指定字段的顺序。
@DateTimeFormat:@DateTimeFormat是SpringMVC提供的一个注解,作用:格式化前台的输入数据,虽然@JSONField、@JsonFormat也都可以做到,而且@DateTimeFormat不好的一点就是,从后端读取数据出来的话,是以long类型输出的,不会格式化为你想要的类型输出.
9. java中得序列化和反序列化。
什么是序列化和反序列化?
序列化:把对象转换成字节序列得过程。
反序列化:把字节序列恢复成java对象得过程。
java实现序列化的必要条件:只有实现了Serializable或者Externalizable接口的类的对象才能被序列化为字节序列。(不是则会抛出异常)
需要注意的点:
a:序列化时,只对对象的状态进行保存,对对象的方法是不管的。
b:父类如果实现了序列化,子类自动实现序列表,不需要显示实现Serializable接口
c:当一个对象的实例对象引用其他对象,序列化该对象时也把引用对象进行序列化。
d:并非所有的对象都可以序列化,至于为什么不可以,有很多原因了,比如:安全方面的原因,比如一个对象拥有private,public等field,对于一个要传输的对象,比如写到文件,或者进行RMI传输等等,在序列化进行传输的过程中,这个对象的private等域是不受保护的;资源分配方面的原因,比如socket,thread类,如果可以序列化,进行传输或者保存,也无法对他们进行重新的资源分配,而且,也是没有必要这样实现;
e:声明为static和transient类型的成员数据不能被序列化。因为static代表类的状态,transient代表对象的临时数据。
f:序列化运行时使用一个称为 serialVersionUID 的版本号与每个可序列化类相关联,该序列号在反序列化过程中用于验证序列化对象的发送者和接收者是否为该对象加载了与序列化兼容的类。为它赋予明确的值。显式地定义serialVersionUID有两种用途。在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID。在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。
g:Java有很多基础类已经实现了serializable接口,比如String,Vector等。但是也有一些没有实现serializable接口的。
h:如果一个对象的成员变量是一个对象,那么这个对象的数据成员也会被保存!这是能用序列化解决深拷贝的重要原因。
dubbo的默认序列化方式为hession2.SpringCloud的默认序列化方式为Jackson
10.什么是浅拷贝、深拷贝?
浅拷贝:浅拷贝会在堆上创建一个新的对象(区别于引用拷贝的一点),不过,如果原对象内部的属性是引用类型的话,浅拷贝会直接复制内部对象的引用地址,也就是说拷贝对象和原对象共用同一个内部对象。
深拷贝:深拷贝会完全复制整个对象,包括这个对象所包含的内部对象。
引用拷贝:指两个不同的引用指向同一个对象。
Object的clone是浅拷贝,深拷贝的实现方式:
1:类和实例变量均实现Cloneable接口,手动给Clone来的对象的成员变量设置值为原对象的值。
2:通过序列化的方式需要实现Serilazable接口。
//深拷贝 public Person deepClone() throws Exception{ ByteArrayOutputStream bo = new ByteArrayOutputStream(); ObjectOutputStream oo=new ObjectOutputStream(bo); oo.writeObject(this);//从流里读出来 ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray()); ObjectInputStream oi=new ObjectInputStream(bi); return (Person) oi.readObject(); }
3:利用Fastjson等第三方工具,先将对象转换成json字符串,在将json字符串转换成对象。
Person clone = JSONObject.parseObject(JSONObject.toJSONBytes(person),Person.class);
11.java中常见的类和类中的方法。
a.Object类。Object是所有类的父类,主要提供了一下11个方法。
/** * native 方法,用于返回当前运行时对象的 Class 对象,使用了 final 关键字修饰,故不允许子类重写。 */ public final native Class<?> getClass() /** * native 方法,用于返回对象的哈希码,主要使用在哈希表中,比如 JDK 中的HashMap。 */ public native int hashCode() /** * 用于比较 2 个对象的内存地址是否相等,String 类对该方法进行了重写以用于比较字符串的值是否相等。 */ public boolean equals(Object obj) /** * naitive 方法,用于创建并返回当前对象的一份拷贝。 */ protected native Object clone() throws CloneNotSupportedException /** * 返回类的名字实例的哈希码的 16 进制的字符串。建议 Object 所有的子类都重写这个方法。 */ public String toString() /** * native 方法,并且不能重写。唤醒一个在此对象监视器上等待的线程(监视器相当于就是锁的概念)。如果有多个线程在等待只会任意唤醒一个。 */ public final native void notify() /** * native 方法,并且不能重写。跟 notify 一样,唯一的区别就是会唤醒在此对象监视器上等待的所有线程,而不是一个线程。 */ public final native void notifyAll() /** * native方法,并且不能重写。暂停线程的执行。注意:sleep 方法没有释放锁,而 wait 方法释放了锁 ,timeout 是等待时间。 */ public final native void wait(long timeout) throws InterruptedException /** * 多了 nanos 参数,这个参数表示额外时间(以毫微秒为单位,范围是 0-999999)。 所以超时的时间还需要加上 nanos 毫秒。。 */ public final void wait(long timeout, int nanos) throws InterruptedException /** * 跟之前的2个wait方法一样,只不过该方法一直等待,没有超时时间这个概念 */ public final void wait() throws InterruptedException /** * 实例被垃圾回收器回收的时候触发的操作 */ protected void finalize() throws Throwable { }
12.java泛型。
泛型类,泛型接口,泛型方法。
泛型类:
泛型接口:
泛型方法:
class Test<T>{ public <E> E getData(String jsonData, Class<E> clazz){ return JSONObject.parseObject(jsonData,clazz); } private T t; public T getT() { return t; } public void setT(T t) { this.t = t; } }
13.java反射机制。
什么是反射机制?
14.常用设计模式。
装饰器模式:可以在不改变原有对象的基础上扩展其功能。装饰器模式通过组合代替继承来扩展原始类的功能。在一些继承关系比较复杂的场景更加实用(IO 这一场景各种类的继承关系就比较复杂)
适配器模式:适配器模式主要用于接口两个互不兼容的类工作,可以联想成我们平时的电源适配器。
适配者:适配器模式中存在被适配的类或对象称之为适配者。
适配器:作用于适配者的类或对象称之为适配器。java中适配器分为对象适配器和类适配器。类适配器使用继承关系来实现,对象适配器使用组合关系来实现。
工厂模式:
代理模式:
观察者模式:
15。java中的IO流
InputStream:所有字节输入流的基类
OutputStream:所有字节输出流的基类
Reader:字符输入流的基类
Writer:字符输出流的基类
BIO:同步阻塞,同步阻塞 IO 模型中,应用程序发起 read 调用后,会一直阻塞,直到内核把数据拷贝到用户空间。
NIO:同步非阻塞
AIO:异步非阻塞