递归的模板
public ListNode reverseList(参数0) {
if (终止条件)
return;
逻辑处理(可能有,也可能没有,具体问题具体分析)
//递归调用
逻辑处理(可能有,也可能没有,具体问题具体分析)
}
leetcode例题
String StringBuffer StringBulider的区别
String每次修改都会创建新的String对象,浪费内存空间
由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。
(1)如果要操作少量的数据用 String;
(2)多线程操作字符串缓冲区下操作大量数据 StringBuffer;
(3)单线程操作字符串缓冲区下操作大量数据 StringBuilder。
二叉查找树
二叉查找树的中序遍历为递增序列
&和&&的区别
Java中&&和&都是表示与的逻辑运算符,都表示逻辑运输符and,当两边的表达式都为true的时候,整个运算结果才为true,否则为false。
&&的短路功能,当第一个表达式的值为false的时候,则不再计算第二个表达式;&则两个表达式都执行。
&可以用作位运算符,当&两边的表达式不是Boolean类型的时候,&表示按位操作
Atomic和volatile对比
Volatile 变量可以确保先行关系,即写操作会发生在后续的读操作之前, 但它并不能保证原子性。例如用 volatile 修饰 count 变量那么 count++ 操作就不是原子性的。
而 AtomicInteger 类提供的 atomic 方法可以让这种操作具有原子性如getAndIncrement()方法会原子性的进行增量操作把当前值加一,其它数据类型和引用变量也可以进行相似操作。
二叉树最近的公共祖先
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。
float的表示范围
float单精度浮点数在计算机占32位,存储在计算机时将32位划分为3个部分,符号位,指数和尾数
符号位占用1位最高位,0表示整数,1表示负数
指数位表示浮点数中的最高位,占有8位,0~255,范围是-127 ~ 128,2^4的话则4位指数部分,也称为阶码,
小数部分将十进制小数转换为二进制所得
代理模式
三大代理静态代理,动态代理,cglib代理
静态代理,通过编写特定的业务代理和额外功能代码去实现代理业务,需要代理对象和目标对象实现一样的接口,代理对象为目标对象的方法进行逻辑的修改
动态代理:利用了JDK API,动态地在内存中构建代理对象,从而实现目标对象的代理功能,因而动态代理又被称为JDK代理或者接口代理,动态代理对象不需要实现目标对象的接口,但要求目标对象必须实现接口,需要实现InvocationHandler.class,并且在invoke方法中重写
1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法
2.创建被代理的类以及接口
3.通过Proxy的静态方法
newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)创建一个代理
4.通过代理调用方法
cglib代理:是一个第三方代码生成类库,运行时在内存中动态生成一个子类对象,从而实现对目标对象功能的扩展
使用动态代理的对象必须实现一个或多个接口
使用cglib代理的对象则无需实现接口,达到代理类无侵入
在JDK动态代理中提供一个Proxy类来创建代理类,而在CGLib动态代理中也提供了一个类似的类Enhancer
SSL如何对Https进行加密
多线程中的CountDownLatch
CountDownLatch是具有synchronized机制的一个工具,目的是让一个或者多个线程等待,直到其他线程的一系列操作完成
利用多线程池写数据库连接池的时候会用到
Java提供了几个类加载器?分别是?怎么对类进行加载的
把实现加载阶段中的通过一个类的全限定名来获取描述此类的二进制字节流这个动作的代码模块称为类加载器
类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。
启动类加载器:负责加载存放在JDK\jre\lib下或被-xbootclasspath参数指定的路径中的,并且能被虚拟机识别的类库,启动类加载器无法被java程序直接引用
扩展类加载器:Extension Classloader负责加载JDK\jre\lib\ext目录中,或者由java.ext.dirs系统变量指定的路径中所有类库,开发者可以直接使用扩展类加载器
应用类加载器:负责加载用户类路径所指定的类,开发者可以直接使用应用类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下就是默认的
类加载的三种方式
1、命令行启动应用时候由JVM初始化加载
2、通过Class.forName()方法动态加载
3、通过ClassLoader.loadClass()方法动态加载
构造类
一个类可以自定义无穷多个构造函数,构造函数是类的一种特殊函数,方法名必须和类名相同,主要工作是完成对类的对象的初始化操作,在创建新对象时,会自动调用构造函数
jdbc连接数据库的步骤
//声明数据库驱动,数据源的url,用于登录数据库的账户和密码(将其他功能封装成方法的时候方便使用)
String driver = "数据库驱动名称";
String url = "数据库连接地址"
String user = "用来连接数据库的用户名";
String pwd = "用来连接数据库的密码";
//加载数据库驱动
Class.forName(driver);
//根据url创建数据库连接对象Connection
Connection con = DriverManage.getConnection(url,user,pwd);
//用数据库连接对象创建Statement对象(或PrepareStatement)
Statement s = con.createStatement();
或
PrepareStatement ps = con.PrepareStatement(sql);
//做数据库的增删改查工作
ResultSet rs = s.executeQuery();
//关闭结果集对象Resultset,statement对象,connection对象,
rs.close();
s.close();
con.close();
//各个步骤的异常处理
多态
public class Person{
private String name = "Person";
int age=0;
}
public class Child extends Person{
public String grade;
public static void main(String[] args){
Person p = new Child();
System.out.println(p.name);
}
}
这段程序中的错误
1.一个java文件里,public 的类只能出现一个,只能出现一个,只能出现一个,否则,不管你用哪一个类名命名文件名编译器都会报错
2.关于多态。子类继承了父类的所有成员,包括private权限的成员变量,但是继承的子类具有私有变量的拥有权但是没有使用权。
3.private的成员变量,根据权限修饰符的访问控制范围,只有在类内部才能被访问,就算是他的子类,也不能访问。
四种线程安全的单例模式
1.饿汉式(线程安全,调用效率高,但是不能延时加载); 2.懒汉式(线程安全,调用效率不高,但是能延时加载); 3.Double CheckLock实现单例:DCL也就是双重锁判断机制(由于JVM底层模型原因,偶尔会出问题,不建议使用); 4.静态内部类实现模式(线程安全,调用效率高,可以延时加载); 5.枚举类(线程安全,调用效率高,不能延时加载,可以天然的防止反射和反序列化调用)
Java关键字
动态规划
leetcode原题
动态规划解析
状态定义:设dp为一维数组,其中dp[i]的值表示斐波那契数列第i个数字
转移方程:dp[i+1]=dp[i]+dp[i-1],即对应数列定义f(n+1)=f(n)+f(n-1)
初始状态:dp[0]=0 dp[1]=1,即初始化前的两个数字
返回值:dp[n]即斐波那契数列的第n个数字
空间复杂度优化
如果新建长度为n的db列表,空间复杂度为O(n)
由于 dp 列表第 i 项只与第 i−1 和第 i−2 项有关,因此只需要初始化三个整形变量 sum, a, b ,利用辅助变量 sum 使 a,b 两数字交替前进即可
节省了 dp 列表空间,因此空间复杂度降至 O(1)
循环求余法
大数越界: 随着 nnn 增大, f(n)f(n)f(n) 会超过 Int32 甚至 Int64 的取值范围,导致最终的返回值错误。
class Solution {
public int fib(int n) {
int constant = 1000000007;
int a=0,b=1,sum;
for(int i=0;i<n;i++){
sum=(a+b)%constant;
a=b;
b=sum;
}
return a;
}
}
判断是否互为字符重排
class Solution {
public boolean CheckPermutation(String s1, String s2) {
if(s1.length()!=s2.length()) return false;
int[] nums = new int[26];
int len = s1.length();
for (int i = 0; i < len; i++) {
nums[s1.charAt(i)-97]++;
nums[s2.charAt(i)-97]--;
}
for (int num : nums) {
if (num != 0) return false;
}
return true;
}
}
线性表和链表的区别
面向过程和面向对象的本质区别
面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了
面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。
MySQL索引结构及其原理
索引是一种数据结果,帮助提高获取数据的速度
大部分数据库系统及文件系统都采用B Tree或者B+ Tree作为索引结构
B-树(B树):多路搜索树,每个结点存储M/2到M个关键字,非叶子结点存储指向关键字范围的子结点;所有关键字在整颗树中出现,且只出现一次,非叶子结点可以命中;
B+树:在B-树基础上,为叶子结点增加链表指针,所有关键字都在叶子结点中出现,非叶子结点作为叶子结点的索引;B+树总是到叶子结点才命中;
B*树:在B+树基础上,为非叶子结点也增加链表指针,将结点的最低利用率从1/2提高到2/3;
使用B Tree的原因
一般来说,索引本身也很大,不可能全存内存,往往以索引文件的形式存在磁盘
(1)单节点能存储更多数据,使得磁盘IO次数更少。
(2)叶子节点形成有序链表,便于执行范围操作。
(3)聚集索引中,叶子节点的data直接包含数据;非聚集索引中,叶子节点存储数据地址的指针。
索引分类
普通索引,唯一索引,联合索引
聚集索引和非聚集索引使用的都是B+树结构
非聚集索引的叶子结点为索引节点,但是有一个指针指向数据节点,MYISAM是非聚集索引
聚集索引叶子节点是数据节点关于聚集索引,innodb会按照如下规则进行处理:
1,如果一个主键被定义了,那么这个主键就是作为聚集索引
2,如果没有主键被定义,那么该表的第一个唯一非空索引被作为聚集索引
3,如果没有主键也没有合适的唯一索引,那么innodb内部会生成一个隐藏的主键作为聚集索引,这个隐藏的主键是一个6个字节的列,改列的值会随着数据的插入自增。
innodb的普通索引,唯一索引,联合索引都是辅助索引,采用非聚集索引结构。InnoDB的所有辅助索引都引用主键作为data域。
聚集索引这种实现方式使得按主键的搜索十分高效,但是辅助索引搜索需要检索两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录。
有效的括号
class Solution {
public boolean isValid(String s) {
LinkedList<Character> stack=new LinkedList<>();
for(char c : s.toCharArray()){
if(c == '[') stack.push(']');
else if (c=='(') stack.push(')');
else if (c=='{') stack.push('}');
else if (stack.isEmpty()||c!=stack.pop()) return false;
}
return stack.isEmpty();
}
}
单调栈
leetcode原题
可以维护一个存储下标的单调栈,从栈底到栈顶的下标对应的温度列表中的温度依次递减,如果一个下标在单调栈里,则表示尚未找到下一次温度更高的下标
正向遍历温度列表。对于温度列表中的每个元素 T[i],如果栈为空,则直接将 i 进栈,如果栈不为空,则比较栈顶元素 prevIndex 对应的温度 T[prevIndex] 和当前温度 T[i],如果 T[i] > T[prevIndex],则将 prevIndex 移除,并将 prevIndex 对应的等待天数赋为 i - prevIndex,重复上述操作直到栈为空或者栈顶元素对应的温度小于等于当前温度,然后将 i 进栈。
class Solution {
public int[] dailyTemperatures(int[] T) {
int length = T.length;
int[] ans = new int[length];
Deque<Integer> stack = new LinkedList<Integer>();
for(int i=0;i<length;i++){
int temperature=T[i];
while(!stack.isEmpty()&&temperature>T[stack.peek()]){
int prevIndex = stack.pop();
ans[prevIndex] = i-prevIndex;
}
stack.push(i);
}
return ans;
}
}
JVM内存的分配
在JVM中内存分为堆内存和栈内存,二者的区别是:当我们创建一个对象时,就会调用对象的构造函数来开辟空间,将对象存储到堆内存中,与此同时在栈内存中生成对应的引用,在后续代码中调用的时候用的都是栈的引用
String对象比较特殊,string赋值在java中叫直接量,并没有和new一样进入堆中, 这种形式的字符串,在JVM内部发生字符串拘留,即当声明这样的一个字符串后,JVM会在常量池中先查找有有没有一个值为"abcd"的对象,如果有,就会把它赋给当前引用.即原来那个引用和现在这个引用指点向了同一对象, 如果没有,则在常量池中新创建一个"abcd",下一次如果有String s1 = “abcd”;又会将s1指向"abcd"这个对象,即以这形式声明的字符串,只要值相等,任何多个引用都指向同一对象.
哈希函数
哈希的过程需要哈希函数进行计算
哈希函数是一种映射关系,根据数据的关键词key,通过一定的函数关系,计算出该元素存储位置的函数,
表示为 address=H[Key]
常见的哈希函数构造方法
- 直接定址法
- 除留余数法
- 数字分析法
- 平方取中法
- 折叠法
- 随机数法
哈希冲突的解决
选用哈希函数计算哈希值时,可能不同的 key 会得到相同的结果,一个地址怎么存放多个数据呢?这就是冲突。
- 链接法
将所有关键字为同义词的结点链接在同一个单链表中,HashMap中用的就是这个 - 开放定址法
用开放定址法解决冲突的做法是:当冲突发生时,使用某种探测技术在散列表中形成一个探测序列。沿此序列逐个单元地查找,直到找到给定的关键字,或者碰到一个开放的地址(即该地址单元为空)为止(若要插入,在探查到开放的地址,则可将待插入的新结点存人该地址单元)。查找时探测到开放的地址则表明表中无待查的关键字,即查找失败
TCP网络编程中RST分节
RST为复位,它是TCP在某些错误情况下所发出的一种TCP分节,
有三个条件可以产生RST
- SYN到达某端口,但此端口上没有在监听的服务器,对于UDP,当一个数据报到达目的端口时,该端口没在使用,它将产生一个ICMP端口不可达的信息,而TCP则使用复位
- TCP想取消一个已有连接
- TCP接受了一个根本不存在的连接上的分节
四种情况会发送RST包:
1、端口未打开
2、请求超时
3、提前关闭
4、在一个已关闭的socket上收到数据
linux文件和目录的权限表示
用rwx这三个字符来代表所有者,用户组和其他用户的权限,或者用数字来表示权限
r:对应数值4
w:对应数值2
x:对应数值1
‘-’ :对应数值0
所以,6就是rw-
4就是r–
5就是r-x
TCP三次握手期间的发送序号和确认序号
TCP第一次握手期间:客户机向服务器发送请求报文段,发送序号为x
TCP第二次握手期间:服务器向客户机发送请求+确认报文段,发送序号为y,确认报文段为x+1
TCP第三次握手期间:客户机向服务器发送确认报文段,发送序号为x+1,确认序号为y+1
抽象类使用原则
- 抽象方法必须为public或protected
- 抽象类不能直接实例化
- 抽象类必须有子类
- 子类必须复写抽象类之中的全部抽象方法
&运算符
两个数都转为二进制,然后从两个数的最高位进行与运算,两个都为真(1),结果才为真(1),否则为假(0)
13:01101
17:10001
结果:00001,既为1
标识符规范
记住标识符只有英文,数字,下划线和$,而且数字不能做开头~
java内部类
每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,
对于内部类都没有影响
内部类可以非常好的解决多重继承的问题
(1)、内部类可以用多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独。
(2)、在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或者继承同一个类。
(3)、创建内部类对象的时刻并不依赖于外围类对象的创建。
(4)、内部类并没有令人迷惑的“is-a”关系,他就是一个独立的实体。
(5)、内部类提供了更好的封装,除了该外围类,其他类都不能访问。
内部类分类
成员内部类
public class Outer{
private int age = 99;
String name = "Coco";
public class Inner{
String name = "Jayden";
public void show(){
System.out.println(Outer.this.name);
System.out.println(name);
System.out.println(age);
}
}
public Inner getInnerClass(){
return new Inner();
}
public static void main(String[] args){
Outer o = new Outer();
Inner in = o.new Inner();
in.show();
}
}
静态内部类
public class Outer{
private int age = 99;
static String name = "Coco";
public static class Inner{
String name = "Jayden";
public void show(){
System.out.println(Outer.name);
System.out.println(name);
}
}
public static void main(String[] args){
Inner i = new Inner();
i.show();
}
}
方法内部类
public class Outer{
public void Show(){
final int a = 25;
int b = 13;
class Inner{
int c = 2;
public void print(){
System.out.println("访问外部类:" + a);
System.out.println("访问内部类:" + c);
}
}
Inner i = new Inner();
i.print();
}
public static void main(String[] args){
Outer o = new Outer();
o.show();
}
}
匿名内部类
public class OuterClass {
public InnerClass getInnerClass(final int num,String str2){
return new InnerClass(){
int number = num + 3;
public int getNumber(){
return number;
}
}; /* 注意:分号不能省 */
}
public static void main(String[] args) {
OuterClass out = new OuterClass();
InnerClass inner = out.getInnerClass(2, "chenssy");
System.out.println(inner.getNumber());
}
}
interface InnerClass {
int getNumber();
}
中间件
中间件是一种独立的系统软件或服务程序,分布式应用软件借助这种软件在不同的技术之间共享资源,中间件位于客户机/ 服务器的操作系统之上,管理计算机资源和网络通讯。是连接两个独立应用程序或独立系统的软件。相连接的系统,即使它们具有不同的接口,但通过中间件相互之间仍能交换信息。执行中间件的一个关键途径是信息传递。通过中间件,应用程序可以工作于多平台或OS环境
throws和throw的区别
1、throws出现在方法头,throw出现在方法体 2、throws表示出现异常的一种可能性,并不一定会发生异常;throw则是抛出了异常,执行throw则一定抛出了某种异常。 3、两者都是消极的异常处理方式,只是抛出或者可能抛出异常,是不会由函数处理,真正的处理异常由它的上层调用处理。
HashMap实现原理
HashMap是基于Hash算法实现的,通过put(key,value)存储,get(key)获取value
当传入 key 时,HashMap 会根据 key,调用 hash(Object key) 方法,计算出 hash 值,根据 hash 值将 value 保存在 Node 对象里,Node 对象保存在数组里
当计算出的 hash 值相同时,称之为 hash 冲突,HashMap 的做法是用链表和红黑树存储相同 hash 值的 value
当 hash 冲突的个数:小于等于 8 使用链表;大于 8 时,使用红黑树解决链表查询慢的问题
HashSet实现原理
HashSet是基于HashMap实现的,HashSet 底层使用HashMap来保存所有元素,
因此HashSet 的实现比较简单,相关HashSet 的操作,基本上都是直接调用底层HashMap的相关方法来完成,HashSet不允许有重复的值,并且元素是无序的。
源码
数组转List,List转数组
数组转 List ,使用 JDK 中 java.util.Arrays 工具类的 asList 方法
List 转数组,使用 List 的toArray方法。无参toArray方法返回Object数组,传入初始化长度的数组对象,返回该对象数组
队列中方法的区别
1、offer()和add()的区别
add()和offer()都是向队列中添加一个元素。但是如果想在一个满的队列中加入一个新元素,调用 add() 方法就会抛出一个 unchecked 异常,而调用 offer() 方法会返回 false。可以据此在程序中进行有效的判断!
2、peek()和element()的区别
peek()和element()都将在不移除的情况下返回队头,但是peek()方法在队列为空时返回null,调用element()方法会抛出NoSuchElementException异常。
3、poll()和remove()的区别
poll()和remove()都将移除并且返回对头,但是在poll()在队列为空时返回null,而remove()会抛出NoSuchElementException异常。
add,element,remove会抛出异常,offer,peek,poll不会抛出异常
迭代器Iterator
首先说一下迭代器模式,它是 Java 中常用的设计模式之一。用于顺序访问集合对象的元素,无需知道集合对象的底层实现。
Iterator 是可以遍历集合的对象,为各种容器提供了公共的操作接口,隔离对容器的遍历操作和底层实现,从而解耦。
缺点是增加新的集合类需要对应增加新的迭代器类,迭代器类与集合类成对增加
list l = new ArrayList();
l.add("aa");
l.add("bb");
l.add("cc");
for (Iterator iter = l.iterator(); iter.hasNext();) {
String str = (String)iter.next();
System.out.println(str);
}
/*迭代器用于while循环
Iterator iter = l.iterator();
while(iter.hasNext()){
String str = (String) iter.next();
System.out.println(str);
}
确保集合不能被修改的方法
不能用final,相当于引用这个集合,集合类的数字还是可以修改
Collections包下的unmodifiableMap方法,通过这个方法返回的map,是不可以修改的。他会报 java.lang.UnsupportedOperationException错。
同理:Collections包也提供了对list和set集合的方法。
Collections.unmodifiableList(List)
Collections.unmodifiableSet(Set)
什么是数据库回表
回表其实是查询的一个步骤,在某些场景下才有,一般数据库建表时,会创建索引,以普通索引为例,创建的索引结构中包含的是聚簇索引的值(一般就是主键id),在根据此普通索引进行查询时,首先会查到普通索引的位置,比如下标是110,那么会从110处取出聚簇值,也就是id值,再拿id值取表中取数据,这就叫回表
什么是聚簇索引,一个表建立后,如果有主键,主键就是默认的聚簇索引,它的特点就是,数据的物理存储顺序和索引顺序一致,一个表建立好了,存储的物理顺序也不会再改变了,所以也说,聚簇索引只有一个。没有主键,就会自动建立
一次编辑
思路是,只有一个地方需要修改,那么不妨定位到不同字符处。有以下两种情况
(1)长度相同:leetcode 与 leetkode。
那么我们需要找到 ‘c’ 和 ‘k’,然后比较 ‘ode’ 和 ‘ode’ 是否相同。
(2)长度不同:leetcode 与 leetode。
我们发现 ‘c’ 和 ‘o’ 不相同,然后比较 ‘ode’ 和 ‘ode’ 是否相同。
作者:luorong
链接:https://leetcode-cn.com/problems/one-away-lcci/solution/zi-fu-chuan-bi-jiao-by-luorong/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public boolean oneEditAway(String first, String second) {
if (first == null || second == null) return false;
int len1 = first.length();
int len2 = second.length();
if (Math.abs(len1 - len2) > 1) return false;
if (len2 > len1) return oneEditAway(second, first);
// 保持第一个比第二个长
for (int i = 0; i < len2; i++){
if (first.charAt(i) != second.charAt(i)){
// 如果是长度相同字符串,那就比较下一个,如果长度不一样,那就从该字符开始进行比较。
return first.substring(i + 1).equals(second.substring(len1 == len2 ? i + 1 : i));
}
}
return true;
}
}
并行和并发的区别
并发:一个处理器可以处理多个任务,逻辑上的同时发生
并行:多个处理器可以处理多个任务,物理上的同时发生
并发
指同一时刻只能够执行一条指令,但是多条指令被快速的进行切换,给人造成了它们同时执行的感觉。但在微观来说,并不同同时进行的,只是划分时间段,分别进行执行。
并行
在同一时刻,有多条指令在多个处理器上同时执行。
守护线程
守护线程(即daemon thread),是个服务线程,准确地来说就是服务其他的线程,这是它的作用——而其他的线程只有一种,那就是用户线程。所以java里线程分2种,
1、守护线程,比如垃圾回收线程,就是最典型的守护线程。
2、用户线程,就是应用程序里的自定义线程。
一般都是用于执行垃圾回收任务
创建线程的Callable和Future方法
- 创建Callable接口的实现类,并实现Call方法,将call方法作为线程执行体,并且有返回值
- 创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
- 使用FutureTask对象作为Thread对象的target创建并启动新线程
- 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
Runnable和Callable的区别
Runnable 接口 run 方法无返回值;Callable 接口 call 方法有返回值,支持泛型
Runnable 接口 run 方法只能抛出运行时异常,且无法捕获处理;Callable 接口 call 方法允许抛出异常,可以获取异常信息
线程的状态
新建,运行,阻塞,等待,超时等待,终止
notify和notifyAll的区别
如果线程调用了对象的 wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁。
当有线程调用了对象的 notifyAll()方法(唤醒所有 wait 线程)或 notify()方法(只随机唤醒一个 wait 线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。也就是说,调用了notify后只要一个线程会由等待池进入锁池,而notifyAll会将该对象等待池内的所有线程移动到锁池中,等待锁竞争
优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用 wait()方法,它才会重新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了 synchronized 代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。
JSP和Servlet的区别
Servlet是一种服务器端的java应用程序,担当客户请求和服务器响应的中间层,Servlet由web服务器加载
JSP:其根本是一个简化的Servlet设计。JSP技术使用Java编程语言编写类XML的tags和scriptlets,来封装产生动态网页的处理逻辑。网页还能通过tags和scriptlets访问存在于服务端的资源的应用逻辑。JSP将网页逻辑与网页设计的显示分离,支持可重用的基于组件的设计,使基于Web的应用程序的开发变得迅速和容易。 JSP(JavaServer Pages)是一种动态页面技术,它的主要目的是将表示逻辑从Servlet中分离出来。
jsp经编译后就变成了servlet,jsp本质就是servlet,jvm只能识别java的类,不能识别jsp代码,web容器将jsp的代码编译成jvm能够识别的java类。
JSP侧重视图,Sevlet主要用于控制逻辑。
Servlet中没有内置对象 。
JSP中的内置对象都是必须通过HttpServletRequest对象,HttpServletResponse对象以及HttpServlet对象得到。
session和cookie的区别
cookie:实际上一小段的文本信息,客户端请求服务器,如果服务器需要记录该用户的状态,就利用response向客户端发送一个cookie
客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,
以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容
cookie 可以让服务端程序跟踪每个客户端的访问,但是每次客户端的访问都必须传回这些Cookie,如果 Cookie 很多,这无形地增加了客户端与服务端的数据传输量
而 Session 的出现正是为了解决这个问题。同一个客户端每次和服务端交互时,不需要每次都传回所有的 Cookie 值,而是只要传回一个 ID,这个 ID 是客户端第一次访问服务器的时候生成的, 而且每个客户端是唯一的。这样每个客户端就有了一个唯一的 ID,客户端只要传回这个 ID 就行了,这个 ID 通常是 NANE 为JSESIONID 的一个 Cookie。
cookie机制:
session机制
Session机制是一种服务端的机制,服务器使用一种类似散列表的结构来保存信息
当程序需要为某个客户端的请求创建一个session的时候,服务器首先检查这个客户端里的请求里是否已包含了一个session标识–sessionID,
如果已经包含一个sessionID,则说明以前已经为此客户端创建过session,服务器就按照sessionID把这个session检索出来使用
如果客户端请求不包含sessionID,则为此客户端创建一个session并且声称一个与此session相关联的sessionID,
sessionID的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串(服务器会自动创建),这个sessionID将被在本次响应中返回给客户端保存。
具体来说cookie机制采用的是在客户端保持状态的方案,而session机制采用的是在服务器端保持状态的方案。同时我们也看到, 由于采用服务器端保持状态的方案在客户端也需要保存一个标识,所以session机制可能需要借助于cookie机制来达到保存标识的目的
cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗,考虑到安全应当使用session
session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用cookie
单个cookie保存的数据不能超过4k,很多浏览器都限制一个站点最多保存20个cookie。
可以将登陆信息等重要信息存放为session。
如何将java对象的详细内容打印下来
可以利用JSONObject和JSONArray.
personJSON = JSONObject.fromObject(person);
然后在System.out.println(personJSON).
防止SQL注入
PreparedStatement:采用预编译语句集,它内置了处理SQL注入的能力,只要使用它的setXXX方法传值即可
使用正则表达式过滤传入的参数
TCP/IP的11种状态
客户端独有的:(1)SYN_SENT (2)FIN_WAIT1 (3)FIN_WAIT2 (4)CLOSING (5)TIME_WAIT 。
服务器独有的:(1)LISTEN (2)SYN_RCVD (3)CLOSE_WAIT (4)LAST_ACK 。
共有的:(1)CLOSED (2)ESTABLISHED 。
为什么三次握手,
因此全双工通信,两端都要确认
Throw和Throws的区别
Throw
作用在方法内,表示抛出具体异常,由方法体内的语句处理。
具体向外抛出的动作,所以它抛出的是一个异常实体类。若执行了Throw一定是抛出了某种异常。
Throws
作用在方法的声明上,表示如果抛出异常,则由该方法的调用者来进行异常处理。
主要的声明这个方法会抛出会抛出某种类型的异常,让它的使用者知道捕获异常的类型。
出现异常是一种可能性,但不一定会发生异常
forward和redirect的区别
forward又叫转发,redirect叫做重定向
区别总结:地址栏,数据共享,应用场景,效率,本质,次数
1)forword是服务器内部的重定向,服务器直接访问目标地址的 url网址,把里面的东西读取出来,但是客户端并不知道,因此用forward的话,客户端浏览器的网址是不会发生变化的。
2)redirect是服务器根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址,所以地址栏显示的是新的地址。
1)由于在整个定向的过程中用的是同一个request,因此forward会将request的信息带到被重定向的jsp或者servlet中使用。即可以共享数据
2)redirect不能共享
1)forword 一般用于用户登录的时候,根据角色转发到相应的模块
2) redirect一般用于用户注销登录时返回主页面或者跳转到其他网站
forword转发是服务器上的行为,而redirect重定向是客户端的行为
forword只有一次请求;而redirect有两次请求
forword效率高,而redirect效率低
TCP和UDP的区别
TCP 是面向连接的,UDP 是面向无连接的
UDP程序结构较简单
TCP 是面向字节流的,UDP 是基于数据报的
TCP 保证数据正确性,UDP 可能丢包
TCP 保证数据顺序,UDP 不保证
UDP不建立连接,只是监听一个地方,有信息发送过来就接受,TCP有三次握手和四次挥手
TCP粘包
发送方发送的多个数据包,到接收方缓冲区首尾相连,粘成一包,被接收
JVM是如何实现synchronize的
JVM中锁有个专门的名字:对象监视器
- 线程状态以及状态转换
当多个线程同时请求某个对象监视器时,对象监视器会设置几种状态用来区分请求的线程
Contention List:所有请求锁的线程将被首先放置到该竞争队列
Entry List:Contention List中那些有资格成为候选人的线程被移到Entry List
Wait Set:那些调用wait方法被阻塞的线程被放置到Wait Set
OnDeck:任何时刻最多只能有一个线程正在竞争锁,该线程称为OnDeck
Owner:获得锁的线程称为Owner
!Owner:释放锁的线程