:看似用法与List一样,但是在接受其他泛型赋值时 会出现编译错误
List
:是一个泛型,在没有赋值前,表示可以接受任何类型的集合赋值,但赋值之后不能往里面随便添加元素,但可以remove和clear
,并非immutable(不可变)
集合。List
一般作为参数来接收外部集合,或者返回一个具体元素类型的集合,也称为通配符集合
List 最大的问题是只能放置一种类型,如果随意转变类型的话,就是破窗理论 ,泛型就失去了意义。为了放置多种受泛型约束的类型,出现了与
两种语法。简单来说, 是Get First,适用于,消费集合元素的场景;是Put First,适用于,生产集合元素为主的场景。
:可以赋值给任意T及T的子类集合,上界为T,取出来的类型带有泛型限制 ,向上强制转型为T。null 可以表示任何类型,所以null除外 ,任何元素都不得添加进集合内
: 可以复制T及任何T的父类集合,下界为T。再生活中,投票选举类似于的操作。选举投票时,你只能往里投票,取数据时,根本不知道时是谁的票,相当于泛型丢失
的场景是put功能受限,而
的场景是get功能受限
public static void test ( List< ? > list) {
}
public < T> void test2 ( List< T> t) {
}
如果参数之间的类型有依赖关系 ,或者返回值是与参数之间有依赖关系的 。那么就使用泛型方法
如果没有依赖关系的 ,就使用通配符,通配符会灵活一些.
java的Math.round(-1.5)等于多少
等于-1,因为在数轴上取值时,中间值(0.5)向右取整,所以正0.5是向上取整,负0.5是直接舍去
封装
把对象的属性私有化,再提供可被外界访问的方法
继承
从已有的类基础上建立新类,新类保存已有类的属性和行为,并能拓展新的功能 子类拥有父类所以属性和方法,但私有属性和方法无法访问,只是拥有
多态
同一种事务,由于环境/条件等因素的不同,呈现的状态不同
方法调用在编程时并不确定,而是在程序运行期间才确定
方法三要素: 返回值类型 参数列表 方法名
多态的两种实现方式
使用父类作为方法形参
实现多态
使用父类作为方法返回值
实现多态
多态作为形参
形式参数 基本类型 引用类型
普通类 当一个形参希望我们传入的是普通类时,我们实际传入的是该类的对象/匿名对象
抽象类 当一个形参希望我们传入的是抽象类时,我们实际传入的是该类的子类对象/子类匿名对象
接口 当一个形参希望我们传入的是接口时,我们实际传入的是该类的实现类对象/实现类匿名对象
注意:当一个方法的形参是引用类型是,建议养成一个好习惯:做非空判断
多态作为返回值 返回值类型 基本类型 引用类型
普通类 当一个方法的返回值是一个普通的类时,实际上返回的是该类的对象,我们可以使用该类的对象接收 抽象类 当一个方法的返回值是一个抽象类时,实际上返回的是该抽象类的子类对象,我们可以使用该抽象类接收 接口 当一个方法的返回值是一个接口时,实际上返回的是该接口的实现类对象,我们可以使用接口接收
当方法的返回值类型是引用类型的时候,可以使用链式调用
集合
Collection
Collection 和 Collections 区别
java.util.Collection
是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection
接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有List与Set。
Collections
是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作
List
ArrayList: 有序,可重复,Object数组
Vector: 有序,可重复,Object数组
LinkedList: 双向链表(JDK1.6前为循环链表,JDK1.7取消了循环)
Set
HashSet: 无序,唯一,基于HashMap
实现,底层采用HashMap
来保存元素
LinkedHashSet: LinkedHashSet
继承于HashSet
,且内部是通过LinkedHashMap
来实现
TreeSet: 有序,唯一,红黑树(自平衡的排序二叉树)
Map
HashMap: JDK1.8之前HashMap
由数组+链表
组成,数组是HashMap
的主体,链表则是主要为了解决 哈希冲突而存在的(“拉链法解决冲突”), JDK1.8以后在 解决哈希冲突 时有了变化,当链表长度 大于阀值(默认为8)时,将链表转为红黑树,以减少搜索时间,线程不安全
LinkedHashMap: LinkedHashMap
继承自HashMap
,所以底层是基于 拉链式散列结构
,即由 数组+链表/红黑树
组成,LinkedHashMap
在前面结构的基础上,另外增加了一条 双向链表
,使上面的结构可以 保持键值对 的 插入顺序。同时通过对链表进行对应的操作,实现了访问顺序相关逻辑,线程不安全
HashTable: 数组+链表
组成,数组使HashMap
的主体,链表则是主要为了解决哈希冲突而存在的,线程安全
TreeMap: 红黑树(自平衡的排序二叉树),线程不安全
如何实现数组和 List 之间的转换?
List转换成为数组:调用ArrayList
的toArray()
方法
数组转换成为List:调用Arrays
的asList()
方法, 不能对List增删,只能查改
一.最常见方式(未必最佳) 通过 Arrays.asList(strArray)
方式,将数组转换List后,不能对List增删,只能查改,否则抛异常。
关键代码:List list = Arrays.asList(strArray);
List list = Arrays.asList( 1, 2, 3) ;
list.remove( 2) ;
System.out.println( list) ;
结果
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.remove( AbstractList.java:161)
原因解析: Arrays.asList(strArray)
返回值是java.util.Arrays
类中一个私有静态内部类java.util.Arrays.ArrayList
,它并非java.util.ArrayList
类。ava.util.Arrays.ArrayList
类具有 set(),get(),contains()
等方法,但是不具有添加add()或删除remove()方法,所以调用add()方法会报错。
使用场景 :Arrays.asList(strArray)方式仅能用在将数组转换为List后,不需要增删其中的值,仅作为数据源读取使用。
二.数组转为List后,支持增删改查的方式
通过ArrayList的构造器,将Arrays.asList(strArray)
的返回值由java.util.Arrays.ArrayList
转为java.util.ArrayList
。
关键代码:ArrayList list = new ArrayList(Arrays.asList(strArray)) ;
String[ ] strArray = new String[ 2] ;
ArrayList< String> list = new ArrayList< String> ( Arrays.asList( strArray)) ;
list.add( "1" ) ;
System.out.println( list) ;
使用场景 :需要在将数组转换为List后,对List进行增删改查操作,在List的数据量不大的情况下,可以使用。
三.通过集合工具类Collections.addAll()方法(最高效)
通过Collections.addAll(arrayList, strArray)
方式转换,根据数组的长度创建一个长度相同的List,然后通过Collections.addAll()
方法,将数组中的元素转为二进制,然后添加到List中,这是最高效的方法。
关键代码:
ArrayList< String> arrayList = new ArrayList(strArray.length); Collections.addAll(arrayList, strArray);
String[ ] strArray = new String[ 2] ;
ArrayList< String> arrayList = new ArrayList< String> ( strArray.length) ;
Collections.addAll( arrayList, strArray) ;
arrayList.add( "1" ) ;
System.out.println( arrayList) ;
使用场景 :需要在将数组转换为List后,对List进行增删改查操作,在List的数据量巨大的情况下,优先使用,可以提高操作速度。
注:附上Collections.addAll()方法源码:
public static < T> boolean addAll( Collection< ? super T> c, T.. . elements) {
boolean result = false ;
for ( T element : elements)
result | = c.add( element) ; //result和c.add( element) 按位或运算,然后赋值给result
return result;
}
ArrayList和LinkedList区别
1.是否保证线程安全: ArrayList
和LinkedLis
t都是不同步的,不保证线程安全 2.底层数据结构: ArrayList
底层使用的是Object数组
,LinkedList
底层使用的是双向链表数据结构
3.插入和删除是否受元素位置的影响: ArrayList
采用数组存储,所以插入删除受元素位置影响
如:执行 add(E e) 方法的时候, ArrayList 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是O(1)。 但是如果要在指定位置 i 插入和删除元素的话( add(int index, E element) )时间复杂度就为 O(n-i)。 因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。
LinkedList
采用链表存储,插入删除不受元素位置的影响,都是类似o(1)
,而数组类似o(n)
4.是否支持快速随机访问: ArrayList
支持,而LinkedList
而不支持高效的随机元素访问 快速随机访问就是通过元素的序号快速获取元素对象(对应get(index x)方法) 5.内存空间占用: ArrayList
的空间浪费主要体现在list
列表的结尾会预留一定的容量空间,而LinkedList
的空间花费则体现 在它的每个元素都需要消耗比ArrayList
更多的资源(因为要存放直接后继和直接前驱以及数据)
list 的遍历方式选择:
实现了RandomAccess
接口的List
,优先选择普通for循环
,其次foreach
未实现RandomAccess
接口的List
,优先选择iterator
遍历(foreach
遍历底层也是通过iterator
实现的)
补充:数据结构基础之双向链表
双向链表也称双链表,是链表的一种,它的每个数据结点中都有 两个指针,分别指向 直接前驱和 直接后继。 从双向链表的任意一个结点开始,都可以很方便地访问它的 前驱结点和后继结点。 一般构建双向循环链表,也是LinkedList
底层使用的是双向循环链表数据结构,如下:
ArrayList和Vector
ArrayList
不是同步的,所以在不需要保证线程安全时使用 Vector
类的所有方法都是同步的。可以由两个线程安全地访问一个Vector
对象,但是一个线程访问Vector
的话代码要在同步操作上耗费大量的时间
Array 和 ArrayList 有何区别?
Array可以容纳基本类型和对象,而ArrayList只能容纳对象
Array是指定大小的,而ArrayList大小是固定的
Array没有提供ArrayList那么多功能,比如addAll、removeAll和iterator等
Queuqe中poll()和remove()有什么区别
相同: 都是返回第一个元素,并在队列中删除返回的对象 不同点: 如果没有元素poll()
会返回null
,而remove()
会直接抛出NoSuchElementException
异常
HashMap的底层实现
第一次添加元素时,为容器赋初始容量为16, 再把 key 根据 扰动函数 得出哈希值,和容量减一按位与运算,得到要存到数组中的 索引,存到数组中, 哈希值重了的话会出现哈希冲突,会在数组对应位置形成链表,链表超过了 8个 时会形成红黑树 形成红黑树的俩条件 链表长度>=8,HashMap数组到64 如果长度>8但数组长度未到64则会扩容,不会树化没;超过8就不会扩容
if ( binCount >= TREEIFY_THRESHOLD - 1 )
treeifyBin ( tab, hash) ;
if ( ++ size > threshold)
resize ( ) ;
如果一个桶采用了树形结构存储,其他桶是不是也采用树形结构存储? 结论是,如果其他桶中bin的数量没有超过TREEIFY_THRESHOLD
,则用链表存储,如果超过TREEIFY_THRESHOLD
,则用树形存储。 由链表变成红黑树也只是当前桶挂载的bin会进行转换,不会影响其它桶的数据结构
JDK1.8前
jdk1.8前HashMap
底层是 数组和链表
结合使用,就是链表散列
。 HashMap
通过key
的hashCode
经过 扰动函数
处理过后的到hash值
,然后通过(n - 1) & hash
判断当前元素存放的位置(n指数组的长度),如果当前位置 存在元素的话,就 判断该元素与 要存入的元素的 hash值
以及key
是否相同, 相同的话, 直接覆盖,不相同就通过 拉链法
解决冲突
扰动函数:HashMap
的hash
方法。使用扰动函数后可以减少碰撞,是为了防止一些实现比较差的hashCode
方法
JDK 1.8 HashMap 的 hash 方法源码: JDK 1.8 的 hash方法 相比于 JDK 1.7 hash 方法更加简化,但是原理不变
static final int hash ( Object key) {
int h;
return ( key == null) ? 0 : ( h = key. hashCode ( ) ) ^ ( h >>> 16 ) ;
}
对比一下 JDK1.7的 HashMap 的 hash 方法源码
static int hash ( int h) {
h ^= ( h >>> 20 ) ^ ( h >>> 12 ) ;
return h ^ ( h >>> 7 ) ^ ( h >>> 4 ) ;
}
两个方法相比较,JDK1.7的性能会差点,因为扰动了4次
拉链法 :将链表数组与数组相结合。 也就是创建个链表数组,数组中的每一格就是一个链表,若遇到哈希冲突,则将冲突的值加到链表中即可 JDK1.8后 JDK1.8后在解决哈希冲突时有了变化,当链表长度大于阀值(默认为8)时,将链表转为红黑树,以减少搜索时间
ThreeMap
,ThreeSet
以及JDK1.8之后的HashMap
底层都用到了红黑树。 红黑树就是为了解决 二叉查找树的缺陷,因为二叉树在某些情况下会退化成一个线性结构
HashMap的put方法逻辑
若HashMap未被初始化,则进行初始化操作
对Key求Hash值,依据Hash值来计算下标
若未发生碰撞(得到相同Hash值),则直接放入桶中
若发生碰撞,则以链表的方式链接在后面
若链表长度超过阈值,且HashMap元素超过最低树化容量,链表转为红黑树,TREEIFY_THRESHOLD=8,MIN_TREEIFY_CAPACITY=64
,如果链表长度超过8,但整个HashMap的数组没到64,那么只会扩容,不会树化
若节点已经存在,则用新值替换旧值
若桶满了(默认容量16*扩容因子0.75),就需要resize
(扩容两倍后重排)
HashMap和HashTable的区别
1.线程是否安全: HashMap
是非线程安全的,Hashtable
是线程安全的。 HashTable
内部的方法基本都经过synchronized
修饰。(如要保证线程安全还是使用ConcurrentHashMap
吧) 2.效率: 因为线程安全的问题,HashMap
要比HashTable
效率要高点。HashTable
基本被淘汰,不要用 3. 对Null key和Null value的支持: HashMap
中,null
可作为键, 但只有一个,可以有 一个或多个 键对应的值为null
。HashTable
中put进去的键值有一个为null
,会抛出NullPointerExecption
4. 底层数据结构: JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。Hashtable 没有这样的机制。 5.初始容量大小和每次扩充容量大小的不同 :
①创建时如果不指定容量初始值,Hashtable 默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。 HashMap 默认的初始化大小为16。 之后每次扩充,容量变为原来的2倍。 ②创建时如果给定了容量初始值,那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充 为2的幂次方大小(HashMap 中的 tableSizeFor() 方法保证,下面给出了源代码)。 也就是说 HashMap 总是使用2的幂作为哈希表的大小,后面会介绍到为什么是2的幂次方。
HashMap 的长度为什么是2的幂次方
为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀。 Hash 值的范围值-2147483648到2147483647,前后加起来大概40亿的映射空间,只要哈希函数映射得比较均匀松散,一般应用是很难出现碰撞的。 但问题是一个40亿长度的数组,内存是放不下的。所以这个散列值是不能直接拿来用的。 用之前还要先做对数组的长度取模运算,得到的余数才能用来要存放的位置也就是对应的数下标。 这个数组下标的计算方法是“ (n - 1) & hash ”。(n代表数组长度)。这也就解释了 HashMap 的长度为什么是2的幂次方。
这个算法应该如何设计呢? 我们首先可能会想到采用%取余的操作来实现。但是,重点是: “取余(%)操作中如果除数是2的幂次则等价于与其除数减一的与(&)操作 (也就是说 hash%length==hash&(length-1)的前提是 length 是2的 n 次方;)。” 并且 采用二进制位操作 &,相对于%能够提高运算效率,这就解释了 HashMap 的长度为什么是2的幂次方。
如何决定使用 HashMap 还是 TreeMap?
对于在Map中插入、删除和定位元素这类操作,HashMap是最好的选择。 然而,假如你需要对一个有序的key集合进行遍历,TreeMap是更好的选择。 基于你的collection的大小,也许向HashMap中添加元素会更快,将map换为TreeMap进行有序key的遍历
HashSet 和 HashMap 区别
HashSet 底层就是基于 HashMap 实现的。HashSet的值存放于HashMap的key上 (HashSet 的源码非常非常少,因为除了 clone() 方法、writeObject()方法、readObject()方法是 HashSet 自己不得不实现之外,其他方法都是直接调用 HashMap 中的方法。
HashMap
HashSet
实现了Map接口
实现了Set接口
存储键值对
仅存储对象
调用put()
向map中添加元素
调用add()
方法向Set中添加元素
HashMap使用键(key)
计算Hashcode
HashSet使用成员对象计算Hashcode值
, 对两个对象来说Hashcode
可能相同, 所以equals()
用来判断对象的相等性, 如果两个对象不同的话,返回false
HashMap比HashSet快,因为使用唯一的键获取对象
HashSet比HashMap慢
ConcurrentHashMap 和 Hashtable HashMap的区别
HashMap线程不安全,数组+链表+红黑树 HashTable线程安全,锁住整个对象,数组+链表 ConcurrentHashMap线程安全,CAS+同步锁,数组+链表+红黑树 HashMap的key,value均可为null,其他两个则不支持
ConcurrentHashMap
和 Hashtable
的区别主要体现在实现线程安全的方式上不同。
底层数据结构: jdk1.7的ConcurrentHashMap
底层采用分段的数组+链表 实现, jdk1.8采用的数据结构和HashMap1.8
一样,数组+链表/红黑二叉树
。 HashTable
和JDK1.8之前的HashMap
的底层数据结构类似,都是数组+链表
, 数组是HashMap
的主体,链表则是为了解决哈希冲突而存在的
实现线程安全的方式(重要): 1.jdk1.7 时,ConcurrentHashMap(分段锁)
对整个桶数组进行了分割分段(Segment),每把锁只锁容器其中一部分数据,多线程访问容器里不同的数据段的数据,就不会出现锁竞争,提高并发访问率 jdk1.8 时已经放弃了Segment
的概念,直接使用Node数组 + 链表 + 红黑树
的数据结构来实现,并发控制使用synchronized
和CAS
来操作。 2. HashTable(同一把锁):
使用synchronized
来保证线程状态,效率低下。 当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用put()
添加元素,另一个线程不能使用put()
添加元素,也不能使用get()
,竞争会越来越激烈效率越低
ConcurrentHashMap线程安全的具体实现方式/底层具体实现
JDK1.7: 首先将数据分为一段一段的存储,然后给 每一段数据配一把锁,当一个线程占用锁访问其中一个数据时,其他段数据也能被其他线程访问 ConcurrentHashMap是由Segment数据结构和HashEntry数据结构组成 Segment
实现了ReentrantLock
,所以Segment
是一种可重入锁,扮演锁的角色。HashEntry
用于存储键值对数据
static class Segment < K, V> extends ReentrantLock implements Serializable { }
一个ConcurrentHashMap
里包含一个Segment
数组。Segment
的结构和HashMap
类似,是一种数组和链表结构,一个Segment
包含一个HashEntry
数组,每个HashEntry
是一个链表结构的元素,每个Segment
守护着一个HashEntry
数组里的元素,当对HashEntry
数组的数据进行修改时,必须首先获得对应的Segment
的锁 JDK1.8: ConcurrentHashMap
取消了Segment
分段锁,采用CAS
和synchronized
来保证并发安全。 数据结构跟HashMap
的结构类似,数组+链表/红黑二叉树
synchronized
只锁定当前链表或红黑二叉树的首结点,这样只要 hash不冲突,就不会产生并发,效率又提升了
ConcurrentHashMap的put方法逻辑
判断Node数组是否初始化,没有则进行初始化操作
通过hash
定位数组的索引坐标,是否有Node节点,没有则使用CAS进行添加(链表的头节点),添加失败则进行下次循环
检查到内部正在扩容,就帮助他一块扩容
如果f!=null
,则使用synchronized
锁住f元素(链表/红黑二叉树的投元素) 如果是Node(链表结构)
则执行链表的添加操作 如果是TreeNode(树形结构)
则执行树添加操作
判断阈值达到8,转为树结构
哪些集合类是线程安全的?
Vector:比ArrayList
多了同步化机制(线程安全),因为效率较低,不建议使用
Statck:堆栈类,后进后出
Hashtable:比HashMap
多了个线程安全
enumeration:枚举,相当于迭代器
JDK1.5后出现了 Java.util.concurrent
并发包的出现,很多集合有了自己对应的线程安全类,如HashMap
对应的线程安全类就是ConcurrentHashMap
迭代器 Iterator 是什么?
迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。 迭代器通常被称为“轻量级”对象,因为创建它的代价小
Iterator 怎么使用?有什么特点?
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 和 ListIterator 有什么区别?
Iterator可用来遍历Set和List集合,但是ListIterator只能用来遍历List
Iterator对集合只能是前向遍历,ListIterator既可以前向也可以后向
tor实现了Iterator接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引,等等
手写LinkedList
LinkedList基于链表,
其存储数据放在结点里
插入和删除操作效率高,不用遍历数据,打断节点连接,重新连接即可
数据结构 LinkedList底层的数据结构是基于双向循环链表的,每个节点分为头部、尾部以及业务数据, 前一个节点尾部指向后一个节点的头部,后一节点的头部指向前一个节点的尾部
但对于头结点和尾节点比较特殊,头结点的头部没有上一个结点,从上图可知并没有指向,尾节点的尾部也没有指向下一个结点。 所以,如可以知道链表是由一个一个节点构成,可以定义一个结点类Node,如下:
public class Node {
Node previous;
Object obj;
Node next;
get ( ) . . set ( ) . . . 有参无参
添加数据add
public class MyLinkedList {
private Node first;
private Node last;
private int size;
public void add ( Object obj) {
Node n = new Node ( ) ;
if ( first== null) {
n. setPrevious ( null) ;
n. setObj ( obj) ;
n. setNext ( null) ;
first = n;
last = n;
} else {
n. setPrevious ( last) ;
n. setObj ( obj) ;
n. setNext ( null) ;
last. setNext ( n) ;
last = n;
}
size++ ;
}
public int size ( ) {
return size;
}
}
查询数据get
public Object get ( int index) {
Node temp = node ( index) ;
if ( temp!= null) {
return temp. obj;
}
return null;
}
public Node node ( int index) {
Node temp = null;
if ( first!= null) {
temp = first;
for ( int i= 0 ; i< index; i++ ) {
temp = temp. next;
}
}
return temp;
}
删除数据remove 只需要打断原有的指向关系,重新连接指向,就可以删除指定位置处的数据,非常高效。 如果是ArrayList删除数据,上面2节点,将会被后面的3节点取代,它要移位,后面的所有节点都要跟着移位,所以ArrayList效率比较低。
public void remove ( int index) {
Node temp = node ( index) ;
if ( temp!= null) {
Node up = temp. previous;
Node down = temp. next;
up. next = down;
down. previous = up;
size-- ;
}
}
指定位置添加数据
IO
java 中 IO 流分为几种
按功能来分:输入流(input)、输出流(output)。
按类型来分:字节流和字符流。
字节流处理单位为 1 个字节,字符流处理单位为 2 个字节。
BIO、NIO、AIO 有什么区别?
BIO:Block IO
同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。
NIO:New IO
同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。
AIO:Asynchronous IO
是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制
Files的常用方法都有哪些?
Files. exists ( ) :检测文件路径是否存在。
Files. createFile ( ) :创建文件。
Files. createDirectory ( ) :创建文件夹。
Files. delete ( ) :删除一个文件或目录。
Files. copy ( ) :复制文件。
Files. move ( ) :移动文件。
Files. size ( ) :查看文件个数。
Files. read ( ) :读取文件。
Files. write ( ) :写入文件
反射
反射创建类实例的三种方式
Class class1 = p1. getClass ( ) ;
Class class3 = Person. class ;
Class class4 = Class. forName ( "cn.xbmchina.Person" ) ;
反射指程序可以访问、检测和修改它本身状态或行为的一种能力
通过反射可以获取字节码文件对象,使用字节码文件对象可以获取到一个类的所有信息,包括私有
可以动态创建,动态赋值,动态调用
什么是 java 序列化?什么情况下需要序列化?
为了保存在内存中的各种对象的状态(也就是实例变量,不是方法),并且可以把保存的对象状态再读出来。 Java提供一种比你自己写要好的保存对象状态的机制,就是序列化
序列化 : 把Java对象转化为字节流的过程 反系列化 : 把字节流转为Java对象的过程
什么情况下需要序列化:
当你想把的内存中的对象状态保存到一个文件中或者数据库中时候
当你想用套接字在网络上传送对象的时候
当你想通过RMI传输对象的时候
如何实现序列化? 实现Serializable
接口即可,一标记接口,不包含任何方法或字段,仅用于标识可序列化的语义
注意事项: transient 修饰的属性,是不会被序列化的 静态static的属性,他不序列化。 实现这个Serializable 接口的时候,一定要给这个 serialVersionUID 赋值 关于 serialVersionUID 的描述: 序列化运行时使用一个称为 serialVersionUID 的版本号与每个可序列化类相关联,该序列号在反序列化过程中用于验证序列化对象的发送者和接收者是否为该对象加载了与序列化兼容的类。如果接收者加载的该对象的类的 serialVersionUID 与对应的发送者的类的版本号不同,则反序列化将会导致 InvalidClassException。可序列化类可以通过声明名为 “serialVersionUID” 的字段(该字段必须是静态 (static)、最终 (final) 的 long 型字段)显式声明其自己的 serialVersionUID
如果试图序列化一个不可序列化的对象怎么办?
将得到一个 RuntimeException
异常:主线程中出现异常 java.io.NotSerializableException
。
Java序列化中如果有些字段不想进行序列化,怎么办?
当某些变量不想被序列化,又不适合使用static关键字
声明,那么此时就需要用transient关键字
来声明该变量。
transient
关键字的作用是:阻止实例中那些用此关键字修饰的的变量序列化; 当对象被反序列化时,被transient
修饰的变量值不会被持久化和恢复。transient只能修饰变量,不能修饰类和方法
注: 对于某些类型的属性,其状态是瞬时的,这样的属性是无法保存其状态的。例如一个线程属性或需要访问IO、本地资源、网络资源等的属性,对于这些字段,我们必须用transient关键字标明,否则编译器将报措 引用文章
动态代理是什么?有哪些应用?
在不动源码的情况下,动态地加入其他代码,运行时动态生成代理类
Spring的AOP
事务
日志
动态代理的几种实现方式,分别说出相应的优缺点。
原理区别:
动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler
来处理。
而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP 2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP 3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
如何强制使用CGLIB实现AOP? (1)添加CGLIB库,SPRING_HOME/cglib/*.jar
(2)在spring配置文件中加入
JDK动态代理和CGLIB字节码生成的区别? (1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类 (2)CGLIB是指定的类生成一个子类,覆盖其中的方法 因为是继承,所以该类或方法最好不要声明成final
多线程
多线程是一种机制,它允许在程序中并发的执行多个线程,且每个线程间相互独立
并行与并发
并行: 在同一个时间刻度上同时执行 并发: 在同一个时间段同时执行
线程和进程
进程: 正在执行的程序 线程: 具有完成独立任务的一条执行路径
一个程序下至少有一个进程,一个进程下至少有一个线程,一个线程下也可以有多个线程来增加程序的执行速度
守护线程
守护线程是 运行在后台的一种特殊进程 它独立于 控制终端 并且 周期性地执行 某种任务或者等待 处理某些发生的事件 在Java中 垃圾回收线程 就是 特殊的守护线程
创建线程的三种方式
继承Thread
重写run()
方法 ,创建对象,调用start()
启动线程
实现Runnable
接口 ,重写run()
,创建Runnable
对象,创建Thread
实现对象,把Runnable
对象包装成Thread
,调用start()
启动
实现Callable
接口 ,会返回结果,且可以抛出经过检查的异常
线程一定不能直接调用run()
方法执行,否则线程会被当成是普通类和普通方法执行,不会出现随机性
优缺点:
继承Thread
类,简单,这个线程就不能再继承其他类了,功能受到约束
实现Runnable
接口,可以继续实现其他接口,继承其他类。功能可以拓展,Runnable
对象适合做线程池
实现Callable
接口,可以继续实现其他接口,继承其他类。功能可以拓展,可以获得线性的执行结果
同步有几种实现方法?
1.synchronized
修饰同步代码块或同步方法 synchronized (同一个数据){}
同一个数据:就是N条线程同时访问同一个数据 public synchronized 返回值 方法名(){}
3. wait
和notify
等待唤醒
Runnable和Callable区别
Runnable
没有返回值,Callable
可以拿到返回值,返回结果,可以看作是Runnable
的补充
线程的状态
新建,就绪,运行,阻塞,死亡
新建:就是刚使用new方法,new出来的线程;
就绪:就是调用的线程的start()
方法后,这时候线程处于等待CPU分配资源阶段,谁先抢的CPU资源,谁开始执行;
运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态,run方法定义了线程的操作和功能;
阻塞:在运行状态的时候,可能因为某些原因导致运行状态的线程变成了阻塞状态,比如sleep()
、wait()
之后线程就处于了阻塞状态,这个时候需要其他机制将处于阻塞状态的线程唤醒,比如调用notify
或者notifyAll()
方法。唤醒的线程不会立刻执行run方法,它们要再次等待CPU分配资源进入运行状态;
销毁:如果线程正常执行完毕后或线程被提前强制性的终止或出现异常导致结束,那么线程就要被销毁,释放资源;
NEW
初始化,尚未启动
RUNNABLE
可运行/运行状态
BLOCKED
阻塞(被同步锁或者IO锁唤醒)
WAITING
永久等待状态
TIMED_WAITING
等待指定的时间重新被唤醒状态
TERMINATED
执行完成,终止状态
保证多线程运行安全
让类无状态
让变量线程私有化
加锁
wait和sleep区别
wait
不用抛异常, 只能在同步方法和同步代码块使用 ,会释放锁 sleep
要抛异常, 可以在任何地方使用,没有释放锁,让出线程,监控,状态依然保持 用法不同:sleep
时间到会自动恢复,wait
可以使用notify()/notifyAll()
直接唤醒
notify和notifyAll区别
notify
: 只会唤醒一个线程,可能会导致死锁 notifyAll
: 将全部线程由等待池移到锁池
run和start区别
start()
方法用于启动线程,run()
方法用于执行线程的运行时代码 run()
可以重复调用,start()
只能调用一次
volatile关键字
修饰变量,不会发生阻塞,解决变量在多个线程中的可见性
synchronized关键字
synchronized
关键字解决的是多个线程之间访问资源的同步性, synchroized
关键字可以保证被它修饰的方法或代码块在某一时刻只能有一个线程执行
另外,在 Java 早期版本中,synchronized属于重量级锁,效率低下,因为监视器锁(monitor)是依赖于底层的操作系统的 Mutex Lock 来实现的,Java 的线程是映射到操作系统的原生线程之上的。 如果要挂起或者唤醒一个线程,都需要操作系统帮忙完成,而操作系统实现线程之间的切换时需要从用户态转换到内核态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高,这也是为什么早期的synchronized 效率低的原因。 庆幸的是在 Java 6 之后Java 官方对从 JVM 层面对synchronized 较大优化,所以现在的 synchronized 锁效率也优化得很不错了。 JDK1.6对锁的实现引入了大量的优化,如自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销。
jdk1.6以后 对synchronized锁做了哪些优化
适应自旋锁 自旋锁:为了减少线程状态改变带来的消耗,不停的执行当前线程
锁消除 不可能存在共享数据竞争的锁进行消除
锁粗化 将连续的加锁精简到只加一次锁
轻量级锁 无竞争条件下通过CAS消除同步互斥
偏向锁 无竞争条件下消除整个同步互斥,连CAS都不操作
如何使用synchronized关键字
synchronized关键字最主要的三种使用方式:
修饰实例方法,作用于当前对象实例加锁,进入同步代码前要获取当前对象实例的锁
修饰静态方法,作用于当前类对象加锁,进入同步代码前要获取当前对象的锁 。 也就是给当前对象加锁,会作用于类的所有对象实例,因为静态成员不属于任何一个实例对象,是类成员 (static
表明这是该类的一个 静态资源, 不管new
了多少个对象, 只有一份, 所以对 该类的 所有对象 都加了锁)。所以一个 线程A调用 一个实例对象的 非静态synchronized
方法,而线程B 需要调用这个实例对象所属类的静态synchronized
方法,是允许的,不会出现互斥现象,因为访问静态synchronized
方法占用的锁,是当前类锁,而访问非静态synchronized
方法占用的锁是当前实例对象的锁
修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。 synchronized(this)
代码块也是锁定当前对象的。synchronized
关键字加到static
静态方法和synchronized(class)
代码块上都是给Class
类上锁。 synchronized
关键字加到非static
静态方法上是给对象实例加锁。 注意:尽量不要用synchronized(String s)
因为JVM
中,字符串常量池具有缓冲功能
synchronized 关键字的具体使用: 单例模式了解吗?来给我手写一下!给我解释一下双重检验锁方式实现单例模式的原理 呗!
双重校验锁实现对象单例(线程安全)
public class Singleton {
private volatile static Singleton user;
private Singleton ( ) { }
public static Singleton getUser ( ) {
if ( user== null) {
synchronized ( Singleton. class ) {
if ( user== null) {
user= new Singleton ( ) ;
}
}
}
return user;
}
}
user采用 volatile
关键字修饰也是很有必要的, user= new Singleton();
这段代码其实是分为三步执行:
分配内存空间
初始化对象
将内存空间的地址赋值给对应的引用
但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1->3->2。 指令重排在单线程环境下不会出先问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例
例如,线程 T1 执行了 1 和 3,此时 T2 调用getUniqueInstance()
后发现uniqueInstance
不为空,因此返回 uniqueInstance
,但此时 uniqueInstance
还未被初始化。 使用 volatile
可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。
synchronized和volatile区别
volatile
是变量修饰符;synchorized
是修饰类,方法,代码块
volatile
仅能实现变量的可见性,不能保证原子性;而synchorized
能保证变量的可见性和原子性
volatile
不会造成线程阻塞;synchorized
可能会
synchronized和Lock区别
synchorized
可以给类,方法,代码块加锁;而Lock
只能给代码块加锁
Lock
是接口,会造成死锁, 可以知道有没有成功获取锁
synchronized
是关键字,不会造成死锁,线程会一直等待,不能响应中断
synchronized
不需要手动获取锁和释放锁,发生异常会自动释放锁,不会造成死锁; 而Lock
需要自己加锁和释放锁,如果使用不当没有unLock()
去释放锁就会造成死锁
synchronized和ReentrantLock区别
都是可重入锁 ReentrantLock
依赖于JDK
实现 synchronized
依赖于JVM
实现,多三项功能
线程可中断
可实现公平锁
可实现选择性通知
start( )和run( )方法
通过start()
调用run()
达到多线程
单独调用run(),同步执行 通过start()
调用run()
,异步执行
启动一个线程是调用start()
,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行,但不意味着线程就会立马执行 run()
可以产生必须退出的标志来停止一个线程
为什么要用线程池
线程池提供了一种限制和管理资源,每个线程池还维护一些基本统计信息,例如已完成任务的数量
降低资源消耗。 通过利用已创建的线程降低线程创建和销毁造成的消耗
提高响应速度。 当任务到达时,任务可以不需要的等到线程创建就能立即执行
提高线程的可管理性。 统一分配,调优和监控
线程池的四种创建方式
newCachedThreadPool
创建可缓存的线程池 newFixedThreadPool
创建固定大小的线程池 newSingleThreadExecutor
创建单线程的线程池 newScheduledThreadPool
创建大小无限的线程池
线程池有那些状态
新建: 创建线程 就绪: 当start()
方法返回后 线程处于 就绪状态 运行: 获得CPU时间,进入运行状态 阻塞: sleep
方法, 获取被 其他线程持有的锁, 等待某个触发时间 死亡: run
方法执行完后,或者因为某些异常产生退出了 run 方法,该线程的生命周期结束
线程池的工作机制
请求过来后,先到线程池中看看核心线程忙不,不忙就处理了它。忙就放在队列中
放入队列时看队列满了没,没满就放入就行了。满的话,线程池扩容,直至最大线程
如果扩容时发现最大线程也是满的,那就执行拒绝策略
submit()和execute()区别
execute
: 只能执行Runnable
类型的任务,无法判断任务是否成功完成,无返回值 submit
: 可以执行Callable
和Runnable
类型的任务,但Runnable
执行无返回值, 返回个future
对象,可以用这个future
来判断任务是否完成
死锁?如何避免
两个以上线程,互相持有对方所需要的资源,导致线程处于相互等待状态,无法执行 产生原因:1.系统资源不足 2.进程运行推进的顺序不合适 3.资源分配不当
产生死锁的四个必要条件:
互斥条件: 进程在某一时间内独占资源
请求和保持条件: 一个进程因请求资源而阻塞时,对己获得的资源保持不放
不剥离条件: 进程已获得资源,在未使用完之前,不能强行剥夺
循环等待条件: 若干进程之间形成一种头尾相接的循环等待资源关系
避免: 1. 加锁顺序相同 2. 加锁时限,设置超时时间 3. 死锁检测 4. 尽量减少同步的代码块
CAS是什么?
CAS是英文单词CompareAndSwap
的缩写,中文意思是:比较并替换。CAS需要有3个操作数:内存地址V,旧的预期值A,即将要更新的目标值B。
CAS指令执行时,当且仅当内存地址V的值与预期值A相等时,将内存地址V的值修改为B,否则就什么都不做。整个比较并替换的操作是一个原子操作
锁
锁分类与使用
线程可以随意抢占CPU的资源,谁也不知道下一刻谁会抢到资源,这导致线程访问共享数据时出现了问题
锁机制是将可能出现问题的代码用锁对象锁起来,被锁起来的代码就叫同步代码块,同一时间只能有一个线程来访问这个同步代码块.这类似于数据库中事务这个概念
乐观锁/悲观锁
不是特指两种类型,指看待并发同步的角度 乐观锁: 每次拿数据时都认为别人不会修改,所以不会上锁,但在更新时会判断在此期间有没有人更新这个数据,但是在更新时会判断其他线程在这之前有没有对数据进行修改,一般使用数据版本机制或CAS操作
实现 适用于读操作多 的应用类型,可以提高吞吐量,在Java中java.util.concurrent.atomic
包下的原子变量类就是使用了乐观锁的一种实现方式CAS(Compare and Swap 比较并交换)
实现的 数据版本机制: 实现有两种,版本号和时间戳 版本号方式:
在数据表上加上个数据版本号version
字段,表示数据被修改的次数,当数据被修改时,version
值会加一。 当线程A要更新数据值时,在读取数据的同时也会读取version
值,在提交更新时,若刚才读取到的version
值相等时才更新,否则重试更新操作,直到更新成功 核心SQL代码:
update table set xxx=#{xxx}, version=version+1 where id=#{id} and version=#{version};
CAS操作:
(Compare and Swap比较并交换) ,当多个线程尝试使用CAS同时更新一个变量时,只有其中一个线程能更新变量的值,而其他线程都失败,失败的线程并不会被挂起,而是这次竞争失败,可以再次尝试 CAS操作包含三个操作数 需要读写的内存位置(V),进行比较的预期原值(A)和拟如的新值(B) 如果内存位置V的值与预期原值A相匹配,那么处理器会自动将该位置值更新为新值B,否则处理器不做任何操作 悲观锁: 总是假设最坏的情况,对于同一个数据的并发操作,一定会发生修改,哪怕没改也认为改了,所以每次拿都上锁,这样别人想拿到这个数据就会阻塞直到拿到锁,synchronized
就是悲观锁,悲观的认为不加锁并发操作一定会出问题 在对任意记录进行修改前,先尝试为该记录加上排他锁(exclusive locking)
如果加锁失败,说明该记录正在被修改,那当前查询要等待或抛出异常,具体响应方式由开发者根据实际决定 如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁 期间如果有其他对该记录做修改或排他锁的操作,都会等待我们解锁或直接抛出异常
悲观所适合写操作多的情况,乐观锁适合读操作多的情况,不加锁会带来大量的性能提升 悲观锁在Java中的使用,就是利用各种锁。 乐观锁在Java中的使用,是无锁编程,常常采用的是CAS算法,典型的例子就是原子类,通过CAS自旋实现原子操作的更新
独享锁/共享锁
独享锁: 该锁一次只能被一个线程所持有 共享锁: 该锁可以被多个线程所持有 Java ReentrantLock
是独享锁。但Lock的另一个实现类ReadWriteLock
,其读锁是共享锁,其写锁是独享锁。 读锁的共享锁可保证并发读是非常高效的,读写,写读,写写的过程是互斥的 独享锁与共享锁通过AQS来实现的,通过实现不同的方法,来实现独享或者共享 synchronized是独享锁
互斥锁/读写锁
独享锁/共享锁就是一种广义的说法,互斥锁/读写锁就是具体的实现 互斥锁在java中具体实现是ReentrantLock
读写锁在java中具体实现是ReadWriteLock
可重入锁
又名递归锁,指在同一个线程在外层方法获取锁时,在进入内层方法会自动获取锁 synchronized也是个可重入锁,可重入锁可一定程度避免死锁
synchronized void setA ( ) throws Exception{
Thread. sleep ( 1000 ) ;
setB ( ) ;
}
synchronized void setB ( ) throws Exception{
Thread. sleep ( 1000 ) ;
}
如果不是可重入锁的话,setB可能不会被当前线程执行,可能造成死锁
公平锁/非公平锁
公平锁: 多个线程按照申请锁的顺序来获取锁 非公平锁: 多个线程获取锁的顺序并不是按照申请锁的顺序,可能申请后的线程比先申请的线程优先获取锁,可能会造成优先级反转或饥饿现象 ReentrantLock
通过构造方法指定该锁是否公平锁,默认非公平锁,非公平锁的优点在于吞吐量比公平锁大 synchronized
也是种非公平锁,但不像ReentrantLock
是通过AQS来实现线程调度,所以并没有办法使其变成公平锁
分段锁
一种锁的设计,不是具体的锁,ConcurrentHashMap
其并发的实现就是通过分段锁的形式来实现高效的并发操作 以ConcurrentHashMap
来说一下分段锁的含义以及设计思想,ConcurrentHashMap
中的分段锁称为Segment,它即类似于HashMap
(JDK7和JDK8中HashMap的实现)的结构,即内部拥有一个Entry数组,数组中的每个元素又是一个链表;同时又是一个ReentrantLock
(Segment继承了ReentrantLock)。
当需要put元素的时候,并不是对整个hashmap进行加锁,而是先通过hashcode来知道他要放在哪一个分段中,然后对这个分段进行加锁,所以当多线程put的时候,只要不是放在一个分段中,就实现了真正的并行的插入。 但是,在统计size的时候,可就是获取hashmap全局信息的时候,就需要获取所有的分段锁才能统计。 分段锁的设计目的是细化锁的粒度,当操作不需要更新整个数组的时候,就仅仅针对数组中的一项进行加锁操作
偏向锁/轻量级锁/重量级锁
指synchronized
锁的三种状态,通过对象监控器在对象头的字段来表明 偏向锁: 一段同步代码一直被一个线程所访问,那么该线程会自动获取锁,降低获取锁的代价 轻量级锁: 当锁时偏向锁时,被另一个线程所访问,偏向锁会升级为轻量级锁,其他线程会通过自旋的方式尝试获取锁,不会阻塞,提高性能 重量级锁: 当锁为轻量级锁时,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数时还没获取到锁,该锁变为重量级锁, 重量级锁会让它申请的线程进入阻塞,性能降低
自旋锁
自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁 好处: 减少上下文切换的消耗 坏处: 循环会消耗CPU
网络编程
TCP协议
面向连接协议,文件传输量没有限制,数据安全,速度慢
三次握手? 目的是建立可靠的通讯信道,就是数据的发送和接收,主要目的是双方确认自己和对方的发送和接收是正常的
第一次握手:Client 什么都不能确认;Server 确认了对方发送正常,自己接收正常。 第二次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:自己接收正常,对方发送正常 第三次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:自己发送、接收正常,对方发送接收正常
四次挥手? 任一方都可在 数据传输结束后发出连接释放的通知,待对方确认后进入半关闭状态。 当另一方也没有数据再发送时,则发出连接释放通知,对方确认后完全关闭TCP连接 客户端-发送一个 FIN,用来关闭客户端到服务器的数据传送 服务器-收到这个 FIN,它发回一 个 ACK,确认序号为收到的序号加1 。和 SYN 一样,一个 FIN 将占用一个序号 服务器-关闭与客户端的连接,发送一个FIN给客户端 客户端-发回 ACK 报文确认,并将确认序号设置为收到序号加1
HTTP协议
超文本传输协议,定义了客户端和服务器之间文件传输的沟通方式 HTTP是一种通信协议,它允许将超文本标记语言HTML文档从web服务器传送到客户端的浏览器面前,无连接,无状态
tcp 和 udp的区别?
TCP面向连接;UDP是无连接的,即发送数据之前不需要建立连接
TCP提供可靠的服务。通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP无法可靠的服务
Tcp传输形式为字节流,udp为数据报文段
TCP对系统资源要求较多,UDP较少
UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信
每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
tcp 为什么要三次握手,两次不行吗?为什么?
为了实现可靠数据传输, TCP 协议的通信双方, 都必须维护一个序列号, 以标识发送出去的数据包中, 哪些是已经被对方收到的。 三次握手的过程即是通信双方相互告知序列号起始值, 并确认对方已经收到了序列号起始值的必经步骤。
如果只是两次握手, 至多只有连接发起方的起始序列号能被确认, 另一方选择的序列号则得不到确认
OSI 的七层模型都有哪些?
应用层:网络服务与最终用户的一个接口
表示层:数据的表示、安全、压缩
会话层:建立、管理、终止会话
传输层:定义传输数据的协议端口号,以及流控和差错校验
网络层:进行逻辑地址寻址,实现不同网络之间的路径选择
数据链路层:建立逻辑连接、进行硬件地址寻址、差错校验等功能
物理层:建立、维护、断开物理连接
1.物理层(网卡): 定义的硬件设备标准,用于计算机之间的数据传输,传输bit流。 2.数据链路层(交换机):数据帧,对bit数据格式化,校验。目的是保障数据传输可靠性 3.网络层(路由选择,点到点):IP寻址,通过IP连接网络上的计算机。 4.传输层(端到端):建立了主机端到端的链接。tcp、udp。ipv6传输效率高就和这层有关。 5.会话层(会话控制):管理不同设备之间通信 6.表示层(数据格式转化):对应用层数据编码和数据格式转换,保障不同设备之间通信(windows和linux)。 7.应用层:提供应用接口,为用户直接提供各种网络服务。
如何实现跨域
服务器端运行跨域 设置CORS等于 *
在单个接口使用注解@CrossOrigin
运行跨域
使用jsonp
跨域
说下jsonp跨域实现原理
JSON with Padding
,利用script
标签的src
属性 连接可以访问不同源的特性,加载远程返回的 JS函数 来执行
Web
HTTP响应状态码
Java的几种对象(PO,VO,DAO,BO,POJO)解释
一、PO(persistant object)持久化对象,可以看成是与数据库中的表映射的java对象。 最简单的PO就是对应数据库中某个表的一条记录,多个记录可以用PO的集合。 PO中不应该包含任何数据库的操作。基本上持久对象生命周期和数据库密切相关。
二、VO(Value Object)值对象。通常用于业务层之间的数据传递,和PO一样也仅仅包含数据而已。 但应是抽象出的业务对象,可以和表对象,这根据业务的需求,通常同DTO(数据传输对象),在web上传递。
三、DAO(data access object)数据库访问对象,此对象用于访问数据库。 通常和PO结合使用,DAO中包含了各种数据库的操作方法。通过它的方法,结合PO对数据库进行相关的操作。
四、BO(business object)业务对象,封装业务逻辑的java对象,通过调用DAO方法,结合PO,VO进行业务操作。
五、POJO(plain old java object)简单无规则java对象,顾名思义POJO类的作用是方便程序员使用数据库中的数据表,对于广大的程序员,可以很方便的将POJO类当做对象来进行使用,当然也是可以方便的调用其get、set方法。POJO类也给我们在struts框架中的配置带来很大的方便。
简介理解: PO: persistant object 持久对象 最形象的理解就是一个PO就是数据库中的一条记录。 好处是可以把一条记录作为一个对象处理,可以方便的转为其他对象。
BO: business object业务对象 主要作用是把业务逻辑封装为一个对象。这个对象可以包括一个或多个其它的对象。 举例说明PO和BO的关系:
比如一个简历,有教育经历、工作经历、关系等等 我们可以把教育经历对应一个PO,工作经历对应一个PO,关系对应一个PO。 建立一个对应简历的BO对象处理简历,每个BO包含这些PO。 这样处理业务逻辑时,我们就可以针对BO去处理。
VO: value object值对象。 ViewObject表现出对象 主要对应界面显示的数据对象。对应一个WEB页面,或者SWT、SWING的一个界面,用一个VO对象对应整个界面的值。
DTO: Date Transfer object 数据传输对象 主要用于远程调用等需要大量传输对象的地方。 举例说明:
比如我们一张表有100个字段,那么对应的PO就有100个属性。 但是我们界面上只要显示10个字段,客户端用WEB service来获取数据,没有必要把整个PO对象传递到客户端, 这时我们就可以用只有这10个属性的DTO来传递结果到客户端,这样也不会暴露服务端表结构,到达客户端以后,如果用这个对象来对应界面显示,那此时他的身份就转为VO。
POJO: plain old java object 简单java对象 POJO是最常见最多变得对象,是一个中间对象,也是我们最常打交道的对象。 一个POJO持久化以后就是PO 直接用它传递,传递过程中就是DTO 直接来对应表示层就是VO
DAO: data access object 数据访问对象 数据访问对象是第一个面向对象的数据库接口,是一个数据访问接口。它可以把POJO持久化为PO,用PO组装出来VO、DTO。 DAO模式是标准的j2EE设计模式之一,开发人员使用这个模式把底层的数据访问操作和上层的商务逻辑分开,一个典型的DAO实现有下列几个组件: 1.一个DAO工厂类 2.一个DAO接口 3.一个实现DAO接口的具体类 4.数据传递对象(有时候叫做值对象)
get 和 post 请求有哪些区别?
GET在浏览器回退时是无害的,而POST会再次提交请求
GET有大小限制,POST没有
GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息
GET产生的URL地址可以被Bookmark,而POST不可以
GET请求会被浏览器主动cache,而POST不会,除非手动设置
GET请求只能进行url编码,而POST支持多种编码方式
GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留
GET请求在URL中传送的参数是有长度限制的,而POST没有
对参数的数据类型,GET只接受ASCII字符,而POST没有限制
GET参数通过URL传递,POST放在Request body中
forward请求转发 和 redirect重定向 的区别?
Forward和Redirect代表了两种请求转发方式:直接转发和间接转发。
直接转发方式(Forward),客户端和浏览器只发出一次请求,Servlet、HTML、JSP或其它信息资源,由第二个信息资源响应该请求,在请求对象request中,保存的对象对于每个信息资源是共享的。
间接转发方式(Redirect)实际是两次HTTP请求,服务器端在响应第一次请求的时候,让浏览器再向另外一个URL发出请求,从而达到转发的目的
jsp 和 servlet 有什么区别?
jsp经编译后就变成了Servlet.(JSP的本质就是Servlet,JVM只能识别java的类,不能识别JSP的代码,Web容器将JSP的代码编译成JVM能够识别的java类)
jsp更擅长表现于页面显示,servlet更擅长于逻辑控制
Servlet中没有内置对象,Jsp中的内置对象都是必须通过HttpServletRequest
对象,HttpServletResponse
对象以及HttpServlet
对象得到
Jsp是Servlet的一种简化,使用Jsp只需要完成程序员需要输出到客户端的内容,Jsp中的Java脚本如何镶嵌到一个类中,由Jsp容器完成。而Servlet是个完整的Java类,这个类的Service方法用于生成对客户端的响应
servlet与流程
Servlet是独立于平台和协议的服务器端的Java应用程序,可以动态生成Web页面,并采用响应
服务器启动通过init()方法
初始化servlet,再根据不同的请求调用doGet()或doPost()方法
,最后通过dostroy()方法
销毁
servlet
是单例的,可以提高性能
jsp 有哪些内置对象?作用分别是什么?
四个作用域,两个输出,三个酱油
request: 客户端请求,包含来自Get
和Post
请求的参数
session: 会话
pageContext: 通过该对象可以获取其他对象
application: 封装服务器运行环境的对象
response: 服务器响应
out: 输出服务器响应的输出流对象
page: JSP页面本身(相当于java的this)
config: web应用的配置对象
exception: 封装页面抛出异常的对象
说一下 jsp 的 4 种作用域?
page: 一个页面相关的对象和属性
request: Web客户机发出的一个请求相关的对象和属性。 一个请求可能跨越多个页面,涉及多个Web组件;需要在页面显示的临时数据可以置于此作用域
session: 某个用户与服务器建立的一次会话相关的对象和属性。 跟某个用户相关的数据应该放在用户自己的session中
application: 整个Web应用程序相关的对象和属性,实质上是跨越整个Web应用程序,包括多个页面、请求和会话的一个全局作用域
会话跟踪有哪些?
Cookie,session,application
Cookie
是http对象,客户端和服务端都可以操纵 cookie
是在客户端保存状态,session
是服务端保存状态, 由于cookie
是保存在客户端本地,所以数据容易被窃取,当访问量很多时使用session
则会降低服务器的性能 application
的作用域是整个工程只有一个,可以在不同浏览器之间共享数据,所有人都可以共享,所有也是不安全的
request ,response,session 和 application是怎么用的
Request
是客户端向服务端发送请求 Response
是服务端对客户端请求做出响应 Session
在servlet中不能直接使用,需要通过getSession()
创建,如果没有设定它的生命周期,或者通过invildate()
方法销毁,关闭浏览器session
就会消失 Application
不能直接创建,存在于服务器的内存中,由服务器创建和销毁
session和cookie区别
存储位置不同: session存在服务器端,cookie存在浏览器端
cookie有容量限制,每个站点下的cookie也有个数限制
session可以存在Redis,数据库,应用程序中;cookie只能存在浏览器中
说一下 session 的工作原理?
session是一个存在服务器上的类似于一个散列表格的文件。 里面存有我们需要的信息,在我们需要用的时候可以从里面取出来。 类似于一个大号的map,里面的键存储的是用户的sessionid,用户向服务器发送请求的时候会带上这个sessionid。这时就可以从中取出对应的值了。
为什么在session少放对象
因为session
底层是由cookie
实现的,当客户端的cookie
被禁用后,session
也会失效 且应尽量少向session
中保存信息,session
的数据保存在服务器端,当有大量session
时,会降低服务器的性能
Request和Session的取值区别,以及出现乱码的解决方式(不能在java代码中设置)
Request
可以通过getAttribute()
方法直接取值,也可通过getParameter()
取值 Session
需要通过request.getSession().getAttribute()
才能取值 Request
是针对一次请求,Session
是针对整个会话 乱码: 在页面通过contentType,pageEncoding,content
设置编码格式,必须要一致
怎么判断用户请求时是第一次,如果客户端和服务端断开怎么连到上一次操作
通过session
的isNew()
可以判断是否是新用户 可以用cookie
来保存信息到客户端,可以连接上一次操作
getParameter和getAttribute区别
getParameter()
获取的是客户端
设置的数据, 永远返回字符串
getAttribute()
获取的是服务器
设置的数据, 返回值是任意类型
既然parameter和attribute都是传递参数,为什么不直接使用parameter
呢? ①服务器端不能通过setParameter(key, value)
来添加参数,因为没有这个函数 所以如果需要在服务器端进行跳转,并需要想下个页面发送新的参数时,则没法实现。 但是Attribute
可以,可以通过setAttribute()
,将值放入到request对象
,然后在其他页面使用getAttribute
获取对应的值,这样就达到一次请求可以在多个页面共享一些对象信息
②parameter
返回值是字符串,意味着不能传递其他的对象,如Map,List
,但是attribute则可以存放任意类型的Java对象
pageContext有什么作用
可以使用pageContext
对象来设定属性,并指定属性的作用范围,提供了对JSP页面内所有的对象及名字空间的访问
过滤器(Filter)怎么执行的
首先初始化过滤器,然后服务器组织过滤器链,所有的请求都必须需要先通过过滤器链, 过滤器链是一个栈,遵循先进后出的原则 ,所有的请求需要经过一个一个的过滤器,执行顺序要根据web.xml
里配置的
的位置前后执行,每个过滤器之间通过chain.doFilter连接, 最后抵达真正请求的资源,执行完后再从过滤器链退出
Servlet和过滤器的区别
Servlet:用来处理客户端发送的请求,然后生成响应传给服务器,最后服务器将响应返回给客户端 Filter: 用于对servlet容器调用servlet的过程进行拦截,可以在serlet进行响应处理前后做一些特殊的处理,如日志,权限,编码等
设计模式
设计模式总结
单例模式
单例模式的核心是保证一个类只有一个实例,并提供一个访问实例的全局访问点
只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,可通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决
使用场景:
Spring中bean对象的模式实现方式
spring mvc 和struts2 框架中,控制器对象是单例模式
servlet中每个servlet的实例
数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源
实现方式
优缺点
饿汉式
线程安全,调用效率高 ,但是不能延迟加载
懒汉式
线程安全,调用效率不高,能延迟加载
双重检测锁式
由于JVM底层内部模型原因,偶尔会出问题。不建议使用
静态内部类式
线程安全,资源利用率高,可以延时加载
枚举单例
线程安全,调用效率高,但是不能延迟加载
饿汉式
public class User {
private User ( ) { }
private static final User user= new User ( ) ;
public static User ( ) {
return User;
}
}
饿汉式在 类创建的同时就 实例化一个 静态对象出来,不管之后用不用这个单例,都会占据一定的内存, 但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成
懒汉式
public class User {
private User ( ) { }
private static User user= null;
public static User getInstance ( ) {
if ( user == null) {
synchronized ( User. class ) {
if ( user == null) {
user = new User ( ) ;
}
}
}
return lazySingleton;
}
}
在getInstance()
中做了两次null
检查,确保了只有第一次调用单例的时候才会做同步,这样也是线程安全的,同时避免了每次都同步的性能损耗 缺点:有同步锁的性能消耗
静态内部类实现
public class User {
private User ( ) { }
public static User getInstance ( ) {
return HolderClass. user;
}
private static class HolderClass {
private static User user= new User ( ) ;
}
private Object readResolve ( ) throws ObjectStreamException {
return HolderClass. user;
}
}
1.外部类没有static
属性,则不会像饿汉式那样立即加载对象。 2.只有真正调用getInstance()
,才会加载静态内部类。加载类时是线程 安全的instance
是static final
类型,保证了内存中只有这样一个实例存在,而且只能被赋值一次,从而保证了线程安全性. 3.兼备了并发高效调用和延迟加载的优势
getInstance
方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本 优势:兼顾了懒汉模式的内存优化(使用时才初始化)以及饿汉模式的安全性(不会被反射入侵) 劣势:需要两个类去做到这一点,虽然不会创建静态内部类的对象,但是其 Class 对象还是会被创建,而且是属于永久带的对象。
双重检测锁
public class SingletonInstance3 {
private static SingletonInstance3 instance = null;
private SingletonInstance3 ( ) { }
public static SingletonInstance3 getInstance ( ) {
if ( instance == null) {
SingletonInstance3 s3 = null;
synchronized ( SingletonInstance3. class ) {
s3 = instance;
if ( s3 == null) {
synchronized ( SingletonInstance3. class ) {
if ( s3 == null) {
s3 = new SingletonInstance3 ( ) ;
}
}
}
instance = s3;
}
}
return instance;
}
}
该模式将同步内容下方到if内部,提高执行效率不必每次获取对象时都进行同步,只有第一次才同步创建了以后就没必要了。 问题:由于编译器优化原因和JVM底层内部模型原因,偶尔会出问题。不建议使用
枚举
public enum SingletonInstance5 {
INSTANCE;
public void singletonOperation ( ) {
}
}
枚举本身就是单例模式。由JVM从根本上提供保障!避免通过反射和反序列化的漏洞 缺点:没延迟加载
单例模式的漏洞 1.通过反射的方式我们依然可用获取多个实例(除了枚举的方式)
public static void main ( String[ ] args) throws Exception, IllegalAccessException {
SingletonInstance1 s1 = SingletonInstance1. getInstance ( ) ;
Class c1 = SingletonInstance1. class ;
Constructor constructor = c1. getDeclaredConstructor ( null) ;
constructor. setAccessible ( true ) ;
SingletonInstance1 s2 = ( SingletonInstance1) constructor. newInstance ( null) ;
}
解决方式:在无参构造方法中手动抛出异常控制
private SingletonInstance2 ( ) {
if ( instance != null) {
throw new RuntimeException ( "单例模式只能创建一个对象" ) ;
}
}
2.通过反序列化的方式也可以破解上面几种方式(除了枚举的方式)
public static void main ( String[ ] args) throws Exception, IllegalAccessException {
SingletonInstance s1 = SingletonInstance. getInstance ( ) ;
ObjectOutputStream oos = new ObjectOutputStream (
new FileOutputStream ( "c:/tools/a.txt" ) ) ;
oos. writeObject ( s1) ;
oos. flush ( ) ;
oos. close ( ) ;
ObjectInputStream ois = new ObjectInputStream (
new FileInputStream ( "c:/tools/a.txt" ) ) ;
SingletonInstance s2 = ( SingletonInstance) ois. readObject ( ) ;
ois. close ( ) ;
}
解决:在单例类中重写readResolve方法并在该方法中返回单例对象
public class SingletonInstance implements Serializable {
private static SingletonInstance instance = null;
private SingletonInstance ( ) {
if ( instance != null) {
throw new RuntimeException ( "单例模式只能创建一个对象" ) ;
}
}
public static synchronized SingletonInstance getInstance ( ) {
if ( instance == null) {
instance = new SingletonInstance ( ) ;
}
return instance;
}
private Object readResolve ( ) throws ObjectStreamException{
return instance;
}
}
总结: 1、单例对象占用资源少,不需要延时加载:
枚举式 好于 饿汉式
2、单例对象占用资源大,需要延时加载:
静态内部类式 好于 懒汉式
工厂模式
定义一个创建对象的接口,让其子类自己决定实例化那个工厂类,工厂模式使其创建过程延迟到子类进行
简单工厂模式 : 由一个工厂对象决定创建出哪一种产品类的实例
工厂方法模式 : 核心的工程类不再负责所以的产品的创建,而是将具体创建的工作交给子类去做。 让核心类成为抽象工厂角色,仅负责给出具体工厂子类必须要实现的接口,而不接触哪一个产品类应当被实例化
抽象工厂模式 : 当有多个抽象角色时,使用的工厂模式。 可以向客户端提供一个接口,使客户端在不必指定产品的具体情况下,创建多个产品族中的产品对象
观察者模式
对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新
代理模式
静态代理
类代理对象与目标对象实现相同的接口或继承相同的父类,需要创建一个代理类 只能为一个被代理类服务,如果需要代理的类比较多,那么会产生过多的代理类。 静态代理在编译时产生class文件,运行时无需产生,可直接使用,效率好。
jdk动态代理
必须实现接口,通过反射来动态代理方法,消耗系统性能。 但是无需产生过多的代理类,避免了重复代码的产生,系统更加灵活。 调用Proxy.newProxyInstance
(3个参数:类加载器,目标对象实现的所有接口,事务处理器对象invocationHandler
,并重写invoke
);生成一个代理实例,通过该代理实例调用方法
cglib动态代理
无需实现接口,通过生成子类字节码来实现,比反射快一点,没有性能问题。 但是由于cglib会继承被代理类,需要重写被代理方法,所以被代理类不能是final类,被代理方法不能是final。
因此,cglib的应用更加广泛一点
你可能感兴趣的:(Java SE)
ProGuard加密混淆SpringBoot应用代码
lbmydream
spring boot 后端 java
背景我们的项目是基于SpringCloud架构的微服务应用,采用Docker离线部署方式交付客户,通过授权证书来控制应用的许可功能模块和使用时间。我们已经在代码层已经实现:基于多维度硬件指纹的绑定验证,cpuid、mac地址、磁盘序列、系统时钟、应用初始时间等双重时间验证机制(系统时间+硬件时钟)安全续期机制支持离线更新防调试/防篡改保护来解决离线容器化部署Java应用程序授权问题。整体流程如下:
OpenCV计算摄影学(2)图像去噪函数denoise_TVL1()
村北头的码农
OpenCV opencv 人工智能 计算机视觉
操作系统:ubuntu22.04OpenCV版本:OpenCV4.9IDE:VisualStudioCode编程语言:C++11算法描述原始-对偶算法是用于解决特定类型变分问题(即,寻找一个函数以最小化某个泛函)的算法。特别地,图像去噪可以被视为一种变分问题,因此可以使用原始-对偶算法来进行去噪,这正是这里实现的内容。需要注意的是,此实现取自2013年7月的一篇博客文章[194],该文章还包含了(
ElasticSearch 数据老化清理之Curator
思快奇
java elasticsearch linux 大数据 分布式
前言随着接入ELK日志分析平台的系统增加,每日海量的日志数据使得磁盘空间越发紧张,从而ES的日志清理也成了一个常规操作。而ES官方也提供了一个很好用的工具——curator来实现这个事情。抽空也部署了一个,以供阅知。Curator简介据官方介绍,curator最早被称为clearESindices.py的python脚本,它的唯一功能就是删除索引清理数据,而后重命名为logstash_index_
vue iview 实现搜索 组件
hamburgerDaddy1
原创 vue
0">搜索历史-->清空{{item.name}}exportdefault{name:'citicSearch',data(){return{search_key:'',searchHistory:[],isRecord:false//搜索历史框显示}},methods:{//清空历史记录clearHistory(){localStorage.removeItem('citicSearchRec
URL传参中文乱码问题
fan510988896
JAVA URL路径传参乱码 URL乱码 路径乱码
今天是Mock单元测试时,有几个测试是从路径获取参数,但是有一个需要传递中文参数,但是传递过去久成了乱码。后找到有效的,记录下来:publicstaticvoidmain(String[]args)throwsException{Stringvalue="%E8%8A%9D%E9%BA%BB%E5%BC%80%E9%97%A8";Stringaa=URLDecoder.decode(value,"
在k8s中,如何在argocd中添加proxy
网络飞鸥
kubernetes argocd 容器
在Kubernetes的ArgoCD中添加代理(Proxy)设置,你可以从多个层面进行操作,下面分别介绍不同组件设置代理的方法。1.ArgoCDServer代理设置ArgoCDServer负责提供WebUI和API服务,要为其设置代理,可以通过修改ArgoCDServer的部署配置来实现。步骤:编辑ArgoCDServer部署kubectleditdeploymentargocd-server-n
Python爬虫selenium框架基本使用
啧不应该啊
Python爬虫 python 爬虫 selenium
一、安装导入使用包管理器安装pip3installselenium二、WebDriver工具要使用这个工具我们需要保证安装了一个浏览器的驱动器。Python的WebDriver是一个用于自动化Web浏览器操作的工具,它属于Selenium的一部分,特别是Selenium2.0及以后版本中,WebDriver已经成为了Selenium的主要组件。WebDriver为Web自动化提供了一个简单的接口,
深入理解SAP HANA Cloud Vector Engine与自查询
VYSAHF
java
学习目标:提示:这里可以添加学习目标例如:一周掌握Java入门知识学习内容:提示:这里可以添加要学的内容例如:搭建Java开发环境掌握Java基本语法掌握条件语句掌握循环语句学习时间:提示:这里可以添加计划学习的时间例如:周一至周五晚上7点—晚上9点周六上午9点-上午11点周日下午3点-下午6点学习产出:提示:这里统计学习计划的总量例如:技术笔记2遍CSDN技术博客3篇习的vlog视频1个
java练习(39)
IIIIIIlllii
java leetcode 开发语言
ps:题目来自力扣三数之和给你一个整数数组nums,判断是否存在三元组[nums[i],nums[j],nums[k]]满足i!=j、i!=k且j!=k,同时还满足nums[i]+nums[j]+nums[k]==0。请你返回所有和为0且不重复的三元组。注意:答案中不可以包含重复的三元组。importjava.util.ArrayList;importjava.util.Arrays;import
《从新手到高手:SEO 优化全方位指南》-查词人蜘蛛池
蜘蛛池.中国
seo 前端 百度 sequoiadb
基础入门篇了解SEO基本概念:SEO即搜索引擎优化,是通过优化网站结构、内容、链接等,提升网站在搜索引擎结果页面中排名的技术和方法。需熟悉搜索引擎的工作原理,包括网页抓取、索引建立和排序机制,同时掌握关键词、标题标签、元描述、外链、内链、锚文本等核心术语12.明确SEO的重要性:SEO能为网站带来自然搜索流量,这种流量质量高、稳定性强。通过优化网站,可在搜索引擎结果页获得更高排名,吸引更多潜在客户
Python爬虫实战:电商数据爬取与价格趋势分析
Python爬虫项目
2025年爬虫实战项目 python 爬虫 开发语言 零售 mongodb 人工智能
摘要本文详细介绍了如何使用Python编写爬虫程序,从电商网站抓取商品数据,并对价格趋势进行分析。我们将使用最新的爬虫技术和数据分析工具,包括Selenium、BeautifulSoup、Pandas和Matplotlib等。通过本文,读者将学习到如何构建一个完整的电商数据爬取与分析系统,并掌握相关技术在实际项目中的应用。关键词Python爬虫、电商数据、价格趋势分析、Selenium、Beaut
MongoDB私人学习笔记
奕辰杰
私人学习笔记 学习 笔记 mongodb java
俗话说“好记性不如烂笔头”,编程的海洋如此的浩大,养成做笔记的习惯是成功的一步!此笔记主要是ZooKeeper3.4.9版本的笔记,并且笔记都是博主自己一字一字编写和记录,有错误的地方欢迎大家指正。一、基础知识:1、MongoDB的名称来源于HumongousDatabase,中文意思就是巨大无比的数据库,顾名思义,MongoDB就是为处理大数据而生,以解决海量数据的存储和高效查询使用为使命。是N
MongoDB 复制(副本集)
froginwe11
开发语言
MongoDB复制(副本集)引言MongoDB是一个高性能、可扩展、易于使用的文档存储系统。它以JSON-like的文档存储结构,支持灵活的数据模型。在分布式系统中,为了提高数据可用性和系统稳定性,常常需要实现数据的备份和冗余。MongoDB提供了副本集(ReplicaSet)功能,可以实现数据的自动备份和故障转移。本文将详细介绍MongoDB副本集的原理、配置和操作。副本集原理MongoDB副本
《百度蜘蛛池:提升网站收录的关键利器》
蜘蛛池.中国
dubbo
T@baidutopseo(蜘蛛池.中国)在互联网流量竞争日益激烈的今天,网站能否被搜索引擎快速收录并获取排名,直接影响着企业的线上曝光与转化效率。作为国内最大的搜索引擎,百度收录规则始终是站长们研究的重点。而"百度蜘蛛池"这一技术工具,凭借其对搜索引擎蜘蛛(Spider)的精准调度能力,已成为提升网站收录效率的"秘密武器"。本文将深入解析蜘蛛池的运作原理、实践价值及合规使用方法。一、蜘蛛池:搜索
【SQL数据库】MySQL的下载和使用(windows)
奖励0216
数据库 sql mysql
1在网页上搜索MySQLcommunity2点击mysqlcommunitydownloads3选择MySQLinstallerforwindows4选择上面那个download5不用登录6下载完就安装7Choosingasetuptype选custom就够了8selectproductsMySQLServes-MySQLServe8.0-选最上面的最新版-点右边的箭头,加入右边Applicati
Docker 安装 PostgreSQL
王梓麒
Docker docker postgresql 容器
1.安装Docker和DockerCompose首先,确保你的服务器上已经安装了Docker和DockerCompose。你可以通过以下命令检查它们是否安装:docker--versiondocker-compose--version2.创建项目目录在你的服务器上创建一个用于存放PostgreSQLDockerCompose配置的目录,例如my-project:mkdirmy-projectcdm
CMake学习记录
Spoon_94
C/C++ CMake
参考文章:CMakePractice文章目录PROJECTSETMESSAGEADD_EXECUTABLE变量ADD_SUBDIRECTORYLIBRARY_OUTPUT_PATH/EXECUTABLE_OUTPUT_PATHINSTALL安装普通文件非目标文件的可执行程序安装(脚本)目录安装CMAKE脚本执行静态库与动态库SET_TARGET_PROPERTIESGET_TARGET_PROPE
spi配置参数CPOL CPHA
辰熤✔
单片机 嵌入式硬件
在SPI(SerialPeripheralInterface,串行外设接口)通信协议中,CPOL和CPHA是两个重要的参数,它们定义了SPI通信中的时钟信号特性。SPI是一种同步串行通信协议,用于在微控制器或其他处理器与外设之间传输数据。CPOL(ClockPolarity):CPOL代表时钟信号的静态(即没有数据传输时的)电平状态。当CPOL=0时,时钟信号在空闲状态下为低电平。当CPOL=
Rapidly Changing Dimension (RCD) in Data Warehouse 杂项维度
胖胖胖胖胖虎
数据库
RapidlyChangingDimension(RCD)inDataWarehouseAdimensionisafastchangingorrapidlychangingdimensionifoneormoreofitsattributesinthetablechangesveryfastandinmanyrows.Handlingrapidlychangingdimensionindatawa
docker-compose -volumes 两种不同定义方式
胖胖胖胖胖虎
Docker docker hadoop big data
学习docker-compose部署hadoop集群、看到docker-compose一种不同volumes定义方式version:"3"services:namenode:image:bde2020/hadoop-namenode:2.0.0-hadoop2.7.4-java8volumes:-namenode:/hadoop/dfs/nameenvironment:-CLUSTER_NAME=
SpringBoot-MyBatis-plus实体类中常用的注解
or77iu_N
MyBatis java mybatis spring boot 数据库
@TableName在Java类上通过添加@TableName注解并指定表名,ORM框架会将该类与指定的数据库表进行映射@TableId(type=IdType.AUTO)在实体类中,使用@TableId注解来标记主键字段。@TableField("xxx")当数据库中的字段名与实体类中的属性名不一致时,可以使用@TableField注解来指定数据库表中的字段名。@TableField(exist
基于SpringBoot+mybatisplus+vueJS的高校选课系统设计和实现
华子w908925859
spring boot vue.js 后端
博主介绍:硕士研究生,专注于信息化技术领域开发与管理,会使用java、标准c/c++等开发语言,以及毕业项目实战✌从事基于javaBS架构、CS架构、c/c++编程工作近16年,拥有近12年的管理工作经验,拥有较丰富的技术架构思想、较扎实的技术功底和资深的项目管理经验。先后担任过技术总监、部门经理、项目经理、开发组长、java高级工程师及c++工程师等职位,在工业互联网、国家标识解析体系、物联网、
python 利用Crypto进行AES解密&加密文件
LingRannn
linux python python
前言:今天写一个程序的时候:加密模式:AES/CBC/PKCS5Padding加密初始化向量:长度为16的空字节数组一直搞不懂长度为16的空字节数组怎么表示然后我用ECB模式发现跟他给的例子密码一样,头大。背景:工作需要,部分数据进行了加密传输,对方使用了AES对密码进行了加密,需要获取到解密的数据。目标:通过密钥成功解密文件。关键词:AES_ECB,AES_CBC,Java和Python的AES
VBA第十四期 一种可以灵活显示的列表
jimin_callon
windows 服务器 linux
一种可以灵活显示的列表。VBA列表设计的时候,我们有时候需要显示足够多的数据,但如果我们一次性定义过多列,例如UserForm2.ListBox1.ColumnCount=100,这样定义有两个弊端,一是经常要拖动滚动条,二是大部分时间不需要看这么多数据。这样我们就需要设计一种可以灵活显示的列表,能够增加和减少显示列数。这里是通过一个文本框和两个按钮,一个增加显示列、一个减少显示列按钮,文本框用于
idea创建第一个springboot程序
勘察加熊人
springboot intellij-idea spring boot java
说明:我计划用idea,创建第一个springboot程序,但是作为新手完全不会弄,今天我就亲自尝试一边,并且出一期详细,完美的教程,亲测可以运行step1.点击file,选new,选择projectstep2.选择左侧的springboot,右侧language勾选java,type选择maven,jdk选择18,java选择17,packaging选择jarstep3.点击nextdevelo
计算机毕业设计springboot的旅游信息管理系统设计与实现hyrsf9【附源码+数据库+部署+LW】
苏苏酱 ゛计算机毕设源码程序
课程设计 spring boot 旅游
本项目包含程序+源码+数据库+LW+调试部署环境,文末可获取一份本项目的java源码和数据库参考。系统的选题背景和意义选题背景:随着社会经济的发展和人们生活水平的提高,旅游业成为了人们休闲娱乐、增长知识、放松身心的重要方式之一。然而,传统的旅游信息管理方式存在着许多问题,如信息不透明、效率低下、服务质量难以保证等。因此,设计和实现一个高效、便捷的旅游信息管理系统具有重要的意义。意义:首先,旅游信息
鸿蒙ArkTs如何实现pdf预览功能?
前言在开发鸿蒙App时,你是否做过pdf预览功能。是否也和我一样碰壁了,来看看我遇到的问题,以及我是如何实现的吧。PDFKit运行示例代码报错therequestedmodule'@hms:officeservice.PdfView'doesnotprovideanexportname'pdfViewManager'whichimportedby'xxxx'真机运行本来以为用真机就能运行了,没想到
springboot毕设项目自驾旅游网站的设计与实现gah85(java+VUE+Mybatis+Maven+Mysql)
计算机程序设计源哥
java spring boot 旅游
springboot毕设项目自驾旅游网站的设计与实现gah85(java+VUE+Mybatis+Maven+Mysql)项目运行环境配置:Jdk1.8+Tomcat8.5+Mysql+HBuilderX(Webstorm也行)+Eclispe(IntelliJIDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot+mybatis+Maven+Vue等等组成,
驱动人生海外版 Driver Talent Pro汉化专业版 v8.1.11.58(支持Windows Server)
S3软件
工具补丁 windows
驱动人生海外版已汉化,而且是免安装便携版,或许它更适合你使用呢,软件能实现智能检测硬件并自动查找安装驱动,为用户提供最新驱动更新,本机驱动备份、还原和卸载等功能。1.全自动检测电脑硬件,自动匹配官方驱动只需一键扫描轻松实现方便、快捷、智能的的硬件识别检测,上百G不断完善的官方驱动库,驱动人生的目标是检测识别所有硬件,并自动为您的计算机下载安装稳定性最好、性能最高的驱动程序。2、全自动检测最新驱动并
算法解析之去重逻辑
算法去重
前言:在之前做题中经常被去重问题困扰,其中最常见的去重思想就是使用集合本身的特性进行去重,比如使用set集合或者map集合进行去重,防止相同元素的重复加入,但是在有些问题时,需要去重的不仅仅是元素,可能是一整个数组或者其他集合,这个时候如果进行一一比较,可能就比较繁琐,需要通过代码逻辑来实现避免重复对象的插入。这里有道题可以很好的理解去重的逻辑,给大家展示一下。leetcode之三数之和注意:本题
java Illegal overloaded getter method with ambiguous type for propert的解决
zwllxs
java jdk
好久不来iteye,今天又来看看,哈哈,今天碰到在编码时,反射中会抛出
Illegal overloaded getter method with ambiguous type for propert这么个东东,从字面意思看,是反射在获取getter时迷惑了,然后回想起java在boolean值在生成getter时,分别有is和getter,也许我们的反射对象中就有is开头的方法迷惑了jdk,
IT人应当知道的10个行业小内幕
beijingjava
工作 互联网
10. 虽然IT业的薪酬比其他很多行业要好,但有公司因此视你为其“佣人”。
尽管IT人士的薪水没有互联网泡沫之前要好,但和其他行业人士比较,IT人的薪资还算好点。在接下的几十年中,科技在商业和社会发展中所占分量会一直增加,所以我们完全有理由相信,IT专业人才的需求量也不会减少。
然而,正因为IT人士的薪水普遍较高,所以有些公司认为给了你这么多钱,就把你看成是公司的“佣人”,拥有你的支配
java 实现自定义链表
CrazyMizzz
java 数据结构
1.链表结构
链表是链式的结构
2.链表的组成
链表是由头节点,中间节点和尾节点组成
节点是由两个部分组成:
1.数据域
2.引用域
3.链表的实现
&nbs
web项目发布到服务器后图片过一会儿消失
麦田的设计者
struts2 上传图片 永久保存
作为一名学习了android和j2ee的程序员,我们必须要意识到,客服端和服务器端的交互是很有必要的,比如你用eclipse写了一个web工程,并且发布到了服务器(tomcat)上,这时你在webapps目录下看到了你发布的web工程,你可以打开电脑的浏览器输入http://localhost:8080/工程/路径访问里面的资源。但是,有时你会突然的发现之前用struts2上传的图片
CodeIgniter框架Cart类 name 不能设置中文的解决方法
IT独行者
CodeIgniter Cart 框架
今天试用了一下CodeIgniter的Cart类时遇到了个小问题,发现当name的值为中文时,就写入不了session。在这里特别提醒一下。 在CI手册里也有说明,如下:
$data = array(
'id' => 'sku_123ABC',
'qty' => 1,
'
linux回收站
_wy_
linux 回收站
今天一不小心在ubuntu下把一个文件移动到了回收站,我并不想删,手误了。我急忙到Nautilus下的回收站中准备恢复它,但是里面居然什么都没有。 后来我发现这是由于我删文件的地方不在HOME所在的分区,而是在另一个独立的Linux分区下,这是我专门用于开发的分区。而我删除的东东在分区根目录下的.Trash-1000/file目录下,相关的删除信息(删除时间和文件所在
jquery回到页面顶端
知了ing
html jquery css
html代码:
<h1 id="anchor">页面标题</h1>
<div id="container">页面内容</div>
<p><a href="#anchor" class="topLink">回到顶端</a><
B树、B-树、B+树、B*树
矮蛋蛋
B树
原文地址:
http://www.cnblogs.com/oldhorse/archive/2009/11/16/1604009.html
B树
即二叉搜索树:
1.所有非叶子结点至多拥有两个儿子(Left和Right);
&nb
数据库连接池
alafqq
数据库连接池
http://www.cnblogs.com/xdp-gacl/p/4002804.html
@Anthor:孤傲苍狼
数据库连接池
用MySQLv5版本的数据库驱动没有问题,使用MySQLv6和Oracle的数据库驱动时候报如下错误:
java.lang.ClassCastException: $Proxy0 cannot be cast to java.sql.Connec
java泛型
百合不是茶
java泛型
泛型
在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,任意化的缺点就是要实行强制转换,这种强制转换可能会带来不安全的隐患
泛型的特点:消除强制转换 确保类型安全 向后兼容
简单泛型的定义:
泛型:就是在类中将其模糊化,在创建对象的时候再具体定义
class fan
javascript闭包[两个小测试例子]
bijian1013
JavaScript JavaScript
一.程序一
<script>
var name = "The Window";
var Object_a = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){
探索JUnit4扩展:假设机制(Assumption)
bijian1013
java Assumption JUnit 单元测试
一.假设机制(Assumption)概述 理想情况下,写测试用例的开发人员可以明确的知道所有导致他们所写的测试用例不通过的地方,但是有的时候,这些导致测试用例不通过的地方并不是很容易的被发现,可能隐藏得很深,从而导致开发人员在写测试用例时很难预测到这些因素,而且往往这些因素并不是开发人员当初设计测试用例时真正目的,
【Gson四】范型POJO的反序列化
bit1129
POJO
在下面这个例子中,POJO(Data类)是一个范型类,在Tests中,指定范型类为PieceData,POJO初始化完成后,通过
String str = new Gson().toJson(data);
得到范型化的POJO序列化得到的JSON串,然后将这个JSON串反序列化为POJO
import com.google.gson.Gson;
import java.
【Spark八十五】Spark Streaming分析结果落地到MySQL
bit1129
Stream
几点总结:
1. DStream.foreachRDD是一个Output Operation,类似于RDD的action,会触发Job的提交。DStream.foreachRDD是数据落地很常用的方法
2. 获取MySQL Connection的操作应该放在foreachRDD的参数(是一个RDD[T]=>Unit的函数类型),这样,当foreachRDD方法在每个Worker上执行时,
NGINX + LUA实现复杂的控制
ronin47
nginx lua
安装lua_nginx_module 模块
lua_nginx_module 可以一步步的安装,也可以直接用淘宝的OpenResty
Centos和debian的安装就简单了。。
这里说下freebsd的安装:
fetch http://www.lua.org/ftp/lua-5.1.4.tar.gz
tar zxvf lua-5.1.4.tar.gz
cd lua-5.1.4
ma
java-递归判断数组是否升序
bylijinnan
java
public class IsAccendListRecursive {
/*递归判断数组是否升序
* if a Integer array is ascending,return true
* use recursion
*/
public static void main(String[] args){
IsAccendListRecursiv
Netty源码学习-DefaultChannelPipeline2
bylijinnan
java netty
Netty3的API
http://docs.jboss.org/netty/3.2/api/org/jboss/netty/channel/ChannelPipeline.html
里面提到ChannelPipeline的一个“pitfall”:
如果ChannelPipeline只有一个handler(假设为handlerA)且希望用另一handler(假设为handlerB)
来
Java工具之JPS
chinrui
java
JPS使用
熟悉Linux的朋友们都知道,Linux下有一个常用的命令叫做ps(Process Status),是用来查看Linux环境下进程信息的。同样的,在Java Virtual Machine里面也提供了类似的工具供广大Java开发人员使用,它就是jps(Java Process Status),它可以用来
window.print分页打印
ctrain
window
function init() {
var tt = document.getElementById("tt");
var childNodes = tt.childNodes[0].childNodes;
var level = 0;
for (var i = 0; i < childNodes.length; i++) {
安装hadoop时 执行jps命令Error occurred during initialization of VM
daizj
jdk hadoop jps
在安装hadoop时,执行JPS出现下面错误
[slave16]root@192.168.11.10:/tmp/hsperfdata_hdfs# jps
Error occurred during initialization of VM
java.lang.Error: Properties init: Could not determine current working
PHP开发大型项目的一点经验
dcj3sjt126com
PHP 重构
一、变量 最好是把所有的变量存储在一个数组中,这样在程序的开发中可以带来很多的方便,特别是当程序很大的时候。变量的命名就当适合自己的习惯,不管是用拼音还是英语,至少应当有一定的意义,以便适合记忆。变量的命名尽量规范化,不要与PHP中的关键字相冲突。 二、函数 PHP自带了很多函数,这给我们程序的编写带来了很多的方便。当然,在大型程序中我们往往自己要定义许多个函数,几十
android笔记之--向网络发送GET/POST请求参数
dcj3sjt126com
android
使用GET方法发送请求
private static boolean sendGETRequest (String path,
Map<String, String> params) throws Exception{
//发送地http://192.168.100.91:8080/videoServi
linux复习笔记 之bash shell (3) 通配符
eksliang
linux 通配符 linux通配符
转载请出自出处:
http://eksliang.iteye.com/blog/2104387
在bash的操作环境中有一个非常有用的功能,那就是通配符。
下面列出一些常用的通配符,如下表所示 符号 意义 * 万用字符,代表0个到无穷个任意字符 ? 万用字符,代表一定有一个任意字符 [] 代表一定有一个在中括号内的字符。例如:[abcd]代表一定有一个字符,可能是a、b、c
Android关于短信加密
gqdy365
android
关于Android短信加密功能,我初步了解的如下(只在Android应用层试验):
1、因为Android有短信收发接口,可以调用接口完成短信收发;
发送过程:APP(基于短信应用修改)接受用户输入号码、内容——>APP对短信内容加密——>调用短信发送方法Sm
asp.net在网站根目录下创建文件夹
hvt
.net C# hovertree asp.net Web Forms
假设要在asp.net网站的根目录下建立文件夹hovertree,C#代码如下:
string m_keleyiFolderName = Server.MapPath("/hovertree");
if (Directory.Exists(m_keleyiFolderName))
{
//文件夹已经存在
return;
}
else
{
try
{
D
一个合格的程序员应该读过哪些书
justjavac
程序员 书籍
编者按:2008年8月4日,StackOverflow 网友 Bert F 发帖提问:哪本最具影响力的书,是每个程序员都应该读的?
“如果能时光倒流,回到过去,作为一个开发人员,你可以告诉自己在职业生涯初期应该读一本, 你会选择哪本书呢?我希望这个书单列表内容丰富,可以涵盖很多东西。”
很多程序员响应,他们在推荐时也写下自己的评语。 以前就有国内网友介绍这个程序员书单,不过都是推荐数
单实例实践
跑龙套_az
单例
1、内部类
public class Singleton {
private static class SingletonHolder {
public static Singleton singleton = new Singleton();
}
public Singleton getRes
PO VO BEAN 理解
q137681467
VO DTO po
PO:
全称是 persistant object持久对象 最形象的理解就是一个PO就是数据库中的一条记录。 好处是可以把一条记录作为一个对象处理,可以方便的转为其它对象。
BO:
全称是 business object:业务对象 主要作用是把业务逻辑封装为一个对象。这个对
战胜惰性,暗自努力
金笛子
努力
偶然看到一句很贴近生活的话:“别人都在你看不到的地方暗自努力,在你看得到的地方,他们也和你一样显得吊儿郎当,和你一样会抱怨,而只有你自己相信这些都是真的,最后也只有你一人继续不思进取。”很多句子总在不经意中就会戳中一部分人的软肋,我想我们每个人的周围总是有那么些表现得“吊儿郎当”的存在,是否你就真的相信他们如此不思进取,而开始放松了对自己的要求随波逐流呢?
我有个朋友是搞技术的,平时嘻嘻哈哈,以
NDK/JNI二维数组多维数组传递
wenzongliang
二维数组 jni NDK
多维数组和对象数组一样处理,例如二维数组里的每个元素还是一个数组 用jArray表示,直到数组变为一维的,且里面元素为基本类型,去获得一维数组指针。给大家提供个例子。已经测试通过。
Java_cn_wzl_FiveChessView_checkWin( JNIEnv* env,jobject thiz,jobjectArray qizidata)
{
jint i,j;
int s