java面试总结
***Java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java 支持 4 种不同的访问权限:***
default: (即缺省,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。--------(接口里的变量都隐式声明为 public static final,而接口里的方法默认情况下访问权限为 public)
**private **: 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
**public **: 对所有类可见。使用对象:类、接口、变量、方法
protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
java中的数据类型:------四类八种(1字节是为8bit)
整数型:byte --1字节 short–2字节 int–4字节 long–8字节
浮点型:float–4字节 double–8字节
布尔类型:boolean–1字节
字符型:char–2字节
java基础:
1.JVM详解及调优
栈是运行时的单位,而堆是存储的单位,栈是解决如何处理数据,而堆是数据如何存放,放在什么地方;
堆里面有GC回收机制,有分代回收,基于对象生命周期进行垃圾回收,把对象分为年青代,年老代,持久代,不同生命周期的对象使用不同的算法进行回收;
年青代:新创建的对象存放的位置;
年老代:年青代中经历了多次回收后依然存活的对象
持久代:用于存放静态文件,如Java类,方法等;
GC回收:垃圾收集,负责清除对象并释放内存.Java提供的GC功能可以自动检测对象是否超过作用域从而达到自动回收内存的目的,从而防止内存泄露;
Java回收算法:
两个最基本的Java回收算法:复制算法和标记清理算法
复制算法:两个区域A和B,初始化对象在A,继续存活的对象被转移到B,此为新生代最常用的算法;
标记清理:一块区域,标记要回收的对象,然后回收,一定会出现碎片,那么引出;
标记-整理算法:多了碎片整理,整理出更大的内存放更大的对象;
新生代基本采用复制算法,老年代采用标记整理算法.cms采用标记清理
整个JVM分为四部分:
类加载器:加载类文件到内存;
执行引擎:也称为解释器,负责解释命令,提交操作系统执行;
本地接口:作用是融合不同的编程语言为Java使用;很少使用;
运行数据区: 是整个JVM的重点,所写的程序都被加载到这里才开始执行;
JVM的内存管理:
Stack栈: 栈内存,是Java程序的运行区,是在线程创建的时候创建,生命期跟随线程的生命期,栈不存在垃圾回收问题,只要线程结束,该栈就Over了;
Heap堆内存:一个JVM实例只存在一个堆内存,大小可调节,类加载器读取文件后,需要把类,方法,常变量放在堆内存中,方便执行,堆分为:新生区,老年区,永久存储区;
方法区:就是被所有线程共享,该区域保存所有的方法和方法字节码,以及一些特殊方法如构造函数,接口代码也在此定义;
程序计数器:每个线程都有一个程序计数器,就是一个指针,指向方法区中的方法字节码,由执行引擎读取下一条命令;
本地方法栈:当线程调用一个本地方法的时候,就会进入一个不接受虚拟机限制的地方,就是本地方法栈;
2.HashMap和HashTable的区别(HashMap的原理)
HashMap和HashTable的区别:
HashMap允许空键值,HashTable不允许;
HashMap继承自AbstractMap,HashTable继承自Dictionary类,两者都实现了Map接口;
HashMap的方法不是同步的,HashTable的方法是同步的;
HashTable线程安全,每个方法中都加入了Synchronize方法,在多线程开发的环境下,可以直接使用Hashtable,HashMap不是线程安全的,在多线程并发的情况下,可能会产生死锁等问题,但是效率要比HahsTable好很多,我们平时就是使用单线程操作的,多线程时,可以使用线程安全的ConcurrentHashMap,ConcurrentHashMap是线程安全的,效率也是比HashTable高很多,因为ConcurrenHashMap使用了分段锁,并不对整个数据进行锁定;
注:ConcurrenHashMap可用作缓存,极少使用;
HashMap的原理:HashMap由数组+链表+红黑树组成的,数组是HashMap的主体,链表是为了解决哈希冲突而存在的,如果定位到的数组位置不含链表,查找添加很快,仅需一次操作即可,如果定位到的数组包含链表,对于添加操作,并没有太大便捷,首先要遍历链表,存在即覆盖,否则新增;对于查找来说,依然需要遍历链表,然后通过key对象的Equals方法逐一不对查找,所以性能考虑,HashMap中的链表出现越少,性能越好;
3.Final,Finally和Finalize的区别
Final:用于声明属性,方法和类,表示属性不可变,方法不可被重写,类不可被继承;
Finally:是异常处理语句结构的一部分,表示总是执行;
Finalize:是Object类的一个方法,在垃圾收集器执行的时候会调用这个对象回收的方法,供垃圾收集时其他资源的回收,比如关闭文件;
4.Final修饰方法和参数(修饰方法:不可重写;修饰类:不可继承;修饰变量: 变常量)
用于声明属性,方法和类;
修饰方法: 不可重写;
修饰类:不可继承;
修饰变量:常变量;
5.ArrayList和LinkedList的区别
ArrayLIst是基于动态数组的数据结构,LinkedList是基于链表的数据结构;
对于随机访问get和set,ArrayList较优,因为LinkedList要移动指针;
对于新增和删除操作add和remove,LinkedList较优,因为ArrayList要移动数据;
6.定时任务
实现方式有两种:一种使用Timer,另一个是使用ScheduledThreadPoolExecutor;
Timer是定义在Timer.class中的一个类,继承了Thread类,所以直接当线程使用;
Timer是单线程的,如果一次执行多个定时任务,就会导致某些任务被其他任务所阻塞;
ScheduledThreadPoolExecutor基于线程池;使用nanoTime来计算执行周期,和系统时间无关,同时可以执行多线程的操作,执行多个定时任务;
7.String类(String,StringBuilder,StringBuffer三者的区别)
运行速度:StringBuilder>StringBuffer>String
String最慢的原因:String为字符串常量,而StringBuilder和StringBuffer均为字符串变量,即String对象一旦创建之后,该对象是不可更改的,但后两者的对象是变量,是可以更改的;
线程安全:在线程安全上,StringBuilder是线程不安全的,而StringBuffer是线程安全的;StringBuffer中有很多方法带有synchronized(同步)关键字,可以保证线程安全,多线程操作的时候,使用StringBuffder,单线程操作的时候,建议使用速度比较快的StringBuilder;
总结:
String:适用于少量的字符串操作的情况;
StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况;
StringBuffder:适用多线程下在字符缓冲区进行大量操作的情况;
8.Servlet生命周期
通过判断客户端发送的请求是post还是get方法来确定执行的是doGet还是doPost方法;
Servlet生命周期:
1.创建时间:当servlet程序被外界第一次访问的时候tomcat服务器内部会自动创建servlet对象,并调用init方法对这个Servlet进行初始化;
2.服务的时间:当servlet创建初始化完成之后,就一直在提供服务,用户每次访问都会调用service方法;Servlet中的service方法就是专门提供出来给用户服务的;
3.销毁的时间:当这个Servlet从项目中被移除,或者tomcat服务器正常关闭(需要手动的去使用stoptomcat),才会被调用;
9.代理模式
代理模式是常用的Java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息,过滤消息,把消息转发给委托类,以及事后处理消息等;
静态代理:由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来,在程序运行之前,代理类的.class文件就已经生成;
静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类;
静态代理事先知道要代理的是什么,而动态代理不知道要代理的什么,只有在运行时才知道;
一种动态代理CGLIB,代理的是类,不需要业务继承接口,通过派生的子类来实现代理,通过在运行时,动态修改字节码达到修改类的目的;
AOP编程就是基于动态代理实现的,例如Spring框架就是动态代理的使用例子;
10.Java的设计模式(单例模式)
大体设计模式分为三大类:
创建型模式,共五种:工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式;
结构型模式,共七种:适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组合模式,享元模式;
行为型模式,共十一种:策略模式,模板方法模式,观察者模式,迭代子模式,责任链模式,命令模式,备忘录模式,状态模式,访问者模式,中介者模式,解释器模式;
单例模式:是一种常见的软件设计模式,在它的核心结构中只包含一个被称为单例的特殊类,通过单例模式可以保证系统中,应用模式的一个类只有一个实例,即一个类只有一个对象实例;
分为懒汉式和饿汉式;饿汉式:调用静态方法的时候,对象被创建;懒汉式:调用的时候被加载;
工厂模式:
普通工厂模式: 建立一个工厂类,对实现了同一接口的一些类进行实例的创建;
多个工厂模式: 对普通工厂方法模式的改进,在普通工厂方法模式,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象;
静态工厂模式:将多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可;
多线程:
1.创建线程的三种方式
继承Thread类
实现Runable接口
使用线程池
2.线程池的创建
线程池的顶级父类是Executors
NewCachedThreadPool:创建一个可缓存线程池,如果有空闲的线程可进行回收,若没有可以回收的则创建新的线程;
NewFixedThreadPool:创建一个固定数量的线程池,可以控制并发量,超出的线程会进入等待状态;
NewScheduledThreadPool:创建一个定长线程池,支持定时及周期性执行任务;
NewSingleTheadPool:创建一个单线程化的线程池,它会用唯一的工作线程来执行任务,保证所有的任务按指定顺序执行;
传统定时器: Timer可以周期的定时的去执行线程调用;
3.几种线程池,区别
可缓存线程池:如果有空闲的线程可以进行回收,如果没有可回收的则创建新的线程
定量线程池:线程池线程数量是固定的,可以控制并发量,超出的线程会进入等待状态;
定长线程池:创建一个固定长度的线程池,可以控制并发量,超出的线程会进入等待状态;
单线程线程池:使用唯一的工作线程来执行任务,保证所有的任务按指定顺序执行;
线程池的优点:
降低资源的损耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗;
提高响应的速度:当任务到达时,任务可以不需要等到线程的创建就能立即执行;
提高线程的可管理性:线程是稀缺资源,如果无限制的创建,逼近会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控;
4.线程安全和生命周期
线程的生命周期:
新建状态:一个新产生的线程从新状态开始了它的生命周期,保值状态直到start(启动)这个线程;
运行状态:新状态线程被start以后,线程就变成可运行状态,一个线程在此状态下被认为是开始执行任务;
就绪状态:当一个线程等待另一个线程执行一个任务的时候,该线程就进入就绪状态,当另一个线程给就绪状态的线程发送信号时,该线程才重新切换到运行状态;
休眠状态:由于一个线层的时间片用完了,该线程从运行状态进入休眠状态,当时间间隔到期或等待的时间发生了,该状态的线程切换到运行状态;
终止状态:一个运行状态的线程完成任务或者其他终止条件发生,该线程就切换到中终止状态;
互斥锁:多线程下,当有线程对已加锁的内容进行操作时,其他线程无法操作。
同步锁:多线程下,有顺序的对共享数据或代码进行执行操作
线程的可用状态:就绪,运行中,等待中,睡眠中,I/O阻塞,同步阻塞,死亡;
5.死锁,同步锁,公平锁,非公平锁,悲观锁,乐观锁,互斥锁
互斥锁:多线程下,当有线程对已加锁的内容进行操作时,其他线程无法操作;
同步锁:多线程下,有顺序的对共享数据或代码进行执行操作;
死锁:两个或多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法执行; 形成死锁的条件:互斥条件,请求和保持条件,不剥夺条件,循环等待条件;
互斥条件:一个资源只能被一个进程占用,直到被该进程释放
请求和保持条件:
不剥夺条件:
循环等待条件:
公平锁:按照线程加锁的顺序来分配,即先到先得;
非公平锁:一种获取锁的抢先机制,是随机获得锁的,这样会有些线程一直拿不到锁,结果也就是不公平的;
乐观锁:认为不会产生并发问题,每次取数据的时候认为不会有其他线程对数据进行修改;
悲观锁:认为肯定会产生并发问题,每次取数据时都认为其他线程会修改,所以都会加锁(读锁,写锁,行锁等),Java中synchronized的思想也是悲观锁;
6.Synchronized和Lock有哪些区别
synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁
7.Wait()和Sleep()的区别
Wait()来自Object类,Sleep()来自Thread类;。,
Wait需要使用notify或notifyAll唤醒,而Sleep方法它时间到会自动醒;
Wait需要放在同步代码块中,Sleep可以在同步中,也可以不在同步中;
Wait在等待的时候会释放锁,sleep不会释放锁;
Wait可以指定时间也可以不指定时间,但sleep必须指定时间;
在java中wait和sleep方法的不同:
最大的不同是等待时wait会释放锁,而sleep一直持有锁,wait通常被用于线程间交互,slepp通常用于暂停执行;
8.原生JDBC的创建流程
加载驱动;
创建连接;
获取会话;
书写sql语句;
获取结果集;
关闭资源;
事务
1.事务,分布式事务(tcc事务补偿)
事务的四大特性:原子性,一致性,隔离性,持久性
原子性:整个事务当中所有的操作,要么全部成功,要么全部失败;
一致性:在事务开始之前和事务结束之后,数据库的信息一定是正确的;
隔离性:一个事务的成功或失败对其他事务是没有任何影响的,2个事务之间是相互独立的
持久性:在事务完成以后,这个事务所对数据库的操作会永久保存在数据库中,不会回滚;
四种隔离级别:读未提交,读已提交,可重复读,可串行化;
读未提交:就是脏读,事务可以读取其他事务未提交的数据
读已提交:一个事务读取了另一个事务已经提交的数据
可重复读:在一个事务中读取到的数据始终保持一致,无论另一个是否提交
可串行化:同时只能执行一个事务,相当于事务中的单线程;
Tcc事务补偿:核心思想:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作;
Try阶段:主要是对业务系统做检测及资源预留
Confirm阶段:主要对业务系统做确认提交,Try阶段执行成功开始执行Confirm阶段时,默认Confirm阶段是不会出错的,即:只要Try成功,Confirm一定成功;
Cancel阶段:主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放;
TCC事务补偿总结:转账时,首先执行Try阶段,调用远程接口冻结双方的交易资金,在Confirm阶段,执行远程调用的转账操作,转账成功进行解冻,失败再次重试,如果多次失败,为了保证系统的幂等性,最后执行Cancel,此处不是回滚,而是在原来扣除的账户上添加同样的资金;
幂等性是系统的接口对外一种承诺(而不是实现), 承诺只要调用接口成功, 外部多次调用对系统的影响是一致的。(声明为幂等的接口会认为外部调用失败是常态, 并且失败之后必然会有重试。这一重试就是分布式事务冲正(或者叫补偿)的一种方式)