原码:就是早期用来表示数字的一种方式: 一个正数,转换为二进制位就是这个正数的原码。负数的绝对值转换成二进制位然后在高位补1就是这个负数的原码
反码:正数的反码就是原码,负数的反码等于原码除符号位以外所有的位取反
补码:正数的补码与原码相同,负数的补码为 其原码除符号位外所有位取反(得到反码了),然后最低位加1.
移码:(又叫增码)是符号位取反的补码
正数的反码和补码都与原码相同。
负数的反码为对该数的原码除符号位外各位取反。
负数的补码为对该数的原码除符号位外各位取反,然后在最后一位加1
各自的优缺点:
原码最好理解了,但是加减法不够方便,还有两个零。。
反码稍微困难一些,解决了加减法的问题,但还是有有个零
补码理解困难,其他就没什么缺点了
浮点数由符号位、有效数字、指数三部分组成。
说明:
第0位用s表示。s=1表示负数,s=0表示正数。s即符号位(Sign)。
第[1,7]位用E表示。称为指数。E即指数(Exponent)
第[8,31]位用M表示。称为尾数。M即尾数(Mantissa)
若E全0,则尾数附加位为0。否则尾数附加位为1。
一个Float浮点数的值为:Value = (-1)^s *(1+M)*2^(E-127)
浮点数值 = -1*2^(129-127)*(2^0+2^-2)
float:2^23 = 8388608,一共七位,这意味着最多能有7位有效数字,但绝对能保证的为6位,也即float的精度为6~7位有效数字;
最开始的计算机都是国外人员使用,在进行编码的时候,只需要表示24个英文字母、一些符号就行,没有多少,所有使用2的8次方-1,128中字符就足够了,其中一位作为奇偶校验位。这就是ASCLL码,但是汉字那么多,仅仅靠ASCLL码肯定不行,必选增加,所有有了GB2312(看名字知道是国人用的)以及后来的GBK(国标扩展)还有后来的GB18030都是国人的自己的编码表,最后由国际组织统一发布了UniCode 就是现在常用的UTF-8,UTF-16等,可以理解成一种压缩方式。
在程序发送数据的时候,应用层按照既定的协议打包数据,随后又传输层加上双方的端口号,由网络层加上双方的IP地址,又链路层加上双方的mac地址(根据ip路由表决定下一网络在哪,但是最终需要通过mac地址(外层包装)来决定传送给谁)并将数据拆分成数据帧,经过多个路由器和网关后,达到目标机器。简单的说就是按照“端口-IP地址-MAC地址”这样的路径进行数据的封装和发送,解包的时候反过来操作即可!
为什么是三次?因为只用通过三次握手才能完全确认链接正常!
其中TCP报文SYN和ACK标志位很重要。SYN(Synchronize Sequence Number)用作建立链接时候同步信号,ACK(Acknowledgement)对于收到的数据进行确认,所收到的数据由确认序列表示!
如果只使用两次握手,可能会造成脏链接,因为TTL( Time To Live- IP包被路由器丢弃之前允许通过的最大网段数量,这个时间设置是为了防止信息在网络上无限期的存在)的存在时间可能超过TCP请求超时时间,如果两次握手可以建立链接。那么当超时请求链接A->B,B同意建立链接,返回数据B-A,链接建立!如果是三次握手,B会向A同意却请求链接,但是A的SYN_SEND状态错误,直接丢弃链接!
断开TCP链接需要4次握手协议,举个简单的例子,A和B都在吃饭(链接转态),两人预定一起去玩游戏,A已经吃完了,A->B说一会去shopping,B->A,我吃饭了就去。吃完了告诉你。B吃完了,B->A ,我吃完了,走吧!A->B好的走,有其他安排给我说,别放我鸽子!A等待一会,没有消息!链接断开!
数据库层面的请求应答时间必须在100ms以内,秒级的SQL查询通常存在巨大的性能提升空间,主要有一下几种方案;
Sql注入:防止方法:过滤特殊字符,禁止使用字符串拼接的SQL语句,严格使用参数绑定传入的SQL查询;合理使用数据库访问框架提供的防止SQL注入的机制,例如mybatis的中#{},不要使用${};
XSS=Cross-Site Scripte(和CSS冲突了,所有叫XSS),XSS是在正常用户请求的HTML页面中执行了黑客提供的恶意代码
实例 :http://xss.demo/self-xss.jsp?userName=张三
&errorMessage=XSS示例
预防方法:XSS字段过滤
CSRF 跨站请求伪造(cross-site Rquest forgery),CSRF是黑客直接盗用用户浏览器中的登录信息,冒充用户去执行黑客指定的操作,实例:
https://net-bank.demo/transfer.do?targetAccount=12345samount=100
防止方法:利用浏览器的同源(如果两个页面的协议,端口(如果有指定)和主机都相同,则两个页面具有相同的源)限制,请求中验证cookie,人机交互(调用转账交易时候,验证短信验证)
7.1HTTPS和HTTP
HTTPS在交换密钥阶段使用非对称密钥加密方式,之后建立通信交换报文阶段则使用对称密钥加密方式。https示意图
https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
https的的请求过程:首先server端使用加密算法生成公匙和私匙,server用公匙去CA机构进行认证,获得数字证书。Client经过3次握手协议后,通过http报文协商使用那种加密算法,这时候获得Service的数字证书(带公匙),client去验证数字证书,正常则client在生成一个对称密匙,之后将url请求用对称密匙加密在用公匙加密,发送给sevice。Service使用私匙进行解密获得对称密匙,之后就对信息进行对称密匙加密进行传输。最后tcp4次挥手断开链接!
分为:静态内部类、成员内部类、局部内部类、匿名内部类
在jdk中定义包内可见的静态内部类非常常见,这样做有很多好处
作用域不会扩散到包外
可以通过“外部类.内部类”的方法直接进行访问
内部类可以访问外部类中的所有静态属性和方法
下面是ConcurrentHashMap中定义的Node静态内部类
this和super都是在实例化阶段调用,所以不能在静态方法和静态代码块中使用this和super关键字(因为静态方法在使用前不用创建任何实例对象,当静态方法调用时,this所引用的对象根本没有产生。)
在创建类对象时候,会先执行父类和子类中的静态代码块,然后在执行父类和子类的构造方法。且静态代码块只运行一次,第二次实例化时候不会运行。
继承(extends)(is-a)
实现(implements)(can-do)
组合(类是成员变量)(contains-a)
聚合(类是成员变量)(has-a)
依赖(import类)(use-a)
将数据对象转换成二进制流的过程称为对象的序列化,反之称为反序列化!
常见的序列化方式三种
11.1 java原生序列化,通过实现Serializable,同时建议是在SerializableUID值,避免每次序列在进行生产。
11.2 Hessian序列化
是一种支持动态类型、跨语言、基于对象传输的网络协议。比java原生序列化强很多。Hessian是把复杂对象所用属性储存在一个Map中进行序列化。所以在父类、子类存在同名的成员变量的情况下,Hessian序列化时候,先序列化子类,然后序列化父类,因此反序列化结果导致子类同名成员变量被父类的值覆盖。
11.3 JSON序列化
JSON是一种轻量级的数据交换格式。JSON序列化就是将数据对象转换为JSON字符串,序列化过程中抛弃了类型信息,所有反序列化时候只有提供类型信息才能准确的反序列化。
Transient 关键字添加后会是本变量不会被序列化。
泛型的本质是参数化类型
约定成俗的表示方式有:
E表示Element,用于集合中的元素;T代表the Type of object,表示某个类。
K表示Key,V表示Value,用于键值对元素。
在包装类型中,除了Float和Double之外(没法缓存,太多了)其他包装类型都用缓存。
类名采用大驼峰(所用首字母大写)命名法,方法名称采用小驼峰(除了首字母之外其他字母都大写)
14.1常量指的是作用域范围内保持不变的量,用final修饰。
全局常量:public static final
类内常量:private static final
常量的定义可以使用Enum枚举表示:
14.2单个方法的长度,代码部分最好不要超过80行。
Java代码一次编译,到处执行的关键在于jvm(java虚拟机),它能够将编译好的字节码class文件通过类加载器加载到jvm虚拟机,jvm会对class字节码解释执行(主流jvm是JIT执行和解释混合执行)经过jit或许解释后生成对应机器码可以被机器识别。不同的jvm可以生产对象不同机器的机器码,例如win的和linux的机器码。
JIT(java in time)是一种动态编译技术,可以理解为一直缓存技术,会将热点代码进行缓存,这样执行的更快。不用每次都执行!
也就是如下:
1.Java源文件—->编译器—->字节码文件
2.字节码文件—->Jvm—->机器码
任何程序都必须加载到内存才能和CPU进行交流,同样class文件必须加载到内存才能进行实例化,类加载器就是将class文件加载到内存中。加载类使用的parents Delegation Modle ,可以翻译成双亲委派模型,最好翻译溯源委派加载模型。意思是说jvm在对一个对象初始化的过程中,jvm会初始化继承树上还没有被初始化过的所有父类,并且会执行链路上所有未执行过得静态代码块、静态变量赋值语句等。Jvm的内存模型:
Jvm各个内存布局模块说明:
堆区市OOM故障发生的主要地点。它储存着几乎所有的实例对象,当无节制的创建大量对象(读取大量数据到内存,实际上是以实例储存,相当于对象),就是超过堆区最大的储存空间就会报错!可以通过-Xms256m -Xmx1024m调整,分别代表最小堆容量和最大堆容量。生产环境中为了避免堆空间不断的扩容和收缩,JVM中Xms和Xmx设置成一致!
堆中分新生代(分Eden区和Survivor区(s1 和 s2))和老年代。新创建的对象放到Eden区,当Eden区填满之后会触发Young Garbage Collection(YGC,垃圾回收),没有被引用的对象之间被清除,依然存货的对象放入Survivor区域。每次YGC之后将存货的对象放入Survivor区未使用的那个区域如s1,s2区域全部清空,内对象移动到s1并且每个对象的计数器加1,如果超过阀值直接移动到老年代。每个对象都有计数器,每次YGC时候加1,-XX:MaxTenuringThreshold参数可以配置计数器的阀值,如果阀值为1,则新生代直接进入老年代。老年代也接纳新生代无法存放的超大对象。YGC之后的对象Survivor区域放不下直接放到老年代,老年代也没法放下触发Full Garbage Collection,即FGC,仍然不能放下,直接造成OOM异常。如果想知道堆中出错的时候堆内存信息,可以设置-XX:+heapDumpOnOutOfMemoryError,让JVM遇到OOM时候打印输出信息!不同的JVM实现不同的回收机制,堆内存划分方式不一样。本书JDK11,JVM为HotSpot
在jdk8版本中,元空间的前身perm(永久代)已经淘汰,jdk7中也只有HotSpot JVM采用永久代,且大小固定,很难调优。主要存放Class的静态信息,Main方法信息,常量信息,静态方法和变量信息,共享变量等信息。如果动态加载过多的类也可能造成Perm的OOm,解决该问题可以设置参数
-XX:PermSize=5M -XX:MaxPermSize=7M
由于perm存在问题,jdk已经移除,改为元空间。Perm区的String常量移动至堆内存,其他类元信息、静态属性等移动到元空间内。
栈是一个先进后出的数据结构。线程私有。每个方法从开始调用到执行完成,就是栈帧从入栈到出栈的过程。StackOverFlowError表示请求的栈溢出,导致内存耗尽,主要出现在递归方法中!
栈帧组成包括:局部变量表、操作栈、动态连接、方法返回地址等。
存放方法参数和局部变量的区域。操作指令STORE就是将操作栈中计算完成的局部变量写会局部变量表的储存空间。
操作栈是一个初始化转态为空的桶式结构栈。在方法执行过程中会有各种指令往栈中写入和提前信息。
每个栈帧都都包含当前方法的引用。
正常返回:执行到任何方法的字节码指令。如:RETURN,IRETURN等。
异常返回:无论何种方式都将返回当前背调用的位置。
线程私有。本地方法栈主外,而JVM栈主内。
本地方法栈是为本地操作系统(Native)方法。这部分不再被JVM控制。本地方法可以通过(JNI)来访问虚拟机的运行时数据区,甚至是寄存器。当大量的本地方法执行的时候,必将削弱JVM性能。对于内存不足的情况,本地方法栈会抛出native heap OutOfMemory。最著名的本地方法:System. currentTimeMillis().
每个线程执行过程中都会当前线程的程序计数器和栈帧。程序计数寄存器用来储存这些信息。
存放cup执行时候,未执行完成的类信息的储存地方。
当JVM接收到new信息时候,首先在metaspace内检查需要创建的类元信息是否存在,不存在,在双亲委派小,是用类加载器进行找到对应额.class文件,找不到报ClassNotFoundException异常,存在进行加载,生成对应的Class类对象。
计算对象占用空间,引用变量分配4个字节,在堆中划分一块内存给新对象,分配空间时候同步进行,保证分配的原子性!
设置各种0值。
设置对象的哈希码、GC信息、锁信息、
初始化成员变量,执行实例化代码,调用构造方法,并在堆内对象的首地址给引用变量。
垃圾回收的目的是清理不在使用的对象,自动释放内存。
判断对象是否应该被回收?
JVM引入了GC Roots(一个对象可以有多少Root,一般有一下几种:Class-系统加载的对象,Thread-活着的线程,JNI Local-JNI方法的的local变量或参数,JNI Global-全局JNI变量等) 概念,如果一个对象与GC Roots之间没有直接或者间接的引用关系,就判定改对象应该被回收。
垃圾回收相应的算法有哪些?
基础的“标记-清除算法”,
较好的“标记-整理算法”,
优秀的“标记-复制算法”,
垃圾回收器(Garbage Collector)是实现垃圾回收算法并应用在JVM环境中的内存管理模块,当前有很多种,简述一下Serial、CMS、G1。
Serial 主要应用于YGC阶段,采用串行单线程的方式完成GC任务。
CMS是回收停顿时间比较短,目前常用的垃圾回收器!采用“标记-清除算法”,
G1是HotSpot在JDK7推出的新一代垃圾回收器,采用“标记-复制算法”,
Java中所有的异常都是Throwable的子类,分为Error(致命的)和Exception(非致命的)
防止NPE异常:
数据结构分类:
线性结构:0至1个直接前继和直接后继。代表有:顺序表、链表、栈、队列等
树结构:0至1个直接前继和0至n个直接后继。
图结构:0至n个直接前继和直接后继,n>=2.代表有:单图、多重图、有向图、多重图。
哈希结构:没有直接前继和直接后继。哈希结构通过某种特定的哈希函数将索引和存储关联起来。
List集合:
ArrayList:是容量可变的非线程安全的集合。内部使用数组进行储存,扩容时候会创建更大的空间,将原集合复制进去。支持快速的随机访问,但是插入或者删除速度较慢,因为可能会移动其他元素。
LinkedList:本质是双向的链表,与ArrayList相比,随机访问较慢,但插入和删除更快。
Queue集合:
队列是一种先进先出的数据结构。只允许一端插入一端获取;自从BlockingQueue(阻塞队列)问世,队列地位大大提升。通常作为数据缓存区(buffer)。
Map集合:
是以key-value键值对作为。HashMap是先线程不安全的。多线程采用-ConcurrentHashMap。TreeMap是key有序的Map类集合。HashMap的key、value都可以为空。
Set集合:
Set是不允许出现重复元素的集合类型。常用的有HashSet、TreeSet和LinkedHashSet三个集合类。HashSet从源码分析是使用HashMap来实现的,只是Value固定为一个静态对象。
ArrayList初始长度为10,HashMap的初始值为16.
长度较长的集合,应给定一个固定初始值。不然假如1000个HashMap的长度的元素,需要扩容七次。很耗资源。
数组与集合的不同点:数组是协变的,而集合是非协变的。
相同点:都是用另一个类型构建的类型
参考来源:https://www.jb51.net/article/156863.htm
class Soup
public void add(T t) {}
}
class Vegetable { }
class Carrot extends Vegetable { }
协变:如果Soup
不变:如果Soup
逆变:如果Soup
实例:
Soup
soup.add(new Tomato());
Vegetable[] vegetables = new Carrot[10];
vegetables[0] = new Tomato(); // 运行期错误
数组协变性,是Java的著名历史包袱之一。使用数组时,千万要小心!实际上上面两个举例的代码都不能正常运行。集合是直接编译期出错,而数组在运行期出错(更麻烦,所以说是个bug)
泛型是不变的,但某些场景里我们还是希望它能协变起来。可以这样:
public void drink(Soup extends Vegetable> soup) {}
但是,这种方法有一个限制。编译器只知道泛型参数是Vegetable的子类,却不知道它具体是什么。所以,所有非null的泛型类型参数均被视为不安全的。说起来很拗口,其实很简单。直接上代码。因为soup的具体类型信息是在运行期才能知道的,编译期并不知道。
public void drink(Soup extends Vegetable> soup) {
soup.add(new Tomato()); // 错误
soup.add(null); // 正确
另一种方式:
public void drink(Soup super Vegetable> soup) {}
这时,Soup
public void drink(Soup super Vegetable> soup) {
soup.add(new Tomato());
}可以正常执行!
总结一下:使用entends的时候,可能有多个不知道具体是那个类,所以只能增加null;而使用super的时候,只是本身和本身的子类可以加进去(肯定包含)。
所有的List Super T>集合可以执行get操作,但是因为类型擦除,不知道具体的类型,只能返回Object,List Extends Cat> 可以返回带类型的元素,但只能返回Cat自身及父类的对象,因为子类类型被擦除了。
如果要从集合中读取类型T的数据,并且不能写入,可以使用 ? extends 通配符;(Producer Extends)
如果要从集合中写入类型T的数据,并且不需要读取,可以使用 ? super 通配符;(Consumer Super)
如果既要存又要取,那么就不要使用任何通配符。
Arrays.asList(数组)返回的是不是我们通常使用的ArrayList,而是Arrays类中的内部类。改类只用set方法,进行值操作。
产生的原因:集合在进行迭代操作中的next()、remove()方法时候,都会调用checkForComdificationf方法,主要判断modCount == expectedModCount。集合每次修改(add,remove等)都会进行计数,叫做:modCount;而expectedModCount 是在Itr中定义的:int expectedModCount = ArrayList.this.modCount。不会改变。改变的就是modCount。原因是在多线程(单线程修改也会)执行时候,modCount改变,而集合expectedModCount没有改变。造成异常。
(其他说明有两个线程(线程A,线程B),其中线程A负责遍历list、线程B修改list。线程A在遍历list过程的某个时候(此时expectedModCount = modCount=N),线程启动,同时线程B增加一个元素,这是modCount的值发生改变(modCount + 1 = N + 1)。线程A继续遍历执行next方法时,通告checkForComodification方法发现expectedModCount = N ,而modCount = N + 1,两者不等,这时就抛出ConcurrentModificationException 异常,从而产生fail-fast机制)
高度:从某条节点出发到叶子节点最长简单路径的条数称为高度。
深度:从根节点出发到某节点的条数,称为节点的深度。
建议:除了局部方法和绝对线程安全的情形下,优先推荐使用ConcurrentHashMap,相对于HashMap两者性能相差无几(put3百万的数据,ConcurrentHashMap比HashMap还有平均快一秒),单前者解决了高并发下的线程安全问题。HashMap的死链问题及扩容数据丢失是慎用HashMap的主要原因。
数据丢失的原因:并发赋值时候被覆盖;(新建数据防止到solt(哈希槽)的第一位,方便下次快速查找,当多个线程同时执行到这时候,后来元素会被覆盖)
已遍历区间新增元素会丢失;
“新表”被覆盖;
迁移丢失。
死链问题:指数据循环引用。CUP飙升。
ConcurrentHashMap在jdk8进行了脱胎换骨的改造。在数据长度小于60的时候,是数组+链表的数据结构。大于60长度是数组+红黑树的数据结构。在多线程并发的时候使用。使用方法和HashMap相同。
线程的生命周期:new(新建转态)、runnable(就绪转态)、running(运行转态)、blocked(阻塞状态)、dead(终止转态)
线程安全的核心理念是:要么只读,要么加锁。合理利用JDK提供的并发包.
测试类名称:通常是test+类名称;或者更容易阅读的should..when结构,类似于shouldGetTicketInfoWhenAllParametersVaild
Junit的断言能够更好的服务于测试,断言封装了常用的逻辑判断,类似于StringUitls.举例说明有:assertSame(判断连个类是否相等)
AssertJ(断言的升级版本),包含许多更好的断言。