目录
面向对象三大特性
- 封装: 把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,
- 继承: 使用已经存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能.
子类拥有父类的非私有的属性和方法
子类可以拥有自己的属性的方法
子类可以重写父类的方法 - 多态: 多态性是指允许不同类的对象对同一消息作出响应.
在Java中有两种形式可以实现多态: 继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口中同一方法) - 抽象: 将以类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象.抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么.
==和equals()的区别
- 基本数据类型之间的比较,用==,比较的是他们的值.
- 引用类型之间用==,比较的是他们在内存中存放的内存地址(堆内存地址).
- equals()是Object类中的方法,默认行为是比较对象的地址值,但一般来说意义不大.
- 在一些类库中,比如String/Integer/Date中equals()被重写了,一般都是来比较对象的成员变量值是否相同,而不是比较对内存地址.
hashcode()和equals()的关系
将集合分成若干个存储区域(可以看成一个个桶),每个对象可以计算出一个哈希码,可以根据哈希码分组,每组分别对应某个存储区域,这样一个对象根据它的哈希码就可以分到不同的存储区域(不同的区域)。
所以再比较元素的时候,实际上是先比较hashcode,如果相等了之后才去比较equal方法。
抽象类和接口对比
含有abstract修饰符的类是抽象类,抽象类中可以有抽象方法,也可以有普通方法,接口(interface)也是一种特殊的抽象类,接口中所有的方法都必须是抽象的,接口中的方法定义默认为public abstract类型,接口中的成员变量类型默认为public static final.
相同点:
- 接口和抽象类都不能实例化
- 都位于继承的顶端,用于被其它实现或继承
- 都包含抽i昂方法,其子类都必须重写这些抽象方法
不同点:
抽象类 | 接口 | |
---|---|---|
声明 | abstract | interface |
实现 | 子类使用extends关键字继承抽象类,如果子类不是抽象类,需要提供抽象类中所有的方法的实现 | 子类使用implements关键字来实现接口,需要提供接口中所有声明的方法的实现 |
构造器 | 可以有构造器 | 不能有构造器 |
访问修饰符 | 任意访问修饰符 | 方法默认修饰符public,不允许定义为private和protected |
多继承 | 一个类最多可以继承一个抽象类 | 一个类可以实现多个接口 |
字段声明 | 字段声明可以是任意的 | 字段默认都是static和final的 |
Spring MVC
MVC是一种软件架构设计思想,基于MVC架构将应用软件进行分层设计和实现,可以分为视图层(View),控制层(Controller),模型层(Model),通过分层设计让程序具备更好的灵活性和可可扩展性.因为这样可以将一个复杂应用程序进行简化,实现各司其职,各尽所能
@RequestMapping,@RequestBody(Json转对象),@ResponseBody(对象转Json)
执行流程:
- 用户发请求至前端控制器DispatcherServlet;
- DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handler;
- 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器一并返回给DispatcherServlet;
- DispatcherServlet调用HandlerAdapter处理器适配器;
- HandlerAdapter经过调用集体处理器(Handler);
- Handler执行完成返回ModelAndView;
- HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet;
- DispatcherServlet将ModelAndView返回给ViewResolver视图解析器进行解析;
- ViewResolver解析后返回具体View;
- DispatcherServlet对View进行渲染视图,响应至用户.
Spring
- 轻量级的IoC和容器框架
- 事务的实现方式:编程式,声明式
- 四个隔离级别: 读未提交,读已提交,可重复读,串行化
IOC
- IoC即控制反转,把由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理.
- 负责创建对象,管理对象,装配对象,配置对象,并且管理这些对象的整个生命周期.
优点: 将代码量降到最低,容易测试,实现松耦合.
依赖注入(DI),更加准确的描述了IoC的设计理念.DI就是由容器动态地将某种依赖关系的目标对象实例注入到系统中的各个模块中.
依赖注入实现方式:
- 构造器依赖注入: 构造器依赖注入通过容器触发一个类的构造器来实现,该类有一系列参数,每个参数代表一个对其它类的依赖.
- Setter方法注入: Setter方法植入是容器通过调用无参构造器或无参static工厂方法实例化bean之后,调用该bean的setter方法,即实现了基于setter的依赖注入.
Spring AOP
相对于OOP面向对象编程,OOP允许开发者定义纵向的关系,并适用于定义横向的关系,导致了大量的代码重复.
AOP,面向切面编程,作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的行为和逻辑,抽取并封装成为一个可重用的模块,这个模块称为切面(Aspect),减少重复代码,降低了各模块之间的耦合度,同时提高了系统的可维护性.可用于权限认证/日志/事务处理等.
SpringAOP中的动态代理主要有两种方式: JDK动态代理和CGLIB动态代理:
- JDK动态代理只提供接口的代理,不支持类的代理,核心InvocationHandler接口和Proxy类
- 如果代理类没有实现InvocationHandler接口,AOP会选择使用CGLIB动态代理来实现目标类
Spring切面可以应用5中类型的通知:
- 前置通知(Before): 在目标方法被调用之前调用通知功能
- 后置通知(After): 在目标方法完成之后调用通知,此时不会关心方法的输出是什么
- 返回通知(After-returning): 在目标方法成功执行之后调用的通知
- 异常通知(After-throwing): 在目标方法抛出异常后调用通知
- 环绕通知(Around): 通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为
Spring Boot
SpringBoot容易上手,提升开发效率,开箱即用,远离繁琐的配置,提供了一系列大型项目通用的非业务性功能,没有代码生成,也不需要XML配置,变成了yml文件或者properties文件,避免了大量的Maven导入和各种版本冲突.
优点: 集成Maven,通过注解简化xml文件配置,避免jar包冲突
常用注解: @SpringbootApplication,@ComponentScan(开启组件扫描),@EnableAutoConfiguration(自动装配)
MyBatis的工作原理
MyBatis是一款优秀的持久层框架,底层基于JDBC实现与数据库的交互,并在JDBC操作的基础上做了封装和优化,它支持定制化SQL、存储过程以及高级映射,避免了几乎所有的JDBC代码和手动设置参数以及获取结果集.
- mybatis应用程序通过SqlSessionFactoryBuilder从mybatis-config.xml配置文件(也可以用Java文件配置的方式,需要添加@Configuration)中构建出会话工厂SqlSessionFactory
- SqlSessionFactory的实例直接开启一个会话对象SqlSession,再通过SqlSession实例获得Mapper对象并运行Mapper映射的SQL语句,完成对数据库的CRUD和事务提交,之后关闭SqlSession。
集合
ArrayList和LinkedList的区别:
ArrayList | LinkedList | |
---|---|---|
数据结构 | 动态数组 | 双向链表 |
访问效率 | 随机查询效率高 | 查询较慢,需从前往后依次查找 |
增删(非首尾增删)效率 | 会影响数组内其他数据的下标,效率较低 | 只需考虑前后元素,故效率高 |
内存占用 | 存储数据,占用内存较小 | 除了数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素 |
线程安全 | 不安全 | 不安全 |
ArrayList和Vector的区别:
两个类都实现了List接口(List接口继承了Collection接口),都是有序集合
- 线程安全: Vector使用了Synchronized来实现线程同步,是线程安全的,ArrayList不安全.
- 可以用Collections.sysnchroizedList(new ArrayList<>());或者JUC下的CopyOnWriteArrayList<>();
- 多个线程调用list时,读取的时候是固定的,写入的时候会有覆盖问题,CopyOnWrite可以在写入的时候避免覆盖,造成数据问题
- 在性能方面ArrayList要优于Vector
- 扩容:ArrayList扩容50%,Vector扩容一倍
Set接口
HashSet是基于HashMap实现的,HashSet的值是存放在HashMap的key上,相关HashSet的操作,基本上都是直接调用底层HashMap的相关方法来完成,HashSet不允许有重复值.初始容量为16,扩容1倍.
HashMap与HashTable有什么区别
HashMap | HashTable | |
---|---|---|
线程安全 | 不安全,单线程下建议使用,多线程下建议使用ConcurrentHashMap | 安全,内部方法基本都经过synchronized修饰 |
效率 | 高 | 基本不使用 |
Null | 一个null键,多个null值 | 不可以有null键和值,如果有,会直接抛空指针异常 |
初始容量 | 初始16,扩容变为2倍 | 初始11,扩容2n+1 |
底层数据结构 | JDK1.8以后的HashMap在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,会将链表转化为红黑树,用来减少搜索时间 | 无 |
解决哈希冲突: 哈希冲突是指哈希函数算出来的地址被别的元素占用了
- 开放定址法
- 再哈希法: 同时构造多个不同的哈希函数
- 链地址法
- 建立公共溢出区:将哈希表分为两部分,基本表和溢出表,凡是和基本表发生冲的元素,一律填入溢出表.
遍历Map集合方式
- 在for-each循环中使用entrySet遍历
- 在for-each循环中遍历key或value
- 使用Iterator遍历
- 通过键找值进行遍历(效率低)
SQL语句的优先级
- FROM字句:执行顺序为从后往前、从右到左。数据量较大的表尽量放在后面。
- WHERE字句:执行顺序为自下而上、从右到左。将能过滤掉最大数量记录的条件写在WHERE字句的最右。
- GROUP BY:执行顺序从右往左分组,最好在GROUP BY前使用WHERE将不需要的记录在GROUP BY之前过滤掉
- HAVING字句:消耗资源。尽量避免使用,HAVING会在检索出所有记录之后才对结果进行过滤,需要排序等操作。
- SELECT字句:少用号,尽量使用字段名称,oracle在解析的过程中,通过查询数据字典将号依次转换成所有列名,消耗时间。
- ORDER BY字句:执行顺序从左到右,消耗资源
多表查询
- 内连接(Inner join)
等值连接、不等值连接、自连接 - 外连接(left join/right join)
左外连接: 以左表为主,先查询出左表,按照on后的关联条件匹配右表,没有匹配到的用null填充
右外连接: 以右表为主,先查询出右表,按照on后的关联条件匹配左表,没有匹配到的用null填充
事务的四大特征
- 原子性: 事务是最小的执行单位,不允许分割,事务的原子性确保动作要么全部完成,要么全部不起作用;
- 一致性: 执行事务前后,数据保持一致,多个事务对同一个数据读取的结果事相同的;
- 隔离性: 并发访问数据库时,一个用户的书屋不被其他事务所干扰,各并发事物之间数据库时独立的
- 持久性: 一个事务被提交时候,对数据库中的数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响.
索引
索引是一种特殊的文件,他们包含者对数据表里所有记录的引用指针.通俗来说,索引就相当于目录,方便查找,但也占用物理空间.
优点:
- 可以大大加快数据的检索速度
- 通过使用索引,可以在查询过程中,使用优化隐藏器,提高系统的性能
缺点:
- 创建索引和维护索引需要耗费时间,当对表中的数据进行增删改操作时,索引也需要动态维护,会降低效率
- 索引需要占物理空间
线程创建方式
- 继承Thread类;
- 实现Runnable接口;
- 实现Callable接口;
- 使用Executors工具类创建线程池.
sleep()和wait()的区别
两者都可以暂停线程的执行
- 类的不同: sleep()是Thread线程类的静态方法,wait()是Object类的方法
- 是否释放锁: sleep()不释放锁,wait()释放锁
- 用途: sleep()通常被用于暂停执行,wait()通常被用于线程间交互/通信
- 用法: sleep()方法执行完成后,线程会自动苏醒,wait()方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的notify()或者notifyAll().
- sleep()可在任何地方使用,wait()只可以存在同步方法或同步代码块中使用.
notify()唤醒一个处于等待状态的线程,注意是调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按照优先级.
notifyAll(): 唤醒所有进入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让他们竞争.
start()和run()的区别
每个线程都是通过它对应的run()来完成操作的,run()成为线程体,通过调用start()来启动线程.
start()用于启动线程,run()用于执行线程运行时的代码.run()可以调用多次,但start()只能调用一次.
调用start()方可启动线程并实现成进入就绪状态,而run()只是Thread类的一个普通方法,还得是在主线程里.
Runnable(1.0以后)和Callable(1.5以后)区别
相同点:
- 都是接口
- 都可以编写多线程程序
- 都采用Thread.start()启动线程
主要区别:
- Runnable接口无返回值;Callable接口call方法有返回值,是一个泛型,和Future,futureTask配合可以用来获取异步执行的结果
- Runnable接口润方法只能抛出运行时异常,且无法捕获异常;Callable接口call方法允许抛出异常,可以获取异常信息.
Synchronized和Lock区别
Synchronized | Lock |
---|---|
内置的Java关键字 | 一个Java接口 |
无法判断获取所得的状态 | 可以判断是否获得了锁 |
会自动释放锁 | 必须手动释放,否则会产生死锁 |
没有获得锁就会一直等下去 | 不一定会一直等下去 |
可重入锁,不可以中断,非公平 | 可重入锁,可判断锁,默认非公平(但可以设置) |
适合锁少量的代码同步问题 | 适合锁大量的同步代码 |
JDBC连接数据库的过程
- 注册驱动
- 获取连接
- 获取传输器(statement对象)
- 执行SQL
- 获取结果集
- 关闭资源
String,StringBuffer,StringBuilder操作字符串
String和StringBuffer、StringBuilder的区别在于String声明的是不可变的对象,每次操作都会变成String对象,然后将指针指向新的String对象,而StringBuffer和StringBuilder实在原有对象的基础上进行操作的,所以在经常改变字符串内容的情况下尽量不要使用String.
StringBuffer和StringBuilder的最大区别在于StringBuffer是线程安全的,而StringBuilder是非线程安全的,但StringBuilder的性能却高于StringBuffer,所以在单线程环境下推荐使用StringBuilder,多线程下推荐使用StringBuffer