JAVA 面试知识点

参考:https://www.cnblogs.com/java1024/p/8594784.html

网络考点

  OSI7层协议模型  

  JAVA 面试知识点

JAVA 面试知识点

TCP(传输控制协议)/UDP(用户数据报协议):

TCP和UDP是OSI模型中的运输层中的协议。TCP提供可靠的通信传输,而UDP则常被用于广播和细节控制交给应用的通信传输

参考:https://www.cnblogs.com/steven520213/p/8005258.html

1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接

2、TCP提供可靠的服务(3次握手)。通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;

UDP尽最大努力交付,即不保证可靠交付

TCP通过校验和,重传控制,序号标识,滑动窗口、确认应答实现可靠传输。如丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。

3、UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。

4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信

5、TCP对系统资源要求较多,UDP对系统资源要求较少。

JAVA 面试知识点

TCP滑动窗口:

  RTT:发送一个数据包到收到对应的ACK,所花费的时间

  RTO:重传时间间隔

  TCP使用滑动窗口做流量控制与乱序重排

所谓滑动窗口协议,自己理解有两点:

  1. “窗口”对应的是一段可以被发送者发送的字节序列,其连续的范围称之为“窗口”;

  2. “滑动”则是指这段“允许发送的范围”是可以随着发送的过程而变化的,方式就是按顺序“滑动”。

在引入一个例子来说这个协议之前,我觉得很有必要先了解以下前提:
  -1. TCP协议的两端分别为发送者A和接收者B,由于是全双工协议,因此A和B应该分别维护着一个独立的发送缓冲区和接收缓冲区,由于对等性(A发B收和B发A收),我们以A发送B接收的情况作为例子;
  -2. 发送窗口是发送缓存中的一部分,是可以被TCP协议发送的那部分,其实应用层需要发送的所有数据都被放进了发送者的发送缓冲区;
  -3. 发送窗口中相关的有四个概念:已发送并收到确认的数据(不再发送窗口和发送缓冲区之内)、已发送但未收到确认的数据(位于发送窗口之中)、允许发送但尚未发送的数据以及发送窗口外发送缓冲区内暂时不允许发送的数据;
  -4. 每次成功发送数据之后,发送窗口就会在发送缓冲区中按顺序移动,将新的数据包含到窗口中准备发送;

常见响应代码

JAVA 面试知识点

Ajax的核心对象

  核心对象是XMLHttpRequest,它可以提供不重新加载页面的情况下更新网页,在页面加载后在客户端向服务器请求数据,在页面加载后在服务器端接受数据,在后台向客户端发送数据。XMLHttpRequest 对象提供了对 HTTP 协议的完全的访问,包括做出 POST 和 HEAD 请求以及普通的 GET 请求的能力。XMLHttpRequest 可以同步或异步返回 Web 服务器的响应,并且能以文本或者一个 DOM 文档形式返回内容。尽管名为 XMLHttpRequest,它并不限于和 XML 文档一起使用:它可以接收任何形式的文本文档。XMLHttpRequest 对象是名为 AJAX 的 Web 应用程序架构的一项关键功能

JAVA特性考点

抽象、封装、继承、多态

抽象:父类为子类提供一些属性和行为,子类根据业务需求实现具体的行为。抽象类使用abstract进行修饰,子类要实现所有的父类抽象方法否则子类也是抽象类。

封装:封装可以隐藏类的内部属性,并且对用户隐藏了数据的访问方式,这样可以保护类的内部状态。封装可以防止类中的方法访问属性,防止对象间的交互,提高Java程序的安全性。

    将类封装起来,不可为外部访问,可以防止开发人员浪费不必要的精力。

继承:子类继承父类的属性和行为,并能根据自己的需求扩展出新的属性和行为,提高了代码的可复用性。

多态:不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态;具体的实现方式就是:接口实现,继承父类进行方法重写,同一个类中进行方法重载。

反射:

JAVA反射机制是在运行状态中,

对于任意一个类,都能够知道这个类的所有属性和方法;

对于任意一个对象,都能够调用它的任意一个方法和属性;

这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

参考大佬的文章,写的不错:

https://blog.csdn.net/sinat_38259539/article/details/71799078

异常:

参考:

http://www.cnblogs.com/lulipro/p/7504267.html

Java 异常的基类为 java.lang.Throwable,java.lang.Error 和 java.lang.Exception 继承 Throwable。

错误:Error类以及他的子类的实例,代表了JVM本身的错误。错误不能被程序员通过代码处理,Error很少出现。因此,程序员应该关注Exception为父类的分支下的各种异常类。

异常:Exception以及他的子类,代表程序运行时发送的各种不期望发生的事件。可以被Java异常处理机制使用,是异常处理的核心。

JAVA 面试知识点

非检查异常(unckecked exception):Error 和 RuntimeException 以及他们的子类。javac在编译时,不会提示和发现这样的异常,不要求在程序处理这些异常。所以如果愿意,我们可以编写代码处理(使用try...catch...finally)这样的异常,也可以不处理。对于这些异常,我们应该修正代码,而不是去通过异常处理器处理 。这样的异常发生的原因多半是代码写的有问题。如除0错误ArithmeticException,错误的强制类型转换错误ClassCastException,数组索引越界ArrayIndexOutOfBoundsException,使用了空对象NullPointerException等等。

检查异常(checked exception):除了Error 和 RuntimeException的其它异常。javac强制要求程序员为这样的异常做预备处理工作(使用try...catch...finally或者throws)。在方法中要么用try-catch语句捕获它并处理,要么用throws子句声明抛出它,否则编译不会通过。这样的异常一般是由程序的运行环境导致的。因为程序可能被运行在各种未知的环境下,而程序员无法干预用户如何使用他编写的程序,于是程序员就应该为这样的异常时刻准备着。如SQLException , IOException,ClassNotFoundException 等。

异常处理:在编写代码处理异常时,对于检查异常,有2种不同的处理方式:使用try...catch...finally语句块处理它。或者,在函数签名中使用throws 声明交给函数调用者去解决。

例题:try{} 里有一个 return 语句,那么紧跟在这个 try 后的 finally{} 里的 代码 会不会被执行,什么时候被执行,在 return 前还是后?

答:会执行,在方法返回调用者前执行。Java 允许在 finally 中改变返回值的做法是不好的,因为如果存在 finally 代码块,try 中的 return 语句不会立马返回调用者,而是记录下返回值待 finally 代码块执行完毕之后再向调用者返回其值,然后如果在 finally 中修改了返回值,这会对程序造成很大的困扰。

请写出 5 种常见到的runtime exception。

NullPointerException:当操作一个空引用时会出现此错误。

NumberFormatException:数据格式转换出现问题时出现此异常。

ClassCastException:强制类型转换类型不匹配时出现此异常。

ArrayIndexOutOfBoundsException:数组下标越界,当使用一个不存在的数组下标时出现此异常。

ArithmeticException:数学运行错误时出现此异常

IllegalArgumentException:不合法的参数异常

servlet生命周期:

JAVA 面试知识点

抽象类和接口的区别:

抽象类:

在了解抽象类之前,先来了解一下抽象方法。抽象方法是一种特殊的方法:它只有声明,而没有具体的实现。抽象方法的声明格式为:

abstract void fun();

  抽象方法必须用abstract关键字进行修饰。如果一个类含有抽象方法,则称这个类为抽象类,抽象类必须在类前用abstract关键字修饰。因为抽象类中含有无具体实现的方法,所以不能用抽象类创建对象。抽象类是用来捕捉子类的通用特性的 。它不能被实例化,只能被用作子类的超类。抽象类是被用来创建继承层级里子类的模板。

包含抽象方法的类称为抽象类,但并不意味着抽象类中只能有抽象方法,它和普通类一样,同样可以拥有成员变量和普通的成员方法。注意,抽象类和普通类的主要有三点区别:

  1)抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public。

  2)抽象类不能用来创建对象;

  3)如果一个类继承于一个抽象类,则子类必须实现父类的抽象方法。如果子类没有实现父类的抽象方法,则必须将子类也定义为为abstract类。

  在其他方面,抽象类和普通的类并没有区别。

接口:

在软件工程中,接口泛指供别人调用的方法或者函数,它是对行为的抽象。

在Java中,定一个接口的形式如下:

[public] interface InterfaceName {
}

接口是抽象方法的集合。如果一个类实现了某个接口,那么它就继承了这个接口的抽象方法。这就像契约模式,如果实现了这个接口,那么就必须确保使用这些方法。接口只是一种形式,接口自身不能做任何事情。

  要让一个类遵循某组特定的接口需要使用implements关键字,具体格式如下:

class ClassName implements Interface1,Interface2,[....]{

}

区别:

1.语法层面上的区别

  1)抽象类可以提供成员方法的实现细节,而接口中只能存在public abstract 方法;

  2)抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的;

  3)抽象类可以有静态代码块和静态方法,而接口中不能含有静态代码块以及静态方法;

  4)一个类只能继承一个抽象类,而一个类却可以实现多个接口。

集合考点

JAVA 面试知识点

JAVA 面试知识点

数组,链表,哈希表。各有优劣,顺便提一下,数组连续内存空间,查找速度快,增删慢;链表充分利用了内存,存储空间是不连续的,首尾存储上下一个节点的信息,所以寻址麻烦,查找速度慢,但是增删快;哈希表呢,综合了它们两个的有点,一个哈希表,由数组和链表组成。假设一条链表有1000个节点,现在查找最后一个节点,就得从第一个遍历到最后一个;如果用哈希表,将这条链表分为10组,用一个容量为10数组来存储这10组链表的头结点(a[0] = 0 , a[1] = 100 , a[2] = 200 …)。这样寻址就快了。

参考:https://blog.csdn.net/HHcoco/article/details/53117525

题例:遍历一个List有哪些不同的方式?

List<String> strList = new ArrayList<>();

//使用for-each循环

for(String obj : strList){

  System.out.println(obj);

}

//using iterator迭代(使用迭代器更加线程安全。由于它能够确保,在当前遍历的集合元素被更改的时候。它会抛出ConcurrentModificationException。)

Iterator<String> it = strList.iterator();

while(it.hasNext()){

  String obj = it.next();

  System.out.println(obj);

}

HashMap

参考:http://youzhixueyuan.com/the-underlying-structure-and-principle-of-hashmap.html

从结构实现来讲,HashMap是:数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的,如下如所示。

JAVA 面试知识点

(1) HashMap:它根据键的hashCode值存储数据,大多数情况下可以直接定位到它的值,因而具有很快的访问速度,但遍历顺序却是不确定的。

HashMap最多只允许一条记录的键为null,允许多条记录的值为null。HashMap非线程安全,即任一时刻可以有多个线程同时写HashMap,可能会导致数据的不一致。如果需要满足线程安全,可以用 Collections的synchronizedMap方法使HashMap具有线程安全的能力,或者使用ConcurrentHashMap。

(2) Hashtable:Hashtable是遗留类,很多映射的常用功能与HashMap类似,不同的是它承自Dictionary类,并且是线程安全的,任一时间只有一个线程能写Hashtable,并发性不如ConcurrentHashMap,因为ConcurrentHashMap引入了分段锁。Hashtable不建议在新代码中使用,不需要线程安全的场合可以用HashMap替换,需要线程安全的场合可以用ConcurrentHashMap替换。

(3) LinkedHashMap:LinkedHashMap是HashMap的一个子类,保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的,也可以在构造时带参数,按照访问次序排序。

(4) TreeMap:TreeMap实现SortedMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。如果使用排序的映射,建议使用TreeMap。在使用TreeMap时,key必须实现Comparable接口或者在构造TreeMap传入自定义的Comparator,否则会在运行时抛出java.lang.ClassCastException类型的异常。

hashmap扩容

当hashmap中的元素个数超过数组大小*loadFactor时,就会进行数组扩容,loadFactor的默认值为0.75,也就是说,默认情况下,数组大小为16,那么当hashmap中元素个数超过16*0.75=12的时候,就把数组的大小扩展为2*16=32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作,所以如果我们已经预知hashmap中元素的个数,那么预设元素的个数能够有效的提高hashmap的性能。比如说,我们有1000个元素new HashMap(1000), 但是理论上来讲new HashMap(1024)更合适,不过上面annegu已经说过,即使是1000,hashmap也自动会将其设置为1024。 但是new HashMap(1024)还不是更合适的,因为0.75*1000 < 1000, 也就是说为了让0.75 * size > 1000, 我们必须这样new HashMap(2048)才最合适,既考虑了&的问题,也避免了resize的问题。

 hashmap遍历

  (1) for each map.entrySet()

Map<String, String> map = new HashMap<String, String>();
for (Entry<String, String> entry : map.entrySet()) {
entry.getKey();
entry.getValue();
}

  (2) 显示调用map.entrySet()的集合迭代器

Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, String> entry = iterator.next();
entry.getKey();
entry.getValue();
}

  (3) for each map.keySet(),再调用get获取

Map<String, String> map = new HashMap<String, String>();
for (String key : map.keySet()) {
map.get(key);
}

  (4) for each map.entrySet(),用临时变量保存map.entrySet()

Set<Entry<String, String>> entrySet = map.entrySet();
for (Entry<String, String> entry : entrySet) {
entry.getKey();
entry.getValue();
}

怎么解决哈希冲突

开放定地址法:当冲突发生时,使用某种探查(亦称探测)技术在散列表中形成一个探查(测)序列。

  沿此序列逐个单元地查找,直到找到给定 的关键字,或者碰到一个开放的地址(即该地址单元为空)为止(若要插入,在探查到开放的地址,则可将待插入的新结点存人该地址单元)。

  查找时探查到开放的 地址则表明表中无待查的关键字,即查找失败。

链地址法:将哈希值相同的元素构成一个同义词的单链表,并将单链表的头指针存放在哈希表的第i个单元中,查找、插入和删除主要在同义词链表中进行。

  链表法适用于经常进行插入和删除的情况。

在java中,链接地址法也是HashMap解决哈希冲突的方法之一,jdk1.7完全采用单链表来存储同义词,jdk1.8则采用了一种混合模式,对于链表长度大于8的,会转换为红黑树存储。

HashMap和Hashtable

首先来看HashMap和HashTable,这两兄弟经常被放到一起来比较,那么它们有什么不一样呢?

a.HashMap不是线程安全的;HashTable是线程安全的,其线程安全是通过Sychronize实现。

b.由于上述原因,HashMap效率高于HashTable。

c.HashMap的键可以为null,HashTable不可以。

d.多线程环境下,通常也不是用HashTable,因为效率低。HashMap配合Collections工具类使用实现线程安全。同时还有ConcurrentHashMap可以选择,该类的线程安全是通过Lock的方式实现的,所以效率高于Hashtable。

ArrayList , LinkedList , Vector

a.ArrayList和Vector本质都是用数组实现的,而LinkList是用双链表实现的;所以,Arraylist和Vector在查找效率上比较高,增删效率比较低;LinkedList则正好相反。

b.ArrayList是线程不安全的,Vector是线程安全的,效率肯定没有ArrayList高了。实际中一般也不怎么用Vector,可以自己做线程同步,也可以用Collections配合ArrayList实现线程同步。

HashSet和TreeSet

Set集合类的特点就是可以去重,它们的内部实现都是基于Map的,用的是Map的key.

1、TreeSet 是二差树实现的,Treeset中的数据是自动排好序的,不允许放入null值。

2、HashSet 是哈希表实现的,HashSet中的数据是无序的,可以放入null,但只能放入一个null,两者中的值都不能重复,就如数据库中唯一约束。

3、比较方法基于hascode()方法和equals()方法

Spring考点

spring特性

  IOC 控制反转:是指容器控制程序对象之间的关系,而不是传统实现中,由程序代码直接操控。控制权由应用代码中转到了外部容器,控制权的转移是所谓反转。

          对于Spring而言,就是由Spring来控制对象的生命周期和对象之间的关系;IoC还有另外一个名字——“依赖注入(Dependency Injection)”。

          从名字上理解,所谓依赖注入,即组件之间的依赖关系由容器在运行期决定,即由容器动态地将某种依赖关系注入到组件之中。

  AOP 面向切面编程:利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了 多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。

          所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的 逻辑或责任封装起来,

          比如日志记录,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。

特点:

1.方便解耦,简化开发
通过Spring提供的IoC容器,我们可以将对象之间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。有了Spring,用户不必再为单实例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
2.AOP编程的支持
通过Spring提供的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻松应付。
3.声明事物的支持
在Spring中,我们可以从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活地进行事务的管理,提高开发效率和质量。
4.方便程序的测试
可以用非容器依赖的编程方式进行几乎所有的测试工作,在Spring里,测试不再是昂贵的操作,而是随手可做的事情。例如:Spring对Junit4支持,可以通过注解方便的测试Spring程序。
5.方便集成各种优秀框架
Spring不排斥各种优秀的开源框架,相反,Spring可以降低各种框架的使用难度,Spring提供了对各种优秀框架(如Struts,Hibernate、Hessian、Quartz)等的直接支持。
6.降低Java EE API的使用难度
Spring对很多难用的Java EE API(如JDBC,JavaMail,远程调用等)提供了一个薄薄的封装层,通过Spring的简易封装,这些Java EE API的使用难度大为降低。

JAVA 面试知识点

spring bean生命周期:

1、实例化一个Bean--也就是我们常说的new;

2、按照Spring上下文对实例化的Bean进行配置--也就是IOC注入;

3、如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String)方法,此处传递的就是Spring配置文件中Bean的id值

4、如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(setBeanFactory(BeanFactory)传递的是Spring工厂自身(可以用这个方式来获取其它Bean,只需在Spring配置文件中配置一个普通的Bean就可以);

5、如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文(同样这个方式也可以实现步骤4的内容,但比4更好,因为ApplicationContext是BeanFactory的子接口,有更多的实现方法);

6、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean初始化结束时调用那个的方法,也可以被应用于内存或缓存技术;

7、如果Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法。

8、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法、;

注:以上工作完成以后就可以应用这个Bean了,那这个Bean是一个Singleton的,所以一般情况下我们调用同一个id的Bean会是在内容地址相同的实例,当然在Spring配置文件中也可以配置非Singleton,这里我们不做赘述。

9、当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用那个其实现的destroy()方法;

10、最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。

spring 注解:

题例:@autowired和@resource的区别

1、 @Autowired与@Resource都可以用来装配bean. 都可以写在字段上,或写在setter方法上。

2、 @Autowired默认按类型装配(这个注解是属业spring的),默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用,如下:

@Autowired
@Qualifier("baseDao")
privateBaseDao baseDao;

3、@Resource(这个注解属于J2EE的),默认按照名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行安装名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

@Resource(name="baseDao")
privateBaseDao baseDao;

什么是 @RestController /* @Controller + @ResponseBody*/

@Controller 只是定义了一个控制器类

@RequestMapping 是一个用来处理请求地址映射的注解,可用于类或方法上。

@Responsebody 注解表示该方法的返回的结果直接写入 HTTP 响应正文(ResponseBody)中,一般在异步获取数据时使用,通常是在使用 @RequestMapping 后,返回值通常解析为跳转路径,加上 @Responsebody 后返回结果不会被解析为跳转路径,而是直接写入HTTP 响应正文中。
作用:
该注解用于将Controller的方法返回的对象,通过适当的HttpMessageConverter转换为指定格式后,写入到Response对象的body数据区。
使用时机:
返回的数据不是html标签的页面,而是其他某种格式的数据时(如json、xml等)使用;

Spring 框架中用到了哪些设计模式

Spring框架中使用到了大量的设计模式,下面列举了比较有代表性的:

代理模式—在AOP和remoting中被用的比较多。
单例模式—在spring配置文件中定义的bean默认为单例模式。
模板方法—用来解决代码重复的问题。比如. RestTemplate, JmsTemplateJpaTemplate。
工厂模式—BeanFactory用来创建对象的实例。
适配器–spring aop
装饰器–spring data hashmapper
观察者– spring 时间驱动模型
回调–Spring ResourceLoaderAware回调接口
前端控制器–spring用前端控制器DispatcherServlet对请求进行分发

微服务间的通信:

  1.远程过程调用(REST、RPC、Apache Thrift)

    优点:

  • 简单,常见
  • 因为没有中间件代理,系统更简单
    缺点:
  • 只支持请求/响应的模式,不支持别的,比如通知、请求/异步响应、发布/订阅、发布/异步响应
  • 降低了可用性,因为客户端和服务端在请求过程中必须都是可用的

  2.消息(RabbitMQ、ActiveMQ、kafka)  

    优点:

  • 把客户端和服务端解耦,更松耦合
  • 提高可用性,因为消息中间件缓存了消息,直到消费者可以消费
  • 支持很多通信机制比如通知、请求/异步响应、发布/订阅、发布/异步响应

    缺点:

  • 消息中间件有额外的复杂性

多线程高并发考点

事务:

事务必须服从ACID原则。ACID指的是原子性(atomicity)、一致性(consistency)、隔离性(isolation)和持久性(durability)。
通俗理解,事务其实就是一系列指令的集合。

  • 原子性(Atomicity):事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。
  • 一致性(Consistency):一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏。
  • 隔离性(Isolation):可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。
  • 持久性(Durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。

线程:

线程是操作系统能够进行运算调度的最小单位,线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。

不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。别把它和栈内存搞混,每个线程都拥有单独的栈内存用来存储本地数据。

参考:https://www.cnblogs.com/dolphin0520/p/3958019.html

例题:如何在java中创建线程

java.lang.Thread 类的实例就是一个线程但是它需要调用java.lang.Runnable接口来执行,由于线程类本身就是调用的Runnable接口。所以你可以

1、继承java.lang.Thread 类

2、直接调用Runnable接口来重写run()方法实现线程

3、实现Callable接口通过FutureTask包装器来创建Thread线程

Java不支持类的多重继承,但允许你调用多个接口。所以如果你要继承其他类,当然是调用Runnable接口好于继承Thread类

 线程同步的方法

1.同步方法 2.同步代码块 3.wait与notify 4.使用特殊域变量(volatile)实现线程同步 5.使用重入锁实现线程同步 6.使用局部变量实现线程同步 7.使用阻塞队列实现线程同步

sleep()和wait()有什么区别? 

sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。 
wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。

Thread 类中的start() 和 run() 方法有什么区别?

start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果不一样。当你调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程。

  1.调用star()方法会创建一个新的子线程并启动

  2.run()方法只是Thread的一个普通方法的调用

JAVA 面试知识点

Thread和rRunnabe区别

  Thread是实现了Runnable接口的类,使得run()支持多线程

BIO/NIO

BIO:同步阻塞式IO,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。 
NIO:同步非阻塞式IO,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。 
AIO(NIO.2):异步非阻塞式IO,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。

进程和线程区别:

  说明:

    进程是资源分配的最小单位,线程是CPU调度的最小单位。

    1.运行一个程序会包含一个进程,一个进程至少包含一个线程,每个线程包含一个子任务

    2.所有与进程有关的资源都被记录在PCB中

    3.进程是抢占处理机的调度单位;每个进程对应一个JVM实例,线程属于某个进程,共享其资源(JVM里的堆)

    4.线程只由堆、栈、程序计数器和TCB(线程控制表)组成

    5.java采用单线程编程模型,程序会自动创建主线程,主线程可以创建子线程,原则上要后于子线程完成执行

  区别:

    1.线程不能看做独立应用,而进程可以看做独立应用

    2.进程有独立的地址空间,相互不影响,线程只是进程的不同执行路径

    3.线程没有独立的地址空间,多进程的程序比多进程的程序健壮

    4.进程切换比线程切花开销大

线程池:

线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时重用这些线程而不是新建一个线程。线程池中线程的数量通常完全取决于可用内存数量和应用程序的需求。然而,增加可用线程数量是可能的。线程池中的每个线程都有被分配一个任务,一旦任务已经完成了,线程回到池子中并等待下一次分配任务。

创建线程池的方式

Java通过Executors提供四种线程池,分别为:
newCachedThreadPool 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

参考:https://www.cnblogs.com/ljp-sun/p/6580147.html

线程池优点

1)重用存在的线程,减少对象创建销毁的开销。

2)可有效的控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。

3)提供定时执行、定期执行、单线程、并发数控制等功能。

锁:

在并发编程中,经常遇到多个线程访问同一个 共享资源 ,这时候作为开发者必须考虑如何维护数据一致性,在java中synchronized关键字被常用于维护数据一致性。synchronized机制是给共享资源上锁,只有拿到锁的线程才可以访问共享资源,这样就可以强制使得对共享资源的访问都是顺序的,因为对于共享资源属性访问是必要也是必须的

乐观锁与悲观锁:

悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。再比如Java里面的同步原语synchronized关键字的实现也是悲观锁。

乐观锁:顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。

CAS算法:即compare and swap(比较与交换),是一种有名的无锁算法。无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(Non-blocking Synchronization)。CAS算法涉及到三个操作数

  • 需要读写的内存值 V
  • 进行比较的值 A
  • 拟写入的新值 B

分布式锁的几种实现方式(redis、zookeeper、数据库)

1.基于数据库实现分布式锁:

  在数据库中创建一个表,表中包含方法名等字段,并在方法名字段上创建唯一索引,想要执行某个方法,就使用这个方法名向表中插入数据,成功插入则获取锁,执行完成后删除对应的行数据释放锁。

2.基于缓存实现分布式锁 :

  (1)获取锁的时候,使用setnx加锁,并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过此在释放锁的时候进行判断。

  (2)获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁。

  (3)释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放。

3.基于Zookeeper实现分布式锁:

  ZooKeeper是一个为分布式应用提供一致性服务的开源组件,它内部是一个分层的文件系统目录树结构,规定同一个目录下只能有一个唯一文件名。基于ZooKeeper实现分布式锁的步骤如下:

  (1)创建一个目录mylock;
  (2)线程A想获取锁就在mylock目录下创建临时顺序节点;
  (3)获取mylock目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前线程顺序号最小,获得锁;
  (4)线程B获取所有节点,判断自己不是最小节点,设置监听比自己次小的节点;
  (5)线程A处理完,删除自己的节点,线程B监听到变更事件,判断自己是不是最小的节点,如果是则获得锁。

高并发下的线程安全实现方式:

  互斥同步、非阻塞同步(CAS)、线程局部变量(threadLocal)、wait和notify、java.util.concurrent并发工具包、volatile保证变量的线程安全等

数据库考点

参考:https://blog.csdn.net/albertfly/article/details/51318995

JAVA 面试知识点

数据库存储过程、触发器

  存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,它存储在数据库中,一次编译后永久有效,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。

  触发器(trigger)是SQL server 提供给程序员和数据分析员来保证数据完整性的一种方法,它是与表事件相关的特殊的存储过程,它的执行不是由程序调用,也不是手工启动,而是由事件来触发,比如当对一个表进行操作( insert,delete, update)时就会激活它执行。触发器经常用于加强数据的完整性约束和业务规则等。

MySQL数据结构:

  红黑树(平衡二叉查找树)是每个节点都带有颜色属性的二叉查找树,颜色或红色或黑色。

  在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:

    1. 节点是红色或黑色。
    2. 根节点是黑色。
    3 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
    4. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
 
 (01) 特性(3)中的叶子节点,是只为空(NIL或null)的节点。
 (02) 特性(5),确保没有一条路径会比其他路径长出俩倍。因而,红黑树是相对是接*衡的二叉树。
 
JAVA 面试知识点
 

红黑树左旋

  JAVA 面试知识点

红黑树右旋

JAVA 面试知识点

MySQL数据库索引:

  索引数据结构(B+树)

  B树:

    1.根节点至少包含2个孩子

    2.树中每个节点最多包含有m个孩子(m>=2)

    3.除根节点和叶节点外,其他每个节点至少有ceil(m/2)(ceil取上限的意思)个孩子

    4.所有叶子节点都位于同一层 

    5.关键字信息,左边要比右边小;个数n满足 ceil(m/2)-1 <= n <= m -1;最左边关键字>孩子的所有关键字,最右边的关键字<孩子的所有关键字,中间的处于区间即可

JAVA 面试知识点

  B+树:

    1.非叶子节点的子树指针与关键字个数相同

    2.非叶子节点的子树指针,指向关键字值的子树

    3.非叶子节点仅用来索引,数据都保存在叶子节点中

    4.所有叶子节点均有一个链指针指向下一个叶子节点

  优点:

    1.磁盘读写代价更低(存放的是索引)

    2.查询效率更加稳定(任何关键字的查询必须走一条从根节点到叶子节点的路)

    3.更有利于对数据库的扫描(每个叶子都是用指针链接起来的)

JAVA 面试知识点

 

MySQL存储引擎:

JAVA 面试知识点

InnoDB 事务型数据库的首选引擎,支持事务安全表(ACID),支持行锁定和外键。MySQL 5.5.5 之后,InnoDB 作为默认存储引擎。

MyISAM  它不支持事务,也不支持外键,尤其是访问速度快,对事务完整性没有要求或者以SELECT、INSERT为主的应用基本都可以使用这个引擎来创建表。     

  每个MyISAM在磁盘上存储成3个文件,其中文件名和表名都相同,但是扩展名分别为:

  • .frm(存储表定义)
  • MYD(MYData,存储数据)
  • MYI(MYIndex,存储索引) 

  MyISAM的表还支持3种不同的存储格式:

  • 静态(固定长度)表
  • 动态表
  • 压缩表

  静态表是默认的存储格式。静态表中的字段都是非变长字段,这样每个记录都是固定长度的,这种存储方式的优点是存储非常迅速,容易缓存,出现故障容易恢复;缺点是占用的空间通常比动态表多。静态表在数据存储时会根据列定义的宽度定义补足空格,但是在访问的时候并不会得到这些空格,这些空格在返回给应用之前已经去掉。同时需要注意:在某些情况下可能需要返回字段后的空格,而使用这种格式时后面到空格会被自动处理掉。
  动态表包含变长字段,记录不是固定长度的,这样存储的优点是占用空间较少,但是频繁到更新删除记录会产生碎片,需要定期执行OPTIMIZE TABLE语句或myisamchk -r命令来改善性能,并且出现故障的时候恢复相对比较困难。
  压缩表由myisamchk工具创建,占据非常小的空间,因为每条记录都是被单独压缩的,所以只有非常小的访问开支。

InnoDB 该存储引擎提供了具有提交、回滚和崩溃恢复能力的事务安全。但是对比MyISAM引擎,写的处理效率会差一些,并且会占用更多的磁盘空间以保留数据和索引。

MEMORY 使用存在于内存中的内容来创建表。每个memory表只实际对应一个磁盘文件,格式是.frm。memory类型的表访问非常的快,因为它的数据是放在内存中的,并且默认使用HASH索引,但是一旦服务关闭,表中的数据就会丢失掉。

MERGE Merge存储引擎是一组MyISAM表的组合,这些MyISAM表必须结构完全相同,merge表本身并没有数据,对merge类型的表可以进行查询,更新,删除操作,这些操作实际上是对内部的MyISAM表进行的。

慢SQL优化

Mysql的优化,大体可以分为三部分:索引的优化,sql语句的优化,表的优化

一条 SQL 语句执行的很慢,那是每次执行都很慢呢?还是大多数情况下是正常的,偶尔出现很慢呢?所以我觉得,我们还得分以下两种情况来讨论。

1、大多数情况是正常的,只是偶尔会出现很慢的情况。

  a.数据库在刷新脏页面 

    当我们要往数据库插入一条数据、或者要更新一条数据的时候,我们知道数据库会在内存中把对应字段的数据更新了,但是更新之后,这些更新的字段并不会马上同步持久化到磁盘中去,而是把这些更新的记录写入到 redo log 日记中去,等到空闲的时候,在通过 redo log 里的日记把最新的数据同步到磁盘中去。

    不过,redo log 里的容量是有限的,如果数据库一直很忙,更新又很频繁,这个时候 redo log 很快就会被写满了,这个时候就没办法等到空闲的时候再把数据同步到磁盘的,只能暂停其他操作,全身心来把数据同步到磁盘中去的,而这个时候,就会导致我们平时正常的SQL语句突然执行的很慢,所以说,数据库在在同步数据到磁盘的时候,就有可能导致我们的SQL语句执行的很慢了。

  b.拿不到锁

    我们要执行的这条语句,刚好这条语句涉及到的表,别人在用,并且加锁了,我们拿不到锁,只能慢慢等待别人释放锁了。或者,表没有加锁,但要使用到的某个一行被加锁了。

    如果要判断是否真的在等待锁,我们可以用 show processlist这个命令来查看当前的状态

2、在数据量不变的情况下,这条SQL语句一直以来都执行的很慢。 

sql优化一般步骤概要:

  a.通过 show status 命令了解各种sql的执行频率
  b.定位执行效率较低的sql语句
  c.通过explain分析低效sql的执行计划
  d.通过 show profile 分析sql
  e.通过trace分析 优化器 如何选择执行计划
  f.确定问题并采取相应的优化措施

索引失效的情

1.列与列对比

某个表中,有两列(id和c_id)都建了单独索引,下面这种查询条件不会走索引

select * from test where id=c_id;

这种情况会被认为还不如走全表扫描。

2.存在NULL值条件

3.NOT条件

4.LIKE通配符

5.条件上包括函数

6.复合索引前导列区分大

当复合索引前导列区分小的时候,我们有INDEX SKIP SCAN,当前导列区分度大,且查后导列的时候,前导列的分裂会非常耗资源,执行计划想,还不如全表扫描来的快,然后就索引失效了。

7.数据类型的转换

Redis考点

  redis性能:10W +QPS(每秒内查询次数)

Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。

JAVA 面试知识点

redis为什么能这么快

  1.redis完全基于内存,绝大部分请求是纯粹的内存操作,执行效率高。采用单进程单线程模型的key-value数据库,将数据存储在内存里面,读写数据不会受硬盘I/O速率的限制。

  2.数据结构简单,对数据操作也简单。不使用表,采用键值对数据结构,不需要预定义以及强制关联其他数据。

  3.采用单线程

  4.适用多路I/O复用模型,非阻塞I/O

多路I/O复用模型

  FD(file descriptor)文件描述符:一个打开的文件通过唯一的描述符进行引用,该描述符是打开文件的元数据到文件本身的映射

  传统的阻塞I/O模型:当适用read/write对某一个文件进行读写时,如果当前的FD不可读或不可写,整个redis服务就不会对其他的操作进行响应,导致整个服务不可用

  多路I/O复用模型:如果一个I/O流进来,我们就开启一个进程处理这个I/O流。那么假设现在有一百万个I/O流进来,那我们就需要开启一百万个进程一一对应处理这些I/O流。一百万个进程,你的CPU占有率会多高,这个实现方式及其的不合理。所以人们提出了I/O多路复用这个模型,一个线程,通过记录I/O流的状态来同时管理多个I/O,可以提高服务器的吞吐能力。实现方式采用多路复用函数,epoll/kqueue/evport/select系统调用,监听文件是否可读或可写

redis数据类型

  字符串类型(string),散列类型(hash),列表类型(list),集合类型(set),有序集合类型(zset)用来做基数统计的算法(HyperLogLog,支持存储地理位置信息(Geo)

JAVA 面试知识点

从海量Key里面查询出某一固定前缀的Key   

  如果使用 Keys pattern (*表示区配所有)命令,犹豫一次性输出海量数据会造成服务器卡顿,故使用另一种方式。

  SCAN  cursor [MATCH patten] [COUNT count]

  1.基于游标的迭代器,需要基于上一次的游标延续之前的迭代过程

  2.以0作为游标开始一次新的迭代,直到命令返回游标0完成一次遍历

  3.不保证每次执行都返回某个给定数量的元素,支持模糊查询

  4.一次返回的数量不可控,只能是大概率符合count参数

什么是Redis持久化?Redis有哪几种持久化方式?优缺点是什么?

  持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。

  RDB(快照)持久化:保存某个时间点的全量数据快照(缺省情况情况下,Redis把数据快照存放在磁盘上的二进制文件中,文件名为dump.rdb。可以配置Redis的持久化策略,例如数据集中每N秒钟有超过M次更新,就将数据写入磁盘;或者你可以手工调用命令SAVE或BGSAVE。)

    SAVE:阻塞redis服务器进程,直到RDB文件被创建完成

    BGSAVE:Fork出一个子进程来创建RDB文件,不阻塞服务器进程

  AOF(Append-Only-File)持久化:保存写状态(备份数据库接收到的指令)

    1.记录下除查询以外的所有变更数据库状态的命令

    2.以append的形式追加保存到AOF文件中(增量)

JAVA 面试知识点

  RDB-AOF混合持久化方式:BGSAVE做镜像全量持久化,AOF做增量持久化

 算法考点

冒泡排序

  JAVA 面试知识点

选择排序

  JAVA 面试知识点

插入排序

  JAVA 面试知识点

归并算法

  JAVA 面试知识点

快速排序

  JAVA 面试知识点

堆排序

  JAVA 面试知识点

LINUX考点

linux常用命令

  

上一篇:Pro Aspnet MVC 4读书笔记(4) - Working with Razor


下一篇:转:JAVA中解决Filter过滤掉css,js,图片文件等问题