抽象类和接口的区别
接口中只能包含抽象方法和静态常量
抽象类中可以有构造方法,成员变量,静态方法,抽象方法和非抽象方法
抽象类中可以没有抽象方法, 但是如果一个类中有抽象方法,那么这个类一定是抽象类
抽象方法不能有方法体
一个类只能继承一个类,但是可以实现多个接口
Java 多态
多态是同一个行为具有多个不同表现形式或形态的能力
多态就是同一个接口,使用不同的实例而执行不同操作
示例:
接口:打印机
实现类1:黑白打印机打印黑白效果
实现类2:彩色打印机打印彩色效果
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
多态的好处:可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。
SVN和git的区别
SVN只能有一个指定中央版本库。当这个中央版本库有问题时,所有工作成员都一起瘫痪
Git每一个开发人员的电脑上都有一个Local Repository,所以即使没有网络也一样可以Commit,查看历史版本记录,创建项 目分支等操作,等网络再次连接上Push到Server端。
最核心的区别Git是分布式的,而Svn不是分布的
AOP(面向切面编程)
package com.example.demoaspect.dy;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
- AOP:动态代理
- 指定程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式.
- 通知方法:
- 前置通知:在目标方法运行之前运行
- 后置通知:在目标方法运行结束运行(无论方法是正常结束还是异常结束)
- 返回通知:在目标方法运行正常返回通知
- 异常通知:在目标方法运行出现异常通知
- 环绕通知:在目标方法运行执行前、后、异常时,都可以通知
*/
@Aspect
@Component
public class LogAspect {
/**
* 抽取公共的切入点表达式
* 1.本类的引用
* 2.其他的切面引用
*/
@Pointcut("execution(* com.example.demoaspect.dy.service.AopService.*(..))")
public void pointCut() {
}
/**
* 前置通知:在目标方法运行之前运行
* @param joinPoint joinPoint
*/
@Before("pointCut()")
public void logStart(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
System.out.println("" + joinPoint.getSignature().getName() + " logStart..." + Arrays.asList(args));
}
/**
* 后置通知:在目标方法运行结束运行(无论方法是正常结束还是异常结束)
*
* @param joinPoint joinPoint
*/
@After("pointCut()")
public void logEnd(JoinPoint joinPoint) {
System.out.println("logEnd...");
}
/**
* 返回通知:在目标方法运行正常返回通知
*
* @param result result
*/
@AfterReturning(value = "pointCut()", returning = "result")
public void logReturn(Object result) {
System.out.println("logReturn...");
}
/**
* 异常通知:在目标方法运行出现异常通知
*
* @param exception 异常
*/
@AfterThrowing(value = "pointCut()", throwing = "exception")
public void logThrown(Exception exception) {
System.out.println("logThrown...");
}
/**
* 环绕通知:手动引导代码执行
*
* @param pdj 可执行体
* @return return
* @throws Throwable Throwable
*/
@Around(value = "pointCut()")
public Object logAround(ProceedingJoinPoint pdj) throws Throwable {
Object result = null;
try {
System.out.println("前置");
result = pdj.proceed();
System.out.println("后置");
} catch (Exception e) {
System.out.println("异常");
}
System.out.println("后置2");
return result;
}
}
什么是反射
Java反射说的是在运行状态中,对于任何一个类,我们都能够知道这个类有哪些方法和属性。
对于任何一个对象,我们都能够对它的方法和属性进行调用。我们把这种动态获取对象信息和调用对象方法的功能称之为反射机制。
队列queue
阻塞
ArrayBlockingQueue 数组存放数据 , lock实现阻塞
LinkedBlockingQueue 单向链表,lock实现阻塞
PriorityBlockingQueue 数组存放数据, lock实现阻塞
DelayQueue 使用优先队列存放数据,每份数据都有过期时间,根据过期时间排优先级
SynchronousQueue SynchronousQueue的put和take是阻塞的,一个线程put,然后阻塞,等待另一个线程take;
或者一个线程take,然后阻塞,等待另一个线程put。
非阻塞
PriorityQueue
ConcurrentLinkedQueue 线程安全完全由CAS操作和队列的算法来保证
对比 PriorityQueue 和 PriorityBlockingQueue
1、PriorityQueue是非线程安全的,PriorityBlockingQueue是线程安全的
2、PriorityBlockingQueue使用重入锁,每一个操作都需要加锁
3、PriorityBlockingQueue扩容时使用了CAS操作
4、两者都使用了堆,算法原理相同
5、PriorityBlockingQueue可以在queue为空时阻塞take操作
集合
list
Vector是一种老的动态数组,是线程同步的,效率很低,一般不赞成使用。
ArrayList,(动态数组)的数据结构
LinkedList,(链表)的数据结构(双向链表结构,也可当作堆栈、队列、双端队列)
当随机访问List时(get和set操作),ArrayList比LinkedList的效率更高,
因为LinkedList是线性的数据存储方式,所以需要移动指针从前往后依次查找。
当对数据进行增加和删除的操作时(add和remove操作),LinkedList比ArrayList的效率更高,
因为ArrayList是数组,所以在其中进行增删操作时,会对操作点之后所有数据的下标索引造成影响,需要进行数据的移动。
set
Set不允许包含相同的元素
比较元素是否相等,先使用hashcode比较,如果不等则不等,如果相等再用equals比较,如果相等则相等
TreeSet有序,不可存储null,
HashSet无序,可以存null,根据元素的hashCode值来决定元素的存储位置
LinkedHashSet,使用链表维护元素的次序,当遍历该集合时候,LinkedHashSet将会以元素的添加顺序访问集合的元素。
LinkedHashSet在迭代访问Set中的全部元素时,性能比HashSet好,但是插入时性能稍微逊色于HashSet。
map
TreeMap默认根据key升序排序,也可以指定排序的比较器,键不能为null
HashMap根据键可以直接获取它的值,具有很快的访问速度,可以使用Collections.synchronizedMap同步
HashTable任一时刻只有一个线程能写Hashtable,导致了Hashtable在写入时会比较慢
LinkedHashMap是hash表和链表的实现,并且依靠着双向链表保证了迭代顺序是插入的顺序
HashMap和HashTable的区别
HashMap允许null值作为key和value,而Hashtable不可以
HashMap不是同步的,而Hashtable是同步的。
IO和NIO
NIO
NIO主要有三大核心部分:Channel(通道),Buffer(缓冲区), Selector(选择区)。
传统IO基于字节流和字符流进行操作,而NIO基于Channel和Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector(选择区)用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个线程可以监听多个数据通道。
通道channel
Channel和传统IO中的Stream很相似。虽然很相似,但是有很大的区别,主要区别为:通道是双向的,通过一个Channel既可以进行读,也可以进行写;而Stream只能进行单向操作,通过一个Stream只能进行读或者写,比如InputStream只能进行读取操作,OutputStream只能进行写操作;
通道是一个对象,通过它可以读取和写入数据,当然了所有数据都通过Buffer对象来处理。我们永远不会将字节直接写入通道中,相反是将数据写入包含一个或者多个字节的缓冲区。同样不会直接从通道中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节。
Channel中最常用的三个类方法就是map、read和write,其中map方法用于将Channel对应的部分或全部数据映射成ByteBuffer,而read或write方法有一系列的重载形式,这些方法用于从Buffer中读取数据或向Buffer中写入数据。
缓冲区buffer
缓冲区实际上是一个容器对象,更直接的说,其实就是一个数组,在NIO库中,所有数据都是用缓冲区处理的。
在读取数据时,它是直接读到缓冲区中的; 在写入数据时,它也是写入到缓冲区中的;
任何时候访问 NIO 中的数据,都是将它放到缓冲区中。
而在面向流I/O系统中,所有数据都是直接写入或者直接将数据读取到Stream对象中
客户端:数据 -> buffer -> channel
服务端:channel -> buffer -> 数据
选择器Selector
Selector类是NIO的核心类,Selector能够检测多个注册的通道上是否有事件发生,如果有事件发生,便获取事件然后针对每个事件进行相应的响应处理。这样一来,只是用一个单线程就可以管理多个通道,也就是管理多个连接。这样使得只有在连接真正有读写事件发生时,才会调用函数来进行读写,就大大地减少了系统开销,并且不必为每个连接都创建一个线程,不用去维护多个线程,并且避免了多线程之间的上下文切换导致的开销。
IO和NIO的区别
1、IO阻塞;NIO非阻塞
2、IO为每个链接创建一个线程;NIO可以由一个线程(或几个线程)负责一批链接
3、IO是面向流的;NIO是面向缓冲区的
IO和NIO的使用场景
1、IO适用于 需要管理同时打开不太多的连接,这些连接会发送大量的数据
2、NIO适用于 需要管理同时打开的成千上万个连接,这些连接每次只是发送少量的数据,采用这种