提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
一.Java
1.面向对象的基本特质是什么?
- 封装:
- 调用程序中的某个方法,获取一个值,至于它执行了什么操作拿到这个值。你不需要知道。
- 用private修饰成员变量,然后用public编写get和set方法,这就是一种封装
- 也保证了数据的安全性和有效性,隐藏了细节。
- 让对象和调用者进行解耦
- 继承(extends):
- 子类继承父类所以功能和成员变量
- 多态:
不同对象调用父类的同一个方法,返回的结果不同,这就是多态。
- 继承
- 重写 (调用的方法是子类重写后的方法)
- 上转型 (父类引用指向子类对象)
2.Arraylist和Linkedlist的区别
- 底层数据结构不同,ArrayList底层基于Object数组实现的 ,LinkedList底层是基于双向链表实现的。(1.7之前是环形列表 )
- 由于底层数据结构不同,他的所适用场景也不同,ArrayList更适合随机查找,LinkedList更适合删除和添加。
- ArrayList和LinkedList都是实现List接口,但LinkedList还实现了Deque接口,可以当作队列使用。
- Arraylist默认添加是从尾部,此时时间复杂度为O(1),扩容策略是扩容到原来的1.5倍。
- Linkedlist默认添加到尾部,时间复杂度为O(1)。
3.Java8新特性(待学习)
待学习
4.==和equals
- ==对于基本数据类型它比较的是值,对于引用类型来说它比较的是对象内存地址
- 两个引用数据类型比较可以使用equals,String数据类型就重写了equals方法。
- equals要注意空指针异常,用常量去比较变量,用不可能为null的比较变量。
5.final的使用
- 如果final被放到了类前,那么这个方法不能被继承。
- 如果final被放到了方法前,那这个方法不能被重写,但可以被重载。
- 如果final放在了静态变量(static)前,有2次赋值机会,声明变量的时候给赋值,如果没有在声明变量的时候给赋值的话,那么可以在静态( static{} )块里给赋值
- 如果final放在了成员变量前,有3次赋值机会,声明期间、非静态代码块、构造器中
- 如果final放在了方法参数前,有1次赋值机会,在方法调用时给赋予参数
- 如果final放在了局部变量前,有2次赋值机会,初始化,和二次赋值。
- 如果final赋给的是基本数据类型的变量值是不能更改的,如果是引用数据类型变量,但引用值的对象其内容是可以修改的。
6.抽象类和接口的区别?
- 抽象类和接口都不能直接实例化。如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
- 抽象类要被子类继承,接口要被类实现。
- 抽象类中可以做方法声明,也可以做方法实现,接口只能做方法声明。
- 抽象类中的变量是普通变量,接口里定义的变量只能是公共的静态的常量、public static final。
- 抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。
- 抽象方法只能声明,不能实现。
- 抽象类里可以没有抽象方法
- 如果—个类里有抽象方法,那么这个类只能是抽象类
- 抽象方法要被实现,所以不能是静态的,也不能是私有的、public、protected和default。
- 接口可以继承接口,并且可多继承接口,但类只能单—继承。
- 接口可以通过匿名内部类实例化。接口是对动作的抽象,抽象类是对根源的抽象。抽象类表示的是,这个对象是什么。而接口表示的是,这个对象能做什么。
7.string stringbuilder stringbuffer的区别
- StringBuilder和StringBuffer非常类似,均代表可变的字符序列,而且方法也一样
- String:不可变字符序列,效率低,但是复用率高。
3.** StringBuffer**:可变字符序列、效率较高(增删)、线程安全 - StringBuilder:可变字符序列、效率最高、线程不安全
8.list和set的区别
- List和Set都是Collection接口从子接口。
- List底层由Object数组实现的,是有序可重复的,可根据索引查询,允许元素重复,可存在多个null。
- HashSet底层由哈希表(HashMap集合)实现的,是无序不可重复,通常不能记住元素的添加顺序,不允许元素重复不支持null,否则返回false。
- 可以使用TreeSet实现有序状态,它实现了Set接口,是非线程安全的,底层由TreeMap(树状结构)实现。
9.Java的异常体系
- Java中的所有异常都来自顶级父级Throwable。
- Throwable下有两个子类:Exception和Error。
- Error是程序无法处理的错误,一旦出现这个错误,则这个程序被迫停止运行。
- Exception不会导致程序停止,又分两个部分RunTimeException程序运行时异常和CheckedException检测异常。
- RunTimeException异常会让当前线程执行失败。(接触最多,一般自定义异常继承这个类)
- CheckedException异常会在查询编译期间报错,会导致程序编译不通过。
10.hashcode和equals
- hashcode()通过算法结合对象的值计算出哈希值。两个相同值不同对象互相比较hashcode(),他们获取到的哈希值一定相等。
- equals用于比较两个对象的值是否相等。
- 在一个数组中用对象哈希值取对象长度的余来决定储存索引位置,用equals来判断 相同索引值 取余的值 和 已经存入的值 比较,判断他们的值是否相等。这样来提高存储后获取的效率。
11.Java的数据类型
- 第一种:基本数据类型
- 第一类:整数型
- byte、short、int、int
- 第二类:浮点型
- float、double
- 第三类:布尔型
- boolean
- 第四类:字符型
- char
- 第一类:整数型
所占大小
一字节占8bit,也就是8个二进制位数占8位。
byte(1字节)、short(2字节)、int(4字节)、int(8字节)
float(4字节)、double(8字节)
boolean(1字节)
char(2字节)
取值范围为[ -2(1字节*8-1) ~ 2(1字节*8-1)-1 ]
默认123的这种直接值被当作int类型看
例如21474836148被当int类型看而它的大小超过了int的取值范围。
所以用21474836148L即可。
二.多线程
1.线程的生命周期
- 新建状态:new 一个Thread之后就是新建状态
- 就绪状态:当被调用的start方法后进入就绪状态,调用start方法之后,Java虚拟机就会为其创建线程栈和程序计数器,然后就会等待调度运行了
- 运行状态:获取CPU时间片进入运行状态,占用CUP开始运行,时间片用完,进入就绪状态,在获取时间片。期间可能进入阻塞状态。
- 阻塞状态:
- 等待阻塞:运行的线程执行o.wait()方法,JVM会把该线程放入等待队列中
- 同步阻塞:运行的在获取对象的同步锁时,该锁被其他线程占用了,JVM则会吧该线程放入锁池中。
- 其他阻塞:运行线程上执行Thread.sleep()或t.join()方法,或者发出I/O请求时,JVN会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或超时、或者I/O处理完毕时,线程重新转入可运行状态。
- 死亡状态:当线程调用stop()或run、main方法结束或异常抛出之后,就进入死亡状态。
2.线程的sleep、wait、join、yield如何使用?
- sleep()方法:让当前线程在睡眠指定时间,谁调用谁睡眠,睡眠期间不会释放锁资源。
- wait()方法:导致当前线程等待。会释放掉它所占用的锁资源,可以通过其他前程调用该对象的notify或notifyAll方法唤醒,
当前线程必须有当前对象锁,否则调用wait()会抛出异常。
- join()方法:线程之间的协程方式,在线程中调用b线程的.join()方法,则线程进入其他阻塞状态,需等待b线程执行完毕后唤醒。
- **yield()方法 **:暂停当前正在执行的线程对象,yield()方法将当前线程重新回到就绪状态,可能刚暂停又立马被执行了。
3.守护线程的理解
- 守护线程创建:在创建守护线程时,需要主动都要用setDaemon()方法并设置参数为true
- 生命周期:当一个进程中只要还有一个用户进程还在运行,那么这个进程就不会结束,但是守护线程不是用户线程,所有即时守护线程还在执行,但用户线程没有了,程序就会直接结束。所有守护线程的生命周期依赖于用户线程。
- 不建议场景:线程池或IO的任务场景,因为如果正在执行这些操作时,用户线程全部执行完毕,进程不会等待守护线程执行完直接结束,可能导致任务未完成或资源没用即时释放的问题。
三.JVM(Java虚拟机)
1.jre jdk jvm的区别
- JVM: Java虚拟机 是Java跨平台的关键,屏蔽了Java在不同操作系统的差异,让相同的Java程序在不同的环境运行出相同的结果
- JRE: Java运行时环境 运行Java已编译程序所必须的软件环境(JVM、Java标准类库)
- JDK: Java开发工具包包含了JRE、编译器以及调试分析的工具软件
- 关系:JDK包含JRE,JRE又包含了JVM(如果只想运行Java程序,只需安装JRE)
2.什么是字节码?
- 字节码是一个Java虚拟机可执行文件的一种格式
- 在字节码的前8个字节是cafe babe(咖啡宝贝),JVM会查看这个开头,通过这个开头判断该文件是否是.class文件
3.什么Java类加载器?
Java类加载器在JVM中,用于加载字节码文件的加载器,Java类加载器总共被分为:
- 启动类加载器:引用类加载器,是由C/C++语言实现的。加载包名为Java、Javax、sun等开头的类。(扩展类加载器和系统类加载器都是由它加载的)
- 扩展类加载器:从Java.ext.dirs系统属性所指定的目录中加载类库,或从JDK的安装目录的jre/lib/ext子目录下加载类库,如果用户创建的jar放在此目录下,也会主动被扩展类加载器加载。(加载核心包之外的扩展类型的包)
- 系统类加载器:它的父类加载器是扩展类加载器,负责加载classpath或系统属性、java.class.path指定路径下的类库,我们程序中写的类默认都是用系统类加载器来完成加载的。
- 自定义加载器:
无
4.双亲委派机制是什么?
- 概念:
- 双亲委派机制是在java1.2后引入的,其工作原理是,如果一个类加载器接收到了类加载请求,不会自己先加载,而是把这个请求委托给父类加载器去执行,如果父类还有父类那么会再次将请求委托给它的父类加载器,依次递归,请求最终将到达顶层的启动类加载器,如果父类可以加载则加载成功,如果加载不了则让子类去加载,这就是双亲委派机制。
- 双亲委派机制的好处:
- 每一个类都只会被加载一次,避免了重复加载。
- 每一个类都会被尽可能的加载
- 有效的避免了某些恶意类的加载
四.Spring
1.什么是Spring?
Spring是一个Java企业级应用的开源的开发框架。Spring框架目标是简化Java企业级应用开发。
Spring的优点:
- 通过控制反转和依赖注入实现 松耦合。
- 支持 面向切面 的编程,并且把应用业务逻辑和系统服务分开。
- 通过切面和面板减少样板式代码。
- 声明式事务的支持。可从单调繁冗的事务管理代码中解脱出来,通过声明式方式灵活地进行事务管理,提高开发效率和质量。
2.如何实现IOC容器
l0C(lnversion of Control),意思是控制反转,不是什么技术,而是一种设计思想,10C意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。
在传统的程序设计中,我们直接在对象内部通过new进行对象创建,是程序主动去创建依赖对象,而IOC是有专门的容器来进行对象的创建,即IOC容器来控制对象的创建
在传统的应用程序中,我们是在对象中主动控制去直接获取依赖对象,这个是正转,反转是由容器来帮忙创建及注入依赖对象,在这个过程过程中,由容器帮我们查找级注入依赖对象,对象只是被动的接受依赖对象.
- 先准备一个基本的容器对象,包含一些map结构的集合,用来方便后续过程中存储具体的对象
- 进行配置文件的读取工作或者注解的解析工作,将需要创建的bean对象都封装成BeanDefinition对象存储在容器中
- 容器将封装好的BeanDefinition对象通过反射的方式进行实例化,完成对象的实例化工作后,进行对象的初始化操作,也就是给类中的对应属性值就行设置,也就是进行依赖注入,完成整个对象的创建,变成一个完整的bean对象,存储在容器的某个map结构中建,
- 通过容器对象来获取对象,进行对象的获取和逻辑处理工作
- 提供销毁操作,当对象不用或者容器关闭的时候,将无用的对象进行销毁
3.AOP的理解?
AOP面向切面编程,它是为解耦而生,AOP在业务类的隔离上,绝对做到了解耦
任何一个系统都是由不同的组件组成的,每个组件负责一块特定的功能,当然会存在很多组件是跟业务无关的,例如日志、事务、权限等核心服务组件,这些核心服务组件经常融入到具体的业务逻辑中,如果我们为每一个具体业务逻辑操作都添加这样的代码,很明显代码几余太多
因此我们需要将这些公共的代码逻辑抽象出来变成一个切面,然后注入到目标对象(具体业务)中去
AOP正是基于这样的一个思路实现的,通过动态代理的方式,将需要注入切面的对象进行代理,在进行调用的时候,将公共的逻辑直接添加进去,而不需要修改原有业务的逻辑代码,只需要在原来的业务逻辑基础之上做一些增强功能即可。
Mybatis
1.#{}和${}的区别
相同点:都能取到变量的值。
区别:
${}会sql注入,而#{}不会,因为#{}使用预编译的方式将要插入数据的位置用’?'代替 防止sql注入
#{}可以实现预编译预编译过程中mybatis主要做了数据类型检测和安全检查两部分
数据类型检测:判断数据类型,如果是数值类型,那么就不加引号,即?, 如果是字符串类型,就加上引号,即’?'。
安全检查:若变量的值带有引号,会对引号进行转义处理,这样可以防止sql注入.
应用场景:
**${}**使用在sql映射文件动态拼接sql时的场景下运用,在传入多个参数进行条件查询。或传入一个pojo进行数据插入
**#{}**适用于动态插入sql语句。也可以用#{sql}代替整个查询语句。有sql注入风险。