1、object类里面有哪些方法?
(1)clone方法
protected方法,实现对象的浅复制,只有实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportedException异常。
(2)getClass方法
final方法,获得运行时类型。
(3)toString方法
该方法用得比较多,一般子类都有覆盖。
(4)finalize方法
该方法用于释放资源。因为无法确定该方法什么时候被调用,很少使用。
(5)equals方法
该方法是非常重要的一个方法。一般equals和==是不一样的,但是在Object中两者是一样的。子类一般都要重写这个方法。
(6)hashCode方法
该方法用于哈希查找,重写了equals方法一般都要重写hashCode方法。这个方法在一些具有哈希功能的Collection中用到。
一般必须满足obj1.equals(obj2)==true。可以推出obj1.hashCode()==obj2.hashCode(),但是hashCode相等不一定就满足equals。不过为了提高效率,应该尽量使上面两个条件接近等价。
(7)wait方法
wait方法就是使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait()方法一直等待,直到获得锁或者被中断。wait(long timeout)设定一个超时间隔,如果在规定时间内没有获得锁就返回。
调用该方法后当前线程进入睡眠状态,直到以下事件发生。
(1)其他线程调用了该对象的notify方法。
(2)其他线程调用了该对象的notifyAll方法。
(3)其他线程调用了interrupt中断该线程。
(4)时间间隔到了。
此时该线程就可以被调度了,如果是被中断的话就抛出一个InterruptedException异常。
(8)notify方法
该方法唤醒在该对象上等待的某个线程。
(9)notifyAll方法
该方法唤醒在该对象上等待的所有线程。
2、为什么在重写equals方法的时候要重写hashcode的方法?
判断两个对象是否相同的时候,先根据hashcode进行的判断,相同的情况下再根据equals()方法进行判断。如果只重写了equals方法,而不重写hashcode的方法,会造成hashcode的值不同,而equals()方法判断出来的结果为true。
3、String里面的hashcode方法是怎么实现的?
先来看源码:
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
String类中的hashCode计算方法还是比较简单的,就是以31为权,每一位字符的ASCII只进行计算,用自然溢出来等效取模。哈希计算公式可以记为:
value[0]*31^(n-1) + value[1]*31^(n-2) + ... + value[n-1]
选择31的理由。从网上的资料来看,一般有如下两个原因:
第一,31是一个不大不小的质数,是作为 hashCode 乘子的优选质数之一。另外一些相近的质数,比如37、41、43等等,也都是不错的选择。那么为啥偏偏选中了31呢?请看第二个原因。
第二、31可以被 JVM 优化,31 * i = (i << 5) - i。即乘法运算可以被移位和减法运算取代,来获取更好的性能。
4、Java中的异常
Throwable: 有两个重要的子类:Exception(异常)和 Error(错误),二者都是 Java 异常处理的重要子类,各自都包含大量子类。异常和错误的区别是:异常能被程序本身可以处理,错误是无法处理。
Exception(异常)分两大类:运行时异常和非运行时异常(编译异常)。程序中应当尽可能去处理这些异常。
1.运行时异常:都是RuntimeException类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。
2.非运行时异常 (编译异常):是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。
5、线程启动三种方式?
1.继承Thread类,重写run方法,创建该类对象,调用start方法开启线程。
2.实现Runnable接口,重写run方法,创建Thread类对象,将Runnable子类对象传递给Thread类对象。调用start方法开启线程。
3.创建FutureTask对象,创建Callable子类对象,重写call(相当于run)方法,将其传递给FutureTask对象(相当于一个Runnable)。创建Thread类对象,将FutureTask对象传递给Thread对象。调用start方法开启线程。这种方式可以获得线程执行完之后的返回值。
6、Java线程中run和start方法的区别?
区别:调用start方法实现bai多线程,而调用run方法没有实现多线程
用start方法来启zhi动线程,真正实现了多线程运行。通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,这里方法run()称为线程体,它包含了要执行的这个线程的内容,run方法运行结束,此线程随即终止。
run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。
总结:调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行。
7、synchronized和锁(ReentrantLock) 区别
相似点:
这两种同步方式有很多相似之处,它们都是加锁方式同步,而且都是阻塞式的同步,也就是说当如果一个线程获得了对象锁,进入了同步块,其他访问该同步块的线程都必须阻塞在同步块外面等待,而进行线程阻塞和唤醒的代价是比较高的。
不同点:
- Lock只适用于代码块锁,而synchronized可用于修饰方法、代码块等。
- synchronized的锁是非公平锁,ReentrantLock默认情况下也是非公平锁,但可以通过带布尔值的构造函数要求使用公平锁。(公平锁是指多个线程在等待同一个锁时,必须按照申请的时间顺序来依次获得锁;而非公平锁则不能保证这一点。非公平锁在锁被释放时,任何一个等待锁的线程都有机会获得锁。)
- 对于Synchronized来说,它是java语言的关键字,是原生语法层面的互斥,需要jvm实现。而ReentrantLock它是JDK 1.5之后提供的API层面的互斥锁,需要lock()和unlock()方法配合try/finally语句块来完成。
- ReentrantLock是等待可中断,synchronized是等待不可中断。(等待可中断是指当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情。可等待特性对处理执行时间非常长的同步快很有帮助。)
8、synchronized关键字修饰方法和代码块时JVM底层做了什么?
一、修饰代码块
synchronized同步语句块使用的是monitorenter和monitorexit指令。其中monitorenter指向同步代码块的开始位置,monitorexit指向同步代码块的结束位置。
二、修饰方法
synchronized修饰方法时使用的是ACC_SYNCHRONIZED标志。该标志指明了该方法是一个同步方法,JVM通过ACC_SYNCHRONIZED访问标志来判断当前方法是否是同步方法,从而执行相应的同步调用。
9、AtomicInteger等了解吗?
主要利用 CAS (compare and swap) + volatile 来保证原子操作。
CAS是英文单词Compare and Swap的缩写,翻译过来就是比较并替换。
CAS机制中使用了3个基本操作数:内存地址V,旧的预期值A,要修改的新值B。更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为B。
10、Java泛型
定义:
泛型,即“参数化类型”。就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
类型擦除:
Java的泛型是伪泛型,这是因为Java在编译期间,所有的泛型信息都会被擦掉,正确理解泛型概念的首要前提是理解类型擦除。Java的泛型基本上都是在编译器这个层次上实现的,在生成的字节码中是不包含泛型中的类型信息的,使用泛型的时候加上类型参数,在编译器编译的时候会去掉,这个过程成为类型擦除。
上限通配符和下限通配符:
<? extends T>上限通配,表示?是T的一个未知子类。
这里?表示一个未知的类,而T是一个具体的类,在实际使用的时候T需要替换成一个具体的类,表示实例化的时候泛型参数要是T的子类。
<? super T>下限通配,表示?是T的一个未知父类。
这里?表示一个未知的类,而T是一个具体的类,在实际使用的时候T需要替换成一个具体的类,表示实例化的时候泛型参数要是T的父类。
11、ArrayList的扩容方式和扩容时机
初始化
ArrayList的底层是一个动态数组,ArrayList首先会对传进来的初始化参数initalCapacity进行判断
如果参数等于0,则将数组初始化为一个空数组,
如果不等于0,将数组初始化为一个容量为10的数组。
扩容时机
当数组的大小大于初始容量的时候(比如初始为10,当添加第11个元素的时候),就会进行扩容,新的容量为旧的容量的1.5倍。
扩容方式
扩容的时候,会以新的容量建一个原数组的拷贝,修改原数组,指向这个新数组,原数组被抛弃,会被GC回收。
12、内存泄露?
当某些对象不再被使用,但是由于仍然 被引用 而导致垃圾收集器不能回收他们。也就是说,应该回收的内存没有被回收。
当我们不断的向集合类内加入元素,而没有对应的删除机制,导致内存一直被占用,这样也不一定就会造成内存泄露。当该集合类仅仅是一个局部变量的时候,当方法运行完成退出的时候,自然会被GC所回收,可是怕的是该集合类是一个全局的属性(比方类中的静态变量),那么会导致该集合类占用的内存仅仅增不减,这样就导致了内存的泄露。所以我们在使用全局性的集合类的时候要注意提供合适的删除策略或者定期清理策略。
13、注解?
作用在代码的注解是:
@Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
@Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
@SuppressWarnings - 指示编译器去忽略注解中声明的警告。
作用在其他注解的注解(或者说 元注解)是:
@Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
@Documented - 标记这些注解是否包含在用户文档中。
@Target - 标记这个注解应该是哪种 Java 成员。
@Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)
14、内部类及其特点?
1、内部类可以直接访问外部类的成员,包括私有
2、外部类想要访问内部类的成员,必须创建对象。