赞
踩
JDK是面向开发人员使用的SDK。
JRE是Java Runtime Enviroment是指Java的运行环境,是面向Java程序的使用者。
编写的Java程序必须要JRE才能运行。
==和equals都是比较栈内存中的地址是否相等 。相等为true 否则为false
==和equals都比较两个值是否相等。相等为true 否则为false
两个对象的 equals()相等 则 hashCode()相等;
两个对象的 equals()不相等 则 hashCode()也可能相等。
final 可修饰类、方法、变量(包括成员变量和局部变量);被final修饰的类不能被继承、被final修饰的方法不能被重写、被final修饰的变量赋值后不可再更改(常量)。
等于 -1 ;Math.round()的作用是向上取整;
但是也有三种特殊情况:
1.如果参数为 NaN(无穷与非数值),那么结果为 0。
2.如果参数为负无穷大或任何小于等于 Long.MIN_VALUE 的值,那么结果等于Long.MIN_VALUE 的值。
3.如果参数为正无穷大或任何大于等于 Long.MAX_VALUE 的值,那么结果等于Long.MAX_VALUE 的值。
Sring属于引用类型;
常用的基本类型为: byte / short / int / long / char / float / double /boolean
java中操作字符串的类有 String / StringBuffer / StringBuilder
其中StringBuffer / StringBuilder 均继承自 String ; 三者都是以char[ ] 的方式保存字符串; 其中String类型的字符串是不可更改的,做增删时会重新创建对象,而后两者做增删时都是对同一对象进行操作;StringBuffer中的方法被synchronized修饰,所以是线程安全的,而StringBuilder则是线程不安全的.
不一样 String str="i" 是在常量池中,而String str = new String("i")是创建了一个对象,分配在堆内存中
1.使用字符串数组,实现从尾部开始逐个逆序放入字符串
- public static String reverse1(String s) {
-
- char[] array = s.toCharArray();
-
- String re = "";
-
- for(int i = array.length - 1; i >= 0; i--){
-
- re += array[i];
- }
- return re;
-
- }
2.调用StringBuffer中的reverse方法
- public static String reverse2(String s) {
-
- return new StringBuffer(s).reverse().toString();
-
- }
暂时只会用这两种....
1.contains() 判断是否包含
2.endwith() 判断是否以指定后缀结束
3.equals() 判断是否相等
4.hashCode() 获取哈希码值
5.indexOf() 获取指定字符第一次出现的索引6.
6.lastIndexOf() 获取指定字符最后一次出现的索引
7.isEmpty() 判断是否为空
8.length() 获取指定字符串的长度
9.matches() 判断字符串是否匹配给定的正则表达式
10.replace() 替换指定字符串并返回一个新的字符串
11.startWith() 判断是否以指定字符串开始
12.substring() 截取并返回新的字符串(注意截取时如指定范围则截取内容含头不含尾)
13.toLowerCase() 转小写
14.toUpperCase() 转大写
15.trim() 去除收尾的空格并返回新的字符串
16.split() 根据指定的正则表达式匹配并拆分字符
17.toCharArray() 将字符串转为一个新数组
抽象类可以没有抽象方法,可以由子类去实现;但是包含一个抽象方法的类一定是抽象类。
普通类中没有抽象方法,可以被实例化(也就是new)
抽象类中可以有抽象方法,不能被实例化;抽象类的子类必须实现该抽象类的抽象方法(重写抽象方法)(除非子类也是抽象类)
不能,因为抽象类是需要被子类继承的,而被final修饰的类不能被继承
抽象类中可以有构造方法,变量,常量,普通方法,抽象方法
接口中没有构造方法,只有常量,都是抽象方法;接口和接口之间可以多继承,接口和类之间可以多实现
1.按照流的流向分,可分为输入流和输出流
2.按照操作单元分,可分为字节流和字符流
3.按照流的角色分,可分为节点流和处理流
BIO (同步阻塞)是传统的java.io包,它是基于流的模式实现的;交互的方式是同步,阻塞方式,也就是说在读入输入流或输出流的同时,在读写动作完成之前,线程会一直阻塞在那里,它们之间的调用时可靠的线性顺序;它的优点就是代码比较简单、直观;缺点就是 IO 的效率和扩展性很低,容易成为应用性能瓶颈。
NIO (同步非阻塞)是java1.4引入的java.nio包,它的优点就是代码比较简单、直观;缺点就是 IO 的效率和扩展性很低,容易成为应用性能瓶颈。
AIO (异步非阻塞)是java1.7之后引入的包(Asynchronous IO),是NIO的升级版本,提供了异步非阻塞的IO操作方式;异步 IO是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。
1.length() 获取文件的字节量大小
2.isFile() 判断是否为文件
3.isDirectory() 判断是否为文件夹
4.getName() 获取文件,文件夹名
5.getParent() 获取父文件夹的路径
6.getAbsolutePath() 获取文件的完整路径
7.createNewFile() 新建文件
8.mkdir() 新建单层不存在的文件夹
9.mkdirs() 新建多层不存在的文件夹
10.delete() 删除文件,空文件夹
11.list() 列出文件夹里的资源名
12.listFile() 列出文件夹里的所有文件的完整路径并把每个资源封装成File数组
JAVA中容器主要分为两大类:Map类跟Collection类; 它们都有一个共同的父接口Iterator
Map是一队成队的键值对象,可以用键来查找值,包含HashMap类和TreeMap类
Collection 是一个独立的元素序列,有三个常用子接口:List / Set / Queue ;其中List必须按照插入的顺序保存元素、Set不能有重复的元素、Queue按照排队规则来确定对象的产生顺序
Collection 是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法;实现该接口的类主要有 List 和 Set ,该接口的设计目的是为各种具体的集合提供最大化的统一操作方式
Collections 是针对集合类的一个包装类,它提供了一系列的静态方法来实现对集合的搜索,排序,线程安全化等操作,其中大多数方法都是用来处理线性表,且Coolections类不能实例化
List 是一个有序的集合
Set 不重复集合,LinkedHashSet按照插入排序,SortedSet可排序,HashSet无序
Map 键值对集合
都是用于存储键(key)和值(value)的对应关系,都是map的实现类,都是使用哈希表的方式存储
1.线程安全性不同:
HashMap不是线程安全的,在多线程并发的环境下,可能会产生死锁等问题(效率高)
Hashtable是线程安全的,它的每个方法中都加入了Synchronize方法。在多线程并发的环境下,可以直接使用Hashtable,不需要自己为它的方法实现同步
2.继承的父类不同:
HashMap是继承自AbstractMap类,而Hashtable是继承自Dictionary类,但二者都同时实现了map,Cloneable(可复制),Serializable(可序列化)这三个接口
3.是否可以存null
HashMap可以使用null(key和value)都可以
HashTable不能存null
4.遍历方法不同
HashMap使用Iterator遍历
HashTable使用Enumeration遍历
5.初始化和扩容方式不同
HashMap默认的初始化大小为16。之后每次扩充,容量变为原来的2倍
Hashtable默认的初始大小为11,之后每次扩充,容量变为原来的2n+1
HashMap 是基于桶(数组和链表)实现;而TreeMap是基于红黑树实现;
HashMap是无序的;TreeMap是根据key值升序来排序的;
HashMap在读数据时性能更高,所以在没有要求排序时,使用HashMap.
当new HashMap( ) 时,底层会开辟空间(桶),初始容量是16,使用默认加载因子0.75;当开始准备数据存入时,计算存放的位置 使用hash值(作为key的标记);判断如果位置是null,没有数据直接存;判断位置有数据,就形成链表(注意 : 当key相同时,覆盖原有的value,当key不同时,形成链表 );当桶内容量达到16*0.75时,会开始rehash(扩容,新桶的容量是原来的2倍)
底层为:数组 + 红黑树
HashSet是基于HashMap实现的,底层就是数组,默认初始容量是16,加载因子0.75,封装了一个 HashMap 对象来存储所有的集合元素,当我们试图把某个类的对象当成 HashMap的 key,或试图将这个类的对象放入 HashSet 中保存时,重写该类的equals(Object obj)方法和 hashCode() 方法很重要,而且这两个方法的返回值必须保持一致:当该类的两个的 hashCode() 返回值相同时,它们通过 equals() 方法比较也应该返回 true。通常来说,所有参与计算 hashCode() 返回值的关键属性,都应该用于作为 equals() 比较的标准。
两者都是非线程安全的;ArrayList底层是数组结构,适合做查询效率更高;LinkedList底层是链表结构,适合做增删效率更高
数组转 List ,使用 JDK 中 java.util.Arrays 工具类的 asList 方法
- import java.util.Arrays;
- import java.util.List;
-
- public class test {
-
- public static void main(String[] args) {
- String[] strs = new String[] {"aaa", "bbb", "ccc"}; //数组
- List<String> list = Arrays.asList(strs); //list
- for (String s : list) {
- System.out.println(s); //循环输出
- }
- }
- }
Vector比ArrayList先存在。Vector是同步的,Vector的对象是线程安全的;ArrayList是异步的,ArrayList的对象不是线程安全的。同步影响执行效率,所以ArrayList比Vector性能好。
ArrayList和Vector都有一个初始的容量大小,当存储的空间不够时,需要增加存储空间,Vector默认增长原来的一倍,而ArrayList是原来的0.5倍。ArrayList与Vector都可以设置初始的空间大小,Vector还可以设置增长的空间大小。
1、存储内容比较:
Array 数组可以包含基本类型和对象类型;
ArrayList 却只能包含对象类型(集合);
2、空间大小比较:
Array 数组的空间大小是固定的,所以需要事前确定合适的空间大小。
ArrayList 集合的空间是动态增长的,而且,每次添加新的元素的时候都会检查内部数组的空间是否足够。
3.方法上的比较:
ArrayList 方法上比 Array 更多样化,比如添加全部 addAll()、删除全部 removeAll()、返回迭代器 iterator() 等。
注:
Array类型的变量在声明的同时必须进行实例化(至少得初始化数组的大小),而ArrayList可以只是先声明。
对于基本数据类型,集合(ArrayList)使用自动装箱来减少编码工作量;但是Array相对较慢。
队列(queue)是一个典型的先进先出(FIFO)的容器。即从容器的一端放入事物,从另一端取出,并且事物放入容器的顺序与取出的顺序是相同的。
相同点:都是返回第一个元素,并在队列中删除返回的对象。
不同点:
remove() ,如果队列为空的时候,则会抛出异常
而poll()只会返回null
Vector:就比Arraylist多了个同步化机制(线程安全)。
Hashtable:就比Hashmap多了个线程安全。
Stack: 栈,也是线程安全的,继承于Vector。
ConcurrentHashMap:是一种高效但是线程安全的集合
Iterator是可以遍历集合的对象,为各种容器提供了公共的接口,隔离对容器的遍历操作和底层实现(解耦)
缺点是增加新的集合类需要对应增加新的迭代器类,迭代器类与集合类也成对增加.
java.lang.Iterable 接口被 java.util.Collection 接口继承,java.util.Collection 接口的 iterator() 方法返回一个 Iterator 对象
next() 方法获得集合中的下一个元素
hasNext() 检查集合中是否还有元素
remove() 方法将迭代器新返回的元素删除
ListIterator继承自Iterator, 且比Iterator有更多的方法
在使用范围上,Iterator可以迭代所有集合;ListIterator只能用于List及其子类
ListIterator 有 add 方法,可以向 List 中添加对象;Iterator 不能
ListIterator 有 set()方法,可以实现对 List 的修改;Iterator 仅能遍历,不能修改
ListIterator 有 hasPrevious() 和 previous() 方法,可以实现逆向遍历;Iterator不能
ListIterator 有 nextIndex() 和previousIndex() 方法,可定位当前索引的位置;Iterator不能
可以使用 Collections. unmodifiableCollection(Collection c) 方法来创建一个只读集合,这样改变集合的任何操作都会抛出 Java.lang.UnsupportedOperationException 异常
- List<String> list = new ArrayList<>();
- list. add("x");
- Collection<String> clist = Collections. unmodifiableCollection(list);
- clist. add("y"); // 运行时此行报错
- System. out. println(list. size());
同理:Collections包也提供了对list和set集合的方法 :
Collections.unmodifiableList(List)
Collections.unmodifiableSet(Set)
-------------------------多线程-------------------------
并行(Parallel):当系统有一个以上CPU时,一个CPU执行一个进程,而另一个CPU可以执行另一个进程。两个进程互不抢占CPU资源,可以同时进行。
并发(Concurrent):多个任务在同一个 CPU 核上,按细分的时间片轮流(交替)执行,但从逻辑上来看那些任务是同时执行。
区别 :
并行:多个事情,在同一时间点上同时发生了;且多个任务之间是不互相抢占资源的。
并发:多个事情,在同一时间段内同时发生了;且多个任务之间是互相抢占资源的。
进程:是执行中一段程序,即一旦程序被载入到内存中并准备执行,它就是一个进程。进程是表示资源分配的的基本概念,又是调度运行的基本单位,是系统中的并发执行的单位
线程:单个进程中执行中每个任务就是一个线程。线程是进程中执行运算的最小单位
根本区别:进程是一段正在执行的程序,是资源分配的基本单元;而线程是CPU调度(程序执行)的基本单元
一个线程只能属于一个进程,但是一个进程可以拥有多个线程。多线程处理就是允许一个进程中在同一时刻执行多个任务
守护线程,专门用于服务其他的线程,如果其他的线程(即用户自定义线程)都执行完毕,连main线程也执行完毕,那么jvm就会退出(即停止运行)此时,连jvm都停止运行了,守护线程当然也就停止执行了
1.继承Thread类创建线程类:
(1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。
(2)创建Thread子类的实例,即创建了线程对象。
(3)调用线程对象的start()方法来启动该线程。
2.通过Runnable接口创建线程类:
(1)定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
(2)创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
(3)调用线程对象的start()方法来启动该线程。
3.通过Callable和Future创建线程(此种方式存在返回值):
(1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
(3)使用FutureTask对象作为Thread对象的target创建并启动新线程。
(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
4.通过线程池启动多线程
使用线程池创建线程的步骤:
(1)使用Executors类中的newFixedThreadPool(int num)方法创建一个线程数量为num的线程池。
(2)调用线程池中的execute()方法执行由实现Runnable接口创建的线程;调用submit()方法执行由实现Callable接口创建的线程。
(3)调用线程池中的shutdown()方法关闭线程池
Runnable接口中的值void run()方法没有返回,它做的事情只是纯粹地去执行run()方法中的代码且不能抛出异常
Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果,可以抛出受检查的异常
分为五种:
新建状态
就绪状态
运行状态
阻塞状态
死亡状态
sleep()和wait()都是使线程暂停的方法
最大区别: sleep()后,程序并不会不释放同步锁
wait()后,程序会释放同步锁。 使得其他线程可以使用同步控制块或者方法
用法不同: sleep()可以用时间指定来使他自动醒过来。如果时间不到你只能调用interreput()来强行打断
wait()可以用notify()直接唤起
属于不同的类属:
sleep()的类是Thread
wait()的类是Object
notify() 方法随机唤醒对象的等待池中的一个线程,进入锁池
notifyAll() 唤醒对象的等待池中的所有线程,进入锁池
启动一个线程需要调用 Thread 对象的 start() 方法
start()是启动线程对象,让被启动的线程执行run()中的任务代码
而直接调用run()并没有启动线程,执行run()的只有main线程
通常开发者都是利用Executors提供的通用线程池创建方法,去创建不同配置的线程池:
1、newCachedThreadPool(),它是用来处理大量短时间工作任务的线程池,具有几个鲜明特点:它会试图缓存线程并重用,当无缓存线程可用时,就会创建新的工作线程;如果线程闲置时间超过60秒,则被终止并移除缓存;长时间闲置时,这种线程池,不会消耗什么资源。其内部使用SynchronousQueue作为工作队列
2、newFixedThreadPool(int nThreads),重用指定数目(nThreads)的线程,其背后使用的是无界的工作队列,任何时候最多有nThreads个工作线程是活动的。这意味着,如果任务数量超过了活动线程数目,将在工作队列中等待空闲线程出现;如果工作线程退出,将会有新的工作线程被创建,以补足指定数目nThreads
3、newSingleThreadExecutor(),它的特点在于工作线程数目限制为1,操作一个无界的工作队列,所以它保证了所有的任务都是被顺序执行,最多会有一个任务处于活动状态,并且不予许使用者改动线程池实例,因此可以避免改变线程数目
4、newSingleThreadScheduledExecutor()和newScheduledThreadPool(int corePoolSize),创建的是个ScheduledExecutorService,可以进行定时或周期性的工作调度,区别在于单一工作线程还是多个工作线程
5、newWorkStealingPool(int parallelism),这是一个经常被人忽略的线程池,Java 8 才加入这个创建方法,其内部会构建ForkJoinPool,利用Work-Stealing算法,并行地处理任务,不保证处理顺序
1.RUNNING:这是最正常的状态,接受新的任务,处理等待队列中的任务。线程池的初始化状态是RUNNING。线程池被一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0
2.SHUTDOWN:不接受新的任务提交,但是会继续处理等待队列中的任务。调用线程池的shutdown()方法时,线程池由RUNNING -> SHUTDOWN
3.STOP:不接受新的任务提交,不再处理等待队列中的任务,中断正在执行任务的线程。调用线程池的shutdownNow()方法时,线程池由(RUNNING or SHUTDOWN ) -> STOP
4.TIDYING:所有的任务都销毁了,workCount 为 0,线程池的状态在转换为 TIDYING 状态时,会执行钩子方法 terminated()。因为terminated()在ThreadPoolExecutor类中是空的,所以用户想在线程池变为TIDYING时进行相应的处理;可以通过重载terminated()函数来实现
当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING
当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。
5.TERMINATED:线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED
接收的参数不一样:
submit有返回值,而execute没有
submit() 的返回值 Future 调用get方法时,可以捕获处理异常
线程的安全性问题体现在:
导致原因:
JVM优化synchronized的运行机制,当JVM检测到不同的竞争状态时,就会根据需要自动切换到合适的锁,这种切换就是锁的升级。升级是不可逆的,也就是说只能从低到高,也就是偏向-->轻量级-->重量级,不能够降级
锁级别:无锁->偏向锁->轻量级锁->重量级锁
锁状态对比:
偏向锁 | 轻量级锁 | 重量级锁 | |
---|---|---|---|
适用场景 | 只有一个线程进入同步块 | 虽然很多线程,但是没有冲突:多条线程进入同步块,但是线程进入时间错开因而并未争抢锁 | 发生了锁争抢的情况:多条线程进入同步块并争用锁 |
本质 | 取消同步操作 | CAS操作代替互斥同步 | 互斥同步 |
优点 | 不阻塞,执行效率高(只有第一次获取偏向锁时需要CAS操作,后面只是比对ThreadId) | 不会阻塞 | 不会空耗CPU |
缺点 | 适用场景太局限。若竞争产生,会有额外的偏向锁撤销的消耗 | 长时间获取不到锁空耗CPU | 阻塞,上下文切换,重量级操作,消耗操作系统资源 |
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。是操作系统层面的一个错误,是进程死锁的简称,最早在 1965 年由 Dijkstra 在研究银行家算法时提出的,它是计算机操作系统乃至整个并发程序设计领域最难处理的问题之一
死锁的四个必要条件:
互斥条件:线程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某资源仅为一个线程所占有。此时若有其他线程请求该资源,则请求线程只能等待
不剥夺条件:线程所获得的资源在未使用完毕之前,不能被其他线程强行夺走,即只能由获得该资源的线程自己主动释放
请求和保持条件:线程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他线程占有,此时请求线程被阻塞,但对自己已获得的资源保持不放
循环等待条件:存在一种线程资源的循环等待链,链中每一个线程已获得的资源同时被链中下一个线程所请求。即存在一个处于等待状态的线程集合{Pl, P2, ..., pn},其中Pi等待的资源被P(i+1)占有(i=0, 1, ..., n-1),Pn等待的资源被P0占有
ThreadLocal 是线程本地存储,在每个线程中都创建了一个 ThreadLocalMap 对象,每个线程可以访问自己内部 ThreadLocalMap 对象内的 value。
经典的使用场景是为每个线程分配一个 JDBC 连接 Connection。这样就可以保证每个线程的都在各自的 Connection 上进行数据库的操作,不会出现 A 线程关了 B线程正在使用的 Connection; 还有 Session 管理 等问题
synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性。
Java中每一个对象都可以作为锁,这是synchronized实现同步的基础:
(1)、volatile只能作用于变量,使用范围较小。synchronized可以用在变量、方法、类、同步代码块等,使用范围比较广。
(2)、volatile只能保证可见性和有序性,不能保证原子性。而可见性、有序性、原子性synchronized都可以包证。
(3)、volatile不会造成线程阻塞。synchronized可能会造成线程阻塞。
synchronized是java内置关键字,在jvm层面,Lock是个java类
采用synchronized不需要用户去手动释放锁,当synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁的占用;而Lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象
synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁
区别: 1.用法不一样。synchronized既可以加在方法上,也可以加载特定的代码块上,括号中表示需要锁的对象。而Lock需要显示地指定起始位置和终止位置。synchronzied是托管给jvm执行的,Lock锁定是通过代码实现的。 2.在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。 3.锁的机制不一样。synchronized获得锁和释放的方式都是在块结构中,而且是自动释放锁。而Lock则需要开发人员手动去释放,并且必须在finally块中释放,否则会引起死锁问题的发生。 4.Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现; 5.synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁; 6.Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。Lock可以提高多个线程进行读操作的效率
synchronized 竞争锁时会一直等待;ReentrantLock 可以尝试获取锁,并得到获取结果
synchronized 获取锁无法设置超时;ReentrantLock 可以设置获取锁的超时时间
synchronized 无法实现公平锁;ReentrantLock 可以满足公平锁,即先等待先获取到锁
synchronized 控制等待和唤醒需要结合加锁对象的 wait() 和 notify()、notifyAll();ReentrantLock 控制等待和唤醒需要结合 Condition 的 await() 和 signal()、signalAll() 方法
synchronized 是 JVM 层面实现的;ReentrantLock 是 JDK 代码层面实现
synchronized 在加锁代码块执行完或者出现异常,自动释放锁;ReentrantLock 不会自动释放锁,需要在 finally{} 代码块显示释放
CAS解决原子性问题,在VEN(但V=E是替换N,不相等),v不等于E,已经被更新,会返回失败
CAS 包含 3 个参数,CAS(V, E, N)。V 表示需要更新的变量,E 表示变量当前期望值,N 表示更新为的值。只有当变量 V 的值等于 E 时,变量 V 的值才会被更新为 N。如果变量 V 的值不等于 E ,说明变量 V 的值已经被更新过,当前线程什么也不做,返回更新失败
当多个线程同时使用 CAS 更新一个变量时,只有一个线程可以更新成功,其他都失败。失败的线程不会被挂起,可以继续重试 CAS,也可以放弃操作
CAS 操作的原子性是通过 CPU 单条指令完成而保障的。JDK 中是通过 Unsafe 类中的 API 完成的
在并发量很高的情况,会有大量 CAS 更新失败,所以需要慎用
----------------------反射----------------------
是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,也有称作“自省”。反射非常强大,它甚至能直接操作程序的私有属性
作用 : 用来解析.class文件里的所有资源,包括:构造方法\成员变量\方法\注解------利用class工具类提供的各种方法,获取各种资源
序列化:将 Java 对象转换成字节流的过程。
反序列化:将字节流转换成 Java 对象的过程。
当 Java 对象需要在网络上传输 或者 持久化存储到文件中时,就需要对 Java 对象进行序列化处理。
序列化的实现:类实现 Serializable 接口,这个接口没有需要实现的方法。实现 Serializable 接口是为了告诉 jvm 这个类的对象可以被序列化
动态代理:就是当想要给实现了某个接口的类中的方法,加一些额外的处理。比如说加日志,加事务等。简单地说就是在运行时,创建目标类,可以调用和扩展目标类的方法,
主要应用的框架:Spring中的AOP,Struts2中的拦截器,加事务,加权限,加日志
首先必须定义一个接口,还要有一个InvocationHandler(将实现接口的类的对象传递给它)处理类。再有一个工具类Proxy(习惯性将其称为代理类,因为调用他的newInstance()可以产生代理对象,其实他只是一个产生代理对象的工具类)。利用到InvocationHandler,拼接代理类源码,将其编译生成代理类的二进制码,利用加载器加载,并将其实例化产生代理对象,最后返回
----------------------对象拷贝----------------------
克隆的对象可能包含一些已经修改过的属性,而 new 出来的对象的属性都还是初始化时候的值,所以当需要一个新的对象来保存当前对象的“状态”就靠克隆方法了
克隆分浅克隆和深克隆,浅克隆后的对象中非基本对象和原对象指向同一块内存,因此对这些非基本对象的修改会同时更改克隆前后的对象。深克隆可以实现完全的克隆,可以用反射的方式或序列化的方式实现
有两种方式:
1). 实现 Cloneable 接口并重写 Object 类中的 clone()方法;
2). 实现 Serializable 接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆
注意:基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型限定,可以检查出要克隆的对象是否支持序列化,这项检查是编译器完成的,不是在运行时抛出异常,这种是方案明显优于使用Object类的clone方法克隆对象。让问题在编译的时候暴露出来总是优于把问题留到运行时
复制一个 Java 对象
浅拷贝:复制基本类型的属性;引用类型的属性复制,复制栈中的变量 和 变量指向堆内存中的对象的指针,不复制堆内存中的对象。
深拷贝:复制基本类型的属性;引用类型的属性复制,复制栈中的变量 和 变量指向堆内存中的对象的指针和堆内存中的对象。
----------------------Java Web----------------------
jsp就是特别的servlet+html
Servlet 适合动态输出 Web 数据和业务逻辑处理,对于 html 页面内容的修改非常不方便;Jsp 是在 Html 代码中嵌入 Java 代码,适合页面的显示,内置对象不同,获取内置对象的方式不同,JSP中的内置对象都是必须通过HttpServletRequest对象,HttpServletResponse对象以及HttpServlet对象得到
JSP中一共预先定义了9个这样的对象,分别为:request、response、session、application、out、pagecontext、config、page、exception
Page:指的是JSP被翻译成Servlet的对象的引用。
pageContext:对象可以用来获得其他8个内置对象,还可以作为JSP的域范围对象使用,pageContext中存的值是当前的页面的作用范围
request:代表的是请求对象,可以用于获得客户机的信息,也可以作为域对象来使用,使用request保存的数据在一次请求范围内有效
Session:代表的是一次会话,可以用于保存用户的私有的信息,也可以作为域对象使用,使用session保存的数据在一次会话范围有效
Application:代表整个应用范围,使用这个对象保存的数据在整个web应用中都有效
Response:是响应对象,代表的是从服务器向浏览器响应数据
Out:JSPWriter是用于向页面输出内容的对象
Config:指的是ServletConfig用于JSP翻译成Servlet后 获得Servlet的配置的对象.
Exception:在页面中设置isErrorPage=”true”,即可使用,是Throwable的引用,用来获得页
4个JSP内置对象的作用域分别为:application、session、request、page
名称 | 作用域 |
application | 在所有应用程序中有效 |
session | 在当前会话中有效 |
request | 在当前请求中有效 |
page | 在当前页面有效 |
一个在浏览器,一个在服务器,一个是ASCLL,一个是任何数据都可以储存,储存时间一个可以长期储存,一个不可以,隐秘性的区别,一个支持跨域,一个不支持
session 的工作原理是客户端登录完成之后,服务器会创建对应的 session,session 创建完之后,会把 session 的 id 发送给客户端,客户端再存储到浏览器中。这样客户端每次访问服务器时,都会带着 sessionid,服务器拿到 sessionid 之后,在内存找到与之对应的 session 这样就可以正常工作了
不能直接使用
但可以通过以下方法 :
1. 机制:spring mvc的入口是servlet,而struts2是filter
2. 性能:spring会稍微比struts快。spring mvc是基于方法的设计,而sturts是基于类
3. 参数传递:struts是在接受参数的时候,可以用属性来接受参数,这就说明参数是让多个方法共享的
4. 设计思想上:struts更加符合oop(面向对象编程)的编程思想, spring就比较谨慎,在servlet上扩展
5. intercepter的实现机制:struts有自己的interceptor机制,spring mvc用的是独立的AOP方式
1:遵循编程规范,首先执行预编译,随后再填写参数,这样参数会替换掉编译好的语句中的?占位符,最后执行完整的sql语句
2:使用存储过程,存储过程(Stored Procedure)是一组完成特定功能的SQL语句集,经编译后存储在数据库中,用户通过调用存储过程并给定参数(如果该存储过程带有参数)就可以执行它,也可以避免SQL注入攻击
3:mybatis半自动持久化框架,其底层原理也是预编译,参数替换占位符, 简单说,#{}是经过预编译的,是安全的;${}是未经过预编译的,仅仅是取变量的值,是非安全的,存在SQL注入
xss(Cross Site Scripting),即跨站脚本攻击,也就是代码注入攻击, 是一种常见于web应用程序中的计算机安全漏洞
避免 :
不管是用户端从任何的输入到任何输出都进行过滤,转义,让攻击者的代码注入不能识别,就可以避免攻击了
CSRF:Cross Site Request Forgery(跨站点请求伪造)。
CSRF 攻击者在用户已经登录目标网站之后,诱使用户访问一个攻击页面,利用目标网站对用户的信任,以用户身份在攻击页面对目标网站发起伪造用户操作的请求,达到攻击目的
避免方法:
----------------------异常----------------------
throws是用来声明一个方法可能抛出的所有异常信息,throws是将异常声明但是不处理,而是将异常往上传,谁调用我就交给谁处理。
而throw则是指抛出的一个具体的异常类型
final:是用来修饰类,方法,变量,并具有不同的意义
fianlly:则是保证Java代码必须被执行的一种机制 : 使用try-finally或者try-catch-finally来进行1.关闭JDBC连接,2.保证unlock锁等动作
finalize则是java.lang.Object的一个方法,保证对象在被垃圾收集前完成特定资源的回收,所以称为java GC的阻碍,成为了特等公民,其次,finalize还可以吞掉Throwable,导致程序检查不出错误也无法运行,现在已经被定义为Old方法
以下三种情况都是可以的:
try-catch
try-finally
try-catch-finally
可以省略catch或者finally。catch和finally不可以同时省略
如果在catch中return了,也会在return之前,先执行finally代码块
1. java.lang.nullpointerexception 空指针异常
2. java.lang.classnotfoundexception 指定类不存在
3. java.lang.illegalargumentexception 方法的参数错误
4.java.lang.ArrayIndexOutOfBoundsException 数组下标越界异常
5. NoSuchMethodException 方法未找到异常
6.java.lang.ExceptionInInitializerError 初始化程序错误
7.java.lang.InstantiationError 实例化错误
----------------------网络----------------------
301 redirect: 301 代表永久性转移(Permanently Moved)
302 redirect: 302 代表暂时性转移(Temporarily Moved )
详细来说,301和302状态码都表示重定向,就是说浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的URL地址,这个地址可以从响应的Location首部中获取(用户看到的效果就是他输入的地址A瞬间变成了另一个地址B)——这是它们的共同点。他们的不同在于。301表示旧地址A的资源已经被永久地移除了(这个资源不可访问了),搜索引擎在抓取新内容的同时也将旧的网址交换为重定向之后的网址;302表示旧地址A的资源还在(仍然可以访问),这个重定向只是临时地从旧地址A跳转到地址B,搜索引擎会抓取新的内容而保存旧的网址
1、请求方不同
redirect:客户端发起的请求
forward:服务端发起的请求
2、浏览器地址表现不同
redirect:浏览器地址显示被请求的
urlforward:浏览器地址不显示被请求的url
3、参数传递不同
redirect:重新开始一个request,原页面的request生命周期结束
forward:forward另一个连接的时候。request变量是在其生命周期内的。另一个页面也可以使用,其实质是把目标地址include
4、底层运作不同
redirect:发送的请求信息又回送给客户机,让客户机再转发到另一个资源上,需要在服务器和客户机之间增加一次通信
forward:服务器端直接找到目标,并include过来
5、定义不同
直接转发方式(Forward):客户端和浏览器只发出一次请求,Servlet、HTML、JSP或其它信息资源,由第二个信息资源响应该请求,在请求对象request中,保存的对象对于每个信息资源是共享的
间接转发方式(Redirect)实际是两次HTTP请求,服务器端在响应第一次请求的时候,让浏览器再向另外一个URL发出请求,从而达到转发的目的
TCP和UDP都是传输层的协议
udp面向无连接,数据量大,不稳定性 / tcp面向连接稳定,安全
TCP(Transmission Control Protocol,传输控制协议)提供的是面向连接的,可靠的字节流服务。通过三次握手建立连接,通讯完成时四次挥手,即客户和服务器交换数据前,必须现在双方之间建立一个TCP连接,之后才能传输数据。并且提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端
UDP(User Data Protocol,用户数据报协议)是一个简单的面向数据报的运输层协议。它不提供可靠性,只是把应用程序传给IP层的数据报发送出去,但是不能保证它们能到达目的地。由于UDP在传输数据报前不用再客户和服务器之间建立一个连接,且没有超时重发等机制,所以传输速度很快
例:
第一次握手 a发给b,此时b知道ab是通的,但a还不知道
第二次握手是b发给a,此时a知道ab和ba都是通的,但b只知道ab通,不知道ba通不通!
第三次握手是a发给b,此时b也知道ba通的,完毕
tcp 粘包可能发生在发送端或者接收端,分别来看两端各种产生粘包的原因:
发送端粘包:发送端需要等缓冲区满才发送出去,造成粘包; 接收方粘包:接收方不及时接收缓冲区的包,造成多个包接收
OSI模型分为七层,自下而上为 物理层(Physical Layer)、数据链路层(Data Link Layer)、网络层(Network Layer)、传输层(Transport Layer)、会话层(Session Layer)、表达层(Presentation Layer)、应用层(Application Layer)
在浏览器进行回退操作时,get请求是无害的,而post请求则会重新请求一次
get请求参数是连接在url后面的,而post请求参数是存放在requestbody内的
get请求因为浏览器对url长度有限制(不同浏览器长度限制不一样)对传参数量有限制,而post请求,为参数存放在requestbody内所以参数数量没有限制(事实上get请求也能在requestbody内携带参数,只不过不符合规定,有的浏览器能够获取到数据,而有的不能)
因为get请求参数暴露在url上,所以安全方面post比get更加安全
get请求浏览器会主动cache,post并不会,除非主动设置
get请求参数会保存在浏览器历史记录内,post请求并不会
get请求只能进行url编码,而post请求可以支持多种编码方式
get请求产生1个tcp数据包,post请求产生2个tcp数据包
浏览器在发送get请求时会将header和data一起发送给服务器,服务器返回200状态码,而在发送post请求时,会先将header发送给服务器,服务器返回100,之后再将data发送给服务器,服务器返回200OK
跨域:当浏览器执行脚本时会检查是否同源,只有同源的脚本才会执行,如果不同源即为跨域
jsonp,cros,nginx跨域
1、jsonp
利用了 script 不受同源策略的限制
缺点:只能 get 方式,易受到 XSS攻击
2、CORS(Cross-Origin Resource Sharing),跨域资源共享
当使用XMLHttpRequest发送请求时,如果浏览器发现违反了同源策略就会自动加上一个请求头 origin;
后端在接受到请求后确定响应后会在后端在接受到请求后确定响应后会在 Response Headers 中加入一个属性 Access-Control-Allow-Origin;
浏览器判断响应中的 Access-Control-Allow-Origin 值是否和当前的地址相同,匹配成功后才继续响应处理,否则报错
缺点:忽略 cookie,浏览器版本有一定要求
3、代理跨域请求
前端向发送请求,经过代理,请求需要的服务器资源
缺点:需要额外的代理服务器
4、Html5 postMessage 方法
允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本、多窗口、跨域消息传递
缺点:浏览器版本要求,部分浏览器要配置放开跨域限制
5、修改 document.domain 跨子域
相同主域名下的不同子域名资源,设置 document.domain 为 相同的一级域名
缺点:同一一级域名;相同协议;相同端口
6、基于 Html5 websocket 协议
websocket 是 Html5 一种新的协议,基于该协议可以做到浏览器与服务器全双工通信,允许跨域请求
缺点:浏览器一定版本要求,服务器需要支持 websocket 协议
7、document.xxx + iframe
通过 iframe 是浏览器非同源标签,加载内容中转,传到当前页面的属性中
缺点:页面的属性值有大小限制
创建一个回调函数,然后在远程服务上调用这个函数并且将JSON 数据形式作为参数传递,完成回调。将JSON数据填充进回调函数
----------------------设计模式----------------------
设计模式的定义:设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结,使用设计模式是为了可重用代码、让代码更容易被他人理解并且保证代码可靠性
创建型模式,共五种:
工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:
适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:
策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录 模式、状态模式、访问者模式、中介者模式、解释器模式
----------------------Spring/Spring MVC----------------------
spring 是一个开源的轻量级 JavaBean 容器框架。使用 JavaBean 代替 EJB ,并提供了丰富的企业应用功能,降低应用开发的复杂性
1.方便解耦,便于开发(Spring就是一个大工厂,可以将所有对象的创建和依赖关系维护都交给spring管理)
2.spring支持aop编程(spring提供面向切面编程,可以很方便的实现对程序进行权限拦截和运行监控等功能)
3.声明式事务的支持(通过配置就完成对事务的支持,不需要手动编程)
4.方便程序的测试,spring 对junit4支持,可以通过注解方便的测试spring 程序
5.方便集成各种优秀的框架()
6.降低javaEE API的使用难度(Spring 对javaEE开发中非常难用的一些API 例如JDBC,javaMail,远程调用等,都提供了封装,是这些API应用难度大大降低)
通过AOP面向切面将非核心部分的共同的部分单独处理,还有IOC(控制反转)将创建对象交给第三方,并依依赖注入,说深一点就是就是将影响多个类的公共行为封装成一个模块就是所谓的切面,这样可以减少重复代码并且降低耦合
控制反转IoC(Inversion of Control)是说创建对象的控制权进行转移,以前创建对象的主动权和创建时机是由自己把控的,而现在这种权力转移到第三方,比如转移交给了IoC容器,它就是一个专门用来创建对象的工厂,你要什么对象,它就给你什么对象,有了 IoC容器,依赖关系就变了,原先的依赖关系就没了,它们都依赖IoC容器了,通过IoC容器来建立它们之间的关系
IoC是设计思想,IoC有三个核心:BeanFactory、反射、DI。BeanFactory利用反射实现对象的创建,DI实现对象关系管理
1. Spring AOP 面相切面编程
2. Spring ORM Hibernate|mybatis|JDO
3. Spring Core 提供bean工厂 IOC
4. Spring Dao JDBC支持
5. Spring Context 提供了关于UI支持,邮件支持等
6. Spring Web 提供了web的一些工具类的支持
7. Spring MVC 提供了web mvc , webviews , jsp ,pdf ,export
Spring通过DI(依赖注入)实现IOC(控制反转),常用的注入方式主要有三种:构造方法注入,setter注入,基于注解的注入
不是线程安全的
原型Bean : 对于原型Bean,每次创建一个新对象,也就是线程之间并不存在Bean共享,自然是不会有线程安全的问题
单例Bean : 对于单例Bean,所有线程都共享一个单例实例Bean,因此是存在资源的竞争
Spring框架支持以下五种bean的作用域:
singleton:单例模式, 在整个Spring IoC容器中,使用 singleton 定义的 bean 只有一个实例
prototype:原型模式, 次通过容器的getbean方法获取 prototype 定义的 bean 时,都产生一个新的 bean 实例
只有在 Web 应用中使用Spring时,request、session、global-session 作用域才有效
request:对于每次 HTTP 请求,使用 request 定义的 bean 都将产生一个新实例,即每次 HTTP 请求将会产生不同的 bean 实例。
session:同一个 Session 共享一个 bean 实例。
global-session:同 session 作用域不同的是,所有的Session共享一个Bean实例。
自动装配的不同模式:
编程式事务和声明式事务两种实现方式
编程式事务允许用户在代码中精确定义事务的边界,而声明式事务(基于AOP)有助于用户将操作与事务规则进行解耦
简单地说,编程式事务侵入到了业务代码里面,但是提供了更加详细的事务管理;而声明式事务由于基于AOP,所以既能起到事务管理的作用,又可以不影响业务代码的具体实现
事务的4个特性:原子性 / 一致性 / 隔离性 / 持久性
分五个级别 :
默认 ISOLATION_DEFAULT(使用数据库的设置)
读未提交(Read uncommitted) 安全性最差,可能发生并发数据问题,性能最好
读提交(read committed) Oracle默认的隔离级别--效率较低,安全性较高
可重复读(repeatable read)MySQL默认的隔离级别,安全性较好,性能一般
串行化(Serializable) 表级锁,读写都加锁,效率低下,安全性高,不能并发
1.用户发送请求至前端控制器DisptcherServlet
2.前端控制器(DispatcherServlet)收到请求调用处理器映射器(HandlerMapping)
3.处理器映射器(HandlerMapping)找到具体的处理器(可根据xml配置文件,注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给前端控制器(DispatcherServlet)
4.前端控制器(DispatcherServlet)调用处理器适配器(HandlerAdapter)
5.处理器适配器(HandlerAdapter)经过适配调用具体的处理器(后端控制器Controller)
6.后端控制器(Controller)执行完成返回到对象ModelAndView
7.处理器适配器(HandlerAdapter)将后端控制器(Controller)执行的结果ModelAndView 返回给前端控制器(DispatcherServlet)
8.前端控制器(DispatcherServlet)将ModelAndView传给视图解析器(ViewReslover)
9.视图解析器(ViewReslover)解析后返回具体的View
10前端控制器(DispatcherServlet)根据View进行渲染视图(即将模型数据填充到视图中)
11.前端控制器(DispatcherServlet) 响应用户
前端控制器(DispatcherServlet)
处理器映射器(HandlerMapping)
处理器适配器(HandlerAdapter)
拦截器(HandlerInterceptor)
语言环境处理器(LocaleResolver)
主题解析器(ThemeResolver)
视图解析器(ViewResolver)
文件上传处理器(MultipartResolver)
异常处理器(HandlerExceptionResolver)
数据转换(DataBinder)
消息转换器(HttpMessageConverter)
请求转视图翻译器(RequestToViewNameTranslator)
页面跳转参数管理器(FlashMapManager)
处理程序执行链(HandlerExecutionChain)
@RequestMapping是一个用来处理请求地址映射的注解,可用于类或者方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径
@Autowired 是一个注释,它可以对类成员变量、方法及构造函数进行标注,让 spring 完成 bean 自动装配的工作
@Autowired 默认是按照类去匹配,配合 @Qualifier 指定按照名称去装配 bean
@Controller 标识是一个Controller,Spring包扫描创建实例
@RequestMapping 请求后的映射路径
@PathVariable 标识接收单个参数
@ResponseBody 返回对象利用jackson工具类转换为json字符串
@RequestParam 参数名和请求参数名称不同时使用,可以设置默认值
----------------------Spring Boot/Spring Cloud----------------------
Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程 / 简单的说就是 : 减少许多文件的配置,封装许多框架的使用方法
Spring Boot的核心功能
1、 可独立运行的Spring项目:Spring Boot可以以jar包的形式独立运行
2、 内嵌的Servlet容器:Spring Boot可以选择内嵌Tomcat、Jetty或者Undertow,无须以war包形式部署项目
3、 简化的Maven配置:Spring提供推荐的基础 POM 文件来简化Maven 配置
4、 自动配置Spring:Spring Boot会根据项目依赖来自动配置Spring 框架,极大地减少项目要使用的配置
5、 提供生产就绪型功能:提供可以直接在生产环境中使用的功能,如性能指标、应用信息和应用健康检查
6、 无代码生成和xml配置:Spring Boot不生成代码。完全不需要任何xml配置即可实现Spring的所有配置
使用SpringBoot的最大好处就是简化配置,它实现了自动化配置。
(1)简化配置,不需要编写太多的xml配置文件;
(2)基于Spring构建,使开发者快速入门,门槛很低;
(3)SpringBoot可以创建独立运行的应用而不需要依赖于容器;
(4)内置tomcat服务器,不需要打包成war包,可以直接放到tomcat中运行;
(5)提供maven极简配置,以及可视化的相关监控功能,比如性能监控,应用的健康程度等;
(6)为微服务SpringCloud奠定了基础,使得微服务的构建变得简单;
(7)Spring可以整合很多各式各样的框架,并能很好的集成;
(8)活跃的社区与论坛,以及丰富的开发文档;
Spring Boot 有两种类型的配置文件,application 和 bootstrap 文件
Spring Boot会自动加载classpath目前下的这两个文件,文件格式为 properties 或 yml 格式
*.properties 文件是 key=value 的形式
*.yml 是 key: value 的形式
*.yml 加载的属性是有顺序的,但不支持 @PropertySource 注解来导入配置,一般推荐用yml文件,看起来更加形象
bootstrap 配置文件是系统级别的,用来加载外部配置,如配置中心的配置信息,也可以用来定义系统不会变化的属性.bootstatp 文件的加载先于application文件
application 配置文件是应用级别的,是当前应用的配置文件
Spring Boot 有两种类型的配置文件,application 和 bootstrap 文件
bootstrap 配置文件是系统级别的,用来加载外部配置,如配置中心的配置信息,也可以用来定义系统不会变化的属性.bootstatp 文件的加载先于application文件
application 配置文件是应用级别的,是当前应用的配置文件
JPA和Hibernate之间的主要区别在于JPA是一个规范。Hibernate是Red Hat对JPA规范的实现,但是其功能是JPA的超集。
微服务架构,是在Spring Boot 基础上构建的,用于快速构建分布式系统的通用模式的工具集
Spring Cloud有以下特点:
1.约定优于配置
2.适用于各种环境.开发,部署在PC Server 或各种云环境均可
3.隐藏了组件的复杂性,并提供声明式,无xml的配置方式
4.开箱即用
5.轻量级的组件. Spring Cloud整合的组件大多比较轻量.例如Eureka,Zuul,等等
6.组件丰富,功能齐全. Spring Cloud 为微服务架构提供了非常完整的支持.例如,配置管理,服务发现,断路器,微服务网关等;
7.选型中立,丰富. 例如,Spring Cloud 支持使用Eureka,Zookeeper或Consul实现服务发现.
8.灵活.Spring Cloud的组成部分是解耦的,开发人员可按需灵活挑选技术选型.
当一个服务调用另一个服务由于网络原因或者自身原因出现问题时,调用者就会等被调用者的响应,当更多的服务请求到这些资源时,导致更多的请求等待,这素以会发生连锁效应,断路器就是解决这一问题的。
断路器有三种状态,完全打开状态、半开状态、关闭态。
完全打开态:一定时间内,达到一定的次数无法调用,并且多次检测没有恢复的迹象,断路器完全打开,那么下次的请求不会到该服务;
半开:短时间内有回复迹象,断路器会将部分请求发送给服务,当能正常调用时,断路器关闭。
关闭:当服务一直处于正常状态,能正常调用,断路器关闭
nacos:服务注册于发现。
Feign:基于动态代理机制,根据注解和选择的机器,拼接请求 url 地址,发起请求。
nginx:实现负载均衡,从一个服务的多台机器中选择一台。
Hystrix:提供线程池,不同的服务走不同的线程池,实现了不同服务调用的隔离,避免了服务雪崩的问题。
gateway:网关管理,由 Zuul 网关转发请求给对应的服务。
-----------------------------------Hibernate-----------------------------------
113. 为什么要使用 hibernate?
114. 什么是 ORM 框架?
115. hibernate 中如何在控制台查看打印的 sql 语句?
116. hibernate 有几种查询方式?
117. hibernate 实体类可以被定义为 final 吗?
118. 在 hibernate 中使用 Integer 和 int 做映射有什么区别?
119. hibernate 是如何工作的?
120. get()和 load()的区别?
121. 说一下 hibernate 的缓存机制?
122. hibernate 对象有哪些状态?
123. 在 hibernate 中 getCurrentSession 和 openSession 的区别是什么?
124. hibernate 实体类必须要有无参构造函数吗?为什么?
-----------------------------------Mybatis-----------------------------------
1、#{}是预编译处理,$ {}是字符串替换。
2、MyBatis在处理#{}时,会将SQL中的#{}替换为?号,使用PreparedStatement的set方法来赋值;MyBatis在处理 $ { } 时,就是把 ${ } 替换成变量的值。
3、使用 #{} 可以有效的防止SQL注入,提高系统安全性。
1.数组分页
2.SQL分页
3.拦截器分页
4.RowBounds分页
RowBounds 表面是在“所有”数据中检索数据,其实并非是一次性查询出所有数据,因为 MyBatis 是对 jdbc 的封装,在 jdbc 驱动中有一个 Fetch Size 的配置,它规定了每次最多从数据库查询多少条数据,假如你要查询更多数据,它会在你执行 next()的时候,去查询更多的数据。
逻辑分页: 从数据库将所有记录查询出来,存储到内存中,展示当前页,然后数据再直接从内存中获取。优点:效率高;缺点:占用内存比较高。
物理分页: 只从数据库中查询当前页的数据。优点:不占用很多内存;缺点:效率比价低(相比于逻辑分页)。
Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false。
它的原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。这就是延迟加载的基本原理。
一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该 Session 中的所有 Cache 就将清空,默认打开一级缓存。
二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap 存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现Serializable序列化接口(可用来保存对象的状态),可在它的映射文件中配置;
Mybatis和hibernate不同,它不完全是一个ORM框架,因为MyBatis需要程序员自己编写Sql语句。
Mybatis直接编写原生态sql,可以严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,因为这类软件需求变化频繁,一但需求变化要求迅速输出成果。但是灵活的前提是mybatis无法做到数据库无关性,如果需要实现支持多种数据库的软件,则需要自定义多套sql映射文件,工作量大。
Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件,如果用hibernate开发可以节省很多代码,提高效率。
Mybatis有三种基本的执行器(Executor):
SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。
ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map内,供下一次使用。简言之,就是重复使用Statement对象。
BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。
分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql,根据dialect方言,添加对应的物理分页语句和物理分页参数。
Mybatis自定义插件针对Mybatis四大对象(Executor、StatementHandler 、ParameterHandler 、ResultSetHandler )进行拦截,具体拦截方式为:
Executor:拦截执行器的方法(log记录)
StatementHandler :拦截Sql语法构建的处理
ParameterHandler :拦截参数的处理
ResultSetHandler :拦截结果集的处理
Mybatis自定义插件必须实现Interceptor接口:
自定义插件demo:
一个@Intercepts可以配置多个@Signature,@Signature中的参数定义如下:
type:表示拦截的类,这里是Executor的实现类;
method:表示拦截的方法,这里是拦截Executor的update方法;
args:表示方法参数。
-----------------------------------MySql-----------------------------------
1、第一范式:
当关系模式R的所有属性都不能在分解为更基本的数据单位时,称R是满足第一范式的,简记为1NF。满足第一范式是关系模式规范化的最低要求,否则,将有很多基本操作在这样的关系模式中实现不了。
2、第二范式:
如果关系模式R满足第一范式,并且R得所有非主属性都完全依赖于R的每一个候选关键属性,称R满足第二范式,简记为2NF。
3、第三范式:
设R是一个满足第一范式条件的关系模式,X是R的任意属性集,如果X非传递依赖于R的任意一个候选关键字,称R满足第三范式,简记为3NF。
一般情况下,我们创建的表的类型是InnoDB,如果新增一条记录(不重启mysql的情况下),这条记录的id是8;但是如果重启(文中提到的)MySQL的话,这条记录的ID是6。因为InnoDB表只把自增主键的最大ID记录到内存中,所以重启数据库或者对表OPTIMIZE操作,都会使最大ID丢失。
但是,如果我们使用表的类型是MylSAM,那么这条记录的ID就是8。因为MylSAM表会把自增主键的最大ID记录到数据文件里面,重启MYSQL后,自增主键的最大ID也不会丢失。
方法一:
[root@db01 ~]# mysql 登录的时候会显示数据库的版本
方法二:
mysql> status;
方法三:
mysql> select version();
ACID 一般是指数据库事务的ACID,也就是数据库四大特性
1.Atomicity 原子性
2.Consistency 一致性
3.Isolation 隔离性
4.Durability 持久性
Atomicity(原子性):一个事务(transaction)中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被恢复(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。即,事务不可分割、不可约简。
Consistency(一致性):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设约束、触发器、级联回滚等。
Isolation(隔离性):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
Durability(持久性):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
char长度固定不可变;var char长度可变。char如果插入的长度小于定义长度时,则用空格填充;varchar小于定义长度时,还是按实际长度存储,插入多长就存多长。
对 char 来说,最多能存放的字符个数 255,和编码无关。
而 varchar 呢,最多能存放 65532 个字符。varchar的最大有效长度由最大行大小和使用的字符集确定。整体最大长度是 65,532字节。
1、精度不一样,float是单精度,double是双精度;
2、表示小数的范围不一样,double能表示的范围比float大;
3、double在内存中,占8个字节,float在内存中,占4个字节。
1.内连接,显示两个表中有联系的所有数据;
2.左链接,以左表为参照,显示所有数据,右表中没有则以null显示
3.右链接,以右表为参照显示数据,,左表中没有则以null显示
索引是满足某种特定查找算法的数据结构,而这些数据结构会以某种方式指向数据,从而实现高效查找数据。具体来说 MySQL 中的索引,不同的数据引擎实现有所不同,但目前主流的数据库引擎的索引都是 B+ 树实现的,B+ 树的搜索效率,可以到达二分法的性能,找到数据区域之后就找到了完整的数据结构了,所有索引的性能也是更好的。
使用 explain 查看 SQL 是如何执行查询语句的,从而分析你的索引是否满足需求。explain 语法:explain select * from table where type=1。
MySQL 的事务隔离是在 MySQL. ini 配置文件里添加的,在文件的最后添加:
transaction-isolation = REPEATABLE-READ 1 可用的配置值:READ-UNCOMMITTED、READ-COMMITTED、REPEATABLE-READ、SERIALIZABLE。READ-UNCOMMITTED:未提交读,最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读)。
读提交
读未提交
可重复读
串行化
MySQL存储引擎主要有:MyIsam、InnoDB、Memory、Blackhole、CSV、Performance_Schema、Archive、Federated、Mrg_Myisam。
但是最常用的是InnoDB和Mylsam。
MyISAM 只支持表锁,InnoDB 支持表锁和行锁,默认为行锁。表级锁:开销小,加锁快,不会出现死锁。锁定粒度大,发生锁冲突的概率最高,并发量最低。行级锁:开销大,加锁慢,会出现死锁。锁力度小,发生锁冲突的概率小,并发度最高。
乐观锁:每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在提交更新的时候会判断一下在此期间别人有没有去更新这个数据。
悲观锁:每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻止,直到这个锁被释放。数据库的乐观锁需要自己实现,在表里面添加一个 version 字段,每次修改成功值加 1,这样每次修改的时候先对比一下,自己拥有的 version 和数据库现在的 version 是否一致,如果不一致就不修改,这样就实现了乐观锁。
使用 show processlist 命令查看当前所有连接信息。使用 explain 命令查询 SQL 语句执行计划。开启慢查询日志,查看慢查询的 SQL。
为搜索字段创建索引。
避免使用 select *,列出需要查询的字段。
垂直分割分表。
选择正确的存储引擎。
-----------------------------------Redis-----------------------------------
Redis:REmote DIctionary Server(远程字典服务器)
是一个高性能的(key/value)分布式内存数据库,基于内存运行
并支持持久化的NoSQL数据库,是当前最热门的NoSql数据库之一,
也被人们称为数据结构服务器。
使用场景:
1、存储方式不同
memecache 把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小;redis有部份存在硬盘上,这样能保证数据的持久性,支持数据的持久化(笔者注:有快照和AOF日志两种持久化方式,在实际应用的时候,要特别注意配置文件快照参数,要不就很有可能服务器频繁满载做dump)。
2、数据支持类型不同
redis在数据支持上要比memecache多的多。
3、使用底层模型不同
新版本的redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。
4、运行环境不同
redis目前官方只支持LINUX 上去行,从而省去了对于其它系统的支持,这样的话可以更好的把精力用于本系统 环境上的优化,虽然后来微软有一个小组为其写了补丁。但是没有放到主干上。
官方FAQ表示,因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了
如果在请求数据时,在缓存层和数据库层都没有找到符合条件的数据,也就是说,在缓存层和数据库层都没有命中数据,那么,这种情况就叫作缓存穿透。
第一种解决方案:就是把空对象缓存起来。当第一次从数据库中查询出来的结果为空时,我们就将这个空对象加载到缓存,并设置合理的过期时间,这样,就能够在一定程度上保障后端数据库的安全。
第二种解决缓存穿透问题的解决方案:就是使用布隆过滤器,布隆过滤器可以针对大数据量的、有规律的键值进行处理。一条记录是不是存在,本质上是一个Bool值,只需要使用 1bit 就可以存储。我们可以使用布隆过滤器将这种表示是、否等操作,压缩到一个数据结构中。比如,我们最熟悉的用户性别这种数据,就非常适合使用布隆过滤器来处理。
Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。
Redisson、Jedis、lettuce等等,官方推荐使用Redisson。
Jedis 和 Redisson 都是Java中对Redis操作的封装。Jedis 只是简单的封装了 Redis 的API库,可以看作是Redis客户端,它的方法和Redis 的命令很类似。Redisson 不仅封装了 redis ,还封装了对更多数据结构的支持,以及锁等功能,相比于Jedis 更加大。但Jedis相比于Redisson 更原生一些,更灵活。
一种是RDB持久化(原理是将Reids在内存中的数据库记录定时dump到磁盘上的RDB持久化),另外一种是AOF持久化(原理是将Reids的操作日志以追加的方式写入文件)。
RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。
AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。
为了保证分布式锁的可用性,至少要确保锁的实现要同时满足以下几点:
一般来说,实现分布式锁的方式有以下几种:
1)setnx、expire是分两步操作而非一个原子性操作,可能会出现setnx成功,expire失败的情况下,导致锁没有设置过期时间而变成死锁;
2)没有设置等待时间,获取锁失败则不会尝试重新获取锁了。
3)释放锁时,判断锁是否已锁、是不是自己的锁、释放锁也是分了三步操作而非一个原子性操作。可能会出现判断是自己的锁了,即将执行释放锁操作但还未执行时,如果锁在此时超时释放了,别的线程同时获得了这个锁,我们继续执行释放锁的操作会导致释放了别人的锁;
4)没有实现锁的超时续期,导致可能业务还没执行完就过期释放了这个锁,从而出现线程安全问题;
5)上锁失败,就判断是否设置了过期时间,若是没有,则给它设置过期时间;这里虽然可以避免了死锁的问题,但是不够严谨,无法判断别的线程业务代码的执行时间,过期时间设置无法确定合不合理
1、缩减键值对象
2、共享对象池
3、字符串优化
4、编码优化
5、控制key的数量
JVM:类加载器,运行时数据区(Java内存),执行引擎,本地接口
1.类加载器(Class Loader):加载类文件到内存。Class loader只管加载,只要符合文件结构就加载,至于能否运行,它不负责,那是有Exectution Engine 负责的。
2.执行引擎(Execution Engine):也叫解释器,负责解释命令,交由操作系统执行。
3.本地库接口(Native Interface):本地接口的作用是融合不同的语言为java所用
4.运行时数据区(Runtime Data Area):
Java虚拟机在执行Java程序的过程中会把它管理的内存分为若干个不同的数据区域。这些区域有着各自的用途,一级创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则依赖用户线程的启动和结束而建立和销毁
堆与栈的区别有:1、栈由系统自动分配,而堆是人为申请开辟;2、栈获得的空间较小,而堆获得的空间较大;3、栈由系统自动分配,速度较快,而堆一般速度比较慢;4、栈是连续的空间,而堆是不连续的空间。
堆和栈的区别主要有五大点,分别是:
1、申请方式的不同。栈由系统自动分配,而堆是人为申请开辟;
2、申请大小的不同。栈获得的空间较小,而堆获得的空间较大;
3、申请效率的不同。栈由系统自动分配,速度较快,而堆一般速度比较慢;
4、存储内容的不同。栈在函数调用时,函数调用语句的下一条可执行语句的地址第一个进栈,然后函数的各个参数进栈,其中静态变量是不入栈的。而堆一般是在头部用一个字节存放堆的大小,堆中的具体内容是人为安排;
5、底层不同。栈是连续的空间,而堆是不连续的空间。
1.队列(Queue):是限定只能在表的一端进行插入和在另一端进行删除操作的线性表
2.栈(Stack):是限定只能在表的一端进行插入和删除操作的线性表
3.队列先进先出(FIFO),栈先进后出(FILO)
双亲委派模型要求除顶层启动类加载器外其余类加载器都应该有自己的父类加载器;类加载器之间通过复用关系来复用父加载器的代码
类加载的过程主要分为三个部分:
加载:加载指的是把class字节码文件从各个来源通过类加载器装载入内存中。
链接
初始化
而链接又可以细分为三个小部分:
验证
准备
解析
什么是垃圾?
一般有两种方法来判断:
引用计数器:为每个对象创建一个引用计数,有对象引用时计数器 +1,引用被释放时计数 -1,当计数器为 0 时就可以被回收。它有一个缺点不能解决循环引用的问题;
可达性分析:从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是可以被回收的。
就是指在程序代码之中普遍存在的,类似“Object obj=new Object()” 这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象实例。
一些有用但是并非必需,用软引用关联的对象,系统将要发生OOM之前,这些对象就会被回收。
是一些有用(程度比软引用更低)但是并非必需,用弱引用关联的对象,只能生存到下一次垃圾回收之前,GC发生时,不管内存够不够,都会被回收
最弱,被垃圾回收的时候收到一个通知
一个对象 实例是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用 来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象 实例被收集器回收时收到一个系统通知
常用的垃圾回收算法有四种:标记-清除算法、复制算法、标记-整理算法、分代收集算法。
新生代收集器:
Serial
ParNew
Parallel Scavenge
老年代收集器:
Serial Old
CMS
Parallel Old
堆内存垃圾收集器:
G1
CMS全称为:Concurrent Mark Sweep意为并发标记清除,他使用的是标记清除法。主要关注系统停顿时间。
CMS 处理过程有七个步骤:
1. 初始标记(CMS-initial-mark) ,会导致swt;
2. 并发标记(CMS-concurrent-mark),与用户线程同时运行;
3. 预清理(CMS-concurrent-preclean),与用户线程同时运行;
4. 可被终止的预清理(CMS-concurrent-abortable-preclean) 与用户线程同时运行;
5. 重新标记(CMS-remark) ,会导致swt;
6. 并发清除(CMS-concurrent-sweep),与用户线程同时运行;
7. 并发重置状态等待下次CMS的触发(CMS-concurrent-reset),与用户线程同时运行;
新生代回收器:Serial、ParNew、Parallel Scavenge
老年代回收器:Serial Old、Parallel Old、CMS
整堆回收器:G1
新生代垃圾回收器一般采用的是复制算法,复制算法的优点是效率高,缺点是内存利用率低;老年代回收器一般采用的是标记-整理的算法进行垃圾回收。
分代回收器有两个分区:老生代和新生代,新生代默认的空间占比总空间的 1/3,老生代的默认占比是 2/3。 新生代使用的是复制算法,新生代里有 3 个分区:Eden、To Survivor、From Survivor,它们的默认占比是 8:1:1,
它的执行流程如下:
把 Eden + From Survivor 存活的对象放入 To Survivor 区;
清空 Eden 和 From Survivor 分区; From Survivor 和 To Survivor 分区交换,From Survivor 变 To Survivor,To Survivor 变 From Survivor。
每次在 From Survivor 到 To Survivor 移动时都存活的对象,年龄就 +1,当年龄到达 15(默认配置是 15)时,升级为老生代。大对象也会直接进入老生代。
老生代当空间占用到达某个值之后就会触发全局垃圾收回,一般使用标记整理的执行算法。
以上这些循环往复就构成了整个分代垃圾回收的整体执行流程。
常用调优工具分为两类,jdk自带监控工具:jconsole和jvisualvm,第三方有:
MAT(Memory AnalyzerTool)、GChisto。 jconsole,Java Monitoring and Management Console是从java5开始,在JDK中自带的
java监控和管理控制台,用于对JVM中内存, 线程和类等的监控。
jvisualvm,jdk自带全能工具,可以分析内存快照、线程快照;监控内存变化、GC变化等。
MAT,Memory Analyzer Tool,一个基于Eclipse的内存分析工具,是一个快速、功能丰富
的Javaheap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗。
GChisto,一款专业分析gc日志的工具。
jps,JVM Process Status Tool,显示指定系统内所有的HotSpot虚拟机进程。
jstat,JVM statistics Monitoring是用于监视虚拟机运行时状态信息的命令,它可以显示出
虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。
jmap,JVM Memory Map命令用于生成heap dump文件
jhat,JVM Heap Analysis Tool命令是与jmap搭配使用,用来分析jmap生成的dump,jhat
内置了一个微型的HTTP/HTML服务器,生成dump的分析结果后,可以在浏览器中查看
jstack,用于生成java虚拟机当前时刻的线程快照。
jinfo,JVM Configuration info 这个命令作用是实时查看和调整虚拟机运行参数。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。