Java基础Java八种基本数据类型及所占字节(8位二进制称为一字节)1字节(byte)=8比特(bit)byte:8bitboolean:8short:16char:16int:32float:32long:64double:64Java中Class类的作用与深入理解Class类可以帮助我们在程序运行时分析类,说白了就是获取类中的值。我们向Class提供一个类或一个类的类名,Class就可以提供我们很多信息,比如属性/方法/修饰符/构造器/类名等等。然后我们就可以进一步进行反射。——————————————————————————————————————————————————————————Java反射绕过泛型检查 //指定list的泛型为Integer ArrayList<Integer> list = new ArrayList<>(); //Integer类型的数据可以正常添加 list.add(1); //如果添加String类型的数据,则编译器报错,无法通过编译 //list.add("a"); //通过反射绕过泛型检查,即泛型擦除 Class<ArrayList> listClass = ArrayList.class; Method addMethod = listClass.getMethod("add", Object.class); //编译器不再报错,程序也可以正常执行 addMethod.invoke(list, "a");———————————————————————————————————————————————————————————String类是不可变类,即一旦一个String对象被创建以后,包含在这个对象中的字符序列是不可改变的,直至这个对象被销毁。StringBuffer对象则代表一个字符序列可变的字符串,当一个StringBuffer被创建以后,通过StringBuffer提供的append()、insert()、reverse()、setCharAt()、setLength()等方法可以改变这个字符串对象的字符序列。一旦通过StringBuffer生成了最终想要的字符串,就可以调用它的toString()方法将其转换为一个String对象。StringBuffer类中的方法都添加了synchronized关键字,也就是给这个方法添加了一个锁,用来保证线程安全。StringBuilder和StringBuffer基本相似,两个类的构造器和方法也基本相同。————————————————————————————————————————————————————————————Class对象的生成方式如下:1.Class.forName("类名字符串") (注意:类名字符串必须是全称,包名+类名);2.类名.class;3.实例对象.getClass();————————————————————————————————————————————————————————int和Integer的区别1、Integer是int的包装类,int则是java的一种基本数据类型2、Integer变量必须实例化后才能使用,而int变量不需要3、Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值4、Integer的默认值是null,int的默认值是05. 集合用Integer,数组用int。==用int equal用Integer————————————————————————————————————————————————————————java8的新特性 待总结——————————————————————————————————————————————————————————接口和抽象类的区别抽象类要被子类继承,接口要被类实现。接口只能做方法声明,抽象类中可以作方法声明,也可以做方法实现。(抽象类可以不含抽象方法)接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。抽象类和接口都是用来抽象具体对象的,但是接口的抽象级别最高。抽象类主要用来抽象类别,接口主要用来抽象功能。接口是设计的结果,抽象类是重构的结果。——————————————————————————————————————————————————————————————————为什么重写equals还要重写hashcode?Java对于eqauls方法和hashCode方法是这样规定的:(1)如果两个对象相同(equals方法返回true),那么它们的hashCode值一定要相同;(2)如果两个对象的hashCode相同,它们并不一定相同。当然,你未必要按照要求去做,但是如果如果hashcode不同,而equals相同,相同的对象可以出现在Set集合中,同时增加新元素的效率会大大下降——————————————————————————————————————————————————————————————————final关键字修饰类:该类无法被继承;final类中的所有成员方法都会被隐式地指定为final方法。修饰方法:此方法不能被重写。但可以被子类继承。父类中private final 方法不能被继承,子类可以定义同名同参的新方法。(类的private方法会隐式地被指定为final方法。)修饰变量:饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化;如果final修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引用所指向的对象的内容是可以发生变化的;当用final作用于类的成员变量时,成员变量(注意是类的成员变量,局部变量只需要保证在使用之前被初始化赋值即可)必须在定义时或者构造器中进行初始化赋值。———————————————————————————————————————————————————————————————————————关于异常try来执行一段程序,如果出现异常,系统会抛出(throws)一个异常,这时候你可以通过它的类型来捕捉(catch)它,或最后(finally)由缺省处理器来处理。用try来指定一块预防所有”异常”的程序。紧跟在try程序后面,应包含一个catch子句来指定你想要捕捉的”异常”的类型。throw语句用来明确地抛出一个”异常”。throws用来标明一个成员函数可能抛出的各种”异常”。Finally为确保一段代码不管发生什么”异常”都被执行一段代码。——————————————————————————————————————————————————————————————————————————Comparable和Comparator接口的作用以及它们的区别Comparable接口位于 java.lang包下,Comparator接口位于java.util包下。Comparable: 内部比较器,一个类如果想要使用 Collections.sort(list) 方法进行排序,则需要实现该接口Comparator: 外部比较器用于对那些没有实现Comparable接口或者对已经实现的Comparable中的排序规则不满意进行排序.无需改变类的结构,更加灵活。(策略模式)Comparable只有compareTo()方法,返回负数,0,正数来表明输入对象小于,等于,大于已经存在的对象。Comparator接口包含compare()和equals()两个方法。compare()方法用来给两个输入参数排序,返回负数,0,正数表明第一个参数是小于,等于,大于第二个参数。equals()方法???——————————————————————————————————————————————————————————————————————泛型和泛型限定符(extends 和super ):是指 “上界通配符(Upper Bounds Wildcards) 只能放它和它的子类 不能往里存,只能往外取,读取出来的东西只能存放在它或它的父类里。:是指 “下界通配符(Lower Bounds Wildcards) 只能放它和它的父类 可以往里存,往外取只能放在Object对象里 ————————————————————————————————————————————————————————————————————Object类的方法 Object()默认构造方法。 clone() 创建并返回此对象的一个副本。 equals(Object obj) getClass() hashCode() notify() notifyAll() toString() wait() wait(long timeout) wait(long timeout, int nanos)——————————————————————————————————————————————————————————————————————————String为什么不可变?(不能改变对象内的成员变量,包括基本数据类型的值不能改变,引用类型的变量不能指向其他的对象,引用类型指向的对象的状态也不能改变。) 从实现上: final 类,且类内部的 value 字节数组也是 final 的 从设计理念上: 字符串是不可变时字符串池才有可能实现; 不可变话账号密码等字符串类型不会被黑客改变,安全; 多线程安全的,同一个字符串实例可以被多个线程共享; 在它创建的时候 hashcode 就被缓存了,不变性也保证了 hash 码的唯一性,适合作为 Map 的键。value,offset和count这三个变量都是private的,并且没有提供setValue, setOffset和setCount等公共方法来修改这些值,所以在String类的外部无法修改String。也就是说一旦初始化就不能修改, 并且在String类的外部不能访问这三个成员。————————————————————————————————————————————————————————————————快速失败(fail-fast)和安全失败(fail-safe)的区别?概念:java.util包下面的所有的集合类都是快速失败的,而java.util.concurrent包下面的所有的类都是安全失败的。快速失败的迭代器会抛出ConcurrentModificationException异常,而安全失败的迭代器永远不会抛出这样的异常。——————————————————————————————————————————————————————————————————动态链接与静态链接一段代码从文本编辑器上产生到最终能够在机器上运行包含了以下几个阶段:编译: 编译器通过词法分析,语法分析,语义分析等,将一段代码翻译成汇编语言汇编:将汇编语言翻译成机器指令链接:解决符号之间的重定位问题装载:将可执行文件加载到内存静态链接就是在装载之前,就完成所有的符号引用的一种链接方式。静态链接的处理过程分为2个步骤:1.空间与地址的分配。扫描所有的目标文件,合并相似段,收集当中所有的符号信息。2.符号解析与重定位。调整代码位置。缺点:浪费内存空间。在多进程的操作系统下,同一时间,内存中可能存在多个相同的公共库函数。程序的开发与发布流程受模块制约。 只要有一个模块更新,那么就需要重新编译打包整个代码。动态链接基本思想就是将对符号的重定位推迟到程序运行时才进行。只要推迟到运行时进行符号的重定位,就能解决静态链接的两个缺点。对于静态链接来说,系统只需要加载一个文件(可执行文件)到内存即可,但是在动态链接下,系统需要映射一个主程序和多个动态链接模块,因此,相比于静态链接,动态链接使得内存的空间分布更加复杂。————————————————————————————————————————————————————————————————Java异常体系结构Thorwable类所有异常和错误的父类,有两个子类Error和Exception,分别表示错误和异常。 Error是程序无法处理的错误,比如OutOfMemoryError、ThreadDeath等。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。异常类Exception又分为运行时异常(RuntimeException)和非运行时异常。 — 运行时异常都是RuntimeException类及其子类异常,如NullPointerException、IndexOutOfBoundsException等, 这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。 — 非运行时异常是RuntimeException以外的异常,类型上都属于Exception类及其子类。 从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。throws:用来声明一个方法可能产生的所有异常,不做任何处理而是将异常往上传,谁调用我我就抛给谁。 调用带throws声明的方法,必须显式捕获该异常,否则,必须在该方法中再次声明抛出。 层层抛出最终由虚拟机处理,把异常的名称,异常出现的位置,异常原因,等信息输出打印在控制台,并同时将程序停止执行。throw:就是自己处理一个异常,有两种方式要么是自己捕获异常try...catch代码块,要么是抛出一个异常(throws 异常)———————————————————————————————————————————————————————————————— 短知识static方法和普通方法的区别:static在方法区中加载,普通方法在Jvm栈中加载。static方法的局部变量在方法区自定义类需要用在set map集合中,需要重写hashcode和equal方法。Java中是如何支持正则表达式操作的?String类提供了支持正则表达式操作的方法,包括:matches()、replaceAll()、replaceFirst()、split()。 此外,Java中可以用Pattern类表示正则表达式对象,它提供了丰富的API进行各种正则表达式操作在Java中如何跳出当前的多重嵌套循环?在最外层循环前加一个标记如A,然后用break A;可以跳出多重循环。&和&&的区别? &运算符有两种用法:(1)按位与;(2)逻辑与。&&运算符是短路与运算。&&左边的表达式的值是false,右边的表达式会被直接短路掉,不会进行运算。数组(Array)和列表(ArrayList)的区别? Array可以包含基本类型和对象类型,ArrayList只能包含对象类型。 Array大小是固定的,ArrayList的大小是动态变化的。 ArrayList提供了更多的方法和特性,比如:addAll(),removeAll(),iterator()等等。我们add入list集合的是data对象的引用的复制品,而不是date对象本身,date对象本身在虚拟机内只存在一份,引用和引用复制品指向的都是同一个date对象,所以通过一个引用更改data对象的属性,是可以导致引用复制品指向的对象属性也变化的。请你解释为什么会出现4.0-3.6=0.40000001这种现象?2进制的小数无法精确的表达10进制小数,计算机在计算10进制小数的过程中要先转换为2进制进行计算,这个过程中出现了误差。Object若不重写hashCode()的话,hashCode()如何计算出来的?Object 的 hashcode 方法是本地方法,也就是用 c 语言或 c++ 实现的,该方法直接返回对象的内存地址。方法覆盖必须有相同的方法名,参数列表和返回类型。a=a+b、a+=b的区别?+=可能会触发类型转换,如果a、b类型不同时通过反射获取和设置对象私有字段的值?类对象的getDeclaredField()方法字段(Field)对象,然后再通过字段对象的setAccessible(true)将其设置为可以访问,接下来就可以通过get/set方法来获取/设置字段的值了。Static Nested Class静态内部类 和 普通内部类的不同? Static Nested Class静态内部类可以在不创建实例的条件下直接创建,因为它只访问静态方法和成员,它和类直接绑定,并且不能访问任何非静态类型的方法和成员变量。但是Inner class内部类是和实例绑定的,他可以访问实例的成员变量和方法,所以在创建他之前必须先创建一个实例,然后通过实例创建它才行。非静态内部类: 成员内部类:类中方法外 局部内部类:方法中 匿名内部类finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源Query接口的list方法和iterate方法有什么区别?待总结面向对象的特征:抽象、继承、封装、多态。通过反射创建对象: String.class.newInstance() String.class.getConstructor(String.class).newInstance("Hello")Integer能不能用==判断相等?cache中已有-128到127,不在这范围的会新new ,这时可以理解比较是内存地址,也就是是不是同一对象;所以说当Integer的值不在-128到127的时候使用==方法判断是否相等就会出错,在这个范围之内的就会没有问题!!!HashMap的时间复杂度?只有那个hash算法尽量减少冲突,才能使链表长度尽可能短,理想状态为1。因此可以得出结论:HashMap的查找时间复杂度只有在最理想的情况下才会为O(1),而要保证这个理想状态不是我们开发者控制的。———————————————————————————————————————————————————————————————————Java中是否可以覆盖(override)一个private或者是static的方法? private只能够被自身类访问,子类不能访问private修饰的成员,所有不能override一个private方法;子类继承了父类的所有属性和方法或子类拥有父类的所有属性和方法是对的,只不过父类的私有属性和方法,子类是无法直接访问到的。即只是拥有,但是无法使用。 static方法是与类绑定的与任何实例都无关,随着类的加载而加载, static是编译时静态绑定的,override是运行时动态绑定的。形式上static可以override,但是实际上并不能被override。——————————————————————————————————————————————————————————————————String是否能能继承?String类是被final关键字修饰的,并且String类实际是一个被final关键字修饰的char[]数组,所以实现细节上也是不允许改变。wait方法的底层原理:说明Collection 和 Collections的区别: Collection是集合类的上级接口,继承与他的接口主要有Set 和List. Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。Iterator和ListIterator的区别是:Iterator可用来遍历Set和List集合, ListIterator只能用来遍历List。Iterator对集合只能是前向遍历, ListIterator既可以前向也可以后向。ListIterator实现了Iterator接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引,等等。什么是迭代器? Iterator提供了统一遍历操作集合元素的统一接口, Collection接口实现Iterable接口,每个集合都通过实现Iterable接口中iterator()方法返回Iterator接口的实例, 然后对集合的元素进行迭代操作.举例说明同步和异步: 同步就是指阻塞式操作,而异步就是非阻塞式操作。临界资源/不希望等待某方法结束才继续——————————————————————————————————————————————————————————————————Collection List —ArrayList 默认长度为10的数组,适合查找和遍历,不适合插入删除。当数组的大小大于初始容量的时候(比如初始为10,当添加第11个元素的时候), 就会进行扩容,新的容量为旧的容量的1.5倍。以新的容量建一个原数组的拷贝,修改原数组,指向这个新数组,原数组被抛弃,会被GC回收。 —Vector 数组实现,线程安全。自动扩容为两倍。 —LinkList 链表,适合插入删除。具有List中没有定义的方法,专门操作表头表尾的元素,可以当作堆栈,队列,双向队列使用 对于非线程安全的list,可以通过工具类Collections中的synchronizedList方法将其转换成线程安全的容器后再使用 Set:不允许元素重复,想要两个不同的对象视为相等,需要覆盖Object类的hashCode方法和equals方法 —HashSet 底层为数组和链表,初始长度16,负载因子0.75,超过后扩大为原来两倍。底层为HashMap(的key) value是一个值为null的Object对象 扩大为原来两倍? 因为Hashmap计算存储位置时,使用了(n - 1) & hash。只有当容量n为2的幂次方,n-1的二进制会全为1,位运算时可 以充分散列,避免不必要的哈希冲突,所以扩容必须2倍就是为了维持容量始终为2的幂次方。 —TreeSet 底层红黑树,插入对象时进行排序,只能插入相同类的对象。自定义的类要实现Comparable接口,覆写compareTo函数。 —LinkHashSet 添加的时候维护了双向链表,记录添加顺序。添加顺序就是遍历顺序。底层是一个哈希表(数组+链表/红黑树) , 多了一条链表(记录元素的存储顺序),保证元素的有序 Map —HashMap:数组+链表+红黑树(Java7的时候为数组+链表),链表元素超过8之后转化为红黑树,线程不安全。键和值可以为null,最多只允许一条记录的键为null —ConcurrentHashMap:ConcurrentHashMap 类中包含两个静态内部类 HashEntry 和 Segment。HashEntry 用来封装映射表的键 / 值对;Segment 用来充当锁的角色。 是一个Segment数组,Segment继承ReentrantLock(自旋锁)来进行加锁(分段锁),线程安全。默认为16个Segment,初始化时可设置大小,但不可扩容 (Segment可扩容)。Java8也引入红黑树。 —HashTable:键和值不能为null,线程安全(synchronized方法)。不建议使用。需要线程安全可以用ConcurrentHashMap,不需要可以用HashMap —TreeMap:基于红黑树的NavigableMap的实现,线程非安全,不允许null,可以根据键值排序,遍历时是排过序的,存入TreeMap的元素应当 实现Comparable接口或者实现Comparator接口 —LinkHashMap:HashMap子类,保存了记录的插入顺序。遍历时也是排过序的。———————————————————————————————————————————————————————————————————————————hashmap死循环(1.7):进行put操作会引起死循环HashMap之所以在并发下的扩容造成死循环,是因为,多个线程并发进行时,因为一个线程先期完成了扩容,将原的链表重新散列到自己的表中,并且链表变成了倒序,后一个线程再扩容时,又进行自己的散列,再次将倒序链表变为正序链表。于是形成了一个环形链表,当表中不存在的元素时,造成死循环。————————————————————————————————————————————————————————————————————————List集合中subList(int fromIndex, int toIndex)方法调用subList()方法之后便不能再对原有集合进行增加或删除,如果修改了原list的大小,那么之前产生的子list将会失效,变得不可使用。subList的返回对象为List,不可强转为ArrayList,如强转,则报出类转换异常subList(int fromIndex, int toIndex)截取数据时是前闭后开的——————————————————————————————————————————————————————————————————————————forward和redirect的区别forword是服务器内部的重定向,服务器直接访问目标地址的 url网址;redirect是服务器根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址,所以地址栏显示的是新的地址。在整个定向的过程中用的是同一个request,因此forward会将request的信息带到被重定向的jsp或者servlet中使用。即可以共享数据;redirect不能共享。forword效率高,而redirect效率低。forword转发是服务器上的行为,而redirect重定向是客户端的行为。forword只有一次请求;而redirect有两次请求。————————————————————————————————————————————————————————————————————深拷贝和浅拷贝: 对于基本数据类型:深拷贝浅拷贝一样,都是值的传递 对于引用数据类型:深拷贝是复制一个相同的引用对象,浅拷贝是将引用指向相同的对象。——————————————————————————————————————————————————————————————————————判断线程是否存活:通过Thread类中的isAlive()方法判断线程是否处于活动状态;线程启动后,只要没有运行完毕,都会返回true。————————————————————————————————————————————————————————————————————Java 获取环境变量的方式很简单: System.getEnv() 得到所有的环境变量System.getEnv(key) 得到某个环境变量的值————————————————————————————————————————————————————————————————————Java获取时间戳的方式:System.currentTimeMillis(); Calendar.getInstance().getTimeInMillis(); new Date().getTime(); 第一种方式效率最快,第二种方式效率最慢JAVA时间戳长度是13位————————————————————————————————————————————————————————————————————————为什么JDK 8要引入红黑树?红黑树虽然本质上是一棵二叉查找树,但它在二叉查找树的基础上增加了着色和相关的性质使得红黑树相对平衡,从而保证了红黑树的查找、插入、删除的时间复杂度最坏为O(log n)。在jdk1.8版本后,java对HashMap做了改进,在链表长度大于8的时候,将后面的数据存在红黑树中,以加快检索速度。红黑树:性质1. 节点是红色或黑色。性质2. 根节点是黑色。性质3.所有叶子都是黑色。(叶子是NIL节点)性质4. 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)性质5.. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。——————————————————————————————————————————————————————————————————————什么是java序列化?Java中的序列化机制能够将一个实例对象(只序列化对象的属性值,而不会去序列化什么所谓的方法。)的状态信息写入到一个字节流中使其可以通过socket进行传输、或者持久化到存储数据库或文件系统中;然后在需要的时候通过字节流中的信息来重构一个相同的对象。一般而言,要使得一个类可以序列化,只需简单实现java.io.Serializable接口即可。java 默认不支持序列化 ?不是所有类都可以序列化: (1) 太依赖于底层实现的类(too closely tied to native code)。比如java.util.zip.Deflater。 (2) 对象的状态依赖于虚拟机内部和不停变化的运行时环境。比如java.lang.Thread, java.io.InputStream (3) 涉及到潜在的安全性问题。比如:java.lang.SecurityManager, java.security.MessageDigest (4) 全是静态域的类,没有对象实例数据。要知道静态域本身也是存储在方法区中的。序列化可以让我们轻而易举的接触到对象的私有数据域,这是多么危险的漏洞呀!————————————————————————————————————————————————————————————————————————Java中有哪些锁锁的分类有:公平锁/非公平锁、共享锁/排它锁、互斥锁/读写锁、乐观锁/悲观锁。可重入锁/不可重入锁、分段锁、轻量级锁/重量级锁/偏向锁、自旋锁。————————————————————————————————————————————————————————————————BIO:同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。NIO :同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。AIO :异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。同步阻塞IO:在此种方式下,用户进程在发起一个IO操作以后,必须等待IO操作的完成,只有当真正完成了IO操作以后,用户进程才能运行。JAVA传统的IO模型属于此种方式!同步非阻塞IO:在此种方式下,用户进程发起一个IO操作以后边可返回做其它事情,但是用户进程需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问,从而引入 不必要的CPU资源浪费。其中目前JAVA的NIO就属于同步非阻塞IO。异步阻塞IO:此种方式下是指应用发起一个IO操作以后,不等待内核IO操作的完成,等内核完成IO操作以后会通知应用程序,这其实就是同步和异步最关键的区别,同步必须等 待或者主动的去询问IO是否完成,那么为什么说是阻塞的呢?因为此时是通过select系统调用来完成的,而select函数本身的实现方式是阻塞的,而采用select函数 有个好处就是它可以同时监听多个文件句柄,从而提高系统的并发性!异步非阻塞IO:在此种模式下,用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处 理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了。————————————————————————————————————————————————————————————————编译程序和解释程序有何区别?1、编译程序是整体编译完了,再一次性执行。2、而解释程序是一边解释,一边执行。 解释一句后就提交计算机执行一句,并不形成目标程序。就像外语翻译中的“口译”一样,说一句翻一句,不产生全文的翻译文本。3、编译器是把源程序的每一条语句都编译成机器语言,并保存成二进制文件,这样运行时计算机可以直接以机器语言来运行此程序,速度很快。4、而解释器则是只在执行程序时,才一条一条的解释成机器语言给计算机来执行,所以运行速度是不如编译后的程序运行的快的。这是因为计算机不能直接认识并执行我们写的语句,它只能认识机器语言(是二进制的形式)JIT即时编译器——————————————————————————————————————————————————————————————————————lambda表达式:本质是接口的实例,接口抽象方法只有一个。(o1,o2)->Integer.compare(o1,o2)箭头操作符左边:实质上是重写的抽象方法的形参列表,参数类型可以省略(类型推断),只有一个参数可以省略小括号箭头操作符右边:实质上是抽象方法的方法体,只有一行(可能时return语句)可以省略大括号和return关键字。——————————————————————————————————————————————————在UML的类图中,常见的有以下几种关系: 泛化(Generalization), 实现(Realization), 关联(Association), 聚合(Aggregation), 组合(Composition), 依赖(Dependency)【泛化关系】:是一种继承关系, 表示一般与特殊的关系, 它指定了子类如何特化父类的所有特征和行为. 例如:老虎是动物的一种, 即有老虎的特性也有动物的共性.【实现关系】:是一种类与接口的关系, 表示类是接口所有特征和行为的实现.【关联关系】:是一种拥有的关系, 它使一个类知道另一个类的属性和方法;如:老师与学生,丈夫与妻子;代码体现:成员变量【聚合关系】:是整体与部分的关系, 且部分可以离开整体而单独存在. 如车和轮胎是整体和部分的关系, 轮胎离开车仍然可以存在。聚合关系是关联关系的一种,是强的关联关系【组合关系】:是整体与部分的关系, 但部分不能离开整体而单独存在. 如公司和部门是整体和部分的关系, 没有公司就不存在部门。组合关系是关联关系的一种,是比聚合关系还要强的关系【依赖关系】:是一种使用的关系, 即一个类的实现需要另一个类的协助, 所以要尽量不使用双向的互相依赖。代码表现:局部变量、方法的参数或者对静态方法的调用———————————————————————————————————————————————————————————————————————— JVM/JMM类加载过程: 加载: 通过全限定类名获得二进制字节流 将字节流的静态存储结构转化为方法区运行时数据结构 在堆中生成class对象,作为该类数据的访问入口 验证: 验证是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,不会威胁到jvm的安全 准备: 为类的静态变量分配内存,初始化为系统的初始值。对于final static修饰的变量,直接赋值为用户的定义值。 解析: 解析是将常量池内的符号引用转为直接引用(如物理内存地址指针) 初始化: 编译器按语句在源文件中出现的顺序,依次自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并产生 () 方法。 如果类中没有静态语句和静态代码块,那可以不生成()` 方法。 当初始化一个类的时候,如果发现其父类还没有进行过初始化、则需要先触发其父类的初始化。 注意:()方法:对象构造时用以初始化对象的,构造器以及非静态初始化块中的代码。如果 () 方法中触发了对象的初始化, 也就是 () 方法,那么会进入执行 () 方法,执行 () 方法完成之后,再回来继续执行 () 方法。——————————————————————————————————————————————————————————双亲委派机制BootstrapClassLoader(启动类加载器)—加载Java核心库 JAVA_HOME\lib目录下的类 ExtClassLoader (标准扩展类加载器)载加载扩展库,JAVA_HOME\lib\ext目录下的类 AppClassLoader(系统类加载器)—java编写,加载程序所在的目录上的类库CustomClassLoader(用户自定义类加载器)—用户自定义的类加载器,可加载指定路径的class文件1.子类先委托父类加载2.父类加载器有自己的加载范围,范围内没有找到,则不加载,并返回给子类3.子类在收到父类无法加载的时候,才会自己去加载作用: 防止重复加载同一个.class。 保证核心.class不能被篡改。打破双亲委派自定义类加载器,重写loadClass方法;使用线程上下文类加载器;———————————————————————————————————————————————————————————三种JVM: sun公司:HotSpot BEA:Jrockit IBM:J9 VM————————————————————————————————————————————————————————————Java内存模型:1 线程私有: —程序计数器:唯一一个无OOM的区域。记录当前线程运行的指令行号(由字节码执行引擎修改)线程之间切换。 —虚拟机栈:和线程生命周期相同。 —栈帧:每调用一个方法创建一个栈帧。 —本地变量表 —操作数栈:临时内存区域存放操作数。 —对运行时常量池的引用 —本地方法栈:为虚拟机使用到的 Native 方法服务(某些类中的native方法进入这里),Navtive 方法是 Java 通过 JNI (本地接口)直接调用本地 C/C++ 库 虚拟机栈和本地方法栈均可能发生异常: 线程请求深度大于虚拟机所允许的深度。 若虚拟机允许动态拓展,若无法申请到足够内存。OOM2 线程共享: —方法区(永久代/元空间):常量final,静态变量static,类信息class(字节码文件加载到这里),运行时常量池 —类实例区(堆):会出现OOM异常 —新生代(8:1:1,因为经过统计,每次gc会有90%的对象被回收,所以要预留空间去保存剩下的10%) —老年代3.直接内存:不受JVM GC管理——————————————————————————————————————————————————————常量池: java虚拟机在启动的时候会实例化9个对象池,用来存放8种基本类型的包装类对象 和 String对象。如果直接赋值,那么先看一下被赋予的值(例如这里的 “123”)在对象池中是否存在,如果存在,就指向该对象,如果不存在就在对象池中创建这个对象;String str = new String(“abc”);至少会创建一个对象,也有可能创建两个。因为用到new关键字,肯定会在堆中创建一个String对象,如果字符池中已经存在”abc”,则不会在字符串池中创建一个String对象,如果不存在,则会在字符串常量池中也创建一个对象。————————————————————————————————————————————————————————————如何确定垃圾对象 引用计数法: 缺点:一是需要额外的空间来存储计数器,以及繁琐的更新操作。 二是无法处理循环引用对象。 可达性分析算法:解决了循环引用的问题。 发现不可达,就会被第一次标记并进行一次筛选,对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,虚拟机将这2种情况都视为 “没有必要执行”finalize()方法,则意味可直接回收。 如果对象被判定为有必要执行finalize()方法,这个对象会被放置在一个F-Queue的队列中,由一个虚拟机自动创建的、低优先级的Finalizer线程去执行。 但虚拟机并不承诺会等待finalize()方法执行完。 finalize()方法是对象逃脱死亡命运的最后一次机会,稍后GC将对F-Queue队列中的对象进行第二次小规模的标记,对象只要在fianlize()方法中重新与 GC Roots节点上的任意一个对象关联就可以在第二次标记时被移出“即将回收”的集合。 需要注意的是,任何一个对象的fianlize()方法都只会被系统自动调用一次。GC Roots 虚拟机栈(栈帧中的本地变量表)中引用的对象 方法区中类静态属性/常量引用的对象 本地方法栈中JNI(即一般说的Native方法)引用的对象——————————————————————————————————————————————————————————————————垃圾收集算法:复制算法,标记清除算法,标记整理算法分代收集算法:年轻代存活率低,采用复制算法;老年代存活率高采用标记清除(内存碎片较少)+标记整理混合实现。分区收集算法:Minor GC:清理新生代;当新生代无法为新生对象分配内存空间的时候。Full GC :是清理整个堆,包括新生代和老年代和元空间。触发条件 (1)调用System.gc时,系统建议执行Full GC,但是不必然执行 (2)老年代空间不足 (3)方法区空间不足 (4)通过Minor GC后进入老年代的平均大小大于老年代的可用内存 (5)由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小Major GC :和Full GC某些情况下是可以画等号的。一般来说,大对象会被直接分配到老年代,所谓的大对象是指需要大量连续存储空间的对象,最常见的一种大对象就是大数组。————————————————————————————————————————————————————————————————Full GC频繁的原因:1. 年老代空间比较小解决方法:第一,增大年老代空间。第二:使用CMS GC,对年老代进行回收,减少full gc发生的几率。2. 调用了System.gc()解决方法:-XX:+DisableExplicitGC 忽略手动调用GC, System.gc()的调用就会变成一个空调用,完全不触发GC———————————————————————————————————————————————————————————————————————————垃圾收集器新生代:Serial收集器(单线程,复制)—>ParNew收集器(多线程,复制)—>Parallel Scavenge收集器(多线程,复制,高效):目标是达到一个可控的吞吐量。老年代:Serial Old收集器(单线程,标记整理)—>Parallel Old收集器(多线程,标记整理)—>CMS收集器(多线程,标记清除)G1收集器:把堆分区,并且跟踪每个区域垃圾回收进度,后台维护一个优先级列表。根据所允许的收集时间,优先回收垃圾最多的区域,可以精确控制停顿时间。 CMS收集器和G1收集器的区别:CMS收集器是老年代的收集器,G1收集器收集范围是老年代和新生代;CMS收集器以最小的停顿时间为目标的收集器,G1收集器可预测垃圾回收的停顿时间;CMS收集器是使用“标记-清除”算法,会产生内存碎片,提供了-XX:UseCMSCompactAtFullCollection开关参数,用于在CMS收集器不得不进行Full GC时开启内存碎片的合并整理过程;G1收集器使用的是“标记-整理”算法。————————————————————————————————————————————————————————————————Java中四种引用类型强引用:无论如何不会被回收。软引用:内存不足时会被回收,需要SoftReference类实现。弱引用:只要垃圾回收机制一运行就会被回收,需要WeakReference类实现。虚引用:必须和引用队列(ReferenceQueue)联合使用,主要用来跟踪对象被垃圾回收的状态,需要PhantomReference类实现。一个对象,可以被多种类型引用同时指向,强度最高的决定他的生命周期。————————————————————————————————————————————————————————————————JVM参数及调优内存大小相关:Xms:启动时初始占用内存大小(年轻代大小 + 年老代大小 + 持久代大小 )Xmx:最大可占用的内存大小Xmn:年轻代大小XX:NewSize XX:MaxNewSizeXX:PermSize:持久代初始值XX:MaxPermSize:持久代最大值Xss: 每个线程的堆栈大小XX:NewRatio:年老代与年轻代比值XX:SurvivorRatio:Eden区与Survivor区的比值。XX:MaxTenuringThreshold=?:设置垃圾最大年龄回收器选择相关:UseParNewGC: ParNew + Serial OldUseParallelGC: Parallel Scavenge + Serial OldUseParallelOldGC: Parallel Scavenge + Parallel OldUseConcMarkSweepGC :ParNew + CMS(Serial Old备胎)UseSerialGC:Serial+Serial Old 并行收集器:吞吐量优先 并发收集器:响应时间优先-XX:ParallelGCThreads=20 :并行收集器的线程数-XX:MaxGCPauseMillis=100 : 每次年轻代回收的最长时间-XX:GCTimeRatio:设置垃圾回收时间占程序运行时间的百分比-XX:+ScavengeBeforeFullGC:Full GC前调用YGC-XX:+UseAdaptiveSizePolicy :并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等, 此值建议使用并行收集器时,一直打开。-XX:CMSFullGCsBeforeCompaction :由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。 此值设置运行多少次GC以后对内存空间进行压缩、整理。-XX:+UseCMSCompactAtFullCollection :打开对年老代的压缩。可能会影响性能,但是可以消除碎片辅助信息:-XX:+PrintGC-XX:+PrintGCDetails-XX:+PrintGCTimeStamps-Xloggc:filename:把相关日志信息记录到文件以便分析调优总结:年轻代: 响应时间优先: 吞吐量优先:年老代: 响应时间优先: 吞吐量优先:——————————————————————————————————————————————————————————————java.lang.Runtime类中的 freeMemory(), totalMemory(), maxMemory()maxMemory()这个方法返回的是java虚拟机(这个进程)能构从操作系统那里挖到的最大的内存totalMemory()这个方法返回的是java虚拟机现在已经从操作系统那里挖过来的内存大小 freeMemory()这个方法返回的是java虚拟机挖过来而又没有用上的内存————————————————————————————————————————————————————CMG发生几次STW(行垃圾收集算法时,Java应用程序的其他所有线程都被挂起(除了垃圾收集帮助器之外))?两次。1.初始标记:标记一下GC-ROOT直接关联对象。 STW2.并发标记:GC-ROOT追踪,其他线程同步工作。3.重新标记:更新在第二阶段标记产生变动的对象。 STW4.并发清除:清理对象。————————————————————————————————————————————————————逃逸分析:逃逸分析并不是直接的优化手段,而是一个代码分析,通过动态分析对象的作用域,为其它优化手段如栈上分配、标量替换和同步消除等提供依据。 如果不存在逃逸行为,则可以对该对象进行如下优化:同步消除、标量替换和栈上分配。 1.方法逃逸:当一个对象在方法中定义之后,作为参数传递到其它方法中; 2.线程逃逸:如类变量或实例变量,可能被其它线程访问到。同步消除线程同步本身比较耗,如果确定一个对象不会逃逸出线程,无法被其它线程访问到,那该对象的读写就不会存在竞争,则可以消除对该对象的同步锁,通过-XX:+EliminateLocks可以开启同步消除。标量替换如果逃逸分析发现一个对象不会被外部访问,并且该对象可以被拆散,那么经过优化之后,并不直接生成该对象,而是在栈上创建若干个成员变量;通过-XX:+EliminateAllocations可以开启标量替换, -XX:+PrintEliminateAllocations查看标量替换情况。栈上分配类似于标量替换。——————————————————————————————————————————————————————————————————内存问题排查1.内存溢出:可以通过加上 -XX:+HeapDumpOnOutOfMemoryError 参数,该参数作用是:在程序内存溢出时输出 dump 文件。有了 dump 文件,就可以通过 dump 分析工具进行分析了,比如常用的 MAT,Jprofile,jvisualvm 等工具都可以分析。2.GC 不健康: GC 的优化有 2 个维度,一是频率,二是时长。JDK 提供了很多的工具,比如 jmap ,jcmd 等, 还有一个比较常用的工具是 jstat,该工具可以查看 GC 的详细信息,比如 eden ,from,to,old 等区域的内存使用情况。 还有一个工具是 jinfo,该工具可以查看当前 jvm 使用了哪些参数,并且也可以在不停机的情况下修改参数。————————————————————————————————————————————————————————————如何检查内存泄漏?要定位问题,首先你需要获取服务器jvm某刻内存快照。jdk自带的jmap可以获取内存某一时刻的快照,导出为dmp文件后,就可以用Eclipse MAT来分析了,找出是那个对象使用内存过多。——————————————————————————————————————————————————————————————————jdk8环境下,默认使用 Parallel Scavenge(新生代)+ Serial Old(老年代)—————————————————————————————————————————————————————— 多线程线程安全的本质?线程安全本质是由于多个线程对同一个堆内存中的Count变量操作的时候,每一个线程会在线程内部创建这个堆内存Count变量的副本,线程内所有的操作都是对这个Count副本进行操作。这时如果其他线程操作这个堆内存Count变量,改变了Count值对这个线程是不可见的。当前线程操作完Count变量将值从副本空间写到主内存(堆内存)的时候就会覆盖其他线程操作Count变量的结果,引发线程不安全问题。——————————————————————————————————————————————————————JMM要求:可见性;原子性;有序性 ---主内存---工作内存 从主内存变量拷贝到工作内存,修改后写回主内存。线程间通信通过主内存完成。 可见性要求主内存的变量修改后其他线程工作内存立马得到通知。 原子性要求线程任务不被分割或加塞。完整性,要么同时成功要么同时失败_________________________________________________________________________________________________________________Volatile:JVM提供的轻量级同步机制(保证JMM的两个要求) ---保证可见性(无此关键字,其他线程将无法立即感知到变量的最新值) int i=0; 线程1:i++; 线程2:while(i==1) 将无限循环,无法感知 ----不保证原子性(例如10个线程对同一变量从0开始+10次,最终结果会丢数据,不一定是100) 例如:i++底层为三条指令,多个线程争抢CPU,某个线程在最后一条指令执行(写回主内存) 之前被挂起,被其他线程抢先写进主内存,本线程没有收到最新值的通知(纳秒级别的 时间差),随后写进主内存将之前值覆盖 解决:sync java.util.concurrent.atomic包下的原子整型类,如AtomicInteger ----禁止指令重排序 (有序性) 为提高性能,编译器与处理器常常对底层指令做重排。重排时必须考虑数据依赖性,单线程可 以保证数据一致性,但是多线程下线程交替执行,无法保证一致性。 ---内存屏障作用: 保证特定操作的执行顺序 保证某些变量的内存可见性单例模式下线程不安全问题:可通过双端检锁+volatile(DCL不一定安全,因为指令重排) new对象可以分为三步: 1 分配内存空间 2 初始化对象 3 引用指向内存空间 指令重排有可能132,返回引用有值但实际内存无实例的情况。 private static volatile SingletonDemo instance=null; public static SingletonDemo getInstance(){ if(instance==null){ synchronized(SingletonDemo.class){ if(instance==null){instance=new SingletonDemo();} //volatile禁止对象创建时指令之间重排序,所以其他线程不会访问到一个未初始化的对象,从而保证安全性。 } }}底层实现:观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令lock前缀指令实际上相当于一个内存屏障(也成内存栅栏),内存屏障会提供3个功能:1)它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;2)它会强制将对缓存的修改操作立即写入主存;3)如果是写操作,它会导致其他CPU中对应的缓存行无效。volatile使用场景:(一个线程写,多个线程读) 状态变量:由于boolean的赋值是原子性的,所以volatile布尔变量作为多线程停止标志还简单有效的. 对象完整发布:这里要提到单例对象的双重检查锁,对象完整发布也依赖于happens before原则 —————————————————————————————————————————————————原子类底层3原理—CAS自旋锁:比较并交换-->compareAndSet方法CAS是一条CPU并发原语,Compare-And-Swap,这个过程是原子的。比synchronized并发性高。sun.misc.Unsafe是CAS的核心类,它的方法都是本地方法,直接操作操作系统的资源。Unsafe类中的compareAndSwapInt是一个本地方法。举例:AtomicInteger类中的 getAndIncrement方法 public final int getAndIncrement(){return unsafe.getAndAddInt(this,valueoffset,1)} unsafe类中的getAndAddInt方法 public final int getAndAddInt(Object var1,long var2,int var4){ int var5; do{ var5=this.getIntVolatile(var1,var2); }while(!this.compareAndSwapInt(var1,var2,var5,var5+var4)) <------------- return var5; }var1: AtomicInteger对象本身var2: 对象值的引用地址var4 :需要变动的数量var5:根据var1和var2找出的主内存真实值。CAS缺点:------循环时间长的话开销大------只能保证一个共享变量的原子操作------ABA问题————————————————————————————————————————————ABA问题:首尾一样,中间被改过好多次AtomicReferance原子引用: user z3 = new user("z3", 22); user li4=new user("li4",25); AtomicReference atomicReference = new AtomicReference<>(); atomicReference.set(z3); System.out.println(atomicReference.compareAndSet(z3, li4)+"\t"+atomicReference.get().toString());//true System.out.println(atomicReference.compareAndSet(z3, li4)+"\t"+atomicReference.get().toString());//false解决ABA问题:AtomicStampReferance 带时间戳(版本号)的原子引用。——————————————————————————————————————————————集合类不安全问题java.util.ConcurrentModificationException解决方法:1.采用线程安全集合类 2.采用工具类: List list=Collections.synchronizedList(new ArrayList<>()) 3.java.util.concurrent包下的类: new CopyOnWriteArrayList<>() 底层原理:写时复制,读写分离。新容器加锁修改完成后,原引用指向新引用。并发读 java.util.concurrent包下的ConcurrentHashMap是HashMap的线程安全类——————————————————————————————————————————————公平和非公平锁:是否按照申请锁的顺序来获取锁。非公平锁吞吐量更大。并发包中的ReentrantLock(JDK5.0新增)默认非公平锁,可以指定构造函数传入Boolean类型synchronized是非公平锁可重入锁(递归锁):同一个线程外层方法获取锁后,进入内层方法自动获取锁。线程可以进入任何一个它已经拥有的 锁所同步着的代码块。可重入锁的意义在于防止死锁。 实现原理:实现是通过为每个锁关联一个请求计数和一个占有它的线程。当计数为0时,认为锁是未被占有的。 线程请求一个未被占有的锁时,jvm将记录锁的占有者,并且讲请求计数器置为1 。如果同一个线程再次请求这 个锁,计数将递增;每次占用线程退出同步块,计数器值将递减。直到计数器为0,锁被释放。 ReentrantLock和synchronized都是可重入锁 代码1:public sync method1(){method2()} public sync method2(){} 代码2:lock.lock(); lock.lock(); try{}finally{lock.unlock();lock.unlock()}自旋锁:采用循环的方式去尝试获取锁,减少了线程上下文切换消耗,但会消耗CPU独占锁(写锁):一次只能被一个线程占有。ReentrantLock和synchronized都是独占锁共享锁(读锁):互斥锁:读写锁: ReentrantReadWriteLock rwlock=new ReentrantReadWriteLock(); rwlock.readLock().lock(); 可加塞。 rwlock.writeLock().lock();————————————————————————————————————————————————java.util.concurrent.CountDownLatch 让一些线程阻塞,等待其他线程执行完再进行。 CountDownLatch countDownLatch=new CountDownLatch( 6); ....... countDownLatch.countDown( ); ...... countDownLatch.await( ); //某个线程等待countDownLatch变为零java.util.concurrent.CyclicBarrier 让一组线程到达一个屏障,直到最后一个线程到达屏障后,屏障才会开门, 所有被拦截的线程才会继续干活。 CyclicBarrier cyclicBarrier=new CyclicBarrier(7, ()->{sout("*******召唤神龙")}) ................. cyclicBarrier.await( );java.util.concurrent.Semaphore 信号量一是用于多个共享资源互斥使用,二是用于并发线程数的控制。 Semaphore semaphore=new Semaphore(3); //模拟3个车位 ....... semaphore.acquire(); ....... semaphore.release();——————————————————————————————————————————————阻塞队列:BlockingQueue 不需要我们关心什么时候阻塞线程,什么时候唤醒线程,BlockingQueue包办。Collection->Queue->BlockingQueue->ArrayBlockingQueue,LinkedBlockingQueue,PriorityBlockingQueue, DelayQueue,SynchronousQueue,LinkedTransferQueue,LinkedBlockDeque方法类型 抛出异常 (直接抛异常) 特殊值 (返回Boolean) 阻塞(一直等待) 超时(设置等待时间)插入 add(e) offer(e) put(e) offer(e,time,unit)移除 remove() poll() take() poll(time,unit)检查 element() peek() ——————————————————————————————————————————————————————————Lock(Lock的实现类一共有三个,一个是ReentrantLock,另两个是ReentrantReadWriteLock类中的两个静态内部类ReadLock和WriteLock)和synchronized区别:1.底层实现上来说,synchronized 是JVM层面的锁,是Java关键字,通过monitor对象来完成(monitorenter与monitorexit),对象只有在同步块或同步方法中才能调用wait/notify方法; ReentrantLock 是从jdk1.5以来(java.util.concurrent.locks.Lock)提供的API层面的锁。synchronized 的实现涉及到锁的升级,具体为无锁、偏向锁、自旋锁、向OS申请重量级锁; ReentrantLock实现则是通过利用CAS(CompareAndSwap)自旋机制保证线程操作的原子性和volatile保证数据可见性以实现锁的功能。2.是否可手动释放:synchronized 不需要用户去手动释放锁,synchronized 代码执行完后系统会自动让线程释放对锁的占用; ReentrantLock则需要用户去手动释放锁,如果没有手动释放锁,就可能导致死锁现象。一般通过lock()和unlock()方法配合try/finally语句块来完成,使用释放更加灵活。3.是否可中断:synchronized是不可中断类型的锁,除非加锁的代码中出现异常或正常执行完成; ReentrantLock则可以中断,可通过trylock(long timeout,TimeUnit unit)设置超时方法或者将lockInterruptibly()放到代码块中,调用interrupt方法进行中断。4.是否公平锁:synchronized为非公平锁; ReentrantLock则即可以选公平锁也可以选非公平锁,通过构造方法new ReentrantLock时传入boolean值进行选择,为空默认false非公平锁,true为公平锁。5.synchronized不能精确唤醒;Lock可绑定多个condition,用来实现分组唤醒需要唤醒的线程,可精确。————————————————————————————————————————————————————————————————————————为啥有synchronized了还用lock?Synchronized申请资源的时候,如果申请不到,线程就直接进入阻塞状态,进入阻塞状态就啥事都干不了了,所以也释放不了已经占有的资源。所以就搞了个Lock。Lock搞了3种方法来破环不可抢占的条件。1、void lockInterruptibly() throws InterruptedException;这是个支持中断的API。让线程阻塞(lock下是等待状态)的时候可以响应中断信号,从而有机会释放已占有的资源来破环不可抢占的条件。2、boolean tryLock();这就是在获取锁的时候,如果获取不到就直接返回,这样也有机会释放已占有的资源来破环不可抢占的条件。3、boolean tryLock(long time, TimeUnit unit) throws InterrptedException;这是个支持超时的API,也就是让在一段时间内获取不到资源的线程直接返回一个错误,不进入阻塞状态,那也有机会释放已占有的资源来破环不可抢占的条件。———————————————————————————————————————————————————————————生产者消费者模式三种写法(避免虚假唤醒采用while)callable接口锁绑定多个condition,精确唤醒 死锁编码和定位———————————————————————————————————————————————————————————线程池ExecutorService threadPool=Executors.newFixedThreadPool( ); 创建一个指定工作线程数量的线程池,每当提交一个任务就创建一个工作线程;只有核心线程并且这些核心线程不会被回收 Executors.newSingleThreadExecutor( ); Executors.newCachedThreadPool( ); 是一种线程数量不定的线程池,并且其最大线程数为Integer.MAX_VALUE Executors.newScheduledThreadPool( );核心线程数量是固定的,而非核心线程数是没有限制的,并且当非核心线程闲置时会被立即回收线程池底层其实是调用的:new ThreadPoolExecutor(....... 线程池参数)七大参数: corePoolSize:常驻核心线程数 maxmumPoolSize: 线程池能够同时执行的最大线程数,必须大于等于一 keepAliveTime:多余线程存活时间 unit:keepAliveTime的时间单位 workQueue:任务阻塞队列 threadFactory:线程工厂(使用默认) handler: 拒绝策略,内置拒绝策略都实现了RejectedExecutionHandler接口 AbortPolicy: 默认,直接抛出异常 CallerRunsPolicy: 不报异常,也不抛弃,将任务回退给调用者(例如main线程),降低新任务的流量。 DiscardOldestPolicy: 抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交当前任务。 DiscardPolicy: 直接丢弃任务。线程池底层原理:任务来了先占据corePoolSize,然后再占据任务队列,如果都满了,会开通额外线程直到满足maxmumPoolSize,存在加塞情况。都满了之后,开启拒绝策略。 如果有多余线程闲着,超过keepAliveTime然后线程数会缩回到corePoolSize 线程池最好不要使用Executors去创建,而是通过ThreadPoolExecutor的方式手写改造。手写线程池ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2,5,1L,TimeUnit.SECONDS,new LinkedBlockingQueue(3), Executors.defaultThreadFactory(),new ThreadPoolExecutor.DiscardOldestPolicy());如何合理配置线程池? CPU密集型(该任务需要大量运算而没有阻塞):一般公式:CPU核数+1个线程的线程池 IO密集型:CPU核数✖2 CPU核数/(1-阻塞系数)线程池有什么优势? 降低资源消耗。 提高响应速度。 提高线程的可管理性。线程池的状态RUNNING:能够接收新任务,以及对已添加的任务进行处理。SHUTDOWN:不接收新任务,但能处理已添加的任务。STOP:线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。TIDYING:当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。TERMINATED:线程池彻底终止,就变成TERMINATED状态。————————————————————————————————————————————————————————Java线程实现方式 继承Thread类 实现Runnable接口(有返回值实现Callable接口) 利用线程池线程生命周期: 新建状态:new创建之后 就绪状态:调用start方法之后 运行状态:获得CPU,开始执行run方法 阻塞状态:某种原因放弃CPU使用权 执行wait方法后 获取锁时该锁被别的线程占用 执行sleep方法或join方法后,发出I/O请求后。(当sleep方法或join方法超时或者I/O处理完后,重新转入就绪状态) 线程死亡:run或call方法执行完之后,正常结束。 线程抛出异常或者Error 调用stop方法。但该方法天生不安全。————————————————————————————————————————————————————————线程/Thread类基本方法 start run stop:线程不安全,立即释放锁可能会数据不一致。 suspend:容易造成死锁。 sleep:线程休眠,进入timed-waiting状态。 yield:让出当前线程的CPU。 interrupt:改变线程内部中断标识位。 join:当前线程调用某线程的join方法,当前线程进入阻塞。直到某线程结束,当前线程才变为就绪状态。 notify:随机唤醒一个在此对象监视器上等待的单个线程。 notifyAll:唤醒全部。 —notify()或者notifyAll()调用时并不会真正释放对象锁, 必须等到synchronized方法或者语法块执行完才真正释放锁.——————————————————————————————————————————————————————————sleep与wait的区别:(相同:都进入阻塞态) sleep属于Thread类,wait属于Object类。 sleep不释放锁,wait释放锁。 wait只用在同步代码块或方法中。start和run的区别: 调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行。——————————————————————————————————————————————————————————请简述一下线程的sleep()方法和yield()方法有什么区别?①sleep()方法不考虑线程的优先级;yield()方法只会给相同优先级或更高优先级的线程;②sleep()方法后转入阻塞,yield()方法后转入就绪;③ sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常;④ sleep()方法比yield()方法(跟操作系统CPU调度相关)具有更好的可移植性。——————————————————————————————————————————————————————————————————非静态的同步方法,锁是this-当前类实例对象静态的同步方法,锁是当前类的Class对象同步代码块,锁是synchronized括号里配置的对象。当一个线程进入一个对象的synchronized方法A之后,其它线程不可进入此对象的synchronized方法B,因为对象锁已经被拿走,需要在等锁池等待。同步方法和同步代码块的区别是什么? 同步方法默认用this或者当前类class对象作为锁; 同步代码块可以选择以什么来加锁,比同步方法要更细颗粒度,我们可以选择只同步会发生同步问题的部分代码而不是整个方法。————————————————————————————————————————————————————————阻塞队列底层怎么实现的——————————————————————————————————————————————————————————AQS/synchronizedAQS:AbstractQueuedSynchronizer,抽象队列同步器。是java并发包的基础类,ReentrantLock、ReentrantReadWriteLock底层都是基于AQS来实现的。 ReentrantLock内部包含了一个AQS对象,内部有一个核心的变量叫做state,是int类型的,代表了加锁的状态。初始状态下,这个state的值是0。另外, 这个AQS内部还有一个关键变量,用来记录当前加锁的是哪个线程,初始化状态下,这个变量是null。 加锁的过程,直接就是用CAS操作将state值从0变为1,然后设置当前加锁线程是自己。AQS内部还有一个等待队列,专门放那些加锁失败的线程!synchronized: 同步代码块:执行monitorenter,就会获取当前对象的一个所有权,这个时候monitor进入数为1,当前的这个线程就是这个monitor的owner。 如果你已 经是这个monitor的owner了,你再次进入,就会把进入数+1. 同理,当他执行完monitorexit,对应的进入数就-1,直到为0,才可以被其他线程持有。 同步方法:会先判断是否有标志位ACC_SYNCHRONIZED,然后,ACC_SYNCHRONIZED会去隐式调用刚才的两个指令:monitorenter和monitorexit。 所以归根究底,还是monitor对象的争夺。————————————————————————————————————————————————————————————————————synchronized锁优化(1.6之后)锁升级无锁:偏向锁:偏向锁会偏向于第一个获得它的线程,如果在接下来的执行过程中,该锁没有被其他的线程获取,则持有偏向锁的线程将永远不需要同步。轻量级锁:如果明显存在其它线程申请锁,那么偏向锁将很快升级为轻量级锁。些等待竞争锁的线程就不需要做内核态和用户态之间的切换进入阻塞挂起状态,它们只需要等一等(自旋)重量级锁:很多线程竞争锁,其他线程试图获取锁时,都会被阻塞,只有持有锁的线程释放锁之后才会唤醒这些线程。锁粗化:锁粗化就是将多个连续的加锁、解锁操作连接在一起,扩展成一个范围更大的锁,避免频繁的加锁解锁操作。锁消除:Java虚拟机在JIT编译时(可以简单理解为当某段代码即将第一次被执行时进行编译,又称即时编译),通过对运行上下文的扫描,经过逃逸分析,去除不可能存在共享资源竞争 的锁,通过这种方式消除没有必要的锁,可以节省毫无意义的请求锁时间。synchronized底层实现对象在内存中的存储布局,可以分为三个区域: 对象头,实例数据,对齐填充。对象头主要包括两部分数据:类型指针,标记字段对象头是我们需要关注的重点,它是synchronized实现锁的基础,因为synchronized申请锁、上锁、释放锁都与对象头有关。对象头主要结构是由Mark Word 和 Class Metadata Address组成,其中Mark Word存储对象的hashCode、锁信息或分代年龄或GC标志等信息,Class Metadata Address是类型指针指向对象的类元数据,JVM通过该指针确定该对象是哪个类的实例。锁的类型和状态在对象头Mark Word中都有记录,在申请锁、锁升级等过程中JVM都需要读取对象的Mark Word数据。每一个锁都对应一个monitor对象,在HotSpot虚拟机中它是由ObjectMonitor实现的(C++实现)。每个对象都存在着一个monitor与之关联,对象与其monitor之间的关系有存在多种实现方式,如monitor可以与对象一起创建销毁或当线程试图获取对象锁时自动生成,但当一个monitor被某个线程持有后,它便处于锁定状态。——————————————————————————————————————————————————————————————————————————如何检查死锁:第一个姿势:使用 jps + jstack第二个姿势:使用jconsole,在window打开 JConsole,JConsole是一个图形化的监控工具!第三个姿势:使用Java Visual VM,在window打开 jvisualvm,jvisualvm是一个图形化的监控工具!—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————创建多线程方式 创建多线程方式一:继承Thread类,重写run方法,创建对象,调用start。同一个线程对象不能调用start两次,需要重新创建一个线程对象。 创建多线程,直接调用run方法,可以运行,但是都是在主线程运行。(应该调用start方法) 创建多线程方式二:创建实现Runnable接口的实现类,重写run方法,创建对象,该对象作为参数传进Thread类的构造器并创建Thread类的对象, 调用start方法。 创建多线程方法三:实现Callable接口(有返回值;可以抛出异常;带泛型) 创建Callable实现类;实现call方法;创建实现类对象;将此对象传入FutureTask构造器创建对象;将此对象传入Thread类中创建 Thread对象;调用start方法;FutureTask对象调用get方法可以获得call方法返回值。 创建多线程方法四:线程池。 优先选择实现接口的方式:打破单继承;处理有共享数据。————————————————————————————————————————————————————————————————————同步代码块:synchronized(同步监视器){同步代码} 锁要唯一继承:Windows2.class充当监视器实现:this充当监视器同步方法:实现:抽取同步代码为函数,声明函数为synchronized,在run中调用该函数。This为锁继承:同上,同时该函数声明为static。当前类对象为锁。同步方法同样涉及到同步监视器,只是不需要显式声明。线程通信:wait()/notify()/notyfyAll():这三个方法必须在同步代码块或方法中(同步监视器调用)。notify唤醒优先级高的线程。————————————————————————————————————————————————————————————————————什么是ThreadLocal?ThreadLocal主要方法public T get() { }public void set(T value) { }public void remove() { }protected T initialValue() { }每个线程中都有一个独立的ThreadLocalMap属性。ThreadLocal类通过操作每一个线程特有的ThreadLocalMap,从而实现了变量访问在不同线程中的隔离。ThreadLocalMap是ThreadLocal类的一个静态内部类,ThreadLocalMap存储的键值对中的键是this对象指向的ThreadLocal对象,而值就是你所设置的对象了。(可以把每个ThreadLocal对象理解为线程的一个属性,如果需要为每个线程设置多个需要互相隔离的属性值,就需要多创建几个ThreadLocal对象,该对象作为ThreadLocalMap的键去取得对应的值)内存泄漏问题:如果我们ThreadLocal是null了,也就是要被垃圾回收器回收了,但是此时我们的ThreadLocalMap生命周期和Thread的一样,它不会回收,这时候就出现了一个现象。那就是ThreadLocalMap的key没了,但是value还在,这就造成了内存泄漏。解决办法:使用完ThreadLocal后,执行remove操作,避免出现内存溢出情况。static class ThreadLocalMap { static class Entry extends WeakReference> { Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; }}//还有很多很多//ThreadLocalMap底层是一个Entry数组}——————————————————————————————————————————————————————————————————————————生产者消费者模式思考:sleep方法应该在同步代码或方法之外,否则类似于生产者和消费者不停地争夺锁,系统会间歇性偏向某一方。(生产者一直获取到或者消费者一直获取到锁。)即使不使用sleep,也会造成以上情况。(无论是否使用wait-notify)只有使用sleep且放在同步代码之外,才会正常。不用wait-notify模式也可以实现,但这类似于忙等,但wait类似于sleep and wake up。前者不放弃CPU,后者放弃CPU。若线程资源宝贵使用后者,要求延迟低使用前者。进入wait状态的线程被唤醒后,是接着上次执行的地方接着执行的。虚假唤醒:假设两个消费者同时阻塞,当生产者生产后把他俩全唤醒。假设if语句中进行数量判断,若无产品则wait。只有一个能得到锁,继续执行wait后面的代码进行消费;另一个线程之后若得到锁的话,也会继续执行之后的消费但此时无产品。所以把if判断换做while循环,只有符合条件才可以跳出,否则将不会执行后面的消费代码。(或者把之后的消费代码换成else应该也可以)—————————————————————————————————————————————————————————————————————— 数据库InnoDB:支持主外键,支持事务,行锁,缓存索引和真实数据。将数据表分为.frm 和 idb两个文件进行存储。推荐使用整型的自增主键。(插入新数据时预防 节点分裂)。MyISAM:不支持主外键和事务,表锁,只缓存索引。索引与数据分离的形式,将数据保存在三个文件中.frm.MYD,.MYIs。SQL性能下降的原因:语句烂;索引失效;关联join太多;服务器调优及参数设置。七种join理论:select * from tablea A left join tableb B on A.Key=B.Key; 共有 A独有select * from tablea A right join tableb B on A.Key=B.Key; 共有 B独有select * from tablea A inner join tableb B on A.Key=B.Key; 共有select * from tablea A left join tableb B on A.Key=B.Key where B.Key is null; A独有select * from tablea A right join tableb B on A.Key=B.Key where A.Key is null; B独有select * from tablea A full outer join tableb B on A.Key=B.Key; 全有 MySQL不支持此语法,可以采用unionselect * from tablea A full outer join tableb B on A.Key=B.Key where A.Key is null or B.Key is null; 各自独有————————————————————————————————————————————————索引:是帮助MySQL高效获取数据的数据结构。用于排序和快速查找。提高数据检索效率,降低排序成本。虽然提高了查询速度,却降低了更新速度。分类:单值索引唯一索引:索引列的值必须唯一,允许有空值。复合索引结构:B+Tree索引:非叶子节点不存储数据,只存储索引,可以放更多索引,读到内存中的索引信息就会更多一些,相当于减少了磁盘IO次数;叶子节点存储所有索引;叶子节点用双向指针连接,提高区间的访问性能。(有利于范围查找)Hash索引:会有hash碰撞,且不利于范围查找。full-text索引R-tree索引不适合建索引的情况:表数据太少;经常修改的表;数据重复且分布平均的表字段。————————————————————————————————————————————————————————————————B+Tree索引和Hash索引区别:哈希查询效率快,但不稳定,可能发生哈希碰撞。(如何解决?)哈希只能用于=号查询。无法进行范围查询。哈希无法利用索引进行排序。hash索引任何时候都避免不了回表查询数据,而B+树在符合某些条件(聚簇索引,覆盖索引等)的时候可以只通过索引完成查询。(聚簇索引到底是指索引和数据在一起,还是指索引顺序和数据物理存储顺序一样?聚簇索引叶子节点到底存的是数据本身还是数据物理地址的引用???)哈希索引不能利用部分索引来进行查询,因为计算哈希值是按照所有联合的索引进行的。B树也叫平衡树!!!balance树!————————————————————————————————————————————————————————————————Explain+sql语句获得执行计划表:id — id相同,执行顺序由上到下;id不同,值越大优先级越高;(操作表的执行顺序)select_type — 查询类型:simple,primary,subquery,derived,union,union result(数据读取操作的操作类型)table — 操作的表type —访问类型:all(全表扫描)<index(全索引扫描)<range(范围查找)<ref(一对多查询)<eq_ref(多表联合唯一查询)<const(唯一性查询)<system(表只有一行数据)possible_keys — 显示可能应用到这张表的索引,未必一定使用(查询涉及的字段若存在索引则列出)key — 实际用到的索引(查询中若使用了覆盖索引,则该索引只出现在key列表中!!!!)!!覆盖索引:SQL只需要通过索引就可以返回查询所需要的数据,而不必通过二级索引查到主键之后再去查询数据。key_len — 索引中使用的字节数,索引字段最大可能长度,并非实际使用长度。ref — 显示索引的哪一列被使用了,如果可能的话,是一个常数。哪些列或常量被用于查找索引列上的值rows — 每张表有多少行被优化器查询Extra — 额外重要信息:Using filesort:无法利用索引进行排序,使用一个外部索引排序。Using temporary:使用了临时表保存中间结果,group by 和order by常见。Using index:表示使用了覆盖索引,避免了访问数据行,效率不错。如果同时出现using where,表明索引被用来执行索引键值的查找;如果没出现using where,表示索引用来读取数据而没有执行查找动作。using join buffer:使用了连接缓存。impossible where:where字句的值总是false where name=a and name=bselect tables optimized away:distinct:————————————————————————————————————————————————索引优化select * .....where a=1 and b>1 order by c index a_c 索引不失效select * from a left join b on a.id=b.id; 左连接用于如何确定从右表搜索行,左边一定都有。左表建索引没有性能提升,建在右表有提升。左连接建右表!!!右连接建左表!!!join语句的优化:尽可能减少join语句中的nestedloop的循环总次数:永远用小的结果集驱动大的结果集;优先优化nestedloop的内层循环;保证join语句中被驱动的表上join条件字段已经被索引;无法保证被驱动的表上join条件字段已经被索引且内存资源充足的情况下,不要吝啬join buffer的设置。————————————————————————————————————————————————最左前缀法则:查询从索引的最左前列开始并且不跳过中间列。在索引上做操作(计算,函数,自动或手动类型转换),会导致索引失效转向全表扫描。例如:left(name,4)="july"会失效。存储引擎不能使用索引中范围条件右边的列。范围之后全失效。例如:where name="july" and age>25 and pos="manager" pos无法使用索引。 ?????尽量使用覆盖索引。MySQL在使用不等于(!=或<>)的时候无法使用索引导致全表扫描。is null 和 is not null也无法使用索引。like以通配符开头(‘%abc..’)mysql索引失效。 %加在右边不会失效。解决%在左边失效的问题:使用覆盖索引。索引失效是指:不通过索引直接去查全表。使用覆盖索引自然只查索引就可以返回查询的数据,所以索引不会失效。select id .....where name like %aa 不会失效!id为主键,mysql联合索引默认包含主键!字符串(varchar)不加单引号会索引失效。会发生类型转换。使用or连接会索引失效。——————————————————————————————————————————————————索引的缺点:占用物理内存;进行增删改的时候也要维护索引,增加了系统开销。——————————————————————————————————————————————————select * .................where c1='1' and c2 like 'kk%' and c3='3' 三个索引都用到了!既然如此,为什么范围之后全失效?select * from ......where c1='1' and c2='2' and c4='4' order by c3; idx_c1234 ???? 为什么只用到两个,表示疑惑select * .................where c1='1' and c2='2' order by c3,c2 无filesortgroup by基本上需要排序,会有临时表产生,和order by差不多————————————————————————————————————————————————优化步骤:观察生产的慢SQL情况。开启慢查询日志,设置阈值并抓取。explain分析show profile:能够查出最近15条SQL语句的运行状态(包含运行过程中执行了哪些操作,各占用了多长时间),以便开发者的分析。SQL数据库服务器调优————————————————————————————————————————————————数据库锁分类:按对数据的操作类型:读锁:共享锁写锁:排他锁按照粒度:行锁:偏写表锁:偏读页锁:————————————————————————————————————————————————————MyISAM(表锁)在执行查询,会自动给表加读锁,增删改时自动加写锁。加读锁后,只能读,不能写。不能读别的表,除非释放当前读锁。其他线程可以读,但修改此表会被阻塞直到此线程释放读锁。加写锁后,可以读写,不能读别的表除非释放当前锁。其他线程读写会被阻塞直到此线程释放写锁。总之:读锁会阻塞写,但不会阻塞读。写锁会把独写都阻塞。查看加锁的表:show open tables分析表锁定:show status like ‘table%’Table_locks_immediate:可以立即获取锁的次数Table_locks_waited:发生等待锁的次数————————————————————————————————————————————————————事务四大特性:原子性(undo log),一致性(原子性,持久性,隔离性),隔离性(读写锁+MVCC),持久性(redo log)。InnoDB(行锁):支持事务。并发事务带来读一致性问题。用事务隔离机制来解决:脏读 不可重复读 幻读未提交读: 是 是 是已提交读: 否 是 是可重复读: 否 否 是可串行化: 否 否 否未提交读:有可能读到其他事务未提交的数据。已提交读:只能读取到已经提交的数据。oracle等默认该级别。可重复读:事务1提交修改后的数据,事务2未必能马上查询出最新的数据。只有提交该事务后才能保证是最新的数据。MySQL默认级别,通过MVCC机制实现。可串行化:串行化锁定了整张表,幻读不存在的!!!MySQL解决幻读:1.提高隔离级别到可串行化。2.在可重复读级别下,通过MVCC和next-key lock解决。— 在快照读(select)情况下,mysql通过mvcc来避免幻读。— 在当前读(update,insert,delete)情况下,mysql通过next-key来避免幻读。更新条件为索引字段,但是并非唯一索引(包括主键索引),那么此时更新会使用Next-Key Lock(间隙锁+行锁)。使用Next-Key Lock的原因:首先要保证在符合条件的记录上加上排他锁,会锁定当前非唯一索引和对应的主键索引的值;(行锁)还要保证锁定的区间不能插入新的数据。(间隙锁)注:索引失效行锁变表锁。分析表锁定:show status like ‘innodb_row_lock%’Innodb_row_lock_time_avg:等待平均时长Innodb_row_lock_waits:等待总次数Innodb_row_lock_time:等待总时长—————————————————————————————————————————————————————锁机制可以控制并发操作,但是其系统开销较大,而MVCC可以在大多数情况下代替行级锁,使用MVCC,能降低其系统开销。InnoDB默认的隔离级别REPEATABLE READ需要两个不同的事务相互之间不能影响,而且还能支持并发,这点悲观锁是达不到的,所以REPEATABLE READ采用的就是乐观锁,而乐观锁的实现采用的就是MVCC。乐观锁在真正的提交之前,不加读/写锁,而是先看一下数据的版本/时间戳,等到真正提交的时候再看一下版本/时间戳,如果两次相同,说明别人期间没有对数据进行过修改,那么就可以放心提交。使用原来的锁机制来保证数据一致性的这种锁叫悲观锁,而对开启MVCC机制的锁,叫做乐观锁。MVCC机制:多版本并发控制机制,通过保存数据在某个时间点的快照来实现的。通过在每行记录后面保存两个隐藏的列来实现的,这两个列,分别保存了这个行的最近修改/插入事务id,一个保存的是行的删除的事务id。每个事务都有个id,按照递增 — 增:同一事务中(假设事务id=1)插入两条记录,记录的数据版本号为事务id=1,删除版本号为null — 查:1、查找数据版本号,早于(小于等于)当前事务id的数据行。 这样可以确保事务读取的数据是事务之前已经存在的。或者是当前事务插入或修改的。 2、查找删除版本号为null 或者大于当前事务版本号的记录。 这样确保取出来的数据在当前事务开启之前没有被删除。(null会造成幻读) — 删: — 改:当一个事务中 修改一条记录时, 是先复制该数据,新数据数据版本号为当前事务id,删除版本号为 null 。然后更新 原来数据的删除版本号为 当前事务id。——————————————————————————————————————————————————————————————————————MySQL中日志文件1:重做日志(redo log)确保事务的持久性。redo日志记录事务执行后的状态,用来恢复未写入data file的已成功事务更新的数据。防止在发生故障的时间点,尚有脏页未写入磁盘,在重启mysql服务的时候,根据redo log进行重做,从而达到事务的持久性这一特性。2:回滚日志(undo log)保证数据的原子性,保存了事务发生之前的数据的一个版本,可以用于回滚。3:二进制日志(binlog)用于复制,在主从复制中,从库利用主库上的binlog进行重播,实现主从同步;用于数据库的基于时间点的还原。4:错误日志(errorlog)记录着mysqld启动和停止,以及服务器在运行过程中发生的错误的相关信息。在默认情况下,系统记录错误日志的功能是关闭的。5:慢查询日志(slow query log)慢日志记录执行时间过长和没有使用索引的查询语句,报错select、update、delete以及insert语句,慢日志只会记录执行成功的语句。6:一般查询日志(general query log)记录了服务器接收到的每一个查询或是命令,因为mysql服务器需要不断地记录日志,开启General log会产生不小的系统开销,默认是关闭的。7:中继日志(relay log)——————————————————————————————————————————————————————————————间隙锁的危害:语句1:........where a>1 and a<6语句2:insert into (2,600)........语句2新增的“2”在a的范围内,会被锁定无法插入直到语句1提交。————————————————————————————————————————————————面试题:如何锁定某一行?select * from ... where a=8 for update 其他对这一行的操作将被阻塞直到此事务结束后提交。————————————————————————————————————————————————主从复制:————————————————————————————————————————————————redis支持数据类型:字符串:列表:新消息排行等功能,Lists的另一个应用就是消息队列。集合:微博粉丝,关注。Redis还为集合提供了求交集、并集、差集等操作散列表:结构化信息。如用于姓名,年龄,性别等有序集合:游戏排行榜等。Redis 中 zset 不是单一结构完成,是跳表(有序)和哈希表(无序)共同完成。跳表对于范围查找效率较高,类似于二分法。跳表:每隔一个节点就提取出来一个元素到上一层,把这一层称作索引,其中的down指针指向原始链表。查找logn支持持久化:AOF—写命令记录到单独的日志文件中,安全但文件大,恢复速度慢,数据量大的时候效率相对低。AOF 文件写满:RDB—redis默认方式,数据以快照方式保存。安全性差,隔一段时间持久化一次。支持事务支持主从复制优势:高性能:直接访问内存;单线程;哈希查找;多路复用。高并发:比数据库承受能力强过期策略:定期删除:每隔一段时间执行一次删除过期key操作定时删除:在设置key的过期时间的同时,为该key创建一个定时器,让定时器在key的过期时间来临时,对key进行删除惰性删除:key过期的时候不删除,每次通过key获取值的时候去检查是否过期,若过期,则删除设置过期时间:EXPIRE key "seconds" PEXPIRE key "milliseconds"——————————————————————————————————————————————内存淘汰策略:(它们的触发条件都是Redis使用的内存达到阈值时。)volatile-lru 从已设置过期时间的数据集中挑选最近最少使用的数据淘汰volatile-lfu 从已设置过期时间的数据集中挑选最不经常使用的数据淘汰volatile-ttl 从已设置过期时间的数据集中挑选将要过期的数据淘汰volatile-random 从已设置过期时间的数据集中挑选任意数据淘汰 allkeys-lru 当内存不足写入新数据时淘汰最近最少使用的Key allkeys-lfu 当内存不足写入新数据时移除最不经常使用的Key allkeys-random 当内存不足写入新数据时随机选择key淘汰 no-eviction 当内存不足写入新数据时,写入操作会报错,同时不删除数据——————————————————————————————————————LRU 策略的实现:———————————————————————————————————————————————聚集索引叶子节点存储的是对数据库页(数据库保存数据最小单元)的引用。有主键主键就是聚集索引,没有主键,第一个唯一非空索引为聚集索引。都没有的话生成隐藏的唯一索引作为聚集索引。非聚集索引非聚集索引通常情况下保存的是对聚集索引的引用。查找非聚集索引其实也是对聚集索引的查找。索引B+ Tree的叶子节点存储了主键的值为非聚簇索引。需要回表查询多次。非聚集索引未必会查询多次,例如覆盖索引。————————————————————————————————————————————————数据库三大范式第一范式:每一列都是不可分割的原子项第二范式:第一范式的基础上,非码属性完全依赖于候选码(消除非主属性对主码的部分函数依赖)第三范式:第二范式的基础上,任何非主属性不依赖于其他非主属性(消除传递依赖)————————————————————————————————————————————————解决redis和mysql数据不一致问题法一:先删除缓存写数据库等待一定时间,确保读请求结束,写请求可以删除读请求造成的缓存脏数据。再次删除缓存 法二:直接写入Redis中去,然后间隔一段时间,批量将所有的写入请求,刷新到MySQL中去 法三:我们可以对MySQL的binlog进行订阅,这样一旦MySQL中产生了新的写入、更新、删 除等操作,就可以把binlog相关的消息推送至Redis,Redis再根据binlog中的记录,对Redis进行更新。————————————————————————————————————————————————————数据库优化:SQL的优化:通过慢查询日志发现有效率问题的SQL;慢查询日志内容分析。索引的优化:选择合适的索引列;索引字段越小越好;离散度大得列放在联合索引前面;InnoDB 每个索引都会加上主键,联合索引不要加上主键;过多的索引会增加数据库判断使用什么索引来查询的开销,所以,有时候也会出现以去掉重复或者无效的索引为优化手段的优化方式;主键已经是索引了,所以primay key 的主键不用再设置unique唯一索引了。数据表结构的优化: (1)使用可存下数据的最小的数据类型。 (2)使用简单地数据类型,int要比varchar类型在mysql处理上更简单。 (3)尽可能使用not null定义字段,这是由innodb的特性决定的,因为非not null的数据可能需要一些额外的字段进行存储,这样就会增加一些IO。 可以对非null的字段设置一个默认值。 (4)尽量少用text,非用不可最好分表,将text字段存放到另一张表中,在需要的时候再使用联合查询,这样可提高查询主表的效率。 数据库配置的优化。硬件优化。————————————————————————————————————————————————————————InnoDB支持三种行锁定方式:行锁(Record Lock):锁直接加在索引记录上面,锁住的是key。间隙锁(Gap Lock):锁定索引记录间隙,确保索引记录的间隙不变。间隙锁是针对事务隔离级别为可重复读或以上级别的。Next-Key Lock :行锁和间隙锁组合起来就叫Next-Key Lock。——————————————————————————————————————————————————————————倒排索引 待总结。知乎———————————————————————————————————————————————————————————————————————缓存雪崩:大量缓存同时失效,直击数据库。设置不同的过期时间。通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。缓存穿透:缓存和数据库都没有的数据。布隆过滤器。空值缓存。缓存击穿:特殊的缓存雪崩,指某一个热点数据缓存失效,大量请求直击数据库。设置热点数据永远不过期。加互斥锁。——————————————————————————————————————————————————————————————————————布隆过滤器:它专门用来检测集合中是否存在特定的元素。BF是由一个长度为m比特的位数组(bit array)与k个哈希函数(hash function)组成的数据结构。当要插入一个元素时,将其数据分别输入k个哈希函数,产生k个哈希值。以哈希值作为位数组中的下标,将所有k个对应的比特置为1。当要查询(即判断是否存在)一个元素时,同样将其数据输入哈希函数,然后检查对应的k个比特。如果有任意一个比特为0,表明该元素一定不在集合中。如果所有比特均为1,表明该集合有(较大的)可能性在集合中。优点不需要存储数据本身,只用比特表示,因此空间占用相对于传统方式有巨大的优势,并且能够保密数据;时间效率也较高,插入和查询的时间复杂度均为O(k);哈希函数之间相互独立,可以在硬件指令层面并行计算。缺点存在假阳性的概率,不适用于任何要求100%准确率的情境;只能插入和查询元素,不能删除元素,这与产生假阳性的原因是相同的。我们可以简单地想到通过计数(即将一个比特扩展为计数值)来记录元素数,但仍然无法保证删除的元素一定在集合中。———————————————————————————————————————————————————————————————————————MySQL与nosql的区别。——————————————————————————————————————————————————分库分表(中间件:cobar、TDDL、atlas、sharding-jdbc、mycat)分表就是把一个表的数据放到多个表中,然后查询的时候你就查一个表。单表数据量太大,会极大影响你的sql执行的性能,到了后面你的sql可能就跑的很慢了。分库就是将一个库的数据拆分到多个库中,访问的时候就访问一个库好了。一个库一般最多支撑到并发2000,一定要扩容了,而且一个健康的单库并发值你最好保持在每秒1000左右,不要太大。水平拆分就是把一个表的数据给弄到多个库的多个表里去,但是每个库的表结构都一样,只不过每个库表放的数据是不同的,所有库表的数据加起来就是全部数据。水平拆分数据分片规则:根据数值范围,按照时间区间或ID区间来切分;根据数值取模,一般采用hash取模mod的切分方式。垂直拆分就是把一个有很多字段的表给拆分成多个表,或者是多个库上去。每个库表的结构都不一样,每个库表都包含部分字段。一般来说,会将较少的访问频率很高的字段放到一个表里去,然后将较多的访问频率很低的字段放到另外一个表里去。因为数据库是有缓存的,你访问频率高的行字段越少,就可以在缓存里缓存更多的行,性能就越好。这个一般在表层面做的较多一些。————————————————————————————————————————————————————————————————————————引擎InnoDB:自5.5版本开始,InnoDB是MySQL数据库的默认引擎(之前是MyISAM)。支持事务,行锁,支持外键。MyISAM:不支持事务,表锁,不支持外键。查询速度快。MEMORY:使用存储在内存中的内容来创建表,而且数据全部放在内存中。每个表对应一个磁盘文件,文件名与表名相同,类型为frm类型。该文件中只存储表的结构,而其数据文件,都是存储在内存中。查询速度快,默认哈希索引,也可以使用B树索引。ARCHIVE:只支持insert和select操作,支持行级锁和缓冲区,可以实现高并发的插入。以zlib对表数据进行压缩,磁盘I/O更少。只允许在自增ID列上加索引。Federated:可以直接在本地构建一个federated表来连接远程数据表,配置好了之后本地表的数据可以直接跟远程数据表同步。实际上这个引擎里面是不真实存放数据的,所需要的数据都是连接到其他MySQL服务器上获取。——————————————————————————————————————————————————————————————————————常用查询命令SHOW ENGINES 查询存储引擎SHOW VARIABLES LIKE 'storage_engine' 查看默认引擎SHOW INDEX FROM <表名> [ FROM <数据库名>] 查看索引————————————————————————————————————————————————————redis实现分布式锁:不同的jvm访问同一代码块。为什么Redis可以方便地实现分布式锁1、Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对Redis的连接并不存在竞争关系。2、Redis的SETNX命令可以方便的实现分布式锁。加锁:SETNX key value 当且仅当 key 不存在时将 key 的值设为 value,并返回1;若给定的 key 已经存在,则 SETNX 不做任何动作,并返回0,并循环去获取。解锁:del(key)设置超时时间:expire(key, 30)(以防中途挂机永远无法释放锁)更新版:set(key,1,30,NX)加锁的同时设置超时时间。综上所述,可以通过setnx的返回值来判断是否获取到锁,并且不用担心并发访问的问题,因为Redis是单线程的,所以如果返回1则获取到锁,返回0则没获取到。当业务操作执行完后,一定要释放锁,释放锁的逻辑很简单,就是把之前设置的key删除掉即可,这样下次又可以通过setnx该key获取到锁了。隐患:1线程在超时时间内未完成同步代码,自动释放锁;2线程获得锁,当1线程执行完后释放锁会释放2线程的锁;并且还会出现并发的可能性。————————————————————————————————————————————————————————————————————Redisson实现Redis分布式锁的底层原理1.加锁:会使用lua脚本传入锁名称,超时时间,客户端id,并记录加锁次数。(setnx+过期时间这两个操作必须是原子性的,所以这可以用lua脚本解决)2.锁互斥:另一个线程尝试加锁,发现锁已经存在并且客户端id不是自己,会获取失败,返回锁剩余时间。然后while循环不断尝试加锁。3.watch dog自动延期:若超时时间内未完成业务代码,还不能释放锁。看门狗每10秒检查一下,若还持有锁,则延长锁的生存时间。4.可重入机制:加锁次数+1.5.释放锁:每释放一次加锁次数-1,为0时释放锁。隐患:对某个redis master实例,写入了myLock这种锁key的value,此时会异步复制给对应的master slave实例。但是这个过程中一旦发生redis master宕机,主备切换,redis slave变为了redis master。接着就会导致,客户端2来尝试加锁的时候,在新的redis master上完成了加锁,而客户端1也以为自己成功加了锁。此时就会导致多个客户端对一个分布式锁完成了加锁。这时系统在业务语义上一定会出现问题,导致各种脏数据的产生。————————————————————————————————————————————————————————————————————数据库char和varchar的区别长度不同:char不论输入多长都是占用4个字节;而varchar包含的长度是内容+1个字节效率不同:char类型每次修改的数据长度相同,效率更高;varchar类型每次修改的数据长度不同,效率更低。存储不同:char占用存储空间较大。————————————————————————————————————————————————————————————sql删除重复数据,保留一个(业务层面可以考虑加唯一索引)delete from table where sex = (select sex from table group by sex having count(*) > 1)这是删除所有Delete from table where id not in (select min(id) as id from table group by sex)—————————————————————————————————————————————— 计算机基础————————————————————————————————————————————长连接和短连接短连接是指通信双方有数据交互时,就建立一个TCP连接,数据发送完成后,则断开此TCP连接;长连接,指在一个TCP连接上可以连续发送多个数据包,在TCP连接保持期间,如果没有数据包发送,需要双方发检测包以维持此连接。单工、半双工和全双工单工(Simplex)方式:通信双方设备中发送器与接收器分工明确,只能在由发送器向接收器的单一固定方向上传送数据。半双工(Half Duplex)方式:通信双方设备既是发送器,也是接收器,两台设备可以相互传送数据,但某一时刻则只能向一个方向传送数据。全双工(Full Duplex)方式:通信双方设备既是发送器,也是接收器,两台设备可以同时在两个方向上传送数据。http1.1以下版本为短连接,tcp连接发送信息等待接受信息后断开.http1.1 是半双工,建立长连接,出现多路复用,可先后发送多个http请求,不用等待回复,但是回复按顺序一个一个回复.(当前主流)http2.0是全双工,一个消息发送后不用等待接受,第二个消息可以直接发送.————————————————————————————————————————————get和post区别:get post服务器上获取数据 向服务器传送数据参数包含在URL中 通过request body传递参数数据量较小 数据量较大安全性非常低 安全性较高请求能够被缓存产生一个TCP数据包 产生两个TCP数据包————————————————————————————————————————————————HTTP与HTTPS的区别:HTTPS在HTTP的基础上加入了SSL/TLS协议,SSL/TLS依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密HTTP+加密+认证+完整性保护=HTTPS HTTP HTTPS URL 以 HTTP:// 开头 URL 以 HTTPS:// 开头 不安全 安全 效率高 无需证书 需证书(需要费用)————————————————————————————————————————————————HTTPS的对称与非对称加密HTTPS相比HTTP,在请求前多了一个「握手」的环节。在握手时,你和你想访问的网站会交换一个密钥。对称加密加密与解密使用的是同样的密钥,所以速度快,但由于需要将密钥在网络传输,所以安全性不高。非对称加密使用了一对密钥,公钥与私钥,所以安全性高,但加密与解密速度慢。解决的办法是将对称加密的密钥使用非对称加密的公钥进行加密,然后发送出去,接收方使用私钥进行解密得到对称加密的密钥,然后双方可以使用对称加密来进行沟通。若中间人劫持伪造公钥私钥发给客户端,同样不安全,所以有了证书体系,网站需要购买证书。HTTPS的安全性仍然保证在『证书签发机构一定都是很有良心的』这种脆弱的基础上。————————————————————————————————————————————————————————什么是套接字(Socket)多个TCP连接或多个应用程序进程可能需要 通过同一个TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了称为套接字 (Socket)的接口。生成套接字,主要有3个参数:通信的目的IP地址、使用的传输 层协议(TCP或UDP)和使用的端口号。————————————————————————————————————————————————常见的加密解密算法Base64位加密(可加密解密):最简单的加密方式,没有密钥,这种方式只要让别人拿到你的密文,就可以直接解密MD5加密(加密不可逆):不可逆算法。具有很高的安全性。它对应任何字符串都可以加密成一段唯一的固定长度的代码。sha1加密(加密不可逆):基于MD5,加密后的数据长度更长。因此,比MD5更加安全,但SHA1的运算速度就比MD5要慢了。AES加密(需要密钥才能解密):对称密钥加密,加密和解密都是用同一个解密规则.甲方必须把加密规则告诉乙方,否则无法解密。保存和传递密钥,就成了最头疼的问题。RSA加密(公钥加密,私钥解密):它是目前最重要的加密算法!计算机通信安全的基石,保证了加密数据不会被破解。甲乙双方通讯,乙方生成公钥和私钥,甲方获取公钥,并对信息加密(公钥是公开的,任何人都可以获取),甲方用公钥对信息进行加密,此时加密后的信息只有私钥才可以破解,所以只要私钥不泄漏,就能保证信息的安全性。————————————————————————————————————————————————————————————————————死锁四个必要条件:互斥使用;占有和等待;不可抢占;循环等待(死锁——>四个条件)循环等待不一定死锁,例如同类资源不止一个。死锁的处理策略:1.鸵鸟策略:无为而治2.死锁检测与死锁恢复:检测:每种类型一个资源的死锁检测:从一个结点开始进行深度搜索,检测是否有环存在。每种类型多个资源的死锁检测:略恢复:抢占恢复回滚恢复杀死进程恢复3.死锁预防:在程序运行之前预防发生死锁。(破坏四个必要条件)4.死锁避免:银行家算法 — 在资源分配之前预先判断这次分配是否会进入不安全状态,以此决定是否要答应请求。(不安全状态未必会发生死锁)步骤:1.检查此次申请是否超过之前声明的最大需求数;2.检查系统剩余资源是否可以满足这次请求;3.试探着分配,更改数据结构4.用安全性算法检查此次分配是否会导致系统进入不安全状态。 安全性算法步骤: 1.检查当前资源是否可以满足某个进程最大需求数,如果可以则加入安全序列,并收回全部资源; 2.重复上述过程,看是否最终能把所有进程都加入安全序列。————————————————————————————————————————————————————————浏览器输入url地址到显示主页的过程?DNS解析:浏览器缓存、操作系统缓存、host文件、本地DNS服务器查询TCP连接:A:SYN=1,seq=x B B:对面发送和自己接收没问题 (客户端进入SYN_SEND状态)A: SYN/ACK=1,ack=x+1,seq=y B A:自己发送接收和对面发送接收没问题 (服务器端进入SYN_RECV状态)A:ACK=1,ack=y+1,seq=x+1(可携带数据) B B:自己发送接收和对面发送接收没问题 (双方进入ESTABLISHED状态)发送HTTP请求:服务器处理请求并返回HTTP报文:1xx:指示信息–表示请求已接收,继续处理。2xx:成功–表示请求已被成功接收、理解、接受。3xx:重定向–要完成请求必须进行更进一步的操作。301是永久重定向302是临时重定向4xx:客户端错误–请求有语法错误或请求无法实现。5xx:服务器端错误–服务器未能实现合法的请求。浏览器解析渲染页面:关闭TCP或者继续连接:A:FIN=1,seq=u B (客户端进入FIN_WAIT_1状态,服务器端进入CLOSE_WAIT状态)A: ACK=1, seq=v,ack=u+1 B (客户端进入FIN_WAIT_2状态)A: FIN=1, ACK=1,ack=u+1,seq=w B (服务器端进入LAST_ACK状态,客户端进入TIME_WAIT状态)A:ACK=1,seq=u+1,ack=w+1 B (服务器进入CLOSE状态)TCP第四次挥手为什么要等待2MSL?1.为了保证客户端发送的最后一个ACK报文段能够到达服务器。因为这个ACK有可能丢失,从而导致处在LAST-ACK状态的服务器收不到对FIN-ACK的确认报文。服务器会超时重传这个FIN-ACK,接着客户端再重传一次确认,重新启动时间等待计时器。最后客户端和服务器都能正常的关闭。假设客户端不等待2MSL,而是在发送完ACK之后直接释放关闭,一但这个ACK丢失的话,服务器就无法正常的进入关闭连接状态。2.再经过2MSL时间,就可以使本连接持续的时间所产生的所有报文段都从网络中消失。这样就可以使下一个新的连接中不会出现这种旧的连接请求的报文段。(否则新旧数据会混淆)——————————————————————————————————————————————————————超时重传和快速重传快速重传:如果在超时重传定时器溢出之前,接收到连续的三个重复冗余ACK(其实是收到4个同样的ACK,第一个是正常的,后三个才是冗余的),发送端便知晓哪个报文段在传输过程中丢失了,于是重发该报文段,不需要等待超时重传定时器溢出,大大提高了效率。超时重传:在发送某一个数据以后就开启一个计时器,在一定时间内如果没有得到发送的数据报的ACK报文,那么就重新发送数据,直到发送成功为止。————————————————————————————————————————————————————原码:最高位表示正负号(1正0负),剩余位表示值的绝对值。正数:原码,反码,补码都是一样的。负数:反码:符号位不变,其余位取反;补码:反码+1计算机使用补码存储数据。————————————————————————————————————————————————————————处理机的三级调度:高级调度(作业调度):它决定处于输入池中的哪个后备作业可以调入主系统做好运行准备,称为一个或一组就绪进程。在系统中每个作业只需经过一次高级调度。中级调度:它决定处于交换区的哪个就绪进程可以调入内存,直接参与对CPU的竞争;而在内存资源不足时,为了将进程调入内存,则必须将内存中处于阻塞状态的进程调出至交换区,这相当于将处于内存的进程与交换区的进程交换位置。低级调度:又称为短程调度、进程调度。它决定内存中的哪个就绪进程可以占用CPU,低级调度是操作系统中最活跃最核心的调度程序。进程调度(低级调度)方式:非剥夺方式:分派程序一旦把处理机分配给某进程后便让它一直运行下去,直到进程完成或发生某事件而阻塞时,才把处理机分配给另一个进程。剥夺方式:当一个进程正在运行时,系统可以基于某种原则,剥夺已分配给它的处理机,将之分配给其它进程。剥夺原则有:优先权原则、短进程优先原则、时间片原则。调度算法适用于早期批处理系统:1 先来先服务2 短作业/进程优先:会导致饥饿3 最短剩余时间优先(抢占式的短作业优先):会导致饥饿。每次有新进程进入就绪队列就进行调度。4 高响应比优先:不会导致饥饿。非抢占式 响应比=(等待时间+要求服务时间)/要求服务时间适用于交互式系统:5 时间片轮转:时间片太大会退化为先来先服务算法;太小会导致进程切换过于频繁。6 优先级调度:有抢占式和非抢占式;还可分为静态或动态优先级。会导致饥饿7 多级反馈队列:有抢占式和非抢占式,对以上算法的折中。建立多个就绪队列,优先级从高到低,时间片从小到大。会导致饥饿实时系统衡量进程调度性能的指标有:CPU利用率系统吞吐量:单位时间内完成作业的数量周转时间带权周转时间等待时间响应时间CPU-I/O执行期进程切换步骤:挂起一个进程,将这个进程在CPU中的状态(上下文信息)存储于内存的PCB中。在PCB中检索下一个进程的上下文并将其在CPU的寄存器中恢复。跳转到程序计数器所指向的位置(即跳转到进程被中断时的代码行)并恢复该进程。进程状态:1.等待态:等待某个事件的完成;往往是由于等待外设,等待主存等资源分配或等待人工干预而引起的。2.就绪态:等待系统分配处理器以便运行;3.运行态:占有处理器正在运行。————————————————————————————————————————————————————————————进程和线程:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位。进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;同一类线程共享代码和数据空间,线程之间切换的开销小。如果一个进程内有多个线程,线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。————————————————————————————————————————————————————————————进程同步的方式:待总结。————————————————————————————————————————————————————————————http报文都有什么:请求报文有:请求行:请求方法、URI、HTTP协议版本,他们之间用空格分隔。例如:GET /index.html HTTP/1.1请求头部:请求头部紧跟着请求行,该部分主要是用于描述请求正文。主要是用于说明请求源、连接类型、以及一些Cookie信息等。例如:Host: www.enjoytoday.cnConnection: keep-aliveUpgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.84 Safari/537.36Cookie:...... 空行 请求正文:和请求头部通过一个空行进行隔开,一般用于存放POST请求类型的请求正文,如上的请求体为:username=hfcai&sex=man响应报文有:状态行:主要给出响应HTTP协议的版本号、响应返回状态码、响应描述,同样是单行显示。格式为:HTTP/1.1 200 OK响应头部:主要是返回一些服务器的基本信息,以及一些Cookie值等。如上的响应头为: Date: Sat, 01 Jul 2017 14:51:26 GMT Server: Apache/2.4.7 (Ubuntu) Set-Cookie: JSESSIONID=84C993F5E433C4DE9BFBA57150FFC065.ajp13_worker;path=/;HttpOnly Content-Language: zh-CN Vary: Accept-Encoding Content-Encoding: gzip Content-Length: 7333 Keep-Alive: timeout=5, max=100 Connection: Keep-Alive Content-Type: text/html;charset=UTF-8 空行 响应体:为请求需要得到的具体数据,可以为任何类型数据,一般网页浏览返回的为html文件内容——————————————————————————————————————————————————————————TCP UDP面向连接 无连接提供可靠服务 不保证,可能丢包点到点 一对多,多对多,一对一,多对一对系统资源要求较多 较少传输效率较低 效率较高,实时性好传输单位:TCP报文段 用户数据报FTP Telnet SMTP POP3 HTTP DNS SNMP TFTP——————————————————————————————————————————————————————————进程间的八种通信方式----共享内存是最快的 IPC 方式无名管道高级管道有名管道消息队列信号量信号共享内存套接字————————————————————————————————————————————————————————————Http1.0/http2.0区别(后续学习1.1)1.0解析基于文本协议;2.0基于二进制,实现方便且健壮。1.0长连接复用;2.0多路复用。1.0header带有大量信息,而且每次都要重复发送;2.0header压缩,使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小。2.0服务端推送能把客户端所需要的资源伴随着index.html一起发送到客户端,省去了1.0客户端重复请求的步骤。——————————————————————————————————————————————————————————————XSS攻击和CSRF攻击XSS是跨站脚本攻击,就是在用户的浏览器中执行攻击者的脚本,来获得其cookie等信息。预防:尽量不用 innerHTML,改用 innerText ,这样 script就会被当做文本如果非要用 innerHTML ,就需要对用户输入的字符进行过滤;CSRF是“跨站请求伪造。攻击者向目标网站注入一个恶意的URL地址,利用 等标签或者使用Javascript脚本向目标网站发生一个get请求,该请求会携带cookie信息,所以也就借用了用户的身份,也就是伪造了一个请求。预防:— 判断 referer头,如果不是来自本网站的请求,就判定诶 csrf攻击。但是该方法只能防御跨站csrf攻击,不能防御同站的csrf攻击。— 使用验证码 每一个重要的POST提交页面,使用一个验证码。— 使用token,每一个网页包含一个web server产生的token,提交时,也将该token提交到服务器,服务器进行判断,如果token不对,就判定为csrf攻击,将敏感操作由get改为post,然后在表单中使用token,尽量使用post也有利于防御CSRF攻击——————————————————————————————————————————————————————————————SYN攻击原理与实现大量发送伪造源IP的SYN包也就是伪造第一次握手数据包,造成半连接队列溢出,正常客户端连接将被丢弃。配合IP欺骗(伪造大量不存在的IP地址),SYN攻击能达到很好的效果。关于SYN攻击防范技术.归纳起来,主要有两大类,一类是通过防火墙、路由器等过滤网关防护,另一类是通过加固TCP/IP协议栈防范.但必须清楚的是,SYN攻击不能完全被阻止。——————————————————————————————————————————————————————————————————————CDN技术原理 — CDN网络是在用户和服务器之间增加Cache层,主要是通过接管DNS实现,将用户的请求引导到Cache上获得源服务器的数据。 — 当用户访问加入CDN服务的网站时,域名解析请求将最终由 智能调度DNS 负责处理.它通过一组预先定义好的策略,将当时最接近用户的节点地址提供给用户,使用户可以 得到快速的服务。同时它需要与分布在各地的CDN节点保持通信,跟踪各节点的健康状态,容量等,确保将用户的请求分配到就近可用的节点上。用到的技术:— 负载均衡:— 服务器负载均衡:够在性能不同的服务器之间进行任务分配— 服务器整体负载均衡:允许Web网络托管商、门户站点和企业根据地理位置分配内容和服务。— 动态内容分发与复制技术 :将占网站主体的大部分静态网页、图像和流媒体数据分发复制到各地的加速节点上。— 缓存技术CDN网络架构:中心指CDN网管中心和DNS重定向解析中心,负责全局负载均衡;边缘主要指异地节点,CDN分发的载体,主要由Cache(高速缓存服务器)和负载均衡器等组成。————————————————————————————————————————————————————————————————————————负载均衡算法:— 轮询法:将请求按顺序轮流分配到后台服务器上,均衡的对待每一台服务器,而不关心服务器实际的连接数和当前的系统负载。— 随机法: 通过系统随机函数,根据后台服务器列表的大小值来随机选取其中一台进行访问。— 源地址哈希法:根据服务消费者请求客户端的IP地址,通过哈希函数计算得到一个哈希值,将此哈希值和服务器列表的大小进行取模运算— 加权轮询法:跟配置高、负载低的机器分配更高的权重,使其能处理更多的请求,而配置低、负载高的机器,则给其分配较低的权重,降低其系统负载。在获取服务器地址之前增加了一段权重计算代码,根据权重的大小,将地址重复增加到服务器地址列表中,权重越大,该服务器每轮所获得的请求数量越多。— 加权随机法:跟加权轮询法类似,根据后台服务器不同的配置和负载情况,配置不同的权重。不同的是,它是按照权重来随机选取服务器的,而非顺序。— 最小连接数法:据后端服务器当前的连接情况,动态地选取其中当前积压连接数最少的一台服务器来处理当前的请求,尽可能地提高后端服务的利用效率。——————————————————————————————————————————————————————————————————————————为什么说HTTP协议是一种无连接,无状态的协议?无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传。————————————————————————————————————————————————————————————————————基于 TCP工作的协议及其端口号:FTP 20,21 SSH 22 Telnet 23 SMTP 25 DNS 53 HTTP 80 BGP 179 HTTPS 443基于UDP工作的协议及其端口: DNS 53 TFTP 69 RIP 520 DHCP 67,68可能在IP报头的协议字段中指定的协议:协议 协议号ICMP 1IP 4TCP 6IGRP 9UDP 17IPV6 41GREE 47EIGRP 88OSPF 89PIM 103VRRP 112L2TP 115ISIS 124dns同时占用tcp和udp的53端口!区域传送时使用TCP,域名解析时使用UDP协议。————————————————————————————————————————————————————————————————————————拥塞控制:慢开始:发送方维持一个叫做拥塞窗口cwnd,开始时发送方cwnd=1,每经过一个传输伦次(RTT时间),cwnd加倍。为了防止拥塞窗口cwnd增长过大而引起网络拥塞,设置一个慢开始门限ssthresh。1.当cwnd<ssthresh,使用上述的慢开始算法2.当cwnd>ssthresh,停止使用慢开始,使用拥塞避免算法拥塞避免:让拥塞窗口cwnd缓慢增大,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd+1,这样cwnd就按线性增大。假设到24的时候,网络出现拥堵,我们更新ssthresh=24/2=12,之后再重新执行之前的两个算法。 快重传:假如M2收到了,M3没有收到,之后的M4,M5,M6又发送了,快重传要求接收方立刻发送M2确认报文,此时接收方一共连续给发送方反馈了4个M2确认报文。 那么快重传规定,发送方只要连续收到3个重复确认,立即重传M3 快恢复:.当发送方连续收到三个重复确认,把cwnd值设置为ssthresh减半之后的值(ssthresh先减半,再设置cwnd等于此值),然后执行拥塞避免算法,线性增大cwnd————————————————————————————————————————————————————————————————————————TCP流量控制:滑动窗口:数据链路层和传输层流量控制的区别——————————————————————————————————————————————————————————————————————RTT发送一个数据包到收到对应的 ACK所花费的时间RTO定时器重传时间间隔没有回应 ACK 则等到 RTO 到期进行重传根据 RTT 计算出来——————————————————————————————————————————————————————————————————————硬链接和软链接的区别硬连接的作用是允许一个文件拥有多个有效路径名,这样用户就可以建立硬连接到重要文件,以防止“误删”的功能。只删除一个连接并不影响索引节点本身和其它的连接,只有当最后一个连接被删除后,文件的数据块及目录的连接才会被释放。也就是说,文件才会被真正删除。软链接:文件A的内容是文件B的路径,读取文件A时,系统会自动将访问者导向文件B。因此,无论打开哪一个文件,最终读取的都是文件B。——————————————————————————————————————————————————————————————————————查看IP地址:ifconfig/ip addr查看网络流量:iftop(yum install epel-release yum install iftop)装上了 EPEL,就像在 Fedora 上一样,可以通过 yum install 软件包名,即可安装很多以前需要编译安装的软件、常用的软件或一些比较流行的软件,比如现在流行的nginx、htop、ncdu、vnstat、axel、cmake3等等查看端口:lsof -i:端口号/netstat -tunlp |grep 端口号——————————————————————————————————————————————————————————————————————常用指令待总结。——————————————————————————————————————————————————————————————————————Linux API 提供的IO多路复用的三种机制Select,Poll,Epoll select poll epoll操作方式 遍历 遍历 回调底层实现 数组 链表 红黑树IO效率 每次调用都进行线性遍历,时间复杂度为O(n) 每次调用都进行线性遍历,时间复杂度为O(n) 事件通知方式,每当fd就绪,系统注册的回调函数就会被调用,将就绪fd放到readyList里面,时间复杂度O(1)最大连接数 1024(x86)或2048(x64) 无上限 无上限fd拷贝 每次调用select,都需要把fd集合从用户态拷贝到内核态 每次调用poll,都需要把fd集合从用户态拷贝到内核态 调用epoll_ctl时拷贝进内核并保存,之后每次epoll_wait不拷贝epoll为何比select和poll快1.epoll减少了文件描述符从用户态到内核态的拷贝2.减少了对监听文件描述符的遍历3.返回的都是发生事件的fd,不需要额外的循环做判断。——————————————————————————————————————————————————————————————————————————————————————————如何解决拆包粘包1.使用带消息头的协议、消息头存储消息开始标识及消息长度信息,服务端获取消息头的时候解析出消息长度,然后向后读取该长度的内容。2.设置定长消息,服务端每次读取既定长度的内容作为一条完整消息。3.设置消息边界,服务端从网络流中按消息编辑分离出消息内容。——————————————————————————————————————————————————————————Linux命令df -hl 查看磁盘剩余空间cat /proc/meminfo | grep MemTotal 查看内存大小netstat -ntlp //查看当前所有tcp端口·netstat -ntulp |grep 80 //查看所有80端口使用情况·netstat -lanp //查看一台服务器上面哪些服务及端口ps -ef |grep mysqld //查看一个服务有几个端口。比如要查看mysqld——————————————————————————————————————————————————利用三元组(ip 地址,协议,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。 框架IOC控制反转:把创建对象的权力交给框架。包括依赖注入和依赖查找。使用java的反射机制,根据配置文件在运行时动态的去创建对象以及管理对象,并调用对象的方法的。AOP:用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理。——————————————————————————————————————————————————————————IOC:反射+工厂模式//解析<bean .../>元素的id属性得到该字符串值为“courseDao”String idStr = "courseDao";//解析<bean .../>元素的class属性得到该字符串值为“com.qcjy.learning.Dao.impl.CourseDaoImpl”String classStr = "com.qcjy.learning.Dao.impl.CourseDaoImpl";//利用反射知识,通过classStr获取Class类对象Class<?> cls = Class.forName(classStr);//实例化对象Object obj = cls.newInstance();//container表示Spring容器container.put(idStr, obj);//解析<property .../>元素的name属性得到该字符串值为“courseDao”String nameStr = "courseDao";//解析<property .../>元素的ref属性得到该字符串值为“courseDao”String refStr = "courseDao";//生成将要调用setter方法名String setterName = "set" + nameStr.substring(0, 1).toUpperCase()+ nameStr.substring(1);//获取spring容器中名为refStr的Bean,该Bean将会作为传入参数Object paramBean = container.get(refStr);//获取setter方法的Method类,此处的cls是刚才反射代码得到的Class对象Method setter = cls.getMethod(setterName, paramBean.getClass());//调用invoke()方法,此处的obj是刚才反射代码得到的Object对象setter.invoke(obj, paramBean);————————————————————————————————开启AOP:通过标签aop:aspectj-autoproxy/来完成开启AOP功能;另一种开启spring aop的方式是通过注解的方式,使用的注解是@EnableAspectJAutoProxy,可以通过配置类的方式完成注册——————————————————————————————————————————————使用反射的好处:高内聚,低耦合String className = readfromXMlConfig;//从xml 配置文件中获得字符串Class c = Class.forName(className);factory = (AInterface)c.newInstance();上面代码就消灭了类的名称,无论类名如何改,都不需要修改代码。————————————————————————————————————————————————————————————Spring提供了两种方式来生成代理对象: JDKProxy和Cglib,具体使用哪种方式生成由AopProxyFactory根据AdvisedSupport对象的配置来决定。默认的策略是如果目标类是接口,则使用JDK动态代理技术,否则使用Cglib来生成代理。动态代理通过反射调用目标对象的方法,并进行加强。原生动态代理:如果目标对象没有实现任何接口,将无法创建代理对象。(代理对象和目标对象要实现同一接口)CGLib (Code Generation Library) 是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。广泛应用于AOP框架。————————————————————————————————————————————————————————————————Spring事务的种类:spring支持编程式事务管理和声明式事务管理两种方式:①编程式事务管理使用TransactionTemplate。②声明式事务管理建立在AOP之上的。其本质是通过AOP功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。——————————————————————————————————————————————————————————————spring的事务传播行为:① PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。② PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。‘③ PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。④ PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。⑤ PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。⑥ PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。⑦ PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。————————————————————————————————————————————————————————————spring实例化的三种方式:1使用类构造器实例化:2. 使用静态工厂方法实例化:3. 使用实例工厂方法实例化:——————————————————————————————————————————————————————————依赖注入的三种方式:构造方法的注入;set方法的注入;接口注入。——————————————————————————————————————————————————————————————Bean的生命周期单例对象: singleton出生:当容器创建时对象出生——活着:只要容器还在,对象一直或者——死亡:容器销毁,对象消亡。单例对象的生命周期和容器相同多例对象: prototype出生: 使用对象时spring框架为我们创建——活着:对象只要是在使用过程中就一直活着——死亡:当对象长时间不用且没有其它对象引用时,由java的垃圾回收机制回收——————————————————————————————————————————————————————————————Spring里applicationContext和beanfactory区别applicationContext:创建容器时实例化。适用于单例模式。立即加载。但此接口可以根据对象是多例还是单例选择合适的加载时机。ClassPathXmlApplicationContext: 它是从类的根路径下加载配置文件 推荐使用这种FileSystemXmlApplicationContext: 它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。AnnotationConfigApplicationContext: 当我们使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。beanfactory:使用bean时才创建。适用于多例模式。延迟加载——————————————————————————————————————————————————————————————————BeanFactory和FactoryBean的区别 BeanFactory是接口,提供了OC容器最基本的形式,给具体的IOC容器的实现提供了规范, FactoryBean也是接口,为IOC容器中Bean的实现提供了更加灵活的方式,FactoryBean在IOC容器的基础上给Bean的实现加上了一个简单工厂模式和装饰模式 (如果想了解装饰模式参考:修饰者模式(装饰者模式,Decoration) 我们可以在getObject()方法中灵活配置。其实在Spring源码中有很多FactoryBean的实现类.——————————————————————————————————————————————————————————————————spring启动流程:————————————————————————————————————————————————————————————————SpringMVC执行流程:1.用户发送请求至前端控制器DispatcherServlet2.DispatcherServlet收到请求调用处理器映射器HandlerMapping。3.处理器映射器根据请求url找到具体的处理器,生成处理器执行链HandlerExecutionChain(包括处理器对象和处理器拦截器)一并返回给DispatcherServlet。4.DispatcherServlet根据处理器Handler获取处理器适配器HandlerAdapter执行HandlerAdapter处理一系列的操作,如:参数封装,数据格式转换,数据验证等操作5.执行处理器Handler(Controller,也叫页面控制器)。6.Handler执行完成返回ModelAndView7.HandlerAdapter将Handler执行结果ModelAndView返回到DispatcherServlet8.DispatcherServlet将ModelAndView传给ViewReslover视图解析器9.ViewReslover解析后返回具体View10.DispatcherServlet对View进行渲染视图(即将模型数据model填充至视图中)。11.DispatcherServlet响应用户。———————————————————————————————————————————————————————————————循环依赖如何解决关于循环依赖的问题,Spring提供了通过设计缓存的方式来解决的,而设计为三级缓存,主要是为了解决bean初始化过程中,实例被放入缓存之后,实例的引用还可能在调用initializeBean方法时被替换的问题。对于构造器的循环依赖,三级缓存设计是无法解决的,这属于java语言的约束;但是spring提供了一种使用@Lazy的方式,绕过这个限制,使得构造器的循环依赖在特定情况下(循环链中的某个注入打上@Lazy注解)也能解决。—————————————————————————————————————————————————————————————————spring boot自动配置原理:@SpringBootApplication@SpringBootConfiguration:继承了Configuration,表明这是个配置类。可以用java代码的形式实现spring中xml配置文件配置的效果,并会将当前类内声明的一个或多个以@Bean注解标记的方法的实例纳入到spring容器中,并且实例名就是方法名。@ComponentScan:自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义; 将这些bean定义加载到IoC容器中.@EnableAutoConfiguration@Import(AutoConfigurationImportSelector.class)selectImports()方法通过SpringFactoriesLoader.loadFactoryNames()扫描所有具有META-INF/spring.factories的jar包,spring.factories文件也是一组一组的key=value的形式,key是EnableAutoConfiguration类的全类名,而它的value是一个xxxxAutoConfiguration的类名的列表.selectImports()方法找到所有JavaConfig自动配置类的全限定名对应的class,然后将所有自动配置类加载到Spring容器中。——————————————————————————————————————————————————————————————————————Spring Boot启动加载过程——————————————————————————————————————————————————————————————————————如何自定义一个启动器。————————————————————————————————————————————————————————————————spring boot怎么处理异常Spring Boot提供了一个默认的映射:/error,当处理中抛出异常之后,会转到该请求中处理,并且该请求有一个全局的错误页面用来展示异常内容。自定义创建全局异常处理类:通过使用@ControllerAdvice定义统一的异常处理类,而不是在每个Controller中逐个定义。@ExceptionHandler用来定义函数针对的异常类型,最后将Exception对象和请求URL映射到error.html中;实现error.html页面展示:在templates目录下创建error.html,将请求的URL和Exception对象的message输出。——————————————————————————————————————————————————————————————————spring支持哪些日志框架Logback和log4j————————————————————————————————————————————————————————————springboot默认日志框架默认情况下,Spring Boot会用Logback来记录日志,并用INFO级别输出到控制台。Logback是log4j框架的作者开发的新一代日志框架,它效率更高、能够适应诸多的运行环境,同时天然支持SLF4J。SLF4J——Simple Logging Facade For Java,它是一个针对于各类Java日志框架的统一Facade抽象。——————————————————————————————————————————————————————————————如何实现一个rpc框架。——————————————————————————————————————————————————————————————MyBatis怎么防止sql注入?#将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。方式无法防止Sql注入。#{}是经过预编译的,是安全的;${}是未经过预编译的,仅仅是取变量的值,是非安全的,存在SQL注入。——————————————————————————————————————————————————————————————————mybatis执行步骤读取配置文件创建 SqlSessionFactory 的构建者对象使用构建者创建工厂对象 SqlSessionFactory.使用 SqlSessionFactory 生产 SqlSession 对象使用 SqlSession 创建 dao 接口的代理对象使用代理对象执行方法——————————————————————————————————————————————————————————————————mybatis二级缓存一级缓存:sqlsession范围的缓存,当调用 SqlSession 的修改,添加,删除,commit(),close()等会清空一级缓存。二级缓存:mapper级别缓存,多个sqlsession调用同一个mapper映射的语句只查询一次。——————————————————————————————————————————————————————————————————Mybatis的原理1 加载mybatis全局配置文件(数据源、mapper映射文件等),解析配置文件,MyBatis基于XML配置文件生成Configuration,和一个MappedStatement(包括了参数映射配置、动态SQL语句、结果映射配置),其对应着<select | update | delete | insert>标签项。2 SqlSessionFactoryBuilder通过Configuration对象生成SqlSessionFactory,用来开启SqlSession。3 用户程序调用mybatis接口层api(即Mapper接口中的方法);SqlSession通过调用api的Statement ID找到对应的MappedStatement对象;通过Executor(负责动态SQL的生成和查询缓存的维护)将MappedStatement对象进行解析,sql参数转化、动态sql拼接,生成jdbc Statement对象;JDBC执行sql;借助MappedStatement中的结果映射关系,将返回结果转化成HashMap、JavaBean等存储结构并返回。————————————————————————————————————————————————————————————————Session和cookie?cookie — 客户端请求服务器时,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。而客户端浏览器会把Cookie保存起来。当浏览器再请求服务器时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器通过检查该Cookie来获取用户状态。Session — 当用户第一次请求servlet,服务器就会创建一个新的Session对象,然后将SessionID存放早cookie中,通过cookie把sessionID发送到客户端。客户端下一次访问的时候,就会将SessionID发送到服务器以便再次找到这个session对象,完成会话跟踪所以如果用户将cookie关闭session也将会失效。Cookie被禁了怎么办?保持登录的关键不是 cookie,而是通过cookie 保存和传输的 session ID,其本质是能获取用户信息的数据。除了 cookie,还通常使用 HTTP 请求头来传输。或者在url后面拼接参数。————————————————————————————————————————————————————————————————有哪些常见的内存泄漏场景?(没答上来,扯了一句ThreadLocal如何解决内存泄漏的)1.非静态内部类的静态实例非静态内部类会持有外部类的引用,如果非静态内部类的实例是静态的,就会长期的维持着外部类的引用,组织被系统回收,解决办法是使用静态内部类2.多线程相关的匿名内部类和非静态内部类匿名内部类同样会持有外部类的引用,如果在线程中执行耗时操作就有可能发生内存泄漏,导致外部类无法被回收,直到耗时任务结束,解决办法是在页面退出时结束线程中的任务3.Handler内存泄漏Handler导致的内存泄漏Handler内部message是被存储在MessageQueue中的,有些message不能马上被处理,存在的时间会很长,导致handler无法被回收,如果handler是非静态的,就会导致它的外部类无法被回收,解决办法是1.使用静态handler,外部类引用使用弱引用处理2.在退出页面时移除消息队列中的消息4.Context导致内存泄漏根据场景确定使用Activity的Context还是Application的Context,因为二者生命周期不同,对于不必须使用Activity的Context的场景(Dialog),一律采用Application的Context,单例模式是最常见的发生此泄漏的场景,比如传入一个Activity的Context被静态类引用,导致无法回收5.静态View导致泄漏使用静态View可以避免每次启动Activity都去读取并渲染View,但是静态View会持有Activity的引用,导致无法回收,解决办法是在Activity销毁的时候将静态View设置为null(View一旦被加载到界面中将会持有一个Context对象的引用,在这个例子中,这个context对象是我们的Activity,声明一个静态变量引用这个View,也就引用了activity)6.WebView导致的内存泄漏WebView只要使用一次,内存就不会被释放,所以WebView都存在内存泄漏的问题,通常的解决办法是为WebView单开一个进程,使用AIDL进行通信,根据业务需求在合适的时机释放掉7.资源对象未关闭导致如Cursor,File等,内部往往都使用了缓冲,会造成内存泄漏,一定要确保关闭它并将引用置为null8.集合中的对象未清理集合用于保存对象,如果集合越来越大,不进行合理的清理,尤其是入股集合是静态的————————————————————————————————————————————————————————————————————————依赖注入通过反射机制src,源码包下的文件都会被整合到类路径下Java:/bin/web:/web-info/classes/单实例默认在容器创建时就已经创建。多实例在获取时才开始创建,每次获取时都创建一个新的。bean的创建默认框架利用反射进行的。(除了利用工厂模式之外)我们可以为bean自定义一些生命周期方法,spring在创建和销毁的时候会调用这些方法。spring有一个后置处理器接口,可以在bean初始化前后调用方法。Javabean的属性名由gettersetter方法名决定。内部bean不能被ref引用到,只能由内部使用。username是spring的一个关键字,引用外部配置文件时最好避开key为username,否则有可能无法正确获取值。配置文件里要创建集合类的bean需要用到util名称空间,如果是bean内部的集合类则不需要如此。spring容器底层是个map一旦有切面类加入容器(@Aspect,@Component),容器则会创建目标对象的代理对象;否则创建原对象。若目标对象没有实现任何接口,则cglib负责创建代理对象;否则创建jdk原生代理对象。非环绕通知的其他通知方法只在目标方法的某个节点运行;环绕通知方法则负责编写整个流程,包括前置,目标方法运行,返回,异常,后置等方法。RequestParam注解用来解决前台参数名和后台形参名不一致无法封装的问题。(若前台参数名和后台形参名一样则不需要此注解)RequestBody 注解不适用于get方法,用于获取请求体数据。通过@requestBody可以将请求体中的JSON字符串绑定到相应的bean上,当然,也可以将其分别绑定到对应的字符串上。ResponseBody注解用户将服务端返回对象转为json字符串。(需要引入jakson jar包)PathVariable注解用于获取url路径的值异常处理:异常都是向上抛出的,最终由DispatchServlet找异常处理器进行处理。mybatis中,映射位置文件必须和dao接口目录结构相同————————————————————————————————————————————————————————————————————SSM整合1.导入jar包,导入log4j配置文件2.编写spring框架配置文件applicationContext.xml:引入约束,开启注解扫描。并且配置controller注解不扫描,只管理service和dao层3.在web.xml文件中配置前端控制器和解决中文乱码的过滤器4.编写springmvc配置文件:引入约束,开启注解扫描,只扫描controller注解;配置视图解析器对象;过滤静态资源;开启springmvc的注解支持。5.spring整合springmvc:在web.xml中配置spring的监听器,默认只加载web-info目录下的applicationContext.xml文件。我们可以设置配置文件的路径解决此问题。6.编写SqlMapConfig配置文件:引入约束,配置环境;(数据库连接参数可以引入外部配置文件)引入映射配置文件,可以用class/package属性或者resource属性(使用xml文件编写sql语句)。7.spring整合mybatis:在applicationContext.xml文件中配置数据库连接池;配置sqlsessionfactory工厂对象;配置dao接口所在包。(此时可将SqlMapConfig文件删除)拓展:applicationContext.xml文件中配置声明式事务管理:配置事务管理器;配置事务通知;配置AOP增强。tomcat服务器可以下载软件,也可以引入jar包————————————————————————————————————————————————————————————————————springboot可省略web.xml文件,已经自动配置好每个启动器背后都是一堆依赖。spring boot启动默认读取application.properties文件,所有配置信息都放进这里。可以添加配置文件读取类和配置类,配置类中引用读取类使用相应配置。修改默认配置在此文件中修改。一个配置类相当于一个xml配置文件.springboot四种属性注入方式:autowired/构造方法注入/@bean方法形参注入/@bean方法上使用@ConfigurationProperties(prefix="xxx"),此方法要求返回值有set方法。spring boot实战:导入父依赖;整合mvc:导入web启动器;静态资源放在默认路径下(如resources下的static目录下);配置拦截器:编写拦截器代码;编写Java配置类,实现WebMvcConfigurer接口,实现拦截器注册方法并且添加拦截路径。整合数据源:导入jdbc启动器(自带hikaricp连接池);导入MySQL驱动;配置四大参数。整合mybatis:导入mybatis启动器;覆盖mybatis参数(如pojo包,mapper映射xml文件位置等);定义接口添加@Mapper注解。@Repository需要在Spring中配置扫描地址,然后生成Dao层的Bean才能被注入到Service层中:如下,在启动类中配置扫描地址:(@Mapper不需要)@SpringBootApplication //添加启动类注解@MapperScan("com.anson.dao") //配置mapper扫描地址整合通用mapper:引入启动器;接口继承Mapper整合事务:添加Transactional注解。————————————————————————————————————————————————————————————————————————————循环引用的对象转为 JSON 造成栈溢出,一般是因为对象中的循环引用引起不断递归。常见的作法就是:换一种 JSON 的序列化工具,比如 fastjson 默认支持消除对同一对象循环引用;transient 修饰属性;显式排除对象的某些属性。—————————————————————————————————————————————————————————————————————— 算法/设计模式涉及到去重,可以考虑用Set集合类。添加失败会返回false获得队列最大值,要求时间复杂度为1,可以维护一个双端队列,从中获取( 滑动窗口最大值)——————————————————————————————————————————————————————————————单例模式:要将构造函数设为private,以防外面再造。对象声明为private static 懒汉式:使用的时候再创建对象。getInstance方法在多线程下不安全。解决办法: 方法加synchronized,性能差; 双端检锁机制; 静态内部类。 饿汉式:类初始化时就实例化。天生线程安全。——————————————————————————————————————————————————————————————代理模式:——————————————————————————————————————————————————————————————工厂模式:以动物举例。工厂方法模式:定义一个抽象工厂类和一个抽象动物类,不同的工厂创建一个对应的动物,不同工厂类继承抽象工厂,不同动物类继承抽象动物类。当需要新的动物时,只需要增加一个动物类和对用的工厂类。简单/静态工厂模式:一个具体的工厂针对不同的实现了同一接口的动物类进行实例化。工厂根据传入的不同参数创建不同的对象。抽象工厂模式:——————————————————————————————————————————————————————————————建造者模式:——————————————————————————————————————————————————————————————装饰器模式:——————————————————————————————————————————————————————————————一致性hash算法的原理传统哈希:服务器数量变动时,所有缓存的位置都会发生改变。(对服务器数量取模)一致性哈希:使用服务器的IP或主机名作为关键字进行哈希对2的32方取模,服务器位置是固定的。据Key使用相同的函数Hash计算出哈希值,并确定此数据在环上的位置,从此位置沿环顺时针查找,遇到的服务器就是其应该定位到的服务器。一致性Hash算法对于节点的增减都只需重定位环空间中的一小部分数据,有很好的容错性和可扩展性。节点分布不均匀面造成数据倾斜,一致性Hash算法引入了虚拟节点机制,即对每一个服务器节点计算多个哈希,每个计算结果位置都放置一个此服务节点,称为虚拟节点。具体操作可以为服务器IP或主机名后加入编号来实现。数据定位算法不变,只需要增加一步:虚拟节点到实际点的映射。————————————————————————————————————————————————————————————————散列表(哈希表):空间换时间,所需空间较大。hash 表查询是被设计成O(1)的 但是事实上要根据hash算法的碰撞几率定的,在最差的情况下也是个O(n)的。哈希函数:直接寻址法/数字分析法/平方取中法/取随机数法/除留取余法解决冲突:除留取余法/再哈希法/链地址法/建立一个公共溢出区————————————————————————————————————————————————————————————————幂等性:原本是数学上的概念,即使公式:f(x)=f(f(x)) 能够成立的数学性质。用在编程领域,则意为对同一个系统,使用同样的条件,一次请求和重复的多次请求对系统资源的影响是一致的。数据库增加或者修改需要保证幂等性。——————————————————————————————————————————————————————————————排序算法:插入排序:n2 1 稳定选择排序:n2 1冒泡排序:n2 1 稳定希尔排序:n1.3 1堆排序:nlog2n 1快速排序:nlog2n nlog2n归并排序:nlog2n n 稳定计数排序:n+k n+k 稳定桶排序:n+k n+k 稳定基数排序:n*k n+k 稳定——————————————————————————————————————————————————从两个文件(各含50亿个url)中找出共同的url遍历文件a,对每个url求取hash(url)%1000,然后根据所得值将url分别存储到1000个小文件(设为a0,a1,...a999)当中。遍历文件b,采取和a相同的方法将url分别存储到1000个小文件(b0,b1....b999)中。我们只要求出1000对小文件中相同的url即可。(比如对于a0 vs b0,我们可以遍历a0,将其中的url存储到hash_map当中。然后遍历b0,如果url在hash_map中,则说明此url在a和b中同时存在,保存到文件中即可。 )如果分成的小文件不均匀,导致有些小文件太大(比如大于2G),可以考虑将这些太大的小文件再按类似的方法分成小小文件即可。——————————————————————————————————————————————RESTful到底是个什么?Server提供的RESTful API中,URL中只使用名词来指定资源,原则上不使用动词。用HTTP协议里的动词来实现资源的添加,修改,删除等操作。Server和Client之间传递某资源的一个表现形式,比如用JSON,XML传输文本,或者用JPG,WebP传输图片等。用 HTTP Status Code传递Server的状态信息。比如最常用的 200 表示成功,500 表示Server内部错误等。————————————————————————————————————————————————————1到n中减少了一个数,顺序被打乱,找出缺失的数用1^2^...^n的结果在逐个异或当前输入数据。时间复杂度:O(n) 空间复杂度:O(1)————————————————————————————————————————————————LRU就是当你内存中数据到达指定容量的时候,LRU选择将最长时间没有被使用过的那个键值对从内存中移除。思路:hash+双向链表添加键值对,若hash表中不存在,则添加进表中,并添加节点到双向链表的头节点之后(若此时达到缓存最大容量,则删除双向链表尾节点之前的节点并删除hash表中的键值对);若hash中存在,则更新hash表中此键值对,并且在双向链表中删除旧节点,并添加新节点到头节点之后。获取键值对的值,若hash表中存在,则在双向链表中删除旧节点,并添加新节点到头节点之后(其实新旧节点一样);若不存在,返回-1class LRUCache {class ListNode{ int key; int val; ListNode pre; ListNode next; public ListNode(){}; public ListNode(int key, int val){ this.key = key; this.val = val; }}Map<Integer, ListNode> map = new HashMap<>();ListNode head;ListNode tail;int size;int capacity;public LRUCache(int capacity) { this.size = 0; this.capacity = capacity; head = new ListNode(); tail = new ListNode(); head.next = tail; tail.pre = head;}public int get(int key) { ListNode tmp = map.get(key); if (tmp == null) return -1; moveToHead(tmp); return tmp.val;}private void moveToHead(ListNode node){ remove(node); add(node);}private void remove(ListNode node){ node.pre.next = node.next; node.next.pre = node.pre;}private void add(ListNode node){ node.pre = head; node.next = head.next; head.next.pre = node; head.next = node;}public void put(int key, int value) { ListNode tmp = map.get(key); if (tmp == null){ ListNode node = new ListNode(key, value); map.put(key, node); add(node); size++; if (size > capacity){ ListNode del = tail.pre; int removedel = del.key; map.remove(removedel); remove(del); } }else{ tmp.val = value; moveToHead(tmp); }}}————————————————————————————————————————————————————————————