Q:JDK和JRE区别?html
JDK是整个JAVA的核心,包括了Java运行环境JRE,一堆Java工具和Java基 础的类库。经过JDK开发人员将源码文件(java文件)编译成字节码文件(class文 件)。JRE是Java运行环境,不含开发环境,即没有编译器和调试器。将class文件 加载到内存准备运行。
Q:final关键字,抽象类可使用final修饰吗?java
1.用来修饰数据,包括成员变量和局部变量,该变量只能被赋值一次且它的 值没法被改变。对于成员变量来说,必须在声明时或者构造方法中对它赋值; 2.修饰方法,表示该方法没法被重写; 3.修饰类,表示该类没法被继承。抽象类是被用于继承的,final修饰表明不可修改、不可继承的。因此不能用 final修饰抽象类。
Q:JAVA容器程序员
ArrayList底层数组实现,封装了常见的增删改查操做,而且支持动态扩容。适 合查找多的场合。LinkedList基于链表实现的列表。适合增删状况较多的场合。TreeSet,基于二叉排序树(红黑树)实现的。TreeSet里最典型的就是它用到 了两种排序方式,即基于元素对象自身的实现的Comparable接口的天然排序, 以及基于更为灵活不与单个元素绑定的Comparator接口的客户化排序。本身在 构造的时候传入一个比较器便可。HashMap是用来存储键值对的映射关系,底层是用数组+链表实现的。结合put 操做讲一下。HashSet其实就是基于HashMap实现的,只不过将值固定为一个固定的值。LinkedHashMap,支持按照插入顺序排序。PriorityQueue优先级队列,一个基于优先级堆的无界优先级队列
Q:线程安全体如今哪些方面?JAVA怎么保证线程安全?锁在项目中具体怎么使用?算法
1、线程安全在三个方面体现 1.原子性:提供互斥访问,同一时刻只能有一个线程对数据进行操做,(atomic,synchronized); 2.可见性:一个线程对主内存的修改能够及时地被其余线程看到,(synchronized,volatile); 3.有序性:一个线程观察其余线程中的指令执行顺序,因为指令重排序,该观察结果通常杂乱无序,(happens-before原则)。 2、如何保证线程安全 1.保证原子性: 锁和同步 经常使用的保证Java操做原子性的工具是锁和同步方法(或者同步代码块)。使 用锁,能够保证同一时间只有一个线程能拿到锁,也就保证了同一时间只有一个 线程能执行申请锁和释放锁之间的代码。与锁相似的是同步方法或者同步代码块。使用非静态同步方法时,锁住的是 当前实例;使用静态同步方法时,锁住的是该类的Class对象;使用静态代码块 时,锁住的是synchronized关键字后面括号内的对象。不管使用锁仍是synchronized,本质都是同样,经过锁来实现资源的排它 性,从而实际目标代码段同一时间只会被一个线程执行,进而保证了目标代码段 的原子性。这是一种以牺牲性能为代价的方法。 2.保证可见性: Java提供了volatile关键字来保证可见性。因为JMM是基于共享内存实现线 程通讯的,因此会存在缓存一致性的问题。当使用volatile修饰某个变量时,它 会保证对该变量的修改会当即被更新到内存中,而且将其它缓存中对该变量的缓 存设置成无效,所以其它线程须要读取该值时必须从主内存中读取,从而获得最 新的值。 3.保证有序性: 编译器和处理器对指令进行从新排序时,会保证从新排序后的执行结果和代 码顺序执行的结果一致,因此从新排序过程并不会影响单线程程序的执行,却可 能影响多线程程序并发执行的正确性。Java中可经过volatile在必定程序上保证顺序性,另外还能够经过 synchronized和锁来保证顺序性。synchronized和锁保证顺序性的原理和保证原子性同样,都是经过保证同 一时间只会有一个线程执行目标代码段来实现的。除了从应用层面保证目标代码段执行的顺序性外,JVM还经过被称为 happens-before原则隐式地保证顺序性。两个操做的执行顺序只要能够经过 happens-before推导出来,则JVM会保证其顺序性,反之JVM对其顺序性不做 任何保证,可对其进行任意必要的从新排序以获取高效率。 4.其它保证线程安全的方法: (1)尽量避免引发非线程安全的条件——共享变量。若是能从设计上避免 共享变量的使用,便可避免非线程安全的发生,也就无须经过锁或者 synchronized以及volatile解决原子性、可见性和顺序性的问题。 (2)不可变对象 可使用final修饰的对象保证线程安全,因为final修饰的引用型变量(除String外)不 可变是指引用不可变,但其指向的对象是可变的,因此此类必须安全发布,即不能对外提供 能够修改final对象的接口。
Q:JAVA怎么避免死锁?数据库
参考:http://ifeve.com/deadlock-prevention/ 致使死锁的缘由: 1、加锁顺序 当多个线程须要相同的一些锁,可是按照不一样的顺序加锁,死锁就很容易发生。若是能确保全部的线程都是按照相同的顺序得到锁,那么死锁就不会发生。 2、加锁时限 另一个能够避免死锁的方法是在尝试获取锁的时候加一个超时时间,这也就意味着在 尝试获取锁的过程当中若超过了这个时限该线程则放弃对该锁请求。若一个线程没有在给定的 时限内成功得到全部须要的锁,则会进行回退并释放全部已经得到的锁,而后等待一段随机 的时间再重试。这段随机的等待时间让其它线程有机会尝试获取相同的这些锁,而且让该应 用在没有得到锁的时候能够继续运行(译者注:加锁超时后能够先继续运行干点其它事情, 再回头来重复以前加锁的逻辑)。 3、死锁检测 死锁检测是一个更好的死锁预防机制,它主要是针对那些不可能实现按序加锁而且锁超时也不可行的场景。每当一个线程得到了锁,会在线程和锁相关的数据结构中(map、graph等等)将其记 下。除此以外,每当有线程请求锁,也须要记录在这个数据结构中。当一个线程请求锁失败时,这个线程能够遍历锁的关系图看看是否有死锁发生。那么当检测出死锁时,这些线程该作些什么呢?一个可行的作法是释放全部锁,回退,而且等待一段随机的时间后重试。这个和简单的 加锁超时相似,不同的是只有死锁已经发生了才回退,而不会是由于加锁的请求超时了。虽然有回退和等待,可是若是有大量的线程竞争同一批锁,它们仍是会重复地死锁(编者 注:缘由同超时相似,不能从根本上减轻竞争)。一个更好的方案是给这些线程设置优先级,让一个(或几个)线程回退,剩下的线程就 像没发生死锁同样继续保持着它们须要的锁。若是赋予这些线程的优先级是固定不变的,同 一批线程老是会拥有更高的优先级。为避免这个问题,能够在死锁发生的时候设置随机的优先级。 顺便复习一下操做系统死锁: 死锁预防:限制申请方式; 互斥:原来独占的资源变得共享,可能会形成程序不肯定性。 占用并等待:必须保证当一个进程请求一个资源的时候,它不持有任何其余资源。(要么所有拿到,要么一点也不占有)它开始执行以前须要进程请求并分配其全部的资源,容许进程请求资源当且仅当进程没有占有任何资源的时候资源利用率低,可能发生饥饿无抢占若是进程占有某些资源,并请求其余不能被当即分配的资源,则释放当前正占有的资源,被抢占资源添加到资源列表中,只有当它可以得到旧的资源以及它请求的新的资源,进程能够获得执行。 循环等待:对全部资源类型进行排序,并要求每一个进程按照资源的顺序进行申请 死锁避免:银行家算法,若是发现分配了资源以后就可能死锁,就不分配资源了。 死锁检测:容许进入死锁状态,主要是经过检测算法看看是否产生了死锁,而后让相应线程进行回滚。 死锁恢复:杀死全部进程,或者根据优先级杀死部分进程,从而解除死锁。
Q:ThreadLocal具体怎么使用?使用在什么场景?编程
参考: (1)https://www.cnblogs.com/ldq2016/p/9041856.html (2)https://www.jianshu.com/p/98b68c97df9b 当使用ThreadLocal维护变量时,ThreadLocal为每一个使用该变量的线程提供 独立的变量副本,因此每个线程均可以独立地改变本身的副本,而不会影响其 它线程所对应的副本。从线程的角度看,目标变量就象是线程的本地变量,这也是类名 中“Local”所要表达的意思。ThreadLocal是如何作到为每个线程维护变量的副本的呢?其实实现的思 路很简单:在ThreadLocal类中有一个Map,用于存储每个线程的变量副本, Map中元素的键为线程对象,而值对应线程的变量副本。ThreadLocal则从另外一个角度来解决多线程的并发访问。ThreadLocal会为每 一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因 为每个线程都拥有本身的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,能够把不安全的变量封装进ThreadLocal。我的理解:每个ThreadLocal内部有一个静态内部 类:ThreadLocalMap,Map里面存储线程本地线程对象(key)和线程的变量副 本(value)可是,Thread内部的Map是由ThreadLocal维护的,由 ThreadLocal负责向map获取和设置线程的变量值。因此对于不一样的线程,每次 获取副本值时,别的线程并不能获取到当前线程的副本值,造成了副本的隔离, 互不干扰。 使用场景:还记得Hibernate的session获取场景吗? private static final ThreadLocal<Session> threadLocal = new ThreadLocal<S ession>(); 23 //获取Session 4 public static Session getCurrentSession(){ 5 Session session = threadLocal.get(); 6 //判断Session是否为空,若是为空,将建立一个session,并设置到本地线程变量中 7 try { 8 if(session ==null&&!session.isOpen()){ 9 if(sessionFactory==null){ 10 rbuildSessionFactory(); // 建立Hibernate的SessionFactory 11 }else{ 12 session = sessionFactory.openSession(); 13 } 14 } 15 threadLocal.set(session); 16 } catch (Exception e) { 17 // TODO: handle exception18 } 19 20 return session; 21 } 为何每一个线程访问数据库都应当是一个独立的Session会话?若是多个线 程共享同一个Session会话,有可能其余线程关闭链接了,当前线程再执行提交 时就会出现会话已关闭的异常,致使系统异常。此方式能避免线程争抢 Session,提升并发下的安全性。使用ThreadLocal的典型场景正如上面的数据库链接管理,线程会话管理等 场景,只适用于独立变量副本的状况,若是变量为全局共享的,则不适用在高并 发下使用。本身使用的一个场景: @Component public class HostHolder { private static ThreadLocal<User> users = new ThreadLocal<User>(); public User getUser() { return users.get(); } public void setUser(User user) { users.set(user); } public void clear() { 11 users.remove();; 12 } 13 } 主要是用来判断当前用户是否登陆。即在某些页面好比发帖等页面须要判断 当前用户是否登陆,若没有登陆则须要跳转到登陆页面。 总结 每一个ThreadLocal只能保存一个变量副本,若是想要上线一个线程能 够保存多个副本以上,就须要建立多个ThreadLocal。ThreadLocal内部的ThreadLocalMap键为弱引用,会有内存泄漏的 风险。适用于无状态,副本变量独立后不影响业务逻辑的高并发场景。若是 若是业务逻辑强依赖于副本变量,则不适合用ThreadLocal解决,须要另寻解决方案。
Q:了解反射吗?怎么用?用在哪里?设计模式
Java反射就是在运行状态中,对于任意一个类,都可以知道这个类的全部属 性和方法;对于任意一个对象,都可以调用它的任意方法和属性;而且能改变它 的属性。总结说:反射就是把java类中的各类成分映射成一个个的Java对象,并 且能够进行操做。Java反射的原理:java类的执行须要经历如下过程:编译:.java文件编译后生成.class字节码文件 加载:类加载器负责根据一个类的全限定名来读取此类的二进制字节流到 JVM内部,并存储在运行时内存区的方法区,而后将其转换为一个与目标类型对 应的java.lang.Class对象实例 连接 验证:格式(class文件规范) 语义(final类是否有子类) 操做 准备:静态变量赋初值和内存空间,final修饰的内存空间直接赋 原值,此处不是用户指定的初值。解析:符号引用转化为直接引用,分配地址 初始化:根据程序员经过程序指定的主观计划去初始化类变量和其余资源, 或者能够从另外一个角度来表达:初始化阶段是执行类构造器<clinit>()方法的过 程。执行静态方法代码块为静态变量赋值。Java的反射就是利用上面第二步加载到jvm中的.class文件来进行操做 的。.class文件中包含java类的全部信息,当你不知道某个类具体信息时,能够 使用反射获取class,而后进行各类操做。 怎么用: 1、经过class.forName(),加载某个类。 2、在运行时构造任意一个类的对象。1 Class cls = Class.forName("com.jdk"); 2 jdk jdkobj = cls.newInstance(); 3、在运行时判断任意一个类所具备的成员变量和方法。 1 Class cls = Class.forName("com.jdk"); 2 Methods methods[]= cls.getDecliedMethod(); 3 Fields fields[] = cls.getDeclieredFields(); 使用场景: Class.forName();数据库注册驱动的时候。编译器智能提示该类有哪些方法可供调用。AOP动态代理。经典的就是在xml文件或者properties里面写好了配置,而后在Java类里面解析 xml或properties里面的内容,获得一个字符串,而后用反射机制,根据这个字 符串得到某个类的Class实例,这样就能够动态配置一些东西,不用每一次都要 在代码里面去new或者作其余的事情,之后要改的话直接改配置文件,代码维护 起来就很方便了,同时有时候要适应某些需求,Java类里面不必定能直接调用另 外的方法,这时候也能够经过反射机制来实现。注解(Annontation)是Java5引入的一种代码辅助工具,它的核心做用是对类、方法、变 量、参数和包进行标注,经过反射来访问这些标注信息,以此在运行时改变所注解对象的行为。
Q:动态代理为何使用反射而不使用继承?数组
这个问题有坑啊,动态代理能够用继承和反射均可以实现。 JDK动态代理:利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用 InvokeHandler来处理。 CGlib动态代理:利用ASM(开源的Java字节码编辑库,操做字节码)开源包,将代理对象类的class文件加载进来,经过修改其字节码生成子类来处理。 区别:JDK代理只能对实现接口的类生成代理;CGlib是针对类实现代理,对指定的类生成 一个子类,并覆盖其中的方法,这种经过继承类的实现方式,不能代理final修饰的类。 总结: 1.JDK代理使用的是反射机制实现aop的动态代理,CGLIB代理使用字节码处理框架asm, 经过修改字节码生成子类。因此jdk动态代理的方式建立代理对象效率较高,执行效率较 低,cglib建立效率较低,执行效率高; 2.JDK动态代理机制是委托机制,具体说动态实现接口类,在动态生成的实现类里面委托 hanlder去调用原始实现类方法,CGLIB则使用的继承机制,具体说被代理类和代理类是继 承关系,因此代理类是能够赋值给被代理类的,若是被代理类有接口,那么代理类也能够赋值给接口。 参考:https://blog.csdn.net/lz1170063911/article/details/79835248 JDK的动态代理(依赖于接口) 1. 在Java的动态代理机制中,有两个重要的类或接口,一个是InvocationHandler接 口,另外一个是Proxy类。 2. InvocationHandler接口是给动态代理类实现的,负责处理被代理对象的操做。 3. Proxy类是用来建立动态代理类实例对象的,只有获得这个对象,才能调用须要代 理的方法。 4. 动态代理的代理类是在静态代理类上进行修改,将动态代理类实现 InvocationHandler接口,重写Invoke方法,Invoke方法经过传入的被代理类方法和 参数来执行。 JDK动态代理和Cglib动态代理的区别: 1. JDK动态代理是实现了被代理对象的接口,Cglib是继承了被代理对象。 2. Cglib由于是继承机制,因此没法代理被final修饰的方法。 3. JDK和Cglib都是在运行期间生产字节码,JDK是直接写class字节码,Cglib使用 ASM框架写class字节码;cglib代理实现更复杂,生成代理类比JDK效率低。 4. JDK调用代理方法,是经过反射实现机制调用,cglib是经过Fashclass机制直接调 用方法,效率更高。Fastcalss机制:为代理类和被代理类个生成一个class,这个class会为代理类或被代理类的方法分配一个 index。这个index当作一个入参,Fashclass就能够直接定位要调用的方法,并直接进行调用。这样省去了反射调用,因此效率高。
Q:设计模式中简单工厂和抽象工厂的区别?缓存
简单工厂模式:
虽然某种程度不符合开闭原则,可是实际实用不少。工厂方法模式:不修改已有类的状况下,经过增长新的工厂类实现扩展。可是容易致使工厂 类泛滥。
抽象工厂模式:
不能够增长产品,能够增长产品族。增长新产品须要修改不少地方。
Q:HTTP状态码?3XX和4XX区别,404是啥?安全
3XX重定向:客户端须要作些额外工做才能获得所须要的资源。它们一般用于 GET请求。他们一般告诉客户端须要向另外一个URI发送GET请求,才能获得所需 的表示。那个URI就包含在Location响应报头里。 301:永久重定向,好比更换了新的IP,服务端就就告诉客户端之后你访问个人 新IP 302:暂时重定向 4XX客户端错误:这些响应代码代表客户端出现错误。不是认证信息有问题,就 是表示格式或HTTP库自己有问题。客户端须要自行改正。 400: 包含语法错误,没法被服务器解析 403: 服务器已经接收请求,可是拒绝执行 404: 请求失败,请求所但愿获得的资源未在服务器上发现 5XX服务端错误:这些响应代码代表服务器端出现错误。通常来讲,这些代码意味着服务器处于不能执行客户端请求的状态,此时客户端应稍后重试。 500: 服务器内部错误,没法处理请求
Q:数据库为何创建索引?如何编程实现数据库抢锁?
这是由于,建立索引能够大大提升系统的性能。 优势: 第一,经过建立惟一性索引,能够保证数据库表中每一行数据的惟一性。 第二,能够大大加快数据的检索速度,这也是建立索引的最主要的缘由。 第三,能够加速表和表之间的链接,特别是在实现数据的参考完整性方面特别 有意义。 第四,在使用分组和排序子句进行数据检索时,一样能够显著减小查询中分组 和排序的时间。 第五,经过使用索引,能够在查询的过程当中,使用优化隐藏器,提升系统的性能。也许会有人要问:增长索引有如此多的优势,为何不对表中的每个列建立一个索引呢?这种想法当然有其合理性,然而也有其片面性。虽然,索引有许多优势,可是,为表中的每个列都增长索引,是很是不明智的。这是由于,增长索引也有许多不利的一个方面, 缺点: 第一,建立索引和维护索引要耗费时间,这种时间随着数据量的增长而增长。 第二,索引须要占物理空间,除了数据表占数据空间以外,每个索引还要占一 定的物理空间,若是要创建聚簇索引,那么须要的空间就会更大。 第三,当对表中的数据进行增长、删除和修改的时候,索引也要动态的维护,这 样就下降了数据的维护速度。 哪些适合创建索引哪些不适合? 适合的:常常须要搜索的列上,常常须要范围查询的,主键等。 不适合的:常常不用来查询的,大字段的好比text段等。 如何编程实现数据库抢锁? 行锁的释放是须要事务提交以后自动释放 共享锁:SELECT ... LOCK IN SHARE MODE; 解释:MySQL会对查询结果集中每行都添加共享锁。锁申请前提:当前没有线程对该结果集中的任何行使用排他锁,不然申请会阻塞。 排他锁:SELECT ... FOR UPDATE; MySQL会对查询结果集中每行都添加排他锁,在事物操做中,任何对记录的更新与删除操 做会自动加上排他锁。锁申请前提:当前没有线程对该结果集中的任何行使用排他锁或共享锁,不然申请会阻塞。 表锁:lock tables ... read/write 释放表锁:对应线程执行 unlock tables 便可 参考:https://www.cnblogs.com/zqyanywn/p/5922656.html
Q:Redis数据结构基础,项目中具体怎么用的?若是把数据都存储在Redis中会不会丢失数据?Redis分布式锁了解吗?
单机Redis会丢失,集群的话不会。 分布式锁:请参考https://www.cnblogs.com/seesun2012/p/9214653.html
Q:大数据问题:硬盘里一个50G大小的文件和另外一个100G文件,里面存储着不一样的名 字,如何在一个内存很小的电脑上实现两个文件的交集运算?
方法一: 分桶+组内Hash索引或者组内使用位图 O(n) 使用哈希切分的方法,将一个大文件里的数据使用一个哈希函数进行切分为 许多小的文件,这样相同的数据必定会进入同一个文件当中去,并进行文件编 号。对另一个文件也是用相同的哈希函数进行切分为相同数目的小文件,这样 咱们只须要将相同编号里的文件进行比较。这样其时间复杂度就会下降为 O(n)。相同的文件查找时能够先对一个文件创建hash索引(桶+链表),而后对另 一个文件依次按照索引进行查找。若hash值相同在进行进一步比较便可。 方法二: 位图 O(n) 这有个前提是文件中必须存储的是数字。那么根据位图,咱们能够将第一个 文件中全部数据映射到位图中去。而后再不断导入第二个文件,若是发现某个数 字已经存储在位图中,就说明这是两个文件的交集。 方法三: 近似解-布隆过滤器 O(n) 将A文件每一个数据通过多个Hash函数映射到一个位图上,而后第二个文件同 样的作法,若是所有命中,说明相同。不然说明不存在。可是这个有必定的错误 率。方法四:多路归并排序 Onlog(n)+O(n) 先将文件划分为不少等量的小文件。而后对每一个小文件导入内存进行内部排 序。这样就有了不少有序的小文件。而后对不少有序的小文件进行多路归并排序,而后不断写入大文件便可。(Onlog(n))最终就获得了一个有序的大文件。最后对两个有序的大文件进行 查找相同的值便可(O(n))。
Q:大数据问题:BBS上不少帖子,发帖最多的人被删除掉了,剩下3我的的帖子数目均超 过1/4,如何找出这三我的?
1.首先统计出数据出现的次数。这个能够采用hash。而后用最小堆即可以求出 出现次数最高的N个数。 2.若是这个文件很是大。能够采起分治法。假设此文件大小为n。而内存中能处 理的为k。则分m次读取。m=n/k+1。每次读取k大小。而后采用1的方法能够 得出k大小中的N个频率最高的。m次后获得m个堆。将此m个堆合并。即可得 到频率最高的N个。 3.堆的维护的代价为lgN;我想仍是比较快的。思路大体以下:每个帖子和用户是一一对应的关系。先根据HashMap<用户ID,帖子数 目>遍历每个帖子,获得每一个用户的发帖子的数目。假设此时用户数目为m, 即Map的大小。而后进行堆排序,因为咱们只须要排名前三的人,全部只需维 护一个大小为3的小顶堆便可。时间复杂度:遍历全部帖子时间复杂度为O(n), 堆排序时间复杂度为O(mlog3)。因为三我的的帖子数目均超过了总帖子的 1/4。能够认为m的数目远远小于n。最终复杂度约等于O(n)。