目录
知识点1:LinkList和ArrayList有什么区别?
1、共同点
2、不同点
知识点2:HashMap的底层原理,取模跟哈希值冲突了怎么办?
知识点3:java中static应用的场景
知识点4:key为null是如何存入到hashMap中的
知识点5:TCP传送数据如何保证可靠性
知识点6:java常用的异常
知识点7:String、StringBuilder、StringBuffer的区别
知识点8:JDK 和 JRE 有什么区别?
知识点9:== 和 equals 的区别是什么?
知识点10:两个对象的 hashCode()相同,则 equals()也一定为 true,对吗?
知识点11:final 在 java 中有什么作用?
知识点12:java 中的 Math.round(-1.5) 等于多少?
知识点13:String 属于基础的数据类型吗?
知识点14:String str="i"与 String str=new String("i")一样吗?
知识点15:如何将字符串反转?
知识点16:String 类的常用方法都有那些?
知识点17:抽象类必须要有抽象方法吗?
知识点18:普通类和抽象类有哪些区别?
知识点19:抽象类能使用 final 修饰吗?
知识点20:接口和抽象类有什么区别?
知识点21:java 中 IO 流分为几种?
知识点22:BIO、NIO、AIO 有什么区别?
知识点23:Files的常用方法都有哪些?
知识点24:Collection 和 Collections 有什么区别?
知识点25:HashMap 和 Hashtable 有什么区别?
知识点26:如何决定使用 HashMap 还是 TreeMap?
知识点27:说一下 HashMap 的实现原理?
知识点28:说一下 HashSet 的实现原理?
知识点29:ArrayList 和 LinkedList 的区别是什么?
知识点30:如何实现数组和 List 之间的转换?
知识点31:ArrayList 和 Vector 的区别是什么?
知识点32:Array 和 ArrayList 有何区别?
知识点33:在 Queue 中 poll()和 remove()有什么区别?
知识点34:哪些集合类是线程安全的?
知识点35:迭代器 Iterator 是什么?
知识点36:Iterator 怎么使用?有什么特点?
知识点37:Iterator 和 ListIterator 有什么区别?
知识点38:HashSet与TreeSet的比较
知识点39:HashMap和ConcurrentHashMap的区别
知识点40:什么是反射?
知识点41:什么是 java 序列化?什么情况下需要序列化?
知识点42:动态代理是什么?有哪些应用?
知识点43:怎么实现动态代理?
知识点44:为什么要使用克隆?
知识点45:如何实现对象克隆?
知识点46:深拷贝和浅拷贝区别是什么?
知识点47:http 响应码 301 和 302 代表的是什么?有什么区别?
知识点48:forward 和 redirect 的区别?
知识点49:简述 tcp 和 udp的区别?
知识点50:tcp 为什么要三次握手,两次不行吗?为什么?
知识点51:说一下 tcp 粘包是怎么产生的?
知识点52:OSI 的七层模型都有哪些?
知识点53:get 和 post 请求有哪些区别?
知识点54:如何实现跨域?
方式一:图片ping或script标签跨域
方式二:JSONP跨域
方式三:CORS
方式四:window.name+iframe
方式五:window.postMessage()
方式六:修改document.domain跨子域
方式七:WebSocket
方式八:代理
知识点55:说一下 JSONP 实现原理?
知识点56:jsp 和 servlet 有什么区别?
知识点57:jsp 有哪些内置对象?作用分别是什么?
知识点58:说一下 jsp 的 4 种作用域?
知识点60:说一下 session 的工作原理?
知识点62:如何避免 sql 注入?
知识点63:什么是 XSS 攻击,如何避免?
1. 验证 HTTP Referer 字段
2. 使用验证码
3. 在请求地址中添加token并验证
4. 在HTTP 头中自定义属性并验证
知识点64:throw 和 throws 的区别?
知识点65: final、finally、finalize 有什么区别?
知识点66: try-catch-finally 中哪个部分可以省略?
知识点67:try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?
知识点68:常见的异常类有哪些?
1、ArrayList和LinkList都实现了以下接口:java.util.List、 Cloneable,、java.io.Serializable 由于实现了java.util.List接口,支持泛型,ArrayList和LinkList都能够用来存放各种数据类型的对象; 实现了Cloneable接口,能够支持克隆 实现了java.io.Serializable接口,能够支持序列化
2、ArrayList和LinkList都不是线程安全的,如果要在多线程的情况下调用它们,可以使用Collertions类中的静态方法 SynchronizedList(),对ArrayList和LinkList进行调用即可,或者使用Vector,Vector也是一个类似于ArrayList的可变长度的数组类 型,它的内部也是使用数组来存放数据对象的。
3、值得注意的是Vector与ArrayList唯一的区别是,Vector是线程安全的,即它的大部分方法都包含有关键字 synchronized,因此,若对于单一线程的应用来说,最好使用ArrayList代替Vector,因为这样效率会快很多(类似的情况有StringBuffer与StringBuilder); 而在多线程程序中,为了保证数据的同步和一致性,可以使用Vector代替ArrayList实现同样的功能。
千言万语:ArrayList底层是一个数组,所以查询快,LinkedList底层是一个链表,所以增删快.
1、ArrayList继承了AbstractList类,而LinkList继承了AbstractSequentialList类,而AbstractSequentialLis继承了AbstractList LinkList继承的AbstractSequentialList实现了get(int index)、set(int index, E element)、add(int index, E element) 和 remove(int index)这些骨干性函数, 降低了List接口的复杂度。
2、ArrayList和LinkList的内部实现的数据结构不同 ArrayList内部是由数组是实现的,而LinkList内部是由循环双向链表实现的。 由于ArrayList是由数组实现的,所以ArrayList在进行查找操作时,速度要优于由链表实现的LinkList, 但是在进行删除添加操作时,LinkList速度要优于ArrayList 所以当进行查找操作更多时,使用ArrayList,而如果进行插入和删除操作更多时,使用LinkList
3、LinkList需要更多的内存空间,因为它除了要存储数据之外,还需要存储该节点的前后节点信息,而ArrayList索引处就是存的数据
HashMap底层是数组+单向链表
数组的规则:hash(key)%len 元素的key的哈希值对数组长度取模得得到
哈希冲突:
HashMap中主要是通过key的hashCode来计算hash值的,只要hashCode相同,计算出来的hash值就一样。 如果存储的对象对多了,就有可能不同的对象所算出来的hash值是相同的,这就是哈希冲突
解决哈希冲突
数组的每个元素都是一个单链表的头节点,链表是用来解决冲突的,如果不同的key映射到了数组的同一位置处,就将其放入单链表中。
HashMap是怎么扩容的?
当HashMap中的元素个数超过数组大小*哈希因子时,就会进行数组扩容,哈希因子的默认值为0.75 ,数组的默认大小为16,当元素超过0.75 * 16时,就将数组扩大2 * 16 = 32 ,扩容是需要进行数组复制的,复制数组是非常消耗性能的操作,所以如果我们已经预知HashMap中元素的个数,那么预设元素的个数能够有效的提高HashMap的性能。
key哈希值是1,另一个key哈希值也是1,但是不同的key,这个怎么走?
解决方案:HashMap的数据结构是:数组Node[]与链表Node中有next Node.
Node 是链表中的元素,每个 Node 都有一个指向下一个 Node 的指针
(1)如果上述的 map[.put(“1”,”A”);map.put(“2”,”B”); 同时计算到的hash值都为123,那么A先放在第一列的第一个位置Node-A,map.put(“2”,”B”);执行时会将Node-A的next(Node) = Node(B),A的下个节点将指向Node(B)。
(2)那么取的时候呢,map.get(“2”),这个时候取得的hash值是1,即table[1],这时table[1]其实是Node-A,Key值不相等,取Node-A的next下个Node,即Node-B,这时Key值相等了,然后返回对应的map.
(1)单例模式上应用过
(2)接口都用的
static:
静态变量 :被static修饰的变量即为静态变量,当JVM加载类后,可以通过类名直接访问,类的所有实例共享一个static变量。
静态方法 :静态方法可以直接通过类名调用,但是不能直接访问所属类的实例变量和方法,只能访问所属类的静态变量和方法,这是因为实例成员只与特定对象关联。
静态代码块 :
为类中独立于类成员的static语句块,不在任何方法体内,当JVM加载类时,就会执行静态代码块,无需等待实例化,static语句块可以多个,JVM会按照它们的先后顺序依次执行。
1.先在table[0]的链表中寻找null key,如果有null key就直接覆盖原来的value,返回原来的value;
2.如果在table[0]中没有找到,就进行头插,但是要先判断是否要扩容,需要就扩容,然后进行头插,此时table[0]就是新插入的null key Entry了。
1、应用数据被分割成TCP认为最适合发送的数据块。
2、超时重传:当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。
3、TCP给发送的每一个包进行编号,接收方对数据包进行排序,把有序数据传送给应用层。
4、校验和:TCP将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP将丢弃这个报文段和不确认收到此报文段。
5、TCP的接收端会丢弃重复的数据。
6、流量控制:TCP连接的每一方都有固定大小的缓冲空间,TCP的接收端只允许发送端发送接收端缓冲区能接纳的我数据。当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失。TCP使用的流量控制协议是可变大小的滑动窗口协议。
7、拥塞控制:当网络拥塞时,减少数据的发送。
(1)OutOfMemoryError
当可用内存不足时,会由JVM抛出OutOfMemoryError。一般由三种原因导致:
堆设置过小,不满足正常的内存需求
代码中存在内存泄露,占用了大量内存而不能被回收
选择的GC算法与某些极端的应用场景不匹配,内存碎片过多,没有足够大的连续空间分配给对象
(2)NullPointerException
是调用了未经初始化的对象或者是不存在的对象
(3)ArrayIndexOutOfBoundsException
异常的解释是"数组下标越界
1、可变与不可变
String类中使用字符数组保存字符串,如下就是,因为有“final”修饰符,所以可以知道string对象是不可变的。
private final char value[];
StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,如下就是,可知这两种对象都是可变的。
char[] value;
2、是否多线程安全
String中的对象是不可变的,也就可以理解为常量,显然线程安全。
StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。
public synchronized StringBuffer reverse() {
super.reverse();
return this;
}
public int indexOf(String str) {
return indexOf(str, 0); //存在 public synchronized int indexOf(String str, int fromIndex) 方法
}
StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
StringBuilder与StringBuffer共同点 :
StringBuilder与StringBuffer有公共父类AbstractStringBuilder(抽象类)。
如果程序不是多线程的,那么使用StringBuilder效率高于StringBuffer。
JDK:Java Development Kit 的简称,java 开发工具包,提供了 java 的开发环境和运行环境。
JRE:Java Runtime Environment 的简称,java 运行环境,为 java 的运行提供了所需环境。
具体来说 JDK 其实包含了 JRE,同时还包含了编译 java 源码的编译器 javac,还包含了很多 java 程序调试和分析的工具。简单来说:如果你需要运行 java 程序,只需安装 JRE 就可以了,如果你需要编写 java 程序,需要安装 JDK。
== 解读
对于基本类型和引用类型 == 的作用效果是不同的,如下所示:
基本类型:比较的是值是否相同;
引用类型:比较的是引用是否相同;
代码示例:
String x = "string";
String y = "string";
String z = new String("string");
System.out.println(x==y); // true
System.out.println(x==z); // false
System.out.println(x.equals(y)); // true
System.out.println(x.equals(z)); // true
代码解读:因为 x 和 y 指向的是同一个引用,所以 == 也是 true,而 new String()方法则重写开辟了内存空间,所以 == 结果为 false,而 equals 比较的一直是值,所以结果都为 true。
equals 解读
equals 本质上就是 ==,只不过 String 和 Integer 等重写了 equals 方法,把它变成了值比较。看下面的代码就明白了。
首先来看默认情况下 equals 比较一个有相同值的对象,代码如下:
class Cat {
public Cat(String name) {
this.name = name;
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Cat c1 = new Cat("王磊");
Cat c2 = new Cat("王磊");
System.out.println(c1.equals(c2)); // false
输出结果出乎我们的意料,竟然是 false?这是怎么回事,看了 equals 源码就知道了,源码如下
public boolean equals(Object obj) {
return (this == obj);
}
原来 equals 本质上就是 ==。
那问题来了,两个相同值的 String 对象,为什么返回的是 true?代码如下:
String s1 = new String("老王");
String s2 = new String("老王");
System.out.println(s1.equals(s2)); // true
同样的,当我们进入 String 的 equals 方法,找到了答案,代码如下:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
原来是 String 重写了 Object 的 equals 方法,把引用比较改成了值比较。
总结 :== 对于基本类型来说是值比较,对于引用类型来说是比较的是引用;而 equals 默认情况下是引用比较,只是很多类重新了 equals 方法,比如 String、Integer 等把它变成了值比较,所以一般情况下 equals 比较的是值是否相等。
不对,两个对象的 hashCode()相同,equals()不一定 true。
代码示例:
String str1 = "通话";
String str2 = "重地";
System.out.println(String.format("str1:%d | str2:%d",
str1.hashCode(),str2.hashCode()));
System.out.println(str1.equals(str2));
执行的结果:
str1:1179395 | str2:1179395
false
代码解读:很显然“通话”和“重地”的 hashCode() 相同,然而 equals() 则为 false,因为在散列表中,hashCode()相等即两个键值对的哈希值相等,然而哈希值相等,并不一定能得出键值对相等。
final 修饰的类叫最终类,该类不能被继承。
final 修饰的方法不能被重写。
final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改。
等于 -1,因为在数轴上取值时,中间值(0.5)向右取整,所以正 0.5 是往上取整,负 0.5 是直接舍弃。
String 不属于基础类型,基础类型有 8 种:byte、boolean、char、short、int、float、long、double,而 String 属于对象。
不一样,因为内存的分配方式不一样。String str="i"的方式,java 虚拟机会将其分配到常量池中;而 String str=new String("i") 则会被分到堆内存中。
使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。
示例代码:
// StringBuffer reverse
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("abcdefg");
System.out.println(stringBuffer.reverse()); // gfedcba
// StringBuilder reverse
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("abcdefg");
System.out.println(stringBuilder.reverse()); // gfedcba
indexOf():返回指定字符的索引。
charAt():返回指定索引处的字符。
replace():字符串替换。
trim():去除字符串两端空白。
split():分割字符串,返回一个分割后的字符串数组。
getBytes():返回字符串的 byte 类型数组。
length():返回字符串长度。
toLowerCase():将字符串转成小写字母。
toUpperCase():将字符串转成大写字符。
substring():截取字符串。
equals():字符串比较。
不需要,抽象类不一定非要有抽象方法。
示例代码:
abstract class Cat {
public static void sayHi() {
System.out.println("hi~");
}
}
上面代码,抽象类并没有抽象方法但完全可以正常运行。
普通类不能包含抽象方法,抽象类可以包含抽象方法。
抽象类不能直接实例化,普通类可以直接实例化。
不能,定义抽象类就是让其他类继承的,如果定义为 final 该类就不能被继承,这样彼此就会产生矛盾,所以 final 不能修饰抽象类,如下图所示,编辑器也会提示错误信息:
实现:抽象类的子类使用 extends 来继承;接口必须使用 implements 来实现接口。
构造函数:抽象类可以有构造函数;接口不能有。
main 方法:抽象类可以有 main 方法,并且我们能运行它;接口不能有 main 方法。
实现数量:类可以实现很多个接口;但是只能继承一个抽象类。
访问修饰符:接口中的方法默认使用 public 修饰;抽象类中的方法可以是任意访问修饰符。
按功能来分:输入流(input)、输出流(output)。
按类型来分:字节流和字符流。
字节流和字符流的区别是:字节流按 8 位传输以字节为单位输入输出数据,字符流按 16 位传输以字符为单位输入输出数据。
BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。
NIO:New IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。
AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。
Files.exists():检测文件路径是否存在。
Files.createFile():创建文件。
Files.createDirectory():创建文件夹。
Files.delete():删除一个文件或目录。
Files.copy():复制文件。
Files.move():移动文件。
Files.size():查看文件个数。
Files.read():读取文件。
Files.write():写入文件。
java.util.Collection 是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有List与Set。
Collections则是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。
hashMap去掉了HashTable 的contains方法,但是加上了containsValue()和containsKey()方法。
hashTable同步的,而HashMap是非同步的,效率上逼hashTable要高。
hashMap允许空键值,而hashTable不允许。
对于在Map中插入、删除和定位元素这类操作,HashMap是最好的选择。然而,假如你需要对一个有序的key集合进行遍历,TreeMap是更好的选择。基于你的collection的大小,也许向HashMap中添加元素会更快,将map换为TreeMap进行有序key的遍历。
HashMap概述: HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
HashMap的数据结构: 在java编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的,HashMap也不例外。HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。
当我们往Hashmap中put元素时,首先根据key的hashcode重新计算hash值,根绝hash值得到这个元素在数组中的位置(下标),如果该数组在该位置上已经存放了其他元素,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放入链尾.如果数组中该位置没有元素,就直接将该元素放到数组的该位置上。
需要注意Jdk 1.8中对HashMap的实现做了优化,当链表中的节点数据超过八个之后,该链表会转为红黑树来提高查询效率,从原来的O(n)到O(logn)
HashSet底层由HashMap实现
HashSet的值存放于HashMap的key上
HashMap的value统一为PRESENT
最明显的区别是 ArrrayList底层的数据结构是数组,支持随机访问,而 LinkedList 的底层数据结构是双向循环链表,不支持随机访问。使用下标访问一个元素,ArrayList 的时间复杂度是 O(1),而 LinkedList 是 O(n)。
1、ArrayList是实现了基于动态数组的数据结构,因为地址连续,一旦数据存储好了,查询操作效率会比较高(在内存里是连着放的)。
2、因为地址连续, ArrayList要移动数据,所以插入和删除操作效率比较低。
3、LinkedList基于链表的数据结构,地址是任意的,所以在开辟内存空间的时候不需要等一个连续的地址,对于新增和删除操作add和remove,LinedList比较占优势。
4、因为LinkedList要移动指针,所以查询操作性能比较低。
适用场景分析:
当需要对数据进行对此访问的情况下选用ArrayList,当需要对数据进行多次增加删除修改时采用LinkedList。
List转换成为数组:调用ArrayList的toArray方法。
数组转换成为List:调用Arrays的asList方法。
Vector是同步的,而ArrayList不是。然而,如果你寻求在迭代的时候对列表进行改变,你应该使用CopyOnWriteArrayList。
ArrayList比Vector快,它因为有同步,不会过载。
ArrayList更加通用,因为我们可以使用Collections工具类轻易地获取同步列表和只读列表。
1、Vector的方法都是同步的,是线程安全的,而ArrayList的方法不是,由于线程的同步必然要影响性能。因此,ArrayList的性能比Vector好 2、当Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而ArrayList只增加50%的大小,这样。ArrayList就有利于节约内存空间。
3、大多数情况不使用Vector,因为性能不好,但是它支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性。
4、Vector可以设置增长因子,而ArrayList不可以。
适用场景分析:
1、Vector是线程同步的,所以它也是线程安全的,而ArrayList是线程异步的,是不安全的。如果不考虑到线程的安全因素,一般用ArrayList效率比较高。
2、如果集合中的元素的数目大于目前集合数组的长度时,在集合中使用数据量比较大的数据,用Vector有一定的优势。
Array可以容纳基本类型和对象,而ArrayList只能容纳对象。
Array是指定大小的,而ArrayList大小是固定的。
Array没有提供ArrayList那么多功能,比如addAll、removeAll和iterator等。
poll() 和 remove() 都是从队列中取出一个元素,但是 poll() 在获取元素失败的时候会返回空,但是 remove() 失败的时候会抛出异常。
vector:就比arraylist多了个同步化机制(线程安全),因为效率较低,现在已经不太建议使用。在web应用中,特别是前台页面,往往效率(页面响应速度)是优先考虑的。
statck:堆栈类,先进后出。
hashtable:就比hashmap多了个线程安全。
enumeration:枚举,相当于迭代器。
迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象,因为创建它的代价小。
Java中的Iterator功能比较简单,并且只能单向移动:
(1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。
(2) 使用next()获得序列中的下一个元素。
(3) 使用hasNext()检查序列中是否还有元素。
(4) 使用remove()将迭代器新返回的元素删除。
Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素。
Iterator可用来遍历Set和List集合,但是ListIterator只能用来遍历List。
Iterator对集合只能是前向遍历,ListIterator既可以前向也可以后向。
ListIterator实现了Iterator接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引,等等。
1.TreeSet 是二叉树实现的,Treeset中的数据是自动排好序的,不允许放入null值 。
2.HashSet 是哈希表实现的,HashSet中的数据是无序的,可以放入null,但只能放入一个null,两者中的值都不能重复,就如数据库中唯一约束 。
3.HashSet要求放入的对象必须实现HashCode()方法,放入的对象,是以hashcode码作为标识的,而具有相同内容的String对象,hashcode是一样,所以放入的内容不能重复。但是同一个类的对象可以放入不同的实例。
适用场景分析:
HashSet是基于Hash算法实现的,其性能通常都优于TreeSet。我们通常都应该使用HashSet,在我们需要排序的功能时,我们才使用TreeSet。
1、HashMap不是线程安全的,而ConcurrentHashMap是线程安全的。
2、ConcurrentHashMap采用锁分段技术,将整个Hash桶进行了分段segment,也就是将这个大的数组分成了几个小的片段segment,而且每个小的片段segment上面都有锁存在,那么在插入元素的时候就需要先找到应该插入到哪一个片段segment,然后再在这个片段上面进行插入,而且这里还需要获取segment锁。
3、ConcurrentHashMap让锁的粒度更精细一些,并发性能更好。
至于两者的底层实现,你如果想通过一篇文章就理解了,那就too young了,好好找些博文+看源码去吧。
反射主要是指程序可以访问、检测和修改它本身状态或行为的一种能力
Java反射:
在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法
Java反射机制主要提供了以下功能:
在运行时判断任意一个对象所属的类。
在运行时构造任意一个类的对象。
在运行时判断任意一个类所具有的成员变量和方法。
在运行时调用任意一个对象的方法。
简单说就是为了保存在内存中的各种对象的状态(也就是实例变量,不是方法),并且可以把保存的对象状态再读出来。虽然你可以用你自己的各种各样的方法来保存object states,但是Java给你提供一种应该比你自己好的保存对象状态的机制,那就是序列化。
什么情况下需要序列化:
a)当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;
b)当你想用套接字在网络上传送对象的时候;
c)当你想通过RMI传输对象的时候;
动态代理:
当想要给实现了某个接口的类中的方法,加一些额外的处理。比如说加日志,加事务等。可以给这个类创建一个代理,故名思议就是创建一个新的类,这个类不仅包含原来类方法的功能,而且还在原来的基础上添加了额外处理的新类。这个代理类并不是定义好的,是动态生成的。具有解耦意义,灵活,扩展性强。
动态代理的应用:
Spring的AOP
加事务
加权限
加日志
首先必须定义一个接口,还要有一个InvocationHandler(将实现接口的类的对象传递给它)处理类。再有一个工具类Proxy(习惯性将其称为代理类,因为调用他的newInstance()可以产生代理对象,其实他只是一个产生代理对象的工具类)。利用到InvocationHandler,拼接代理类源码,将其编译生成代理类的二进制码,利用加载器加载,并将其实例化产生代理对象,最后返回。
想对一个对象进行处理,又想保留原有的数据进行接下来的操作,就需要克隆了,Java语言中克隆针对的是类的实例。
有两种方式:
1). 实现Cloneable接口并重写Object类中的clone()方法;
2). 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆,代码如下:
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class MyUtil {
private MyUtil() {
throw new AssertionError();
}
@SuppressWarnings("unchecked")
public static T clone(T obj)
throws Exception {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bout);
oos.writeObject(obj);
ByteArrayInputStream bin =
new ByteArrayInputStream(bout.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bin);
return (T) ois.readObject();
// 说明:调用ByteArrayInputStream
//或ByteArrayOutputStream对象的close方法没有任何意义
// 这两个基于内存的流只要垃圾回收器清理对象就能够释放资源,
//这一点不同于对外部资源(如文件流)的释放
}
}
下面是测试代码:
import java.io.Serializable;
/**
* 人类
* @author nnngu
*
*/
class Person implements Serializable {
private static final long serialVersionUID
= -9102017020286042305L;
private String name; // 姓名
private int age; // 年龄
private Car car; // 座驾
public Person(String name, int age, Car car) {
this.name = name;
this.age = age;
this.car = car;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
@Override
public String toString() {
return "Person [name=" + name + ",
age=" + age + ", car=" + car + "]";
}
}
/**
* 小汽车类
* @author nnngu
*
*/
class Car implements Serializable {
private static final long serialVersionUID
= -5713945027627603702L;
private String brand; // 品牌
private int maxSpeed; // 最高时速
public Car(String brand, int maxSpeed) {
this.brand = brand;
this.maxSpeed = maxSpeed;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public int getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(int maxSpeed) {
this.maxSpeed = maxSpeed;
}
@Override
public String toString() {
return "Car [brand=" + brand + ",
maxSpeed=" + maxSpeed + "]";
}
}
class CloneTest {
public static void main(String[] args) {
try {
Person p1 = new Person("郭靖", 33,
new Car("Benz", 300));
Person p2 = MyUtil.clone(p1); // 深度克隆
p2.getCar().setBrand("BYD");
// 修改克隆的Person对象p2关联的汽车对象的品牌属性
// 原来的Person对象p1关联的汽车不会受到任何影响
// 因为在克隆Person对象时其关联的汽车对象也被克隆了
System.out.println(p1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
注意:基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型限定,可以检查出要克隆的对象是否支持序列化,这项检查是编译器完成的,不是在运行时抛出异常,这种是方案明显优于使用Object类的clone方法克隆对象。让问题在编译的时候暴露出来总是好过把问题留到运行时。
浅拷贝只是复制了对象的引用地址,两个对象指向同一个内存地址,所以修改其中任意的值,另一个值都会随之变化,这就是浅拷贝(例:assign())
深拷贝是将对象及值复制过来,两个对象修改其中任意的值另一个值不会改变,这就是深拷贝(例:JSON.parse()和JSON.stringify(),但是此方法无法复制函数类型)
301,302 都是HTTP状态的编码,都代表着某个URL发生了转移。
区别:
301 redirect: 301 代表永久性转移(Permanently Moved)。
302 redirect: 302 代表暂时性转移(Temporarily Moved )。
Forward和Redirect代表了两种请求转发方式:直接转发和间接转发。
直接转发方式(Forward),客户端和浏览器只发出一次请求,Servlet、HTML、JSP或其它信息资源,由第二个信息资源响应该请求,在请求对象request中,保存的对象对于每个信息资源是共享的。
间接转发方式(Redirect)实际是两次HTTP请求,服务器端在响应第一次请求的时候,让浏览器再向另外一个URL发出请求,从而达到转发的目的。
举个通俗的例子:
直接转发就相当于:“A找B借钱,B说没有,B去找C借,借到借不到都会把消息传递给A”;
间接转发就相当于:"A找B借钱,B说没有,让A去找C借"。
TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接。
TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付。
Tcp通过校验和,重传控制,序号标识,滑动窗口、确认应答实现可靠传输。如丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。
UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。
每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信。
TCP对系统资源要求较多,UDP对系统资源要求较少。
为了实现可靠数据传输, TCP 协议的通信双方, 都必须维护一个序列号, 以标识发送出去的数据包中, 哪些是已经被对方收到的。 三次握手的过程即是通信双方相互告知序列号起始值, 并确认对方已经收到了序列号起始值的必经步骤。
如果只是两次握手, 至多只有连接发起方的起始序列号能被确认, 另一方选择的序列号则得不到确认。
①. 发送方产生粘包:
采用TCP协议传输数据的客户端与服务器经常是保持一个长连接的状态(一次连接发一次数据不存在粘包),双方在连接不断开的情况下,可以一直传输数据;但当发送的数据包过于的小时,那么TCP协议默认的会启用Nagle算法,将这些较小的数据包进行合并发送(缓冲区数据发送是一个堆压的过程);这个合并过程就是在发送缓冲区中进行的,也就是说数据发送出来它已经是粘包的状态了。
https://mmbiz.qpic.cn/mmbiz_png/QCu849YTaIMnOiaLZLvCauibB6a1NxIwLdQC1gqfKDwusvSlQxEaDnRSySSvcK2VNlpdH9MFfRasTicha14JuvKYQ/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1
②. 接收方产生粘包
接收方采用TCP协议接收数据时的过程是这样的:数据到底接收方,从网络模型的下方传递至传输层,传输层的TCP协议处理是将其放置接收缓冲区,然后由应用层来主动获取(C语言用recv、read等函数);这时会出现一个问题,就是我们在程序中调用的读取数据函数不能及时的把缓冲区中的数据拿出来,而下一个数据又到来并有一部分放入的缓冲区末尾,等我们读取数据时就是一个粘包。(放数据的速度 > 应用层拿数据速度)https://mmbiz.qpic.cn/mmbiz_png/QCu849YTaIMnOiaLZLvCauibB6a1NxIwLdV9Mic1uabwcX3AqZ9Tw9M7lcbwMQxFVUMWmG3SKNRibVLHZgcQvFDbmw/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1
应用层:网络服务与最终用户的一个接口。
表示层:数据的表示、安全、压缩。
会话层:建立、管理、终止会话。
传输层:定义传输数据的协议端口号,以及流控和差错校验。
网络层:进行逻辑地址寻址,实现不同网络之间的路径选择。
数据链路层:建立逻辑连接、进行硬件地址寻址、差错校验等功能。
物理层:建立、维护、断开物理连接。
GET在浏览器回退时是无害的,而POST会再次提交请求。
GET产生的URL地址可以被Bookmark,而POST不可以。
GET请求会被浏览器主动cache,而POST不会,除非手动设置。
GET请求只能进行url编码,而POST支持多种编码方式。
GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
GET请求在URL中传送的参数是有长度限制的,而POST么有。
对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
GET参数通过URL传递,POST放在Request body中。
图片ping常用于跟踪用户点击页面或动态广告曝光次数。 script标签可以得到从其他来源数据,这也是JSONP依赖的根据。
JSONP(JSON with Padding)是数据格式JSON的一种“使用模式”,可以让网页从别的网域要数据。根据 XmlHttpRequest 对象受到同源策略的影响,而利用 元素的这个开放策略,网页可以得到从其他来源动态产生的JSON数据,而这种使用模式就是所谓的 JSONP。用JSONP抓到的数据并不是JSON,而是任意的JavaScript,用 JavaScript解释器运行而不是用JSON解析器解析。所有,通过Chrome查看所有JSONP发送的Get请求都是js类型,而非XHR。
https://mmbiz.qpic.cn/mmbiz_jpg/QCu849YTaIMnOiaLZLvCauibB6a1NxIwLdKs3N3VGEKWICjTuhM5qyWnD7BjupwhylCT1DLuYiaMYB3vt5GH0sWqg/640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1
缺点:
只能使用Get请求
不能注册success、error等事件监听函数,不能很容易的确定JSONP请求是否失败
JSONP是从其他域中加载代码执行,容易受到跨站请求伪造的攻击,其安全性无法确保
Cross-Origin Resource Sharing(CORS)跨域资源共享是一份浏览器技术的规范,提供了 Web 服务从不同域传来沙盒脚本的方法,以避开浏览器的同源策略,确保安全的跨域数据传输。现代浏览器使用CORS在API容器如XMLHttpRequest来减少HTTP请求的风险来源。与 JSONP 不同,CORS 除了 GET 要求方法以外也支持其他的 HTTP 要求。服务器一般需要增加如下响应头的一种或几种:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
跨域请求默认不会携带Cookie信息,如果需要携带,请配置下述参数:
"Access-Control-Allow-Credentials": true
// Ajax设置
"withCredentials": true
window.name通过在iframe(一般动态创建i)中加载跨域HTML文件来起作用。然后,HTML文件将传递给请求者的字符串内容赋值给window.name。然后,请求者可以检索window.name值作为响应。
iframe标签的跨域能力;
window.name属性值在文档刷新后依旧存在的能力(且最大允许2M左右)。
每个iframe都有包裹它的window,而这个window是top window的子窗口。contentWindow属性返回元素的Window对象。你可以使用这个Window对象来访问iframe的文档及其内部DOM。
...