?? 《java面试核心知识突击系列》,持续更新...
?? 作者:一只IT攻城狮
?? 再小的收获x365天都会成就不一样的自己,一起学习,一起进步。
一、接口和抽象类有什么区别?
1、接口使用关键字 interface 来定义。 抽象类使用关键字 abstract 来定义。
2、接口里只能包含抽象方法,静态方法和默认方法,不能为普通方法提供方法实现,抽象类则完全可以包含普通方法。
3、 接口里只能定义静态常量,不能定义普通成员变量,抽象类里则既可以定义普通成员变量,也可以定义静态常量。
4、接口不能包含构造器,抽象类可以包含构造器,抽象类里的构造器并不是用于创建对象,而是让其子类调用这些构造器来完成属于抽象类的初始化操作。
5、接口里不能包含初始化块,但抽象类里完全可以包含初始化块。
6、抽象类的子类使用 extends 来继承;接口必须使用 implements 来实现接口。类可以实现很多个接口;但是只能继承一个抽象类。
7、访问修饰符:接口中的方法默认使用 public 修饰;抽象类中的方法可以是任意访问修饰符。
二、获取一个类Class对象的方式有哪些?
第一种:通过类对象的 getClass() 方法获取,细心点的都知道,这个 getClass 是 Object 类里面的方法。
User user=new User();
//clazz就是一个User的类对象
Class<?> clazz=user.getClass();
第二种:通过类的静态成员表示,每个类都有隐含的静态成员 class。
//clazz就是一个User的类对象
Class<?> clazz=User.class;
第三种:通过 Class 类的静态方法 forName() 方法获取。
Class<?> clazz = Class.forName("com.tian.User");
三、String str="i"与 String str=new String("i")一样吗?
不一样,因为内存的分配方式不一样。String str="i"
的方式,Java 虚拟机会将其分配到常量池中;而 String str=new String("i")
则会被分到堆内存中。
四、Object类常用方法
1、 getClass 方法
final 方法、获取对象的运行时 class 对象,class 对象就是描述对象所属类的对象。这个方法通常是和 Java 反射机制搭配使用的。
2、hashCode 方法
该方法主要用于获取对象的散列值。Object 中该方法默认返回的是对象的堆内存地址。
3、equals 方法
public boolean equals(Object obj) { return (this == obj);}
该方法用于比较两个对象,如果这两个对象引用指向的是同一个对象,那么返回 true,否则返回 false。一般 equals 和 == 是不一样的,但是在 Object 中两者是一样的。子类一般都要重写这个方法。
4、 clone 方法
protected native Object clone() throws CloneNotSupportedException;
该方法是保护方法,实现对象的浅复制,只有实现了 Cloneable 接口才可以调用该方法,否则抛出 CloneNotSupportedException 异常。
默认的 clone 方法是浅拷贝。所谓浅拷贝,指的是对象内属性引用的对象只会拷贝引用地址,而不会将引用的对象重新分配内存。深拷贝则是会连引用的对象也重新创建。
5、 toString 方法
public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
返回一个 String 对象,一般子类都有覆盖。默认返回格式如下:对象的 class 名称 + @ + hashCode 的十六进制字符串。
6、 notify 方法
final 方法,主要用于唤醒在该对象上等待的某个线程。
7、notifyAll 方法
final 方法,主要用于唤醒在该对象上等待的所有线程。
8、wait(long timeout) 方法
public final native void wait(long timeout) throws InterruptedException;
wait 方法就是使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait() 方法一直等待,直到获得锁或者被中断。wait(long timeout) 设定一个超时间隔,如果在规定时间内没有获得锁就返回。
9、wait(long timeout, int nanos) 方法
参数说明:
timeout:最大等待时间(毫秒)
nanos:附加时间在毫秒范围(0-999999)
该方法导致当前线程等待,直到其他线程调用此对象的 notify() 方法或notifyAll()方法,或在指定已经过去的时间。此方法类似于 wait 方法的一个参数,但它允许更好地控制的时间等待一个通知放弃之前的量。实时量,以毫微秒计算,计算公式如下:1000000 * timeout + nanos
在所有其他方面,这种方法与 wait(long timeout) 做同样的事情。特别是 wait(0, 0) 表示和 wait(0) 相同。
10、 wait 方法
public final void wait() throws InterruptedException { wait(0);}
可以看到 wait() 方法实际上调用的是 wait(long timeout) 方法,只不过 timeout 为 0,即不等待。
11、finalize 方法
该方法是保护方法,主要用于在 GC 的时候再次被调用,如果我们实现了这个方法,对象可能在这个方法中再次复活,从而避免被 GC 回收。
五、wait()、notify区别
wait和notify都是Object中的方法
wait和notify执行前,线程都必须获得对象锁
wait的作用是使当前线程进行等待
notify的作用是通知其他等待当前线程的对象锁的线程
wait()会立刻释放synchronized(obj)中的obj锁,以便其他线程可以执行obj.notify()
但是notify()不会立刻立刻释放锁,必须要等notify()所在线程执行完synchronized(obj)块中的所有代码才会释放这把锁。
六、notify和notifyAll的区别
锁池
:
假设线程A已经拥有对象锁,线程B、C想要获取锁就会被阻塞,进入一个地方去等待锁的等待,这个地方就是该对象的锁池;
等待池
:
假设线程A调用某个对象的wait方法,线程A就会释放该对象锁,同时线程A进入该对象的等待池中,进入等待池中的线程不会去竞争该对象的锁。
notify和notifyAll的区别
:
1、notify只会随机选取一个处于等待池中的线程进入锁池去竞争获取锁的机会;
2、notifyAll会让所有处于等待池的线程全部进入锁池去竞争获取锁的机会;
七、try catch finally,try里有return,finally还执行么?
执行,并且finally的执行早于try里面的return
import static java.lang.Math.*;
public class Test{
public static void main(String[] args){
//System.out.println(Math.sin(20));传统做法
System.out.println(sin(20));
}
}
byte a = 127;
byte b = 127;
b = a + b; // 报编译错误:cannot convert from int to byte
b += a;
short s1= 1;
s1 = s1 + 1;
short s1= 1;
s1 += 1;
结论:
1、不管有没有出现异常,finally块中代码都会执行;
2、当try和catch中有return时,finally仍然会执行;
3、finally是在return后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的
值保存起来,管finally中的代码怎么样,返回的值都不会改变,任然是之前保存的值),所以函数
返回值是在finally执行前确定的;
4、finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值。
八、深拷贝和浅拷贝的区别是什么?
深拷贝
:值类型字段会复制一份,引用类型字段所指向的对象,会在内存中也创建一个副本。
浅拷贝
:值类型的字段会复制一份,而引用类型的字段拷贝的仅仅是引用地址,而该引用地址指向的实际对象空间其实只有一份。Object中的clone()方法默认就是执行的浅拷贝。
九、HashMap 与ConcurrentHashMap 的异同
1、都是 key-value 形式的存储数据;
2、 HashMap 是线程不安全的,ConcurrentHashMap 是 JUC 下的线程安全的;
3、HashMap 底层数据结构是数组 + 链表(JDK 1.8 之前)。JDK 1.8 之后是数组 + 链表 + 红黑
树。当链表中元素个数达到 8 的时候,链表的查询速度不如红黑树快,链表会转为红黑树,红
黑树查询速度快;
4、 HashMap 初始数组大小为 16(默认),当出现扩容的时候,以 0.75 * 数组大小的方式进行扩
容;
5、ConcurrentHashMap 在 JDK 1.8 之前是采用分段锁来现实的 Segment + HashEntry,
Segment 数组大小默认是 16,2 的 n 次方;JDK 1.8 之后,采用 Node + CAS + Synchronized
来保证并发安全进行实现
十、Java 中 IO 流分为几种?
按功能来分:输入流(input)、输出流(output)。
按类型来分:字节流和字符流。
字节流和字符流的区别是:字节流按 8 位传输以字节为单位输入输出数据,字符流按 16 位传输以字符为单位输入输出数据。
字节流默认不使用缓冲区;字符流使用缓冲区。
字节流通常用于处理二进制数据,实际上它可以处理任意类型的数据,但它不支持直接写入或读取Unicode码元;字符流通常处理文本数据,它支持写入及读取Unicode码元。