java常见经典面试题
- 1.java三大特性:
封装:把描述一个对象的属性和行为的代码封账在一个模块中,也就是一个类中的行为就是封装。
继承:子类继承父类的特征和行为。子类可以拥有父类的全部属性和方法,子类也可以对父类进行拓展。也可以重写父类的方法。
多态:指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定的,比如:
比如向上转型,重载,方法的覆盖(重写)
重载就是:方法名相同,参数列表不同,与返回值无关
重写发生在子类与父类中:override
- 2.String、StringBuffer、StringBuilder的区别?
String是final类型的,一经定义不允许变更。
对于字符串拼接可变,可使用StringBuffer与StringBuilder来进行操作。
但是StringBuffer性能差于StringBuilder,是因为StringBuffer是线程安全的,它所提供的方法都加了锁(Synchronized),不允许有多个对象同时去对一个字符串进行操作。
三者效率:StringBuilder>StringBuffer>String - 3.java中的容器-集合与图:
集合:Connection包含于:list、set、queue
List集合可分为:ArrayList、LinkedList、vector 有序可重复
ArrayList:基于数组的形式实现的,特点:查找快,增加删除慢
因为ArrayList是基于数组形式的,每次插入元素时要把之前插入的元素向后移动一位,删除一位元素时要把删除的元素位置补上去,导致其他元素的索引位置改变,因为增加删除慢,查找快,增加删除快。
LinkedList:基与链表的形式去实现的额,特点: 增删快,查找慢
因为LinkedList是基于链表的形式去实现的,每次增加元素时会把当前元素保存,然后与前面添加的元素进行连接,所以插入与删除很快,每当插入或者删除时,只需要改变下个连接的对象即可,无需去变动其他元素的位置。
Vector:基于数组的形式实现的,是一个线程安全的集合,查找快,增加删除慢(与ArrayList类似,但时是线程安全的)。
Set集合可分为:HashSet,TreeSet,LinkedHashSet 无序不重复
HashSet:基于散列表的形式实现的,具体的是由一个元素是单向链表的数组组成。hash 算法 主要依赖于hashcode与equals方法,如果hashcode 的值相等,则代表是重复值,不进行存储,不相等,则去存储
TreeSet: 基于二叉树的形式实现的,根据比较的返回值是否是0来决定。 如何保证元素的排序的呢?
A:自然排序 元素具备比较性
让集合中被存储的元素所属的类实现Comparable接口。
B:比较器排序 集合具备比较性
在创建集合对象的时候,让构造方法接收一个Comparator接口的子类对象。
LinkedHashSet:技术数组与哈希表的形式实现,由链表保证有序。由哈希表保证唯一。
queue:数组,先进先出,尾部进,头部出。
MAP集合可以分为HashMap,HashTable 以key-value 的形式存储
HashMap:基于数组与链表的形式实现。可以允许存在一个null的key,非线程安全的,链表长度大于8使用红黑树。
负载因子:0.75 初始化大小:16,最大长度:2^30,每次扩容2,允许null键和null值 HashTable:基于数组+链表的形式实现。不允许存在nul的key与null的value,属于线程安全的。
不允许null键和null值
负载因子:0.75,初始化大小:11,最大长度无限,每次扩容162+1,使用hash算法
concurrentHashMap:是HashTable的替代,比HashTable的扩展性能更好
-
4.IO流:
字节流:InputStream,OutputStream,FileInputStream,FileOutputStream 传递的是字节(二进制文件),比如拷贝文件
字符流:Write,Read,FileWrite,FileRead 传递的是字符
高效字节流:BufferedInputStream、BufferedOutputStream
高效字符流:BufferedWriter、BufferedReader -
5.什么是java序列化,如何实现java序列化?
Java对象的序列化指将一个java对象写入OI流中,与此对应的是,对象的反序列化则从IO流中恢复该java对象。如果要让某个对象支持序列化机制,则必须让它的类是可序列化的,为了让某个类是可序列化的,该类必须实现Serializable接口或Externalizable接口。
-
6.线程
线程创建的方式:
- 1.继承Thread类 - 2.实现Runnable接口 - 3.实现Callable接口,获取一个future对象(有返回任务的线程创建)
线程的生命周期:
创建:新建状态,由jvm进行分配内存,初始化其成员变量
就绪:调用了start方法
运行:处于就绪状态的线程获得了CPU,开始执行run()方法的线程执行体
阻塞:进入了可运行状态,有机会获得CPU的分配进行run()。
结束:正常运行结束或者调用stop()方法,以及异常终止。
阻塞状态分三种:
1.等待阻塞:wait(),调用了wait()方法时,jvm会该线程放入等待队列中。
wait() 属于Object
2.同步阻塞:运行中的线程在获取对象的同步锁时,若该同步锁被其他的线程占用,则jvm会把该线程放入锁池中
3.其他阻塞:sleep()/join(),当线程执行Thread.sleep()方法时或者Thread.join()方法,后者IO读取中,jvm会把线程设置为阻塞状态,当sleep()状态超时,join等待线程终止或者超时,IO处理完毕时,线程重新转入可运行状态。
死亡分为三种情景:
1.正常结束:run()或者call()方法执行完毕时,线程正常结束。
2.异常结束:线程抛出一个未捕获的异常结束。
3.调用stop()方法停止:可能会导致死锁,不推荐使用。
-
7.锁:
1.乐观锁
读多写少,默认认为别人不会修改,并发量低,故不会上锁,在更新之前会判断一下在此期间有没有别人去改动过数据, 采取在写的时候先读取当前版本号,然后加锁,更新时判断当前的值跟传入的值是否一致,一致则去更新,否则不去更新。
2.悲观锁
悲观思想,写多读少,遇到并发写的可能性高,每次去读写数据都会上锁,这样别人在去修改的时候就会block,直到拿到锁, java中的悲观锁如synchronized,一般都是先去获取锁,拿不到才会转为悲观锁。
3.自旋锁
如果持有锁的线程在很短的时间能释放资源,那么那些等待竞争的线程就不需要做内核态和用户之间的切换进入阻塞挂起状态, 他们只需要等一等(自旋),等持有锁的线程释放锁后立即获取锁,这样就可以避免在用户线程和内核的切换的消耗。
4.synchronized同步锁:
synchronized可以把任意一个非null的对象当做锁,他属于独占式的悲观锁,同时属于可重入锁。
synchronized的作用范围:
1.作用于普通方法(锁住的是对象的示例this),
2.作用于静态方法,锁住的是class实例,会锁住所有调用改方法的线程
3.作用于对象
-
8.线程相关的基本方法:
wait(): 调用该方法线程进入等待状态,只有等待另外线程的通知或被中断才会返回,需要注意的调用wait()方法后,会释放对象的锁。因为,wait()一般在同步方法或者同步代码块中。
sleep(): sleep()导致当前线程休眠,与wait()方法不同的时,sleep()不会释放当前占有的锁,sleep()会导致线程进入timed-waiting 状态,知道等待时间结束。
join(): 等待其他线程终止
notyify(): 唤醒正在wait()的线程
notyifyAll():
yield(): yield()会使当前线程让出cpu执行时间片,与其他线程一起重新争取cpu时间片。
interrpt(): 中断一个线程,其本意是给这个线程一个通知信号,会影响这个线程内部的中断标识位,这个线程本身并不会因此而改变状态。 -
9.java线程池工作原理:
创建一个线程池,刚来时里面什么任务都没有,也没有线程。
任务队列作为参数传进来,不过就算此时队列里面有任务,线程池也不会马上去执行它们。
当调用execute()方法添加任务时,线程池会做出判断,
如果正在运行的线程数小于curePoolSize,那么马上创建这个线程运行这个任务。
如果正在运行的线程数量大于或等于curePoolSize,那么将这个任务放入队列。
如果这时候队列满了,而且正在运行的线程数量少于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务
如果队列满了,而且正在运行的线程数量大于或等于maximumPlloSize,那么线程池会抛出异常。
当一个线程的任务完成是,线程池会从对列中取下一个任务来执行
当一个线程无事可做,超过一定的时间时,线程池会判断,如果当前运行的线程数量大于corePoolSize,那么这个线程就会停掉。 所以线程池的所有任务完成后,他会收缩到corePoolSize的大小。
-
10.Spring依赖注入的四种方式:
构造注入
setter注入
静态工厂注入
示例工厂
- 11.5种不同的自动装配
- no:默认的方式是不进行自动装配,通过显式设置 ref 属性来进行装配。
- byName:通过参数名 自动装配,Spring 容器在配置文件中发现 bean 的 autowire 属性被设置成 byname,之后容器试图匹配、装配和该 bean 的属性具有相同名字的 bean。
- byType:通过参数类型自动装配,Spring 容器在配置文件中发现 bean 的 autowire 属性被设置成 byType,之后容器试图匹配、装配和该 bean 的属性具有相同类型的 bean。如果有多个 bean 符合条件,则抛出错误。
- constructor:这个方式类似于 byType, 但是要提供给构造器参数,如果没有确定的带参数的构造器参数类型,将会抛出异常。
- autodetect:首先尝试使用 constructor 来自动装配,如果无法工作,则使用 byType 方式。
- 12.AOP 面向切面编程:
事务、拦截器、权限
两种动态代理:JDK cgLib