目录
JavaSE
头指针和头节点的区别
String和StringBuffer、StringBuilder
为什么HashMap的key和value可以为空,而CurrentHashMap的key不可以为空(临时加题)
框架
JavaSE
冒泡排序
(遇小则换位,每一轮确定一个最大值)步骤:
1.定义数组
2.外循环
3.内循环
4.输出数据(设置第三方)
5.打印数据
头指针和头节点的区别
头指针:
--头指针是指链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针
--头指针具有标识作用,所以头指针冠以链表的名字(指针变量的名字)
--无论链表是否为空,头指针均不为空
--头指针是链表的必要元素
头结点:
--头结点是为了操作的统一和方便而设立的,放在第一个元素的结点之前,其数据域一般无意义(但也可以用来存放链表的长度)
--有了头结点,对在第一元素结点前插入结点和删除第一结点起操作与其它结点的操作就统一了
--头结点不一定是链表的必要元素
final的作用
- 修饰类:
表明该类不可被继承,类中的所有成员方法都隐式的被指定为final方法,成员变量则可以定义为final,也可以不定义为final;
- 修饰方法:
用final修饰方法的原因有两个:
- 锁定这个方法,防止任何继承类修改它的含义;
- 提高效率:在方法前面添加final进行修饰可以提高效率,其原理是基于内联/内嵌(inline)机制,它会使你在调用final方法时,直接将方法的主体插入到调用处,从而省去调用函数所花费的开销。但是如果方法过于庞大或者其中有循环的话,这种提高效率的方法可能会失效。
- 修饰变量:
- 如果修饰的是基本类型数据变量,则该变量的值不能发生改变;
- 如果修饰的是引用类型数据变量,则该变量不会内二次初始化;
解释:由于引用类型数据变量被初始化后,其值是一个地址,所以不会被二次初始化,则地址不改变。
String和StringBuffer、StringBuilder
- String 是 final 修饰的,无法被继承。所以 String 不是 Java 的基本数据类型。字符串在 Java 中是不可变的,因此适合在多线程环境下使用。
String 在 Java 中是不可变的,因此每当我们执行字符串拼接操作时,它都会生成一个新的 String 并丢弃旧的 String 以进行垃圾收集。这些重复的操作会在堆中产生大量垃圾冗余。所以 Java 提供了 StringBuffer 和 StringBuilder 类,应该用于字符串操作。
- StringBuffer 和 StringBuilder 是 Java 中的可变对象。
StringBuffer | StringBuilder |
---|---|
线程安全 | 非线程安全 |
同步 | 非同步 |
始于 Java 1.0 | 始于 Java 1.5 |
慢 | 快 |
- 在 Java 1.4 之前,StringBuffer 是字符串操作的唯一选择。但是,它的一个缺点是所有公共方法都是同步的。 StringBuffer 提供线程安全性,但以性能为代价。
- 在大多数情况下,我们不会在多线程环境中使用 String。所以 Java 1.5 引入了一个新类 StringBuilder,公共方法非同步,线程不安全,以此来提高性能,其它与 StringBuffer 类似。
总结:
- String 是不可变的,而 StringBuffer 和 StringBuilder 是可变类。
- StringBuffer 是线程安全和同步的,而 StringBuilder 不是。这就是 StringBuilder 比 StringBuffer 快的原因。
- 字符串连接运算符 (+) 在内部使用 StringBuilder 类。
- 单线程环境中,要使用 StringBuilder。反之,使用 StringBuffer 进行线程安全的操作。
Error与Exception的区别
Error类和Exception类都是继承Throwable类
- Error(错误)是系统中的错误,程序员是不能改变的和处理的,是在程序编译时出现的错误,只能通过修改程序才能修正。一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢等。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和和预防,遇到这样的错误,建议让程序终止。
- Exception(异常)表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。
Exception又分为两类:
- 编译时异常: 需要用try——catch显示的捕获,对于可恢复的异常使用CheckedException。
- 运行时异常(RuntimeException):不需要捕获,对于程序错误(不可恢复)的异常使用RuntimeException。
线程相关
线程的四种创建方式
- 继承Thread类创建线程
- 实现Runnable接口
- 使用Callable接口,有返回值
- 使用线程池例如用Executor框架
sleep和wait区别?
-
wait() 是Object类 的方法,sleep()是 Thread 类的方法。
-
wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用。
-
sleep不需要被唤醒,wait需要
-
sleep方法没有释放锁,而wait方法释放了锁。
共享锁?
- 共享锁(S锁):共享 (S) 用于不更改或不更新数据的操作(只读操作),如 SELECT 语句。
如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获准共享锁的事务只能读数据,不能修改数据。
- 排他锁(X锁):用于数据修改操作,例如 INSERT、UPDATE 或 DELETE。确保不会同时同一资源进行多重更新。
如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的封锁。获准排他锁的事务既能读数据,又能修改数据。
集合相关
Collection集合:
- List集合中元素有序,可存储重复元素
- ArrayList:底层是数组(线程不安全)
- LinkedList:底层是双链表
- Vector:底层是数组(线程安全)
- Set集合元素是无序,不可重复的元素
- HashSet:底层是HashMap,存放、操作都通过内部Map中key列去操作(用hashcode、equal()判断是否重复、若重复就覆盖)
- LinkedHashSet:HashSet子类,存入顺序与遍历顺序相同
- TreeSet:可被排序的Set集合,以自然顺序排序
- HashSet:底层是HashMap,存放、操作都通过内部Map中key列去操作(用hashcode、equal()判断是否重复、若重复就覆盖)
- Queue队列,保持先进先出原则
Collection关系图
Collection | ||||||
List(有序、可重复) | Set(无序、不重复) | Queue | ||||
ArrayList | LinkedList | Vector | HashMap | TreeSet | ||
LinkedHashSet | ||||||
底层 | 动态数组(线程不安全) | 双链表 | 数组(线程安全) | HashMap | ||
适用 | 查询 | 删、改、查 |
Map集合:
双列集合,元素是无序,key——value形式,key不可重复,value可重复
红黑树:比根小的放左边,比根大放右边
- HashMap:底层是Hash表,key——value均可为null(null—null),线程不同步、不安全
- HashTable:底层是Hash表,key——value不可为null,线程同步、安全,使用synchronized
- TreeMap:底层是二叉树,线程不同步,给Map集合的key排序
Map关系图
Map(无序) | |||
HashMap | HashTable | TreeMap | |
底层: | jdk7前:链表+二叉树 jdk7后:链表+数组+红黑树 | 数组+链表 | 二叉树 |
key—value | 均可为null | 均不可为null | |
线程 | 不同步、不安全 | 同步、安全 使用synchronized | 不同步 |
HashMap存储查询、增删改操作一样好使
为什么HashMap的key和value可以为空,而CurrentHashMap的key不可以为空(临时加题)
蒋蒋话的李姐:当一个线程从ConcurrentHashMap获取某个key,如果返回的结果是null的时候。
这个线程无法确认,这个null表示的是确实不存在这个key,还是说存在key,但是value为空。
这种不确定性会造成线程安全性问题,而ConcurrentHashMap本身又是一个线程安全的集合。
本人李姐:当返回value为空时无法判断key是否为空,1是指定key的value为null,2是key为null相应的value也为null
例如:键值对吗,语文——99,数学——100,英语——0(null),null——null
当返回null无法判断它是哪个key
总结:反推一般线程安全的key——value都不为空吗
使用synchronized,使得锁线程同步,同时线程运行效率降低
TCP三次握手,两次不行吗
为了实现可靠传输,发送方和接收方始终需要同步( SYNchronize )序号。 需要注意的是, 序号并不是从 0 开始的, 而是由发送方随机选择的初始序列号 ( Initial Sequence Number, ISN )开始 。 由于 TCP 是一个双向通信协议, 通信双方都有能力发送信息, 并接收响应。 因此, 通信双方都需要随机产生一个初始的序列号, 并且把这个起始值告诉对方。
jsp和servlet
- 联系:
- JSP文件在容器中会转换成Servlet执行。
- JSP是对Servlet的一种高级封装,本质还是Servlet。
- 区别:
- JSP可以很方便地编写或者修改HTML网页而不用去面对大量的println语句。
Cookie与Session区别
Cookie 和 Session都是用来跟踪浏览器用户身份的会话方式,但是两者的应用场景不太一样。
- Cookie 一般用来保存用户信息 。
比如①我们在 Cookie 中保存已经登录过得用户信息,下次访问网站的时候页面可以自动帮你登录的一些基本信息给填了;
②一般的网站都会有保持登录也就是说下次你再访问网站的时候就不需要重新登录了,这是因为用户登录的时候我们可以存放了一个 Token 在 Cookie 中,下次登录的时候只需要根据 Token 值来查找用户即可(为了安全考虑,重新登录一般要将 Token 重写);
③登录一次网站后访问网站其他页面不需要重新登录。
- Session 的主要作用就是通过服务端记录用户的状态。
典型的场景是购物车,当你要添加商品到购物车的时候,系统不知道是哪个用户操作的,因为 HTTP 协议是无状态的。服务端给特定的用户创建特定的 Session 之后就可以标识这个用户并且跟踪这个用户了。
Cookie 数据保存在客户端(浏览器端),Session 数据保存在服务器端。
Cookie 存储在客户端中,而Session存储在服务器上,相对来说 Session 安全性更高。如果要在 Cookie 中存储一些敏感信息,不要直接写入 Cookie 中,最好能将 Cookie 信息加密然后使用到的时候再去服务器端解密。
禁用Cookie使用Session
一般默认情况下,在会话中,服务器存储 session 的 sessionid 是通过 cookie 存到浏览器里。
如果浏览器禁用了 cookie,浏览器请求服务器无法携带 sessionid,服务器无法识别请求中的用户身份,session失效。
但是可以通过其他方法在禁用 cookie 的情况下,可以继续使用session。
- 通过url重写,把 sessionid 作为参数追加的原 url 中,后续的浏览器与服务器交互中携带 sessionid 参数。
- 服务器的返回数据中包含 sessionid,浏览器发送请求时,携带 sessionid 参数。
- 通过 Http 协议其他 header 字段,服务器每次返回时设置该 header 字段信息,浏览器中 js 读取该 header 字段,请求服务器时,js设置携带该 header 字段。
Session的工作原理
- 用户第一次请求服务器时,服务器端会生成一个sessionid
- 服务器端将生成的sessionid返回给客户端,通过set-cookie
- 客户端收到sessionid会将它保存在cookie中,当客户端再次访问服务端时会带上这个sessionid
- 当服务端再次接收到来自客户端的请求时,会先去检查是否存在sessionid,不存在就新建一个sessionid重复1,2的流程,如果存在就去遍历服务端的session文件,找到与这个sessionid相对应的文件,文件中的键值便是sessionid,值为当前用户的一些信息
- 此后的请求都会交换这个 Session ID,进行有状态的会话。
框架
AOP面向切片编程
(AOP—Aspect Oriented Programming)可以说是对OOP(面向对象编程)的补充和完善,面向对象就是将事物的特性和行为抽象为一个对象,如people类有身高、体重、年龄等属性,也有吃饭、睡觉等行为。把这些特性和行为封装成一个类,然后可以统一调用。面向切片也可以举个例子,比如people类有自己的属性和行为,但是有小一部分人生病要去医院看病,看病这个业务逻辑就不属于哪一个类,因为people泛指所有人,所有人不会都看病。AOP就是把医院看病这一个业务逻辑功能抽取出来,然后动态把这个功能切入到需要的方法(或行为)中,需要的才切入,这样便于减少系统的重复代码,降低模块间的耦合度。常用到AOP的就是安全校验、日志操作、事务操作等,给你先定义好,然后在想用的地方用,这样不会影响已经在服务器运行的项目,然后又能注入新功能,灵活。
IOC控制反转
(IOC—Inversion Of Control)是一种设计思想,就是将原本在程序中需要手动创建对象,现在交由Spring管理创建。举个例子,原本我们要在A类中调用B类的方法,就要直接在A中new出B类对象,然后调用B类中的方法,虽然能实现效果,不过存在一个问题,更改需求会对源代码进行修改,这是大忌。现在创建B对象就交给了Spring,在Spring中,B类对象被看成Bean对象(Spring中类就是Bean),这个Bean对象由spring容器进行创建和管理,当我们在配置文件中配置<Bean>下的<property>子元素(类的属性)时,Spring就会自动执行在A中B对象的setter方法(前提要有),这样的话A对象获取B对象中的方法,由主动new,变成被动等Spring创建。主动变被动,就可以理解成控制反转,通俗讲就是“你别动,我(Spring)来做就好”,主动变被动,这样就大大降低了耦合,Spring中全都用这种思想,即依赖类不由程序员实例化,而是通过Spring容器帮我们new指定实例并且将实例注入到需要该对象的类中,Spring通过DI(依赖注入)实现IOC(控制反转)。
Spring作用域
当通过spring容器创建一个Bean实例时,不仅可以完成Bean实例的实例化,还可以为Bean指定特定的作用域。Spring支持如下5种作用域:
-
singleton:单例模式,在整个Spring IoC容器中,使用singleton定义的Bean将只有一个实例
-
prototype:原型模式,每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新的Bean实例
-
request:对于每次HTTP请求,使用request定义的Bean都将产生一个新实例,即每次HTTP请求将会产生不同的Bean实例。只有在Web应用中使用Spring时,该作用域才有效
-
session:对于每次HTTP Session,使用session定义的Bean豆浆产生一个新实例。同样只有在Web应用中使用Spring时,该作用域才有效
-
globalsession:每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。典型情况下,仅在使用portlet context的时候有效。同样只有在Web应用中使用Spring时,该作用域才有效