1、重载和重写的区别
重载是同一个类中可以声明多个名字相同,但是参数列表不同的方法或者构造器;
重写是子类在继承父类以后,对于父类中声明的某些方法进行覆盖操作;
2、多态时运行时行为还是编译时行为?如何证明
3、final、finally、finalize的区别
4、==和equals的区别
==:运算符
①如果比较的是基本数据类型变量,不比较数据类型,仅仅比较数值大小是否相等
②如果比较的是引用数据类型变量,比较对象的地址值是否相等,即两个引用是否指向同一个对象实体
equals:方法
①equals只能是引用数据类型来进行使用
②Object类中equals的定义,是和==相同的
③String、Date、File、包装类等都重写了equals方法,重写后比较的不再是两个变量的地址值,而是对象的实体内容是否相同
5、下面两个的输出结果分别为多少
//题一
Object o1 = true ? new Integer(1) : new Double(2.0);
System.out.println(o1);
//题二
Object o2;
if(true) {
o2 = new Integer(1);
}else {
o2 = new Double(2.0);
}
System.out.println(o2);
题一结果为1.0:再编译过程中,三目运算符后两个数据必须要统一,那么Integer就要转化为Double,所以结果为1.0
题二结果为1
6.下面三个的输出结果是
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j);
Integer m = 1;
Integer n = 1;
System.out.println(m == n);
Integer x = 128;
Integer y = 128;
System.out.println(x == y);
第一个是false,第二个是true,第三个是false
Integer内部定义了一个IntegerCache结构,IntegerCache中定义了Integer[],保存了从-128–127的整数,如果使用自动装箱的方法,Integer赋值的范围如果在-128~127时,可以直接使用数组中的元素,不用重新new一个。
7.抽象类和接口有哪些异同?
相同点:都不能实例化,都可以被继承
不同点:
抽象类有构造器,接口没有构造器;接口可以多继承,抽象类不能多继承
8.常见的异常有哪些?
9.throw和throws的区别?
throw是抛出一个异常类的对象,生成异常对象的过程,声明在方法体内
throws属于异常处理的一种方式,声明在方法的声明处
10.synchronized和lock的异同?
相同点:都可以解决线程安全问题
不同点:synchronized是隐式锁,在执行完相应的代码后,会自动释放同步监视器;
Lock是显式锁,需要手动启动同步(lock()
),并且结束同步也需要手动实现(unlock()
);
使用Lock,JVM花费更少的时间调度线程,性能更好。具有更好的扩展性
11.如何解决线程安全问题?
synchronized和lock
12、sleep()和wait()的异同?
相同点:都可以使得线程进入阻塞状态
不同点:
sleep()不能够释放锁(同步监视器),wait()能够释放锁(同步监视器);
sleep()声明在Thread类中,wait()声明在Object类中;
sleep()可以在任何场景下调用,wait()只能在同步代码块和同步方法中;
13、生产者/消费者问题
生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品,店员一次只能持有固定数量的产品(比如说20),如果生产者试图生产更多产品,店员会叫生产者停一下,如果店中有空位放产品了再通知生产者继续生产;如果店中没有产品了,店员会告诉消费者等一下,如果店中有产品了再通知消费者来取走产品。
可能出现的两个问题:
生产者比消费者快时,消费者会漏掉一些数据没有取到;
消费者比生产者快时,消费者会取相同的数据。
class Clerk{
private int productCount = 0;
//生产产品
public synchronized void produceProduct() {
if (productCount < 20) {
productCount++;
System.out.println(Thread.currentThread().getName() + " 开始生产第" + productCount + "个产品。");
notify();
}else{
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//消费产品
public synchronized void consumeProduct() {
if (productCount > 0) {
System.out.println(Thread.currentThread().getName() + " 开始消费第" + productCount + "个产品。");
productCount--;
notify();
}else{
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Productor implements Runnable{
private Clerk clerk;
public Productor(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 开始生产产品……");
while (true) {
clerk.produceProduct();
}
}
}
class Customer implements Runnable{
private Clerk clerk;
public Customer(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 开始购买产品……");
while (true) {
clerk.consumeProduct();
}
}
}
public class ProductTest {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Productor productor = new Productor(clerk);
Thread p1 = new Thread(productor);
p1.setName("生产者1");
Customer customer = new Customer(clerk);
Thread c1 = new Thread(customer);
c1.setName("消费者1");
p1.start();
c1.start();
}
}
14、获得两个字符串中最大相同字串
str1 = “abcwerthelloyuiodef”,str2 = “cvhellobnm”
提示:将短的字符串进行长度依次递减的字串与较长的字符串比较
①只有一个最大相同字串
public class CommonString {
public String getCommonString(String str1, String str2){
if (str1 != null && str2 != null){
//先获得两个字符串中一大一小的两个字符串,分别装入maxStr、minStr
String maxStr = (str1.length() >= str2.length()) ? str1 : str2;
String minStr = (str1.length() < str2.length()) ? str1 : str2;
int length = minStr.length();
//一共有较短字符串长度的大分类
for (int i = 0; i < length; i++){
//每次小分类都是从开头开始、慢慢向末尾移动判断
for (int x = 0,y = length - i; y <= length; x++,y++){
String str = minStr.substring(x,y);
if (maxStr.contains(str)){
return str;
}
}
}
}
return null;
}
@Test
public void demo(){
String str1 = "abcwerthelloyuiodef";
String str2 = "cvhellobnm";
System.out.println(getCommonString(str1, str2));
}
}
②有多个最大相同字串
public class CommonString {
public String[] getCommonString(String str1, String str2){
if (str1 != null && str2 != null){
StringBuffer sBuffer = new StringBuffer();
//先获得两个字符串中一大一小的两个字符串,分别装入maxStr、minStr
String maxStr = (str1.length() >= str2.length()) ? str1 : str2;
String minStr = (str1.length() < str2.length()) ? str1 : str2;
int length = minStr.length();
//一共有较短字符串长度的大分类
for (int i = 0; i < length; i++){
//每次小分类都是从开头开始、慢慢向末尾移动判断
for (int x = 0,y = length - i; y <= length; x++,y++){
String str = minStr.substring(x,y);
if (maxStr.contains(str)){
sBuffer.append(str + ",");
}
}
//如果找到了最大相同字符串就停止下一轮的寻找
if (sBuffer.length() != 0){
break;
}
}
String[] split = sBuffer.toString().replaceAll(",$","").split("//,");
return split;
}
return null;
}
@Test
public void demo(){
String str1 = "abcwerthelloyuiodefabcdf";
String str2 = "cvhellobnmabcdf";
String[] result = getCommonString(str1, str2);
System.out.println(Arrays.toString(result));
}
}
15、从XXXX-XX-XX到XXXX-XX-XX的总天数?
方式一:分别获得两个时间的时间戳,然后相减,获得总的毫秒数,然后除以一天的毫秒数,再加上一;
方式二:将总的天数分为两个时间段,一个是整年的时间段,另一个是一年中该日期的天数;
16、ArrayList、LinkedList、Vector三者的异同?
相同点:三者都是List接口的实现类;存储数据的特点都是有序、可重复;
不同点:
ArrayList:作为List接口的主要实现类;线程不安全,效率高;底层使用Object[] elementData
存储;
LinkedList:在进行增、删操作时效率比ArrayList高;底层使用双向链表存储
Vector:作为List最古老的实现类;线程安全,效率低;Object[] elementData
存储;
ArrayList源码分析:
JDK7.0版本:
ArrayList实例化:空参构造器:初始化一个长度为10的Object数组elementData
ArrayList添加操作:add(123):创建一个包装类Integer(123),放入Object[]数组中
数组容量满了:默认情况下,新数组扩容为原来的1.5倍(原有的数组长度>>1),将原来数组中的数据放到新的数组中;
JDK8.0版本:
ArrayList实例化:空参构造器:初始化Object[] elementData为{},并没有直接赋予长度
ArrayList添加操作:add(123):第一次调用时,才创建了长度为10的数组,并将数据放入数组中
数组容量满了:默认情况下,新数组扩容为原来的1.5倍(原有的数组长度>>1),将原来数组中的数据放到新的数组中
LinkedList源码分析:
LinkedList实例化:空参构造器:在内部声明一个Node类型的first、last变量以及int类型的size变量
LinkedList添加操作:add(123):新建一个Node类型的实体变量,将last变量放入Node的头节点,null放入尾节点,数据放入element中
HashSet:Set接口主要实现类;线程不安全;可以存储null值
LinkedHashSet:HashSet子类;遍历内部数据时,可以按照添加顺序遍历;频繁的遍历操作,LinkedHashSet效率高于HashSet
TreeSet:可以按照添加对象的指定属性,进行排序
17、看以下的代码输出
HashSet set = new HashSet();
Person p1 = new Person(1001, "AA");
Person p2 = new Person(1002, "BB");
set.add(p1);
set.add(p2);
p1.name = "CC";
set.remove(p1);
System.out.println(set);
set.add(new Person(1001, "CC"));
System.out.println(set);
set.add(new Person(1001, "AA"));
System.out.println(set);
第一个输出是[Person{id=1002, name='BB'}, Person{id=1001, name='CC'}]
首先根据p1、p2的属性值来算出哈希值,放入对应的位置,p1.name = "CC"
将p1的哈希值改变了,那么当set调用remove()方法的时候查找的时候是按照改变后的哈希值来查找,那么就找不到原来存放"AA"
的位置,那么就p1删除失败
第二个输出是[Person{id=1002, name='BB'}, Person{id=1001, name='CC'}, Person{id=1001, name='CC'}]
按上图,原来存放改变后哈希值的地方并没有存放数据,所以判断没有相同,所以放入
第三个输出是[Person{id=1002, name='BB'}, Person{id=1001, name='CC'}, Person{id=1001, name='CC'}, Person{id=1001, name='AA'}]
原来存放"AA"的位置上判断两个对象不相等,所以可以加进去
18、HashMap的底层实现原理?
19、HashMap和Hashtable的异同?
20、谈谈你对HashMap中put/get方法的认识?如果了解再谈谈HashMap的扩容机制?默认大小是多少?什么是负载因子(或填充比)?什么是吞吐临界值(或阈值、threshold)?
21、Collection和Collections的区别?
22、