JAVA基础阶段总结
重载(Overload)和重写(Override)的区别是什么?
- 重载是某个方法,可以有不同的参数列表、返回值和修饰符。
- 重写是子类继承父类时,子类想对父类的方法做出修改,就可以@Override注释,写出跟父类相同的方法体(返回值的范围可以比父类小),方法内容随意重写。
java8的新特性你都了解哪些?
lamda表达式、方法引用表达式、Stream流、新的时间类等待
在项目中常用的lamada表达式和方法引用表达式,比如对分块文件列表进行按 名称排序,就是用了表达式和comparingInt方法来实现的
抽象类和接口的区别?
- 抽象类一般用作实体类的抽象,用abstract修饰,接口一般是在service层、dao层之类的系统架构上面使用,是对动作的抽象,用interface修饰。
- 一个抽象类只能被单继承(extends),一个接口可以被多实现(implements)。
- 实现一个接口,必须实现接口中的所有方法,抽象类可以有选择地进行继承。
==和equals()的区别是什么?
- ==是判断两个变量或实例是不是指向同一个内存空间。equals是判断两个变量或实例所指向的内存空间的值是不是相同。
- ==是一个操作符,equals是一个方法。
- 常用的Object 的 equals() 方法其实就是 ==;而String 的 equals()重写了此方法,比较对象的值。
所以equals() 方法有没有被重写是判断其与 == 的区别的主要关注点。
阐述final、finally、finalize 的区别
final
- final 是一个修饰符,意为最终态。如果一个类被声明为final,意味着它不能再派生新的子类。
- 修饰类,类不能被继承
- 修饰变量,变量是常量
- 修饰方法,方法不能被重写
finally
finally是异常处理的一部分,用于释放资源,一般来说finally里面的代码肯定会执行
finalize
- finalize()是Object中的方法,用于垃圾回收。当一个对象被虚拟机宣告死亡时会先调用finalize()方法,让此对象处理它生前最后的事情。
String类的常用方法都有哪些?
length()返回字符串的长度 , equals()比较字符串, getByte()返回字符串的byte类数组, trim()去除字符串两端空白, split()分割字符串,返回分割后的字符串数组,replace()字符串替换,toLowerCase()字符串转换小写,toUpperCase()字符串转换大写, substring()截取字符串 ,indexOf()返回指定字符串索引 ...
Break和Continue有什么区别?
用Break语句可以使流程跳出switch语句体,也可以用Break语句在循环结构中终止本层循环体,从而提前结束本层循环。
Continue是结束本次循环,继续下一次循环。
数据类型
String,StringBuffer,StringBuilder的区别是什么?
String是一个用final修饰的字符数组,是一个不可变的对象,每次修改字符串对象都等同于生成了一个新的对象,然后将指针指向新对象。
StringBuffer和StringBuilder是可变的字符序列,都在原来的对象本身进行操作;
StringBuffer是线程安全的;StringBuilder是线程不安全的,但是StringBuilder的速度更快一些。
int 与Interger 的区别?
首先最本质的区别就是 int是一个数据基本类型,而 Integer是一个基本类型的包装类。
- int是直接存储数据值,而Integer 是对象的引用。(当new 一个Interger时,实际上是生成一个指针指向此对象)
- Integer变量必须实例化后才能使用,而int变量不需要。
- int默认值为0,Integer默认值为 null。
集合
平常用过哪些集合?
常用的就是 ArrayList, LinkedList, HashMap, 其他的还有 TreeMap,HashTable,TreeSet,HashSet等。
List,Set,Map的区别?
首先,List和Set都是Collection下的子接口,而Map是与Collection同级的接口。
- Set接口最重要的一点是不包含重复元素,而且是无序的,它下面的TreeSet实现了有序。
- List接口允许重复元素,并且是保证顺序的,可以插入 null元素,其下面常用的有LinkedList和ArrayList。
- Map是通过键值对的方式存储数据,每一个键都可以获取唯一对应的值。
LinkedList 和 ArrayList 的区别?
ArrayList:
- 底层数据结构是数组
- 由于其存放数据的是一块连续的内存空间,就可以根据数组的首地址加上偏移量,直接计算出第n个元素的内存位置,所以它查询快。
- 还是由于其数据结构的原因,当想要在中间插入一条数据时,后面的数据需要全部往后挪一位,甚至是要将新数组重新分配内存空间,增删慢。
LinkedList: - 底层数据结构是双向链表。
- LinkedList存放数据是分散的,每个数据保存自身数据 + 下一个数据内存地址,查询时要从头部顺着链子依次查,所以它查询慢。
- 增删快,LinkedList想要插入数据,只需要将插入位置断开,安插进去,修改的只是节点之间的引用。
ArrayList 为什么线程不安全?怎么解决?
ArrayList添加数据时,会分为两步:存放数据和修改size。当存放数据后线程被抢,然后另一个线程也存放数据,就会导致存放数据实际只有一条,而size为2。
解决方法:1. 使用synchronized关键字。2. 使用Collection类提供的synchronizedList()方法。
怎么删除list中的某个数据?
- 暴力for循环遍历,调用remove
- 原list叫 listA,将某个数据存为另一个 listB,listA调用removeAll方法,删除 listB
- 得到该 list 的迭代器,使用迭代器的remove方法。
java的队列有了解吗?
队列即 Queue,是与LIst,Set同一级别的接口。LinkedList实现了Queue接口下的子接口Deque(双端队列)。
没有实现阻塞接口的有: LinkedList, priorityQueue,ConcurrentLinkedQueue。
实现阻塞接口的:ArrayBlockingQueue
(基于数组的有界队列),LinkedBlockingQueue
(基于链接的有界队列),PriorityBlockingQueue
(基于优先级的无界队列),DelayQueue
,SynchronousQueue
HashMap
HashMap底层结构是什么?
HashMap的底层结构是链表+红黑树。HashMap如果在创建时没有指定容量,默认初始容量为16,扩容阈值为数组长度的0.75.每次扩容翻倍。当HashMap长度小于8时使用链表,而当HashMap长度大于8时自动转化为红黑树。
HashMap是否是线程安全的?如何保证HashMap线程安全?
HashMap不是线程安全的。
- 使用Collection类里面的synchronizedMap()包装一下,即可得到线程安全的map;(使用的是悲观锁)
- 使用ConcurrentHashMap类,可以直接得到一个线程安全的HashMap(采用的是乐观锁)
HashMap为什么初始化大小为16?
HashMap的初始化大小是将初始化大小二进制按位与,而扩容机制是HashMap每次扩容都是以2的整数次幂进行扩容。因此最合适的初始化大小是16,4和8理论上也可以,但是因为太小容易导致map扩容影响性能,太浪费资源。
红黑树和链表怎么进行转化?
当HashMap长度小于8时使用链表,而当HashMap长度大于8时自动转化为红黑树。目的是为了减少搜索时间。
纯链表的平均查找长度为(n+1)/2,红黑树平均查找长度则为log2n。长度为8的时候,红黑树平均查找长度为3,链表平均查找长度为8/2=4,这才有转换为树的必要。链表长度如果小于等于6,6/2=3,速度已经很快,并且转化为树结构和生成树的时间不会很短,所以没必要转成红黑树。
HashMap和TreeMap的区别?
- 实现
HashMap基于哈希表,
TreeMap基于红黑树。 - 存储和遍历
HashMap随机存,随机遍历
TreeMap升序存,排序遍历 - 性能损耗
HashMap基本无损耗
TreeMap插入删除速度慢,需要维护树的平衡。 - 键值对
HashMap键值均可为null;
TreeMap键值均不可为null。 - 安全性和效率
HashMap不安全,效率高;
TreeMap不安全,效率低。
HashMap和HashTable有什么区别?
HashMap线程不安全,效率高,键值允许为null,非同步,适合单线程环境;
HashTable线程安全,效率低,不允许键值为null,同步,适合多线程。
但是多线程环境下仍然不建议使用HashTable,因为
- HashTable是遗留类,内部实现很多没有优化,冗余多。
- 内部全部加了syn锁,重量级锁,效率低。
- 多线程有了更好的同步的ConcurrentHashMap替代。
HashMap和LinkedHashMap有什么区别?
HashMap是最常用的一个map,根据key的hash值来存储数据,底层是链表+红黑树,当长度小于8时使用链表,大于8时自动转化为红黑树。HashMap是线程不安全的,如果需要线程安全,可以使用Collections.synchronizedMap()来获取一个线程安全的map,也可以直接使用ConcurrentHashMap类来获取一个线程安全的HashMap。
LinkedHashMap是HashMap下面的一个子类,跟HashMap的区别就是HashMap是无序的,而LInkedHashMap是可以保存插入顺序的。当对输出顺序有要求的话可以使用,一般而言LinkedHashMap的性能会比HashMap差一点(特殊情况:HashMap容量大而实际数据少时会比LinkedHashMap差,因为LinkedHashMap遍历速度只与实际数据有关,而HashMap与容量也有关)。
锁
synchronized 和 volatile 的区别是什么?
- synchronized 可以作用于变量,方法,对象;volatile 只能作用于变量。
- synchronized 可以保证线程间的有序性,原子性和可见性;volatile只保证了可见性和有序性,无法保证原子性。
- synchronized 有可能导致线程阻塞,volatile 线程不阻塞。
- volatile标记的变量不会编译器优化,synchronized标记的变量可以被编译器优化。
线程
进程和线程的区别?
进程就等于是电脑里运行的程序,线程就等于是可以让一个进程同时执行多个任务,一个进程可以有多个线程。
java里面创建线程的方式
- 实现 Runnable 接口的 run() 方法,然后通过Thread的 start() 方法开启线程。
- 继承 Thread 类,重写 run() 方法,这个本质上与Runnable接口的方式一样,因为 Thread 类本身就实现了 Runnable 接口。
- 实现 Callable 接口的 call() 方法,然后通过 Future 的 get() 方法进行开启线程。与另外两个的区别是这个有返回值。
- 通过 JUC里面的线程池。
java中的线程池是什么,有哪些常用的参数?
线程池的基本功能就是进行线程的复用,当程序需要一个线程时,会先从线程池里面捞一下空闲线程来使用,用完后会将线程重新放入线程池。
corepoolSize
:池中所保存的常驻线程数。
maxmumpoolSize
:池中允许的最大线程数。
keepAliveTime
:多余的空闲线程最多存活时间。
unit
:keepAliveTime 的单位。
handler
:拒绝策略,表示当队列满了,如何拒绝。java默认提供了4种饱和和策略的实现方式:终止,抛弃,抛弃最旧的,调用者运行。
workQueue
:任务队列。
threadFactory
:表示生成线程池中工作线程的线程工厂。
…
常用线程池?
- newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理器需要,可灵活回收空闲线程,若无可回收,则新建线程。
- newFixedThreadPool:创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
- newScheduledThreadpool:创建一个定长线程池,支持定时及周期性任务执行。
- newSingleThreadExecutor:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO,LIFO)执行。
线程池创建方式?
- 使用ThreadPoolExecutor类
- 使用Executor类
两种方式本质上是一种方式,都是通过ThreadPoolExecutor类的方式。
线程池的优点?
- 重用存在的线程。减少对象创建,消亡的开销,性能更好。
- 可有效的控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
- 提供定时执行,定期执行,单线程,并发数控制等功能。
- 降低资源消耗:重复利用线程,减少创建和销毁造成的消耗。
- 提升响应速度:任务到达,不需要创建,立即执行。
- 提高可管理性:线程是CPU调度和分派的基本单位
服务端同步和异步你是怎么理解的?
同步 在一定程度上可以看做是单线程,这个线程请求一个方法后就待这个方法给它回复,否则它不往下执行。
异步在一定程度上可以看做是多线程,请求一个方法后就不管了,继续执行其他方法。
同步,可以理解为在执行完一个函数或方法后,一直等待系统返回值或消息,这时程序是处于阻塞的,只有接收到返回的值或消息后才往下执行其他的命令。
异步,执行完函数或方法后,不必阻塞性地等待返回值或消息,只需要向系统委托一个异步过程,那么当系统接收到返回值或消息时,系统会自动触发委托的异步过程,从而完成一个完整的流程。
JVM
分别解释一下栈和堆
栈是运行时的单位,解决程序如何执行,代表处理逻辑。
堆是存储单位,解决数据存储问题,代表数据
JVM类加载机制,调优机制是什么?
- JVM类加载机制
jvm类加载机制分为五个部分:加载,验证,准备,解析,初始化。
虚拟机将class文件加载到内存,并对数据校验,准备和解析,之后完成初始化。
加载就是JVM将字节码转化为二进制字节流加载到内存中,然后为这个类在JVM方法区创建一个Class对象(访问入口);
验证是连接阶段的第一步,这一步主要就是未来确保class文件符合虚拟机的要求;
准备阶段是最重要的,这个阶段会为类变量分配内存并初始化。初始化时会为类变量赋予其数据类型的零值,而不是代码里初始化的值,例如
public static int number = 3 ;
public static final finalNumber = 3 ;
这里number初始化值是0,而不是3.
但是如果被final修饰,即成为了常量,那么在准备阶段就会被赋值为3,好比这里的finalNumber的值为3。
解析阶段会将常量池内的符号引用替换为直接引用。
1. 符号引用:简单的理解就是字符串,比如引用一个类,java.uitl.ArrayList 这就是一个符号引用,字符串引用的对象不一定被加载。
2. 直接引用:指针或者地址偏移量。引用对象一定在内存(已经加载)。
初始化阶段会执行类构造器的clinit()方法,为类变量赋予正确的初始值(之前在准备阶段只是分配空间并赋予0值,现在才赋予正确值),而初始值设定有两种方法:
- 直接声明类变量的指定初始值,比如上面的number=3,就是此时赋予;
- 使用静态代码块进行指定初始值。
- 调优机制
对jvm内存系统级的调优主要目的是减少GC的频率和full GC的次数,过多的GC和full GC会占用很多的系统资源(主要是CPU),影响系统的吞吐量。
- 老年代空间不足:(会引起Full GC)
- 尽量让对象在年轻代GC时被回收;
- 让对象在年轻代多存活一段时间;
- 不要创建过大的对象;
- 数组避免直接在老年代创建对象。
- 年轻代空间不足:
- 增大年轻代空间,避免太多静态对象;
- 控制好年轻代和老年代的比例;
- 垃圾回收尽量不要手动触发 ,尽量依靠JVM自身的机制。
调优手段主要是通过控制堆内存的各个部分的比例和GC策略