面试题整理—Java初级面试题
Java中有哪几种方式来创建线程执行任务
在Java中本质上只有一种创建线程的方式—–实现Runnable接口
(1)实现Runnable接口
(2)继承Thread
(3)实现Callable接口(可以获取任务结果,使用FutureTask)
(4)使用线程池来创建线程
线程池有哪几种状态,每种状态表示什么?
(1)Running状态:表示线程池正常运行,既能接收任务也会处理队列任务
(2)Shutdown状态:表示线程池处于正在关闭状态,不会接收新任务,但会把队列中的任务处理完,使用shutdow()方法
(3)Stop状态:表示线程池处于正在停止状态,既不会接收任务,也不会处理队列中的任务,使用shutdownow()方法
(4)Tidying:线程池没用线程运行后的状态,并会调用terminated()空方法,给与扩展
(5)Terminated状态:terminated()方法调用后会变成该状态
JDK、JRE、JVM之间的区别
JDK,Java标准开发包,提供编译、运行Java所需的各种工具和资源,包括Java编译器、Java运行时环境,以及常用Java类库
JRE,Java运行环境,用于运行Java字节码文件
JVM,Java虚拟机,是JRE的一部分,负责运行字节码文件
Java代码使用JDK中的编译器javac编译为字节码,再在JVM中运行。
JDK包含了JRE,JRE包含了JVM
hashCode()与equals()之间的关系
在Java中,每个对象都可以调用自己的hashCode()方法来得到自己的哈希值,相当于对象的指纹信息。
当两个对象哈希值相同时,不一定是同一个对象。
在比较两个对象是否相等时,会先比较两个对象的hashCode(),如果哈希值相同,再调用equals()方法比较,如果相同则是两个相等的对象。
String、StringBuffer、StringBuilder的区别
(1)String是不可变的,如果尝试修改,会生成一个新的字符串对象,而StringBuffer和StringBuilder是可变的。
(2)StringBuffer是线程安全的,StringBuilder是线程不安全的,所以在单线程环境下StringBuilder效率会更高。
泛型中extends和super的区别
1.<? extends T>表示包括T在内的任何T的子类
2.<? super T>表示包括T在内的任何T的父类
如List<E extends Number> 可以用于限制泛型的类型
==和equals方法的区别
==如果是基本数据类型,比较的是值,如果是引用类型,比较的是引用地址
equals:具体看各个类重写equals方法之后的比较逻辑。
如String类,不能使用==比较两个字符串的值,而需要使用equals
重载和重写的区别
重载:发生在同一个类中,方法名相同,参数类型、个数、顺序不同,方法返回值和访问修饰符可以不同,但重载和返回值无关,即只有返回值不一样不叫重载
重写:发生在子类中,方法名、参数列表相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类;如果父类方法访问修饰符为private则子类不能重写该方法。
List和Set的区别
List:有序的,按对象进入顺序保存对象,可重复,运行多个null元素,可以使用iterator取出所有元素,再逐一遍历,也可以使用get方法获取下标元素
Set:无序的,不可重复,最多允许一个null元素,取元素时使用iterator接口取得所有元素,再逐一遍历
ArrayList和LinkedList的区别
两者底层数据结构不同,ArrayList底层基于数组实现,LinkedList底层基于链表实现
由于底层不同,适用场景也不同,ArrayList适合随机查找,LinkedList适合删除和添加较多的场景
两者都实现了List接口,但LinkedList还实现了Deque接口,所以可以当作队列使用
Jdk1.7到Jdk1.8 HashMap的变化
- 1.7中底层为数组 链表,1.8中为数组 链表 红黑树,目的是提高插入和查询整体效率
- 1.7链表插入是头插法,1.8为尾插法,因为1.8中插入key和value需要判断列表元素个数,需要遍历链表,所以直接使用尾插法。
- 1.7中哈希算法比较复杂,存在各种右移与异或运算,1.8进行了简化
深拷贝与浅拷贝
深拷贝和浅拷贝指对象的拷贝,一个对象存在两种类型的属性,一种是基本数据类型,一种是实例对象的引用。
浅拷贝指只会拷贝基本数据类型的值,以及实例对象的引用地址,并不会复制引用对象的值,即拷贝出来的对象,内部实例对象指向同一个对象。
深拷贝不仅会拷贝基本数据类型,也会针对实例对象的引用地址所指向的对象进行复制,即深拷贝出来的对象,内部实例对象指向不是同一个对象。
HashMap的扩容机制
HashMap的默认容量为16,默认的负载因子为0.75,当HashMap中元素个数超过容量乘以负载因子的个数时,就创建一个大小为前一次两倍的新数组,再将原来数组中的数据复制到新数组中。当数组长度到达64且链表长度大于8时,链表转为红黑树
想要线程安全的HashMap怎么办?
(1)使用ConcurrentHashMap
(2)使用HashTable
(3)Collections.synchronizedHashMap()方法
ConcurrentHashMap原如何保证的线程安全?
JDK1.7:使用分段锁,将一个Map分为了16个段,每个段都是一个小的hashmap,每次操作只对其中一个段加锁
JDK1.8:采用CAS Synchronized保证线程安全,每次插入数据时判断在当前数组下标是否是第一次插入,是就通过CAS方式插入,然后判断f.hash是否=-1,是的话就说明其他线程正在进行扩容,当前线程也会参与扩容;删除方法用了synchronized修饰,保证并发下移除元素安全
HashTable与HashMap的区别
(1)HashTable的每个方法都用synchronized修饰,因此是线程安全的,但同时读写效率很低
(2)HashTable的Key不允许为null
(3)HashTable只对key进行一次hash,HashMap进行了两次Hash
(4)HashTable底层使用的数组加链表
如何保证ArrayList的线程安全?
(1)使用collentions.synchronizedList()方法为ArrayList加锁
(2)使用Vector,Vector底层与Arraylist相同,但是每个方法都由synchronized修饰,速度很慢
(3)使用juc下的CopyOnWriterArrayList,该类实现了读操作不加锁,写操作时为list创建一个副本,期间其它线程读取的都是原本list,写操作都在副本中进行,写入完成后,再将指针指向副本。
面向对象和面向过程的区别
面向对象有封装、继承、多态性的特性,所以相比面向过程易维护、易复用、易扩展,但是因为类调用时要实例化,所以开销大性能比面向过程低
多态的作用
多态的实现要有继承、重写,父类引用指向子类对象。它的好处是可以消除类型之间的耦合关系,增加类的可扩充性和灵活性。
什么是反射
反射是通过获取类的class对象,然后动态的获取到这个类的内部结构,动态的去操作类的属性和方法。 应用场景有:要操作权限不够的类属性和方法时、实现自定义注解时、动态加载第三方jar包时、按需加载类,节省编译和初始化时间; 获取class对象的方法有:class.forName(类路径),类.class(),对象的getClass()
Java创建对象得五种方式?
(1)new关键字 (2)Class.newInstance (3)Constructor.newInstance
(4)Clone方法 (5)反序列化