1.面向对象和面向过程的区别
面向过程关注于功能实现的步骤,面向对象关注于功能实现的行为。
2.面向对象的四大特性
2.1.封装(业务封装在一个对象里,提供给其他类来访问)
2.2继承()
2.3多态(重写和重载)
2.4抽象(抽象类和接口)
3.访问权限
private 同类可见
default 同包可见
public 全局可见
protected同包,子类可见
4.抽象类和接口的区别
相同点:上层抽象,不能实例化,抽象方法
不同点:抽象类中的非抽象方法,接口中的默认方法:继承一个父类,实现多个接口
1.8之后接口可以有一个默认方法 1.9私有方法(其他类不能访问)
5.重载和重写的区别
重载是指一个类中允许出现多个同名函数,参数不同;重写是指子类重新定义父类的方法。
6.构造器是否可以被重写
不允许(本身就不是一个方法)
7.自动装箱于拆箱
自动装箱:Integer a = 129;相当于(Integer a = Integer.valueOf(129);)走的是valueOf()方法,如果在-128到127之间的话就会读取缓存里面的,如果不是这个范围的话,就会new 一个Integer(源码可读出来)
自动拆箱:Integer a = 129;//装箱
int t = a;//拆箱 相当于 int t = a.intValue();
直接return Integer对象中的int value值(源码可读出来)
public static void main(String[] args) {
Integer a = 123;
Integer b = 123;
System.out.println(a == b);//true -128 – 127
Integer c = 1299;
Integer d = 1299;
System.out.println(c == d);//false
}
8.String和StringBuffer和StringBuilder的区别
性能差别:StringBuilder》Stringbuffer》String
线程安全:StringBuffer线程安全,StringBuilder线程不安全
StringBuffer靠synchronized来保证线程安全
9.hashCode和equals的区别
hashCode()方法根据对象地址或者对象值来计算int型的hash码值,equals来比较两个对象或者字符串是否相同,
如果两个对象根据equals()方法比较是相等的,那么两个对象调用hashcode()方法返回的结果必须相等。
如果两个对象根据equals()方法比较是不相等的,那么两个对象调用hashcode()方法返回的结果不一定不相等。
例子:如果对同个对象进行多次赋值并且在每次赋值之后调用hashcode,那么每次的hashcode值肯定相同,说明hashcode进行对象hash值的是地址。
如果String类型的话,每次改变字符串的值,hashcode肯定会变,因为每次的String都是new出来的
10.集合类
Collection下所有子类集合都用于存储Value,Map下所有子类集合都用于存储Key-Value。
ArrayList是线程不安全的,Vector是线程安全的(二者底层都是数组类型结构),LinkedList线程不安全(底层链表类型结构);
ArrayList每次扩容50%,而Vector每次扩容翻倍;
Set集合存储无序的不可重复元素,允许一个null元素。HashSet对象必须定义hashcode()方法,LinkedHashSet具备HashSet的性能,但内部使用链表维护元素的顺序(插入顺序)。TreeSet底层使用树结构维护有序的元素。
HashMap是线程不安全的,可以存储null值null键和;HashTable是线程安全的,不允许存储null值null键;HashTable因为是线程安全的,所以性能低于HashMap。
5.什么是泛型?为什么要使用?泛型擦除?
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
泛型(JDK1.5特性)之前,当方法的参数类型设置为基类,那么可以往方法中传入该基类下任意类型的对象,这样方法就更具有通用性。另外,将方法参数设置为接口,更加方便(可实现多个接口)。
这样存在的问题是,当需要获取一个值的时候,必须强制类型转换。而强制装换类型的时候,容易使用错误的类型转换导致报错。
泛型擦除是指:在Java中使用泛型创建对象时,在编译期间,所有的泛型信息都会被擦除,编译后会变成原始类型。
主要应用在编码阶段,在编译完之后的class文件中是不存在泛型的
6.Java中的异常
IndexOutOfBoundsEecption:元素越界异常;
ArrayIndexOutOfBoundsEecption:多个元素越界异常;
ClassCastException:类型转换异常;
NullPointerException:空指针异常,null对象的应用;
RuntimeException:运行时异常;
IOException:IO操作异常;
ConnectException:连接失败异常;
7.Java中的BIO,NIO,AIO
BIO(同步阻塞IO):一个连接对应一个线程
当有客户端连接请求时,服务端需要启动一个线程进行处理,如果这个连接不做任何处理,会造成不必要的线程开销,可以通过线程池机制改善,从而实现伪异步IO;
NIO(同步非阻塞IO):N个连接对应一个线程
客户端所有的连接请求都会注册到多路复用器上,服务端通过一个多路复用器来处理所有请求。
AIO(异步非阻塞IO):NIO的2.0版本,引入了异步通道的概念,可以实现异步调用。
异步实现方式:通过java.util.concurrent.Future类来表示异步操作的结果;
在执行异步操作的时候传入java.nio.channels。
8.序列化与反序列化
序列化是指将对象的状态信息转换为可以存储或传输的形式的过程,通过序列化可以将对象的状态保存为字节数组,需要的时候再将字节数组反序列化为对象。
9.IO和NIO区别
NIO是Java1.4的新特性,提供了与标准IO不同的工作方式:
标准IO基于字节流和字符流进行操作,而NIO是基于通道(Channel)和缓冲区(Buffer)进行操作,数据从通道读取到缓冲区中,或者从缓冲区写入到通道。
NIO引入了选择器(Selectors)概念,选择器用于监听多个通道的事件(比如:连接打开、可读、可写),因此NIO可以通过一个线程监听多个数据通道。相比标准IO为每一个连接创建一个线程,NIO大大降低了线程创建的资源开销。
多线程
1.多线程的实现方式
通常使用继承Thread类或实现Runnable接口
还可以通过Callable接口实现。
2.线程的状态转换
new 新建线程
Runnable 可运行状态(执行start()方法,CPU决定是否运行)
Blocking 阻塞状态(线程被阻塞于锁)
Waiting 等待、计时等待(等待某些条件成熟)
Stop 终止状态(线程运行结束)
3.sleep和wait的区别
sleep()是线程Thread的方法,而wait()是Object对象的方法。
sleep()不会释放对象锁、wait()会释放对象锁
sleep()可以在任何地方调用,wait()方法之可以在同步方法或同步块中使用。
yield() 当前线程出让cpu占有权,当前线程变成可运行状态。
wait()\notify()\notifyAll()
调用以前,当前线程必须要持有锁,调用它们线程会释放锁,等待通知机制。
notify() 唤醒一个线程(谨慎使用),具体唤醒哪个线程,由CPU决定。
notifyAll() 所有在对象O上wait的线程全部唤醒(应用较多)
4.如何停止一个线程
run方法代码执行完成
线程运行时抛出一个未捕获的异常,跳出线程
通过标志位跳出线程
interrupt() 向需要中断的线程发送停止指令;isInterrupted() 线程检查自己的中断标志位;Thread.interrupted() 将中断标志位复位为false;
不安全方式
Stop() 立刻停止线程,但不会释放线程运行所应用的资源
Suspend() 立刻挂起线程,但不会释放线程运行锁应用的资源,容易造成死锁
5.volatile关键字
在多个线程之间,访问同一个被volatile修饰的对象时,所有线程共享这个对象的值。
但是volatile不是线程安全的(多个线程同时修改这个变量时,最终结果不一定是最后修改的那个值;可以保证线程的可见性,不可以保证操作的原子性)
6.synchronized如何使用
加锁
可以修饰方法或代码块以同步的方式执行(同一时间只会有一个线程执行)
类锁与实例锁本质上是两把锁,类锁锁的是每一个类的class对象。
7.synchronized和Lock的区别
synchronized是一个Java的关键字,Lock是一个接口;
synchronized代码块执行完或线程抛出异常时结束线程,Lock必须显示调用释放锁的方法:unlock();
synchronized修饰的锁其他线程在等待获取锁的阶段,会一直阻塞等待直到得到锁为止(不可中断锁);Lock有多种方式可以获取锁,不一定一直阻塞等待(可中断锁)。
synchronized无法判断锁的状态,Lock可以判断;
synchronized是非公平锁,而Lock可以设置为公平锁;
Lock用法:
lock()(阻塞线程等待获取锁)
lockInterruptibly():可中断(阻塞线程等待获取锁,会响应中断)
tryLock():尝试非阻塞的获取锁(非阻塞方式尝试获取锁,无法获取则返回false)
unlock()
公平锁与非公平锁:
公平锁,先对锁发出获取请求的一定先获得锁。非公平锁则反之(性能更高)。
ReentrantLock(boolean)可选择公平锁或非公平锁,默认使用非公平锁。
锁的可重入:
递归的时候发生锁的重入
synchronized隐式支持锁的重入
ReentrantLock的lock()支持锁的重入
排它锁:同一时刻只有一个线程获取锁;
读写锁:同一时刻运行多个读线程访问,但是只允许一个写线程,写锁会阻塞所有锁。(ReentrantReadWriteLock,相比synchronized速度更快)
Condition接口有何作用?
Condition接口与Lock配合,来实现等待通知机制。
8.什么是线程安全
当多个线程访问某个类时,这个类始终都能表现出正确的行为,那么就称这个类是线程安全的。
9.死锁
当一个锁未被释放,其他线程无法获取锁的时候,程序产生死锁情况。
死锁的两种情况:
线程thread1先获取锁locka,然后在同步块里嵌套竞争锁lockb。而线程thread2先获取锁lockb,然后在同步块里嵌套竞争锁locka。
Lock.unlock()方法的错误使用,导致死锁。
10.Java线程池
什么是线程池?用于管理线程的一个工具。
线程池的作用?限制系统中执行线程的数量;降低资源的消耗、提高响应速度、提高线程的可管理性。
Java常见的线程池:
Executors.newSingleThreadExecutor:单个线程的线程池;
Executors.newFixedThreadExecutor:固定线程数量的线程池;
Executors.newCacheThreadExecutor:可缓存线程;
Executors.newScheduledThreadPool:创建一个定长线程池,支持定时和周期性的执行线程;
并发工具类和并发容器类
常用的并发工具类:
闭锁:CountDownLatch
栅栏:CyclicBarrier
信号量:Semaphore
交换者:Exchanger
CountDownLatch 闭锁允许一个线程或多个线程等待特定情况,同步完成线程中其他任务。
CyclicBarrier和CountDownLatch都可以协同多个线程,让指定数量的线程等待期他所有的线程都满足某些条件之后才继续执行。CyclicBarrier可以重复使用(reset),而CountDownLatch只能够使用一次,如果还需要使用,必须重现new一个CountDownLatch对象。
构造方法CyclicBarrier(int, Runnable) 所有线程达到屏障后,执行Runnable。
Semaphore 型号量用来控制同时访问特定资源的线程数量。
Exchanger 交换者用于在两个线程之间传输数据,被调用后等待另一个线程达到交换点,然后相互交互数据。
常用的并发容器:
ConcurrentHashMap:JDK1.7实现:分段锁;JDK1.8实现:元素(key)锁+链表+红黑树
SkipList:跳表自动随机维护一套索引,用于高效的索引List中的有序数据。
ConcurrentSkipListMap:TreeMap的并发实现
ConcurrentSkipListSet:TreeSet的并发实现
ConcurrentLinkedQueue:LinkedList的并发实现
CopyOnWriteArrayList:写时复制,在添加元素是,复制一个新的容器,在新容器中新增元素;读数据都在Old容器中操作,进行读写分离。数据一致性较弱,适合读多写少的场景。
CopyOnWriteArraySet:同上