一、基础知识:
1、局部变量需要初始化才可以使用,它从属于方法或语句块。
2、(常量)成员变量不初始化会默认赋予默认值,它从属于对象。
3、静态变量不初始化会默认赋予默认值,它从属于类,并且被所有类共享。
4、字节高到低需要强制转换,右高左低。int a = (int)200L;
5、字节低到高,在不超过低的,会自动转换,右低左高。long b = 100;
6、this和surper():
①this指当前对象。this()方法只能放在构造方法的第一行,来调用另一个构造方法。
super()方法只能放在子类构造方法的第一行,来调用父类的构造方法。
②都不能在static环境中使用,且不能同时出现在同一个构造方法中。
7、static关键字:
被static修饰的成员变量在内存中只有一份且被称为静态资源,它们被放在方法区中所以
也是共享资源。他们都从属类。类加载时,比对象先加载,所以静态方法不能调用非静
态静态方法或属性,相反是可以的。
8、final关键字:
8.1、修饰类,该类不能继承;修饰方法,该方法不能重写;
8.2、修饰基本数据类型变量,变量只能被赋值一次,之后就不能再被赋值了;
8.3、修饰引用变量,该引用指向的内存地址不可改变,但引用类型内部属性值可以改。
9、基本数据类型传递的是原参数的副本不影响原参数,而引用类型传递的是对象的地址,原
参数和副本都指向同一个地址,副本内容的改变,原参数也会随着改变。
10、instanceOf的使用:
11、抽象类和接口:
①抽象类:抽象方法交给子类去实现;不能new,可以有属性,普通方法,构造方法。
②接口:抽象方法交给实现类去实现;不能new,JDK8中可以有属性但默认为静态常
量,有default修饰的默认方法,JDK8可以有静态方法但只能被本接口调用,
JDK9可以有私有方法。
12、非静态内部类、静态内部类、匿名内部类、局部内部类:
①非静态内部类可以访问为外部类的成员,反之不行;非静态内部类不能有静态成员;
②静态内部类只能访问外部类的静态成员,外部类可以把它当做自己的一个静态成员,
它与外部类没有绑定关系,可以独立存在,只有被用到时才会被装载。
③匿名内部类:
④局部内部类仅限于方法内使用:
13、自动装箱和拆箱(Integer)和常量池:
14、String、StringBuffer、StringBuilder的区别 :
①String:不可变字符串,即创建String对象后,该对象中的字符串是不可改变的,直接
存放到常量池中。底层是final修饰的数组,所以是线程安全。每次修改时,如
果常量池不存在就创建一个新对象并放入常量池中,效率最低,而下面两个不
会产生新对象。因为底层重写了equals()方法和hashCode()方法,所以只要内
容相同, 他们的内存地址就相同。
②StringBuffer:可变字符串,它的append方法被synchronized修饰,所以是线程安
全,效率比较低。没有重写equals()方法和hashCode()方法。
③StringBuilder:可变字符串,线程不安全,但效率高。没有重写equals()方法和
hashCode()方法(默认继承object)。
15、collection集合和map集合:数组查改效率高,链表增删效率高。
①collection底层是数组(arrayList)、双向链表(linkedList)、哈希表/数组+链表(hashSet)、 二叉搜索树/红黑树(treeSet)。
②map底层是哈希表/数组+链表(hashMap/linkedHashMap)、平衡二叉搜索树/红黑树(treeMap)。
16、哈希表:有数组和链表组成,put时通过计算键key的哈希码hashcode,将键值对key-value存储在数组中的特定位置;
17、二叉搜索树:通过比较键key的大小来进行存储和检索操作,保持键的有序性。
18、两个对象通过equals方法比较是true,它们的hashCode方法也应该为true。否则就会导
致在使用哈希集合时出现问题。比如,将一个对象添加到哈希集合中后,再通过equals
方法查找该对象时可能无法找到,因为哈希集合会使用不同的散列码进行查找。所以在
重写equals方法时也要重写hashcode方法。
19、Collections工具类-->方便对collection和map集合进行各种操作:
20、编码和解码:字符人看,字节电脑看。
①编码enCode:由字符到字节;
②解码deCode:由字节到字符;(可能发生乱码)
21、字符集:
每个字都有代号,将代号转化成二进制给计算机读。这些代号(码表)由字符集存储。
22、引用分类:
①强引用:程序运行时,gc时不回收;【字符串常量池等】
②软引用:程序运行时,当jvm内存不够,gc时才会被回收;【缓存不经常用的对象】
③弱引用:程序运行时,gc时立即被回收;【WeakHashMap、ThreadLocalMap】
④虚引用:类似无引用,主要用来跟踪对象被回收的状态,不能单独使用,必须与引用
队列(ReferenceQueue)联合使用;【对象回收之前可以获取这个对象再对这
个对象做其他操作】
23、线程状态:
①新建状态:线程刚创建的状态;
②就绪状态:1.线程调用start()方法;2.阻塞事件解除;3.线程调用yield()方法;
4.jvm将当前在cpu执行的线程切换到其他线程(线程的优先级由jvm决定);
③运行状态:线程开始执行run()方法中的代码块;
④阻塞状态:1.线程调用sleep()或wait()方法;2.别的线程调用join()方法;
3.IO操作中的read和write;4.遇到锁;
⑤死亡状态:1.正常代码执行完;2.调用stop()或destroy()方法[不推荐];
24、happenBefore(指令重排)/volatile:
代码执行过程:编译器生成字节码(词法分析->语法分析->语义分析)->虚拟机解释成机
器码(解析字节码->代码优化(指令重排)->由即时编译器(JIT Complier)编译成机器码(一
条条指令)->操作系统和CPU将机器码加载到内存中(值放入对应寄存器/存储器)->CPU从
内存中获取指令并从寄存器中拿值进行计算并将计算结果写回寄存器(保留在寄存器,写
回存储器或输出到外部设备)。
CPU发生指令重排(代码之间不依赖)【减小内存操作速度远慢于CPU的运行速度】:
①虚拟机层面;②硬件层面;
25、ThreadLocal:每个线程独有的变量。
26、InheritableThreadLocal:继承threadLocal上下文环境的数据,主线程会拷贝一份自身数
据给子线程使用。
27、XML解析:
28、脚本引擎(java与js之间的桥梁):
目的:使java可以操作各种脚本语言(js(Rhino)等),可以让一些复杂异变的业务逻辑交给脚本语言来处理。
29、字节码操作【ASM(基于JVM底层的操作和指令)、cglib(基于ASM)】:性能高于反射。
①JAVAssist操作字节码.class文件
二、JVM:
1.栈(先进后出):
1.1、存放基本数据类型、对象的引用和实例方法。
1.2、JVM为每个线程创建一个私有栈,用于存放该线程执行的方法的信息。
1.3、栈由系统自动分配,速度快,是一个连续的内存空间。
2.堆:
2.1、存放对象/对象的属性值/数组【分为新生代、老年代、元空间(永久代)】
2.2、JVM只有一个堆,所以被所有线程共享,是不连续的内存空间,分配灵活。
2.3.1 堆分代:
分代原因:①为了提高分配对象内存和垃圾回收的效率。
②由于内存空间在存放对象时是连续的,GC回收对象时需要遍历所有对象, 回收后内存也会产生大量的碎片(浪费空间)。
所以针对不同的对象进行分代,再进行不同的处理(GC算法),分后内存空间就可能不连续了。
2.3.1.1 新生代:存放新对象,存活率低,回收率高;【采用复制算法】。
又分了Eden区,幸存区(from和to区),空间比例是8:1:1。当Eden区内存不足时,JVM就 会发起轻GC(Minor GC)回收无用对象,当某个对象被轻GC达到一定阈值(默认15次)还
能存活下来,那么该对象就会被转移到老年代。
2.3.1.2 老年代:存放经过15次轻GC还存活的对象和大对象,回收率低;【采用标记-清除和 标记-压缩算法混合】。
当老年区内存不足或调用system.gc()或堆伸缩或元空间满了或老年代空间担保失败时,
会先轻GC进行回收,如果还不够,再重GC(Full/Major GC) 进行回收。
轻GC机制:垃圾收集器将Eden区中的存活对象复制到一个空的幸存区(to区),同时扫描from
区,将存活的对象也复制到to区并更新对象的引用关系,最后将from区和Eden区
进行清理,回收无用对象并释放空间,再将to区标记为from区【复制算法】。
特点:需要频繁的回收,所有GC算法要使用回收速度快,效率高。
重GC机制:垃圾收集器会扫描整个堆,并标记所有存活对象,然后将没有标记的无用对象
进行清理,回收无用对象并释放空间【标记-清除算法】。再将标记的对象往一
端移动,使内存连续,最后进行清理边界以为的无用对象并释放空间【标记-压 缩/整理算法】。
特点:每一次重GC都会伴随一次轻GC,回收时间长。
3.方法区:
3.1、存放static、final、class模板、常量池
3.2、JVM只有一个方法区,所以被所有线程共享,用来存放程序永不变或唯一的内容。
4.程序的运行原理:
5.类加载过程:
Java类会被编译器(javac)经过词法、语法、语义分析等操作,编译成.class(字节码)文件,JVM的字节码验证器对其进行验证(结构、语义、数据流等验证),然后JVM的类加载器将验证好的字节码加载到内存中,经过加载、链接、初始化将其转化成可执行的Java类对象。
①加载:将.class文件加载到内存中,并在方法区中生成一个对应的class对象,作为数
据访问的入口。
②链接:1.验证该文件的正确性;2.给静态变量分配空间和设置默认值;3.将虚拟机常量池
内的符号引用替换成直接引用直接指向内存地址。
③初始化:最后执行类构造器<clinit>()方法,先初始化父类静态成员->子类静态成员->
父类构造方法->父类非静态成员->子类构造方法->子类非静态成员。
当类中的某个方法被第一次调用时,可能有部分字节码会由JVM的即时编译器(JIT Complier)进行分析字节码、代码优化(指令重排),再解释成机器码(一条条指令),接着操作系统和CPU将机器码加载到内存中(值放入对应寄存器/存储器)->CPU从内存中获取指令并从寄存器中拿值进行计算并将计算结果写回寄存器(保留在寄存器,写回存储器或输出到外部设备)。
即时编译器(JIT)主要是针对热点方法的动态优化,将其对应的字节码先编译为本地机器码并缓存起来(下次调用就不需要再编译)以提高下一次的执行速度。主要包括了对方法内联、循环优化、逃逸分析、锁消除和膨胀等技术优化。
JIT对热点方法识别方式:
①基于采样方式探测:周期性对检测各个线程的栈顶,发现某个方法经常出现在栈顶就 会触发JIT。好处是简单,缺点是方法容易被线程阻塞或其 他原因导致无法精确每个方法的热度。
②基于计数器的探测:JVM会为每个方法建立计数器,统计执行的次数,当超过一定阈 值时就会触发JIT。
HotSpot虚拟机中内置了两个JIT编译器:Client Complier(默认C1)和Server Complier(C2),它们在编译代码时采取不同的策略和优化措施,以提高程序的执行性能。目前主流的HotSpot虚拟机中默认是采用解释器与其中一个编译器直接配合的方式工作。
C1:采用轻量级的编译策略,编译快,更注重快速启动和减少初始编译时间,分配堆内存空 间较少,对代码本身需优化较少,适合桌面应用程序或轻量级的服务器应用程序;
C2:采用更加耗时的编译策略,但生成的代码更高效,动态编译优化力度大,包括内联/循环 扩展、逃逸分析等,分配堆内存空间较多(可能浪费)和额外的空间来存储编译结果和优 化信息,例:偏服务器应用程序或对性能要求较高的应用程序;
注:即时编译器分析字节码:包括解析字节码中的操作数和操作符,识别对应的指令和数
据依赖关系等。通过分析字节码,即时编译器可以理解代码
逻辑,并进行后续的优化。
字节码验证器验证字节码:①类型安全;②访问权限;③栈操作;④控制流;⑤异常处理;
6.类加载器的层次结构以及双亲委派机制:
类加载器的层次结构:自定义类加载器->系统类加载器(加载用户类)->扩展类加载器(加载JDK\jre\lib\ext目录中的jar包)->根加载器(加载存放在JDK\jre\lib目录中的核心类库,如rt.jar),一直向上并委托给父类加载,直到根加载器,如果父类加载不到时就让子类去加载该类,直到自定义类加载器,最后还是没有加载到,就抛出异常ClassNotFoundException。
双亲委派机制是在ClassLoader类的loadClass()中实现的,如果我们不想使用双亲委派机制,可以通过自定义类加载器,然后重写loadClass()方法即可。
7.线程上下文类加载器:
为了抛弃双亲委派机制加载链模式,原因某些SPI(JDBC)的接口是java核心类库的一部分,由根加载器来加载的,而SPI实现的java类是由系统类加载器来加载的,这样就导致根加载器无法找到SPI的实现类。所以引入线程上下文类加载器来指定类加载器。
6.GC垃圾回收:
5.1、垃圾回收过程:发现无用对象 ==》回收无用对象占用的内存空间。
5.2、垃圾回收算法:
5.2.1、引用计数法:使用计算器对对象的使用次数进行统计,清除0次的;
缺点:计算器本身的消耗,无法解决对象循环引用的问题。
5.2.2、复制算法:使用计将Eden区和from区存活的对象复制到to区(永远为空),并
清空Eden 区和from区,再将to区标记为from区。
缺点:浪费一半的空间(to区)。
5.2.3、标记-清除:标记所有存活[被引用]对象(第一次扫描),然后将没有标记的无
用对象进行清理(第二次扫描)。
缺点:产生内存碎片。
5.2.4、标记-压缩/整理:标记所有存活[被引用]对象(第一次扫描),再将标记的对象
往一端移动,使得内存连续,最后进行清理边界以为的无
用对象并释放空间(第二次扫描)。
缺点:没有内存碎片,但移动对象需要额外的开销。
5.2.5、引用可达法(根搜索算法):把所有的引用关系看作一张图,从GC root节点
开始出发,标记从根对象可达的所有对象 ,将
没有被标记的对象进行回收。
缺点:①误判:该算法时基于引用关系进行判断的,可能存在某些存活的
对象没有被根对象直接或间接引用,被判为无用对象,然
后被错误的清理回收。
②跨代引用问题:即一个对象被其他代的对象引用,但该对象已经
被标记为无用对象了,由于该算法只考虑从根节
点可达的对象,错误地回收了此对象。
7.分代GC垃圾收集器:
一、年轻代:Serial(默认),ParNew,Parallel Scavenge。
二、老年代:Serial Old(与所有年轻代都兼容);
CMS(排斥年轻代的Parallel Scavenge,默认使用ParNew);
Parallel Old(只和Parallel Scavenge版本搭配)。
三、年轻代和老年代:G1。
①Serial(串行收集器):只用一条回收线程去完成GC工作且在垃圾回收时会暂停其他所有工
作线程(STW),并采用复制算法和标记-压缩/整理进行回收。
优点:简单高效,可以优化内存分配和回收的速度,适用于小型应用,对于性能要
求较高但内存较小的系统来说是一个较好的选择。
②ParNew(并行收集器):减少STW的时间,是Serial的多线程版本。由于存在线程的上下文
的切换,所以在单核cpu下,还要慢于Serial。
③Parallel Scavenge(并行收集器):试图在尽量减少垃圾回收时间,更注重系统吞吐量,它
会根据系统的运行状态动态地调整垃圾回收的参数,
例:1. 垃圾回收的时间比率;2.年轻代大小;3.垃圾回收线
程数;GC算法与Serial一样。 适合那些对响应时间要求
不是特别高、但强调高吞吐量的应用场景。
系统吞吐量 = 代码执行时间 / (代码执行时间+垃圾回收时间)。
④Serial Old(老年代串行收集器):Serial的老年代版本。
⑤Parallel Old(老年代并行收集器):Parallel Scavenge的老年代版本。
⑥CMS:通过并发执行垃圾回收的标记和清除阶段,尽量减少垃圾回收所带来的停顿时间,
以提高应用程序的响应性能;采用并发标记-并发清除算法。
三、网络编程:
1、传输层协议TCP与UDP:
①TCP:面向连接、点到点的通信、基于字节流、高可靠性、占用系统资源多,效率低;
上一层应用层使用TCP协议的有:http、ftp、SMTP等;
②UPD:非面向连接、传输不可靠,数据可能丢失、可广播发送、协议简单,开销小;
上一层应用层使用UDP协议的有:DNS、SNMP(视频)等;
2、socket(套接字)编程:应用层(程序)与传输层(TCP/UDP)之间联系的小口;
3、基于UDP的socket编程:
数据大小不超过60k,每次发送的数据被封装成数据包进行传输,数据包都是字节,所
以不需要利用IO流实现数据传输。【DatagramSocket类、DatagramPacket类】
4、基于TCP的socket编程:一次请求和响应为一个socket
TCP是基于请求-响应模式进行通讯,利用IO流实现数据传输。
5、应用层协议HTTP(浏览器和服务器的交流协议):
6、手写服务器:
①获取请求协议:
②返回响应协议:
③封装响应信息:
④封装请求信息:
⑤处理请求参数:
⑥引入servlet
⑦加入配置文件和反射 :
⑧引入多线程和封装分发器dispatcher:
⑨最后处理错误信息:
四、JDBC:
1、访问数据库流程:
编写程序-->加载jdbc程序-->程序与数据库建立连接(socket连接)-->发送sql语句-->得到 sql语句结果。
2、JDBC加载过程:
加载驱动类-->与数据库建立连接(socket连接[耗时、耗资源])-->获取statement对象[子类 preparedStatement(预编译,效率高、把参数位置用‘?’替代表占位符,防止sql注入)、 callableStatement(继承preparedStatement,用于调用存储过程)]-->开始编写sql语句--> 通过statement对象来执行sql语句-->最后关闭连接、释放资源。
3、事务:ACID原则
开始于写操作-->结束于①提交或回滚;②断开连接;③DCL语句(权限操作);④DML语句执 行失败;⑤DDL语句(创表)。
4、事务原则之隔离性的隔离级别:
①读未提交:可以读到未提交的数据(不加锁实现),存在脏读问题。
②读已提交(RC):只能读到已提交的数据;使用行级别的共享锁,以止其他事务对同一
行数据的修改操作来解决脏读,但仍存在不可重复读和幻读问题。
③可重复读(RR):同一个事务中,多次读取同一数据时结果都一样;通过在第一次读取 数据后,就将这些数据进行加锁(悲观锁),其它事务就无法修改这
些数据,但仍存在幻读问题。
④可串行化:对数据进行加写锁或读锁,其他事务无法对该数据进行访问。
一:脏读:事务a读取了事务b修改的数据,然后事务b进行回滚,此时事务a读的是脏数据。
二:不可重复读:事务a在读取数据并未提交,事务b对该数据进行修改并提交,此时事务a再
次读取该数据时结果却变了。
三:幻读:事务a在读取数据并未提交,事务b插入或删除了一条数据并提交,此时事务a再次
读取该数据时结果却变了。
五、数据结构与算法:
1、逻辑结构:
①线性结构:元素间是一对一关系;
②树状结构:元素间是一对多关系;
③网状结构:元素间是多对对关系;
④集合结构:元素间的关系不确定,具有确定性,唯一性,无序性(hashSet);
2、存储结构:
①顺序存储:一块连续的存储空间,无需额外的存储空间,不适合插入和删除;
②链式存储:不连续的存储空间,靠指针(内存地址)找到下一节点,不适合查找;
③索引存储:需要额外的索引表(所需空间不大)来标识节点的地址(位置);
④散列存储: 根据key算出该节点的存储地址(哈希码),顺序和链式结合,插入和查询快;
3、算法:
①时间复杂度:问题的规模梯度,例:T=n²+n+1;T=10²n²+10²n;T=20²n²;==>T=O(n²)。
O(欧米可荣)符号表示最坏情况下的时间复杂度O(n²);
Ω(欧米伽)符号表示最好情况下的时间复杂度Ω(n²);
Θ(西塔)符号表示O与Ω是一样的T=O(n²)=Ω(n²);
②空间复杂度:问题的所需内存空间;
注:递归算法的时间和空间复杂度最大,但写法简单;