1、Java
Java的优点
Java有哪些特性,举个多态的例子。
abstract interface区别
有抽象方法必定是抽象类吗?抽象类必定有抽象方法吗?
Java的反射机制
super()和this()能不能同时使用
hashcode,equals,Object的这两个方法默认返回什么?描述了一下为何重写equals方法必须重写hashcode方法
final
String,StringBuffer,StringBuilder区别
String为何不可变
String,是否能够继承,“+”怎样实现
字符串常量池
map、list、set的区别
有没有有序的set?
Set如何保证不重复?
说一说对Java io的理解
nio与bio的了解以及说一下区别
Java并发的理解
死锁,死锁缘由
wait和sleep的区别
ArrayList和LinkedList有什么区别?
HashMap 的原理,hashmap的扩容问题,为何HashMap的初始容量会是16,为何是2倍扩容,实现简单的 get/put操做;处理哈希冲突用的哪一种方法(拉链),还知道什么处理哈希冲突的方法(开放地址检测),开放地址检测怎么实现的
从哈希表中删除一个元素,再加入元素时刚好与原来那个哈希冲突,这个元素会放在哪
HashMap、Hashtable、concurrenthashmap
HashTable为何是线程安全的?
HashMap,ConcurrentHashMap以及在什么状况下性能会很差
Thread状态有哪些
多线程实现方法
Java如何实现线程安全
Synchronized和lock区别
Java中都有什么锁
可重入锁的设计思路是什么
乐观锁和悲观锁
juc包内有哪些类
CAS如何实现
BlockQueue见过没?
线程池原理
线程池的排队策略和拒绝策略的试用条件和具体内容。
线程池的类型,详细介绍cached和fixed
corePoolSize参数的意义
线程池新任务到达时会先使用空闲线程仍是加入阻塞队列
Java并发包里面的CountdownLatch怎么使用
volatile和synchronized区别
线程池使用时通常要考虑哪些问题
通常线程和守护线程的区别
一致性Hash原理,实现负载均衡
异常
servlet流程
forward redirect 二次请求
序列化,以及json传输
tomcat均衡方式
netty
2、JVM
JVM内存划分
GC
垃圾回收器
Java对象头
内存泄漏
类加载过程
双亲委派模型,为何要使用双亲委派模型
Java虚拟机的一些参数配置
为何jvm调优常常会将-Xms和-Xmx参数设置成同样
3、数据结构与算法
常见的排序算法时间复杂度
快排算法 写代码
堆排序怎么实现
链表,数组的优缺点,应用场景,查找元素的复杂度
入栈出栈的时间复杂度,链表插入和删除的时间复杂度
如何用LinkedList实现堆栈操做
Arraylist如何实现排序
利用数组,实现一个循环队列类
两个有序数组,有相同的元素,找出来
二叉树怎么实现的
二叉树前中后序遍历 深度 广度
二叉树深度
层序遍历二叉树
树的中序遍历,除了递归和栈还有什么实现方式
二叉搜索树转换成一个排好序的双向链表
判断平衡二叉树
给定一个2叉树,打印每一层最右边的结点
一棵普通树(非二叉搜索树),找出一条路径和最大
一棵树,求全部路径之和
最长公共子序列
反转链表
判断一个数是否是丑数
找出一个字符串中字符连续相同的最长子串,如aabbccc,结果就是ccc
蓄水池抽样算法
寻找一个字符串中第一个只出现一次的字符
给定一个数组,里面只有一个数出现了一次,其余都出现了两次。怎么获得这个出现了一次的数?
给定一个数组,若是有两个不一样数的出现了一次,其余出现了两次,怎么获得这两个数?
海量数据topk问题
4、操做系统
进程和线程区别
不一样进程打开了同一个文件,那么这两个进程获得的文件描述符(fd)相同吗?
操做系统如何实现输出
进程通讯
5、网络
OSI七层网络模型中,你对哪层最了解?了解哪些协议?作过web开发?
HTTP 0.9/1.0/1.1/2
HTTP 和 HTTPS 有什么区别?
知道 HTTPS 通讯过程吗?
TCP三次握手
为何三次握手和四次挥手
TCP与HTTP有什么关系
Tcp链接4次挥手的缘由。Time_wait等待超时了会怎样?
SSL 握手
session/cookie
当你在浏览器地址栏输入一个URL后回车,将会发生的事情?
DNS域名解析过程
ping工做原理
Get和Post请求
HTTP状态码
6、数据库
数据库事务的四个隔离级别,MySql在哪个级别
数据库死锁/如何防止
mysql索引,索引机制,汇集索引和非汇集索引,如何建立索引,实现原理,创建准则,优缺点,注意事项,
索引在什么状况下失效
说一下对B+树的了解
innodb创建的索引,若是字段重复率很高索引,索引是什么样,查找效率如何
innodb在插入的时候,是否会给行上锁
说一下innodb的默认隔离级别
数据库设计(订单、购物车和商品)
sql中join的几种操做的区别
union和union all的区别,谁的效率更高
用distinct和用group by去重,谁的效率更高
sql中的优化,怎么提升查询效率
缓存的穿透和雪崩,解决办法
redis的排序算法
redis集群
redis过时策略
Redis如何解决key冲突
redis数据类型+redis是单线程的么,为何呢
redis和memcache区别
redis与mysql的区别以及优缺点
7、设计模式
单例模式里面的双重检查锁定的原理,以及为何使用volatile
生产者消费者
工厂,说下原理和应用
策略模式
适配器模式
装饰模式
代理模式
线程池使用了什么设计模式
JDK中哪些体现了命令模式
8、框架
介绍下SpringBoot
Spring IOC AOP
Spring IOC有哪些好处
IOC涉及到的设计模式
AOP的应用场景,具体介绍,配置文件中须要写什么?具体注解须要写啥?
说说静态代理和动态代理
Spring事务传播,隔离级别
Spring bean初始化过程
Spring如何生成一个Bean?配置文件写完了以后又怎么生成?
Mybatis 传参
Mybatis中 # 和 $ 区别
Mybatis缓存
SpringMVC的运行流程
说几个SpringMVC的几个注解,都是干啥的?
@autireware和@resource的区别
@PathVariable是干啥的?
说说filter、servlet、listener。
消息队列了解吗?
9、分布式
Raft协议的leader选举,正常状况下,网络抖动形成follower发起leader选举,且该follower的Term比现有leader高。集群中全部结点的日志信息当前一致,这种状况下会选举成功吗?
分布式框架知道哪些?
dubbo怎么用的,有没有参与部署
分布式缓存的理解
10、Linux
linux查询Java进程
linux查看内存占用状况
11、杂项
设计一个秒杀系统,如何保证不超卖,还要保证服务可用
如何设计一个定时器定时完成某个任务?
如何保证集群环境下抢购的并发安全?
Java中你擅长的地方
若是学习一门技术,你会怎么学习
对国内互联网公司目前的开源生态有没有什么了解
你对京东的见解
说出三个京东不如淘宝或者天猫的地方
看过啥书。css
平台无关性、垃圾回收html
封装、继承、多态java
含有abstract修饰符的class即为抽象类,abstract类不能建立的实例对象。含有abstract方法的类必须定义为abstract class,abstract class类中的方法没必要是抽象的。abstract class类中定义抽象方法必须在具体(Concrete)子类中实现,因此,不能有抽象构造方法或抽象静态方法。若是的子类没有实现抽象父类中的全部抽象方法,那么子类也必须定义为abstract类型。node
接口(interface)能够说成是抽象类的一种特例,接口中的全部方法都必须是抽象的。接口中的方法定义默认为public abstract类型,接口中的成员变量类型默认为public static final。mysql
下面比较一下二者的语法区别:linux
抽象类能够有构造方法,接口中不能有构造方法。
抽象类中能够有普通成员变量,接口中没有普通成员变量
抽象类中能够包含非抽象的普通方法,接口中的能够有非抽象方法,好比deaflut方法
抽象类中的抽象方法的访问类型能够是public,protected和(默认类型,虽然
eclipse下不报错,但应该也不行),但接口中的抽象方法只能是public类型的,而且默认即为public abstract类型。
抽象类中能够包含静态方法,接口中不能包含静态方法
抽象类和接口中均可以包含静态成员变量,抽象类中的静态成员变量的访问类型能够任意,但接口中定义的变量只能是public static final类型,而且默认即为public static final类型。
一个类能够实现多个接口,但只能继承一个抽象类。web
有抽象方法不必定是抽象类,也多是接口。抽象类不必定有抽象方法,能够有非抽象的普通方法。redis
在运行状态中,对于任意一个类,都可以知道这个类的全部属性和方法;对于任意一个对象,都可以调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。算法
反射的核心是JVM在运行时才动态加载类或调用方法/访问属性,它不须要事先知道运行对象是谁。spring
不能同时使用,this和super不能同时出如今一个构造函数里面,由于this必然会调用其它的构造函数,其它的构造函数必然也会有super语句的存在,因此在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会经过。
默认的hashCode方法会利用对象的地址来计算hashcode值,不一样对象的hashcode值是不同的。
public boolean equals(Object obj) {
return (this == obj);
}
能够看出Object类中的equals方法与“==”是等价的,也就是说判断对象的地址是否相等。Object类中的equals方法进行的是基于内存地址的比较。
通常对于存放到Set集合或者Map中键值对的元素,须要按须要重写hashCode与equals方法,以保证惟一性。
final关键字能够用于成员变量、本地变量、方法以及类。
final成员变量必须在声明的时候初始化或者在构造器中初始化,不然就会报编译错误。
你不可以对final变量再次赋值。
本地变量必须在声明时赋值。
在匿名类中全部变量都必须是final变量。
final方法不能被重写。
final类不能被继承。
接口中声明的全部变量自己是final的。
final和abstract这两个关键字是反相关的,final类就不多是abstract的。
final方法在编译阶段绑定,称为静态绑定(static binding)。
没有在声明时初始化final变量的称为空白final变量(blank final variable),它们必须在构造器中初始化,或者调用this()初始化。不这么作的话,编译器会报错“final变量(变量名)须要进行初始化”。
将类、方法、变量声明为final可以提升性能,这样JVM就有机会进行估计,而后优化。
按照Java代码惯例,final变量就是常量,并且一般常量名要大写。
String内容不可变,StringBuffer和StringBuilder内容可变;
StringBuilder非线程安全(单线程使用),String与StringBuffer线程安全(多线程使用);
若是程序不是多线程的,那么使用StringBuilder效率高于StringBuffer。
public final class String
implements java.io.Serializable, Comparable
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */ private int hash; // Default to 0</string>
String 的底层实现是依靠 char[] 数组,既然依靠的是基础类型变量,那么他必定是可变的, String 之因此不可变,是由于 Java 的开发者经过技术实现,隔绝了使用者对 String 的底层数据的操做。
String不能够继承,由于String被final修饰,而final修饰的类是不能被继承的。
String为不可变的,每次String对象作累加时都会建立StringBuilder对象。
// 程序编译期即加载完成对象s1为"ab"
String s1 = "a" + "b";
// 这种方式,JVM会先建立一个StringBuilder,而后经过其append方法完成累加操做
String s1 = "a";
String s2 = "b";
String s3 = s1 + s2; // 等效于 String s3 = (new StringBuilder(s1)).append(s2).toString();
List:
能够容许重复的对象。
能够插入多个null元素。
是一个有序容器,保持了每一个元素的插入顺序,输出的顺序就是插入的顺序。
经常使用的实现类有 ArrayList、LinkedList 和 Vector。ArrayList 最为流行,它提供了使用索引的随意访问,而 LinkedList 则对于常常须要从 List中添加或删除元素的场合更为合适。
Set:
不容许重复对象
无序容器,你没法保证每一个元素的存储顺序,TreeSet经过 Comparator 或者 Comparable 维护了一个排序顺序。
只容许一个 null 元素
Set 接口最流行的几个实现类是 HashSet、LinkedHashSet 以及 TreeSet。最流行的是基于 HashMap 实现的 HashSet;TreeSet 还实现了 SortedSet 接口,所以 TreeSet 是一个根据其 compare() 和 compareTo() 的定义进行排序的有序容器。
Map:
Map不是collection的子接口或者实现类。Map是一个接口。
Map 的 每一个 Entry 都持有两个对象,也就是一个键一个值,Map 可能会持有相同的值对象但键对象必须是惟一的。
TreeMap 也经过 Comparator 或者 Comparable 维护了一个排序顺序。
Map 里你能够拥有随意个 null 值但最多只能有一个 null 键。
Map 接口最流行的几个实现类是 HashMap、LinkedHashMap、Hashtable 和 TreeMap。(HashMap、TreeMap最经常使用)
有,LinkedHashSet和TreeSet
HashSet中add()中调用了HashMap的put(),将一个key-value对放入HashMap中时,首先根据key的hashCode()返回值决定该Entry的存储位置,若是两个key的hash值相同,那么它们的存储位置相同。若是这个两个key的equals比较返回true。那么新添加的Entry的value会覆盖原来的Entry的value,key不会覆盖。所以,若是向HashSet中添加一个已经存在的元素,新添加的集合元素不会覆盖原来已有的集合元素。
IO,其实意味着:数据不停地搬入搬出缓冲区而已(使用了缓冲区)。
BIO:同步阻塞式IO,服务器实现模式为一个链接一个线程,即客户端有链接请求时服务器端就须要启动一个线程进行处理,若是这个链接不作任何事情会形成没必要要的线程开销,固然能够经过线程池机制改善。
NIO:同步非阻塞式IO,服务器实现模式为一个请求一个线程,即客户端发送的链接请求都会注册到多路复用器上,多路复用器轮询到链接有I/O请求时才启动一个线程进行处理。
Java是一种多线程编程语言,咱们可使用Java来开发多线程程序。 多线程程序包含两个或多个可同时运行的部分,每一个部分能够同时处理不一样的任务,从而能更好地利用可用资源,特别是当您的计算机有多个CPU时。多线程使您可以写入多个活动,能够在同一程序中同时进行操做处理。
两个或者多个线程之间相互等待,致使线程都没法执行,叫作线程死锁。
互斥条件:使用的资源是不能共享的。
不可抢占条件:线程持有一个资源并等待获取一个被其余线程持有的资源。
请求与保持条件:线程持有一个资源并等待获取一个被其余线程持有的资源。
循环等待条件:线程之间造成一种首尾相连的等待资源的关系。
wait和notify方法定义在Object类中,所以会被全部的类所继承。 这些方法都是final的,即它们都是不能被重写的,不能经过子类覆写去改变它们的行为。 而sleep方法是在Thread类中是由native修饰的,本地方法。
当线程调用了wait()方法时,它会释放掉对象的锁。
另外一个会致使线程暂停的方法:Thread.sleep(),它会致使线程睡眠指定的毫秒数,但线程在睡眠的过程当中是不会释放掉对象的锁的。
由于wait方法会释放锁,因此调用该方法时,当前的线程必须拥有当前对象的monitor,也即lock,就是锁。要确保调用wait()方法的时候拥有锁,即wait()方法的调用必须放在synchronized方法或synchronized块中。
ArrayList是实现了基于动态数组的数据结构,LinkedList基于双向链表的数据结构。
对于随机访问get和set,ArrayList优于LinkedList,由于LinkedList要移动指针。
对于新增和删除操做add和remove,LinedList比较占优点,由于ArrayList要移动数据。
synchronized锁住了
新建、就绪、运行、阻塞、死亡
继承Thread类建立线程类,重写run方法,run方法就是表明线程须要完成的任务,调用线程对象的start()来启动该线程,线程类已经继承了Thread类,因此不能再继承其余父类。
实现Runnable接口建立线程类,定义Runnable实现类,重写run方法
实现Callable接口,重写call()方法,call()做为线程的执行体,具备返回值
线程池,使用线程池产生线程对象java.util.concurrent.ExecutorService、java.util.concurrent.Executors
互斥同步:推荐使用 synchronized 关键字进行同步, 在 concurrent包中有ReentrantLock类, 实现效果差很少. 仍是推荐原生态的synchronized.
非阻塞同步:须要硬件指令完成.经常使用的指令有:
Test-and-Set
Fetch-and-Increment
Swap
Compare-and-Swap (CAS)
Load-Linked/Store-Conditional (LL/SC)
典型的应用在 AtomicInteger 中
无同步方案:将变量保存在本地线程中,就不会出现多个线程并发的错误了。
java中主要使用的就是ThreadLocal这个类。
Lock提供了synchronized关键字所不具有的主要特性有:
尝试非阻塞地获取锁boolean tryLock():当前线程尝试获取锁,若是这一时刻没有被其余线程获取到,则成功获取并持有锁
能被中断地获取锁void lockInterruptibly():当获取到锁的线程被中断时,中断异常抛出同时会释放锁
超时获取锁boolean trylock(long time, TimeUnit unit):在指定截止时间以前获取锁,若是在截止时间仍旧没法获取锁,则返回
synchronized是JVM提供的加锁,悲观锁;lock是Java语言实现的,并且是乐观锁。
ReentrantLock是基于AQS实现的,因为AQS是基于FIFO队列的实现
重量级锁、显式锁、并发容器、并发同步器、CAS、volatile、AQS等
可重入公平锁获取流程
在获取锁的时候,若是当前线程以前已经获取到了锁,就会把state加1,在释放锁的时候会先减1,这样就保证了同一个锁能够被同一个线程获取屡次,而不会出现死锁的状况。这就是ReentrantLock的可重入性。
对于非公平锁而言,调用lock方法后,会先尝试抢占锁,在各类判断的时候会先忽略等待队列,若是锁可用,就会直接抢占使用。
悲观锁:假定会发生并发冲突,则屏蔽一切可能违反数据完整性的操做
乐观锁:假定不会发生并发冲突,只在数据提交时检查是否违反了数据完整性(不能解决脏读问题)
CountDownLatch 同步计数器,主要用于线程间的控制,但计数没法被重置,若是须要重置计数,请考虑使用 CyclicBarrier 。
(线程池的排队策略)
核心线程数
核心线程会一直存活,即便没有任务须要执行
当线程数小于核心线程数时,即便有线程空闲,线程池也会优先建立新线程处理
设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭
这个类是一个同步计数器,主要用于线程间的控制,当CountDownLatch的count计数>0时,await()会形成阻塞,直到count变为0,await()结束阻塞,使用countDown()会让count减1。CountDownLatch的构造函数能够设置count值,当count=1时,它的做用相似于wait()和notify()的做用。若是我想让其余线程执行完指定程序,其余全部程序都执行结束后我再执行,这时能够用CountDownLatch,但计数没法被重置,若是须要重置计数,请考虑使用 CyclicBarrier 。
volatile是变量修饰符,其修饰的变量具备可见性,Java的作法是将该变量的操做放在寄存器或者CPU缓存上进行,以后才会同步到主存,使用volatile修饰符的变量是直接读写主存,volatile不保证原子性,同时volatile禁止指令重排
synchronized做用于一段代码或者方法,保证可见性,又保证原子性,可见性是synchronized或者Lock能保证通一个时刻只有一个线程获取锁而后执行不一样代码,而且在释放锁以前会对变量的修改刷新到主存中去,原子性是指要么不执行,要执行就执行到底
java中的线程分为两种:守护线程(Daemon)和用户线程(User)。
任何线程均可以设置为守护线程和用户线程,经过方法Thread.setDaemon(bool on);true则把该线程设置为守护线程,反之则为用户线程。Thread.setDaemon()必须在Thread.start()以前调用,不然运行时会抛出异常。
惟一的区别是判断虚拟机(JVM)什么时候离开,Daemon是为其余线程提供服务,若是所有的User Thread已经撤离,Daemon 没有可服务的线程,JVM撤离。也能够理解为守护线程是JVM自动建立的线程(但不必定),用户线程是程序建立的线程;好比JVM的垃圾回收线程是一个守护线程,当全部线程已经撤离,再也不产生垃圾,守护线程天然就没事可干了,当垃圾回收线程是Java虚拟机上仅剩的线程时,Java虚拟机会自动离开。
JVM内存模型
程序计数器:记录正在执行的虚拟机字节码指令的地址(若是正在执行的是本地方法则为空)。
Java虚拟机栈:每一个 Java 方法在执行的同时会建立一个栈帧用于存储局部变量表、操做数栈、常量池引用等信息。每个方法从调用直至执行完成的过程,就对应着一个栈帧在 Java 虚拟机栈中入栈和出栈的过程。
本地方法栈:与 Java 虚拟机栈相似,它们之间的区别只不过是本地方法栈为本地方法服务。
Java堆:几乎全部对象实例都在这里分配内存。是垃圾收集的主要区域("GC 堆"),虚拟机把 Java 堆分红如下三块:
新生代
老年代
永久代
新生代又可细分为Eden空间、From Survivor空间、To Survivor空间,默认比例为8:1:1。
方法区:方法区(Method Area)与Java堆同样,是各个线程共享的内存区域。Object Class Data(类定义数据)是存储在方法区的,此外,常量、静态变量、JIT编译后的代码也存储在方法区。
运行时常量池:运行时常量池是方法区的一部分。Class 文件中的常量池(编译器生成的各类字面量和符号引用)会在类加载后被放入这个区域。除了在编译期生成的常量,还容许动态生成,例如 String 类的 intern()。这部分常量也会被放入运行时常量池。
直接内存:直接内存(Direct Memory)并非虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,可是这部份内存也被频繁地使用,并且也可能致使OutOfMemoryError 异常出现。避免在Java堆和Native堆中来回复制数据。
垃圾回收算法包括:标记-清除算法,复制算法,标记-整理算法,分代收集算法。
标记—清除算法:
标记/清除算法,分为“标记”和“清除”两个阶段:首先标记出全部须要回收的对象,在标记完成后统一回收全部被标记的对象。
标记阶段:标记的过程其实就是前面介绍的可达性分析算法的过程,遍历全部的GC Roots对象,对从GC Roots对象可达的对象都打上一个标识,通常是在对象的header中,将其记录为可达对象;
清除阶段:清除的过程是对堆内存进行遍历,若是发现某个对象没有被标记为可达对象,则将其回收。
标记-清除
复制算法:
将内存划分为大小相等的两块,每次只使用其中一块,当这一块内存用完了就将还存活的对象复制到另外一块上面,而后再把使用过的内存空间进行一次清理。
将内存分为一块较大的 Eden 空间和两块较小的 Survior 空间,每次使用 Eden 空间和其中一块 Survivor。在回收时,将 Eden 和 Survivor 中还存活着的对象一次性复制到另外一块 Survivor 空间上,最后清理 Eden 和 使用过的那一块 Survivor。HotSpot 虚拟机的 Eden 和 Survivor 的大小比例默认为 8:1,保证了内存的利用率达到 90 %。若是每次回收有多于 10% 的对象存活,那么一块 Survivor 空间就不够用了,此时须要依赖于老年代进行分配担保,也就是借用老年代的空间。
复制
标记—整理算法:
标记—整理算法和标记—清除算法同样,可是标记—整理算法不是把存活对象复制到另外一块内存,而是把存活对象往内存的一端移动,而后直接回收边界之外的内存,所以其不会产生内存碎片。标记—整理算法提升了内存的利用率,而且它适合在收集对象存活时间较长的老年代。
标记—整理
分代收集算法:
分代回收算法其实是把复制算法和标记整理法的结合,并非真正一个新的算法,通常分为:老年代和新生代,老年代就是不多垃圾须要进行回收的,新生代就是有不少的内存空间须要回收,因此不一样代就采用不一样的回收算法,以此来达到高效的回收算法。
新生代:因为新生代产生不少临时对象,大量对象须要进行回收,因此采用复制算法是最高效的。
老年代:回收的对象不多,都是通过几回标记后都不是可回收的状态转移到老年代的,因此仅有少许对象须要回收,故采用标记清除或者标记整理算法。
HotSpot虚拟机中,对象在内存中的布局分为三块区域:对象头、实例数据和对齐填充。
对象头包括两部分:Mark Word 和 类型指针。
Mark Word:Mark Word用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等,占用内存大小与虚拟机位长一致。
类型指针:类型指针指向对象的类元数据,虚拟机经过这个指针肯定该对象是哪一个类的实例。
类加载的过程主要分为三个部分:
加载:指的是把class字节码文件从各个来源经过类加载器装载入内存中。
连接
初始化:对类变量初始化,是执行类构造器的过程。
连接又能够细分为
验证:为了保证加载进来的字节流符合虚拟机规范,不会形成安全错误。
准备:为类变量(注意,不是实例变量)分配内存,而且赋予初值。
解析:将常量池内的符号引用替换为直接引用的过程。
/** * 快速排序 * * @param array * @param _left * @param _right */ private static void quickSort(int[] array, int _left, int _right) { int left = _left;// int right = _right; int pivot;//基准线 if (left < right) { pivot = array[left]; while (left != right) { //从右往左找到比基准线小的数 while (left < right && pivot <= array[right]) { right--; } //将右边比基准线小的数换到左边 array[left] = array[right]; //从左往右找到比基准线大的数 while (left < right && pivot >= array[left]) { left++; } //将左边比基准线大的数换到右边 array[right] = array[left]; } //此时left和right指向同一位置 array[left] = pivot; quickSort(array, _left, left - 1); quickSort(array, left + 1, _right); } }
public class HeapSort {
/**
* 构建大顶堆
/
public static void adjustHeap(int[] a, int i, int len) {
int temp, j;
temp = a[i];
for (j = 2 i; j < len; j *= 2) {// 沿关键字较大的孩子结点向下筛选
if (j < len && a[j] < a[j + 1])
++j; // j为关键字中较大记录的下标
if (temp >= a[j])
break;
a[i] = a[j];
i = j;
}
a[i] = temp;
}
public static void heapSort(int[] a) { int i; for (i = a.length / 2 - 1; i >= 0; i--) {// 构建一个大顶堆 adjustHeap(a, i, a.length - 1); } for (i = a.length - 1; i >= 0; i--) {// 将堆顶记录和当前未经排序子序列的最后一个记录交换 int temp = a[0]; a[0] = a[i]; a[i] = temp; adjustHeap(a, 0, i - 1);// 将a中前i-1个记录从新调整为大顶堆 } }
}
递归
public int TreeDepth(TreeNode root) { if (root == null) { return 0; } return Math.max(TreeDepth(root.left) + 1, TreeDepth(root.right) + 1); }
非递归,层次遍历
public int TreeDepth_2(TreeNode root) { if (root == null) { return 0; } Queue<TreeNode> queue = new LinkedList<>(); queue.offer(root); int start = 0; int end = 1; int depth = 0; while (!queue.isEmpty()) { TreeNode temp = queue.poll(); start++; if (temp.left != null) { queue.offer(temp.left); } if (temp.right != null) { queue.offer(temp.right); } if (start == end) { start = 0; end = queue.size(); depth++; } } return depth; }
思路:
访问根节点,并将根节点入队。
当队列不空的时候,重复如下操做。
一、弹出一个元素。做为当前的根节点。
二、若是根节点有左孩子,访问左孩子,并将左孩子入队。
三、若是根节点有右孩子,访问右孩子,并将右孩子入队。
public void levelOrder(TreeNode root) {
//使用队列,先进先出
Queue
queue.add(root);
while (!queue.isEmpty()) {
TreeNode temp = queue.poll();
System.out.print(temp.val + " ");
if (temp.left != null) {
queue.offer(temp.left);
}
if (temp.right != null) {
queue.offer(temp.right);
}
}
}
从下往上遍历,若是子树是平衡二叉树,则返回子树高度,不然返回-1
public boolean IsBalanced_Solution(TreeNode root) { return MaxDepth(root) != -1; } public int MaxDepth(TreeNode root) { if (root == null) { return 0; } int leftHeight = MaxDepth(root.left); if (leftHeight == -1) { return -1; } int rightHeight = MaxDepth(root.right); if (rightHeight == -1) { return -1; } return Math.abs(leftHeight - rightHeight) > 1 ? -1 : 1 + Math.max(leftHeight, rightHeight); }
将当前节点和下一节点保存起来,而后将当前节点反转。
public ListNode ReverseList(ListNode head) { //head为当前节点,若是当前节点为空的话,那就什么也不作,直接返回null ListNode pre = null;//pre为当前节点的前一节点 ListNode next = null;//next为当前节点的下一节点 //须要pre和next的目的是让当前节点从pre.head.next1.next2变成pre<-head next1.next2 //即pre让节点能够反转所指方向,但反转以后若是不用next节点保存next1节点的话,此单链表就此断开了 //因此须要用到pre和next两个节点 //1.2.3.4.5 //1<-2<-3 4.5 //作循环,若是当前节点不为空的话,始终执行此循环,此循环的目的就是让当前节点从指向next到指向pre while (head != null) { //先用next保存head的下一个节点的信息,保证单链表不会由于失去head节点的原next节点而就此断裂 next = head.next; //保存完next,就可让head从指向next变成指向pre了 head.next = pre; //head指向pre后,就继续依次反转下一个节点 //让pre,head,next依次向后移动一个节点,继续下一次的指针反转 pre = head; head = next; } //若是head为null的时候,pre就为最后一个节点了,可是链表已经反转完毕,pre就是反转后链表的第一个节点 //直接输出pre就是咱们想要获得的反转后的链表 return pre; }
利用递归走到链表的末端,而后再更新每个节点的next值 ,实现链表的反转。
public ListNode ReverseList(ListNode head) { //若是链表为空或者链表中只有一个元素 if (head == null || head.next == null) return head; //先递归找到到链表的末端结点,从后依次反转整个链表 ListNode reverseHead = ReverseList(head.next); //再将当前节点设置为后面节点的后续节点 head.next.next = head; head.next = null; return reverseHead; }
用LinkedHashMap记录字符出现的次数
public Character firstNotRepeating(String str){ if(str == null) return null; char[] strChar = str.toCharArray(); LinkedHashMap<Character,Integer> hash = new LinkedHashMap<Character,Integer>(); for(char item:strChar){ if(hash.containsKey(item)) hash.put(item, hash.get(item)+1); else hash.put(item, 1); } for(char key:hash.keySet()) { if(hash.get(key)== 1) return key; } return null; }
利用HashSet的元素不能重复,若是有重复的元素,则删除重复元素,若是没有则添加,最后剩下的就是只出现一次的元素
public void FindNumsAppearOnce(int[] array, int num[]) { HashSet<Integer> set = new HashSet<>(); for (int i = 0; i < array.length; i++) { if (!set.add(array[i])) { set.remove(array[i]); } } Iterator<Integer> iterator = set.iterator(); num[0] = iterator.next(); }
用HashMap<K,V>保存数组的值,key为数组值,value为布尔型表示是否有重复
public void FindNumsAppearOnce_2(int[] array, int num[]) { HashMap<Integer, Boolean> map = new HashMap<>(); for (int i = 0; i < array.length; i++) { if (!map.containsKey(array[i])) { map.put(array[i], true); } else { map.put(array[i], false); } } for (int i = 0; i < array.length; i++) { if (map.get(array[i])) { num[0] = array[i]; } } }
利用HashSet的元素不能重复,若是有重复的元素,则删除重复元素,若是没有则添加,最后剩下的就是只出现一次的元素
public void FindNumsAppearOnce(int[] array, int num1[], int num2[]) { HashSet<Integer> set = new HashSet<>(); for (int i = 0; i < array.length; i++) { if (!set.add(array[i])) { set.remove(array[i]); } } Iterator<Integer> iterator = set.iterator(); num1[0] = iterator.next(); num2[0] = iterator.next(); }
用HashMap<K,V>保存数组的值,key为数组值,value为布尔型表示是否有重复
public void FindNumsAppearOnce_2(int[] array, int num1[], int num2[]) { HashMap<Integer, Boolean> map = new HashMap<>(); for (int i = 0; i < array.length; i++) { if (!map.containsKey(array[i])) { map.put(array[i], true); } else { map.put(array[i], false); } } int index = 0;//区分是第几个不重复的值 for (int i = 0; i < array.length; i++) { if (map.get(array[i])) { index++; if (index == 1) { num1[0] = array[i]; } else { num2[0] = array[i]; } } } }
位运算 异或,两个不相等的元素在位级表示上一定会有一位存在不一样。
public void FindNumsAppearOnce_3(int[] array, int num1[], int num2[]) { int diff = 0; for (int num : array) diff ^= num; // 获得最右一位 diff &= -diff; for (int num : array) { if ((num & diff) == 0) num1[0] ^= num; else num2[0] ^= num; } }
进程:进程是操做系统资源分配的基本单位。每一个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1–n个线程。
线程:线程是CPU独立调度的基本单位。同一类线程共享代码和数据空间,每一个线程有独立的运行栈和程序计数器(PC),线程切换开销小。
线程和进程的生命周期:新建、就绪、运行、阻塞、死亡
不一样进程打开同一个文件,文件描述符可能相同可能不一样。
消息传递
管道
消息队列
套接字
共享内存
image
OSI七层网络模型 对应网络协议
应用层 HTTP、TFTP、FTP、NFS、WAIS、SMTP
表示层 Telnet、Rlogin、SNMP、Gopher
会话层 SMTP、DNS
传输层 TCP、UDP
网络层 IP、ICMP、ARP、RARP、AKP、UUCP
数据链路层 FDDI、Ethernet、Arpanet、PDN、SLIP、PPP
物理层 IEEE 802.1A、IEEE 802.2到IEEE 802.11
HTTP/0.9只支持客户端发送Get请求,且不支持请求头。HTTP具备典型的无状态性。
HTTP/1.0在HTTP/0.9的基础上支持客户端发送POST、HEAD。HTTP 1.0须要使用keep-alive参数来告知服务器端要创建一个长链接,但默认是短链接。
HTTP(Hypertext Transfer Protocol)超文本传输协议是用来在Internet上传送超文本的传送协议,它可使浏览器更加高效,使网络传输减小。但HTTP协议采用明文传输信息,存在信息窃听、信息篡改和信息劫持的风险。
HTTPS(Secure Hypertext Transfer Protocol) 安全超文本传输协议是一个安全的通讯通道,它基于HTTP开发,用于在客户计算机和服务器之间交换信息。HTTPS使用安全套接字层(SSL)进行信息交换,简单来讲HTTPS是HTTP的安全版,是使用TLS/SSL加密的HTTP协议。
HTTPS和HTTP的区别主要以下:
https协议须要到ca申请证书,通常免费证书较少,于是须要必定费用。
http是超文本传输协议,信息是明文传输,https则是具备安全性的ssl加密传输协议。
http和https使用的是彻底不一样的链接方式,用的端口也不同,前者是80,后者是443。
http的链接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全.
客户端发送请求到服务器端
服务器端返回证书和公开密钥,公开密钥做为证书的一部分而存在
客户端验证证书和公开密钥的有效性,若是有效,则生成共享密钥并使用公开密钥加密发送到服务器端
服务器端使用私有密钥解密数据,并使用收到的共享密钥加密数据,发送到客户端
客户端使用共享密钥解密数据
SSL加密创建
所谓三次握手(Three-Way Handshake)即创建TCP链接,就是指创建一个TCP链接时,须要客户端和服务端总共发送3个包以确认链接的创建。整个流程以下图所示:
TCP三次握手
第一次握手:Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。
第二次握手:Server收到数据包后由标志位SYN=1知道Client请求创建链接,Server将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认链接请求,Server进入SYN_RCVD状态。
第三次握手:Client收到确认后,检查ack是否为J+1,ACK是否为1,若是正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,若是正确则链接创建成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间能够开始传输数据了。
Server在LISTEN状态下,收到创建链接请求的SYN报文后,能够直接把ACK和SYN放在一个报文里发送给Client。而关闭链接时,当收到对方的FIN报文时,仅仅表示对方再也不发送数据了可是还能接收数据,己方也未必所有数据都发送给对方了,因此己方能够当即close,也能够发送一些数据给对方后,再发送FIN报文给对方来表示赞成如今关闭链接,所以,己方ACK和FIN通常都会分开发送。
http是要基于TCP链接基础上的,简单的说,TCP就是单纯创建链接,不涉及任何咱们须要请求的实际数据,简单的传输。http是用来收发数据,即实际应用上的。
Server在LISTEN状态下,收到创建链接请求的SYN报文后,能够直接把ACK和SYN放在一个报文里发送给Client。而关闭链接时,当收到对方的FIN报文时,仅仅表示对方再也不发送数据了可是还能接收数据,己方也未必所有数据都发送给对方了,因此己方能够当即close,也能够发送一些数据给对方后,再发送FIN报文给对方来表示赞成如今关闭链接,所以,己方ACK和FIN通常都会分开发送。
客户端发送随机数1,支持的加密方法(如RSA公钥加密)
服务端发送随机数2,和服务器公钥,并确认加密方法
客户端发送用服务器公钥加密的随机数3
服务器用私钥解密这个随机数3,用加密方法计算生成对称加密的密钥给客户端,
接下来的报文都用双方协定好的加密方法和密钥,进行加密
经常使用的会话跟踪技术是Cookie与Session。Cookie经过在客户端记录信息肯定用户身份,Session经过在服务器端记录信息肯定用户身份。
联系:
Cookie与Session都是用来跟踪浏览器用户身份的会话方式。
区别:
Cookie数据存放在客户的浏览器上,Session数据放在服务器上。
Cookie不是很安全,别人能够分析存放在本地的Cookie并进行Cookie欺骗,若是主要考虑到安全应当使用加密的Cookie或者Session。
Session会在必定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,若是主要考虑到减轻服务器性能方面,应当使用Cookie。
单个Cookie在客户端的限制是4K,不少浏览器都限制一个站点最多保存20个Cookie。
域名解析 --> 发起TCP的3次握手 --> 创建TCP链接后发起http请求 --> 服务器响应http请求,浏览器获得html代码 --> 浏览器解析html代码,并请求html代码中的资源(如js、css、图片等) --> 浏览器对页面进行渲染呈现给用户
浏览器缓存 --> 系统缓存 --> 路由器缓存 --> ISP(互联网服务提供商)DNS缓存 --> 根域名服务器 --> 顶级域名服务器 --> 主域名服务器 --> 保存结果至缓存
Ping程序的实质是利用了ICMP请求回显和回显应答报文,但ARP请求和应答报文也在其中起了很是重要的做用。
GET 请求:
GET 请求可被缓存
GET 请求保留在浏览器历史记录中
GET 请求可被收藏为书签
GET 请求不该在处理敏感数据时使用
GET 请求有长度限制
GET 请求只应当用于取回数据
POST 请求 :
POST 请求不会被缓存
POST 请求不会保留在浏览器历史记录中
POST 不能被收藏为书签
POST 请求对数据长度没有要求
1XX 信息,服务器收到请求,须要请求者继续执行操做
2XX 成功,操做被成功接收并处理
3XX 重定向,须要进一步的操做以完成请求
4XX 客户端错误,请求包含语法错误或没法完成请求
5XX 服务器错误,服务器在处理请求的过程当中发生了错误
未提交读(READ UNCOMMITTED):事务中的修改,即便没有提交,对其它事务也是可见的。最低级别,任何状况都没法保证。
提交读(READ COMMITTED):一个事务只能读取已经提交的事务所作的修改。换句话说,一个事务所作的修改在提交以前对其它事务是不可见的。可避免脏读的发生。
可重复读(REPEATABLE READ):保证在同一个事务中屡次读取一样数据的结果是同样的。可避免脏读、不可重复读的发生。
可串行化(SERIALIXABLE):强制事务串行执行。可避免脏读、不可重复读、幻读的发生。
在MySQL数据库中,支持上面四种隔离级别,默认的为REPEATABLE READ(可重复读)。
left join / inner join / right join
惰性删除+按期删除
惰性删除
在进行get或set等操做时,先检查key是否过时,
若过时,删除key,而后执行相应操做;
若没过时,直接执行相应操做
按期删除
遍历每一个数据库(就是redis.conf中配置的"database"数量,默认为16)
检查当前库中的指定个数个key(默认是每一个库检查20个key,注意至关于该循环执行20次,循环体时下边的描述)
若是当前库中没有一个key设置了过时时间,直接执行下一个库的遍历
随机获取一个设置了过时时间的key,检查该key是否过时,若是过时,删除key
判判定期删除操做是否已经达到指定时长,若已经达到,直接退出按期删除。
回答存储机制以及持久化
确保一个类最多只有一个实例,并提供一个全局访问点。
public class Singleton {
private volatile static Singleton instance = null;
private Singleton() { } /** * 当第一次调用getInstance()方法时,instance为空,同步操做,保证多线程实例惟一 * 当第一次后调用getInstance()方法时,instance不为空,不进入同步代码块,减小了没必要要的同步 */ public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; }
}
单例模式
SpringBoot就是对各类框架的整合,让框架集成在一块儿更加简单,简化了开发过程、配置过程、部署过程、监控过程。
IOC:控制反转也叫依赖注入,IOC利用java反射机制。所谓控制反转是指,原本被调用者的实例是有调用者来建立的,这样的缺点是耦合性太强,IOC则是统一交给spring来管理建立,将对象交给容器管理,你只须要在spring配置文件总配置相应的bean,以及设置相关的属性,让spring容器来生成类的实例对象以及管理对象。在spring容器启动的时候,spring会把你在配置文件中配置的bean都初始化好,而后在你须要调用的时候,就把它已经初始化好的那些bean分配给你须要调用这些bean的类。
AOP是对OOP的补充和完善。AOP利用的是代理,分为CGLIB动态代理和JDK动态代理。OOP引入封装、继承和多态性等概念来创建一种对象层次结构。OOP编程中,会有大量的重复代码。而AOP则是将这些与业务无关的重复代码抽取出来,而后再嵌入到业务代码当中。实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法建立“方面”,从而使得编译器能够在编译期间织入有关“方面”的代码,属于静态代理。
下降了组件之间的耦合性 ,实现了软件各层之间的解耦
工厂模式
权限管理、日志、事务管理等。
切面经过带有@Aspect注解的类实现。
Spring中定义了四个advice:BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvice。
Before Advice:在方法执行前执行。
AfterAdvice:在方法执行以后调用的通知,不管方法执行是否成功。
After ReturningAdvice:在方法执行后返回一个结果后执行。
After ThrowingAdvice:在方法执行过程当中抛出异常的时候执行。
代理分为静态代理和动态代理,静态代理是在编译时就将接口、实现类、代理类所有手动完成,但若是咱们须要不少的代理,每个都这么手动的去建立实属浪费时间,并且会有大量的重复代码。动态代理能够在程序运行期间根据须要动态的建立代理类及其实例,来完成具体的功能。
Spring事务管理高层抽象主要包括3个接口:
PlatformTransactionManager(事务管理器)
TransactionDefinition(事务定义信息,包含隔离级别、事务传播行为、超时、只读)
TransactionStatus(事务具体运行状态)
Spring事务的本质其实就是数据库对事务的支持
获取链接->开启事务 -> 执行CRUD -> 提交事务/回滚事务 -> 关闭链接
map
@Param注解
JavaBean
客户端发送HTTP请求到服务器
SpringMVC的核心DispatcherServlet将请求交给HandlerMapping处理
HandlerMapping经过查询机制找处处理当前请求的Handler
DispatcherServlet将请求交给这个Handler处理
Handler处理完成后返回一个ModleAndView对象,这个对象包含视图逻辑名和数据对象
返回的视图逻辑名会经过视图解析器解析成真正的视图,并交给DispatcherServlet处理
DispatcherServlet将请求分派给真正的视图对象,并反映到客户端
@Controller:用于标记在一个类上,使用它标记的类就是一个SpringMVC Controller 对象。
@RequestMapping:是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的全部响应请求的方法都是以该地址做为父路径。
@Resource和@Autowired:@Resource和@Autowired都是作bean的注入时使用,其实@Resource并非Spring的注解,它的包是javax.annotation.Resource,须要导入,可是Spring支持该注解的注入。
@ResponseBody:返回的数据不是html标签的页面,而是其余某种格式的数据时(如json、xml等)使用。
@Repository:DAO层
@Service:服务层
@Autowired注解是按类型装配依赖对象,默认状况下它要求依赖对象必须存在,若是容许null值,能够设置它required属性为false。
@Resource注解和@Autowired同样,也能够标注在字段或属性的setter方法上,但它默认按名称装配。名称能够经过@Resource的name属性指定,若是没有指定name属性,当注解标注在字段上,即默认取字段的名称做为bean名称寻找依赖对象,当注解标注在属性的setter方法上,即默认取属性名做为bean名称寻找依赖对象。
@Resources按名称,是JDK的,@Autowired按类型,是Spring的。
@PathVariable是用来对指定请求的URL路径里面的变量。
Listener我是这样理解他的,他是一种观察者模式的实现。
Filter的使用户能够改变一 个request或修改一个response。 Filter 不是一个servlet,它不能产生一个response,可是他可以在一个request到达servlet以前预先处理request,也能够在一个响应离开 servlet时处理response。
通俗的说,就是一个容器,把消息丢进去,不须要当即处理。而后有个程序去从容器里面把消息一条条读出来处理。
dubbo
ps -ef | grep java
top命令提供了实时的运行中的程序的资源使用统计。你能够根据内存的使用和大小来进行排序。
vmstat命令显示实时的和平均的统计,覆盖CPU、内存、I/O等内容。例如内存状况,不只显示物理内存,也统计虚拟内存。
多线程,JVM
书籍+博客+视频
举出三个以上的国内开源框架,越多越好,dubbo、fastjson、sharding-jdbc、Elastic-job...
电商,突出质量
淘宝是C2C,京东和天猫是B2C,淘宝门槛低,种类,国际市场布局
深刻理解Java虚拟机&HEAD FIRST设计模式&高性能MYSQL&Java并发编程实战,看博客比较多,感受博客更有针对性