• 错误习题笔记


    21

    21-2

    1. 抽象类和接口的区别?【1.7、1.8】

    2. java的 Daemon线程,setDaemon( )设置必须要?

    A,在start之前
    B,在start之后
    C,前后都可以

    • java线程是一个运用很广泛的重点知识,我们很有必要了解java的 守护(daemon)线程.
    1. 首先我们必须清楚的认识到java的线程分为两类: 用户线程和 守护(daemon)线程
      • 用户线程: 用户线程可以简单的理解为用户定义的线程,当然包括main线程(以前我错误的认为main线程也是一个daemon线程,但是慢慢的发现原来main线程不是,因为如果我再main线程中创建一个用户线程,并且打出日志,我们会发现这样一个问题,main线程运行结束了,但是我们的线程任然在运行).
      • daemon线程: daemon线程是为我们创建的用户线程提供服务的线程,比如说jvm的GC等等,这样的线程有一个非常明显的特征: 当用户线程运行结束的时候,daemon线程将会自动退出.(由此我们可以推出下面关于daemon线程的几条基本特点)
    2. daemon 线程的特点:
      • 守护线程创建的过程中需要先调用setDaemon方法进行设置,然后再启动线程.否则会报出IllegalThreadStateException异常.(个人在想一个问题,为什么不能动态更改线程为daemon线程?有时间一个补上这个内容,现在给出一个猜测: 是因为jvm判断线程状态的时候,如果当前只存在一个线程Thread1,如果我们把这个线程动态更改为daemon线程,jvm会认为当前已经不存在用户线程而退出,稍后将会给出正确结论,抱歉!如果有哪位大牛看到,希望给出指点,谢谢!)
      • 由于daemon线程的终止条件是当前是否存在用户线程,所以我们不能指派daemon线程来进行一些业务操作,而只能服务用户线程.
      • daemon线程创建的子线程任然是daemon线程.

    12-3

    1. 以下代码段执行后的输出结果为

    import java.util.*;
    
    public class Demo {
        public static void main(String[] args) {
            Collection<?>[] collections =
                    {new HashSet<String>(), new ArrayList<String>(), new HashMap<String, String>().values()};
            Super subToSuper = new Sub();
            for (Collection<?> collection : collections) {
                System.out.println(subToSuper.getType(collection));
            }
        }
    
        abstract static class Super {
            public static String getType(Collection<?> collection) {
                return "Super:collection";
            }
    
            public static String getType(List<?> list) {
                return "Super:list";
            }
    
            public String getType(ArrayList<?> list) {
                return "Super:arrayList";
            }
    
            public static String getType(Set<?> set) {
                return "Super:set";
            }
    
            public String getType(HashSet<?> set) {
                return "Super:hashSet";
            }
        }
    
        static class Sub extends Super {
            public static String getType(Collection<?> collection) {
                return "Sub";
            }
        }
    }
    
    • 结果
    Super:collection
    Super:collection
    Super:collection
    

    2. 执行以下程序后的输出结果是()

    public class Test {
        public static void main(String[] args) {
            StringBuffer a = new StringBuffer("A");
            StringBuffer b = new StringBuffer("B");
            operator(a, b);
            System.out.println(a + "," + b);
        }
    
        public static void operator(StringBuffer x, StringBuffer y) {
            x.append(y);
            y = x;
        }
    }
    
    • a和x是同个地址,b和y是同个地址,然后执行x.append(y)就把y的值放在x的地址里面此时a地址和x是同一个所以a就是AB了,接着执行y=x是把x的地址给y,这时候axy属于同一个地址。所以y=x 只是改变了y的地址没改变b的地址,所以b还是B
      image

    12-11

    1. java中的类加载器

    • 比较两个类是否相等,只有这两个类是由同一个类加载器加载才有意义。否则,即使这两个类是来源于同一个Class文件,只要加载它们的类加载器不同,那么这两个类必定不相等。
      补充:

    1. 什么是类加载器?

    • 把类加载的过程放到Java虚拟机外部去实现,让应用程序决定如何去获取所需要的类。实现这个动作的代码模块称为“类加载器”。
    • 类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
    • 类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。

    2. 有哪些类加载器,分别加载哪些类

    • 类加载器按照层次,从顶层到底层,分为以下三种:
    1. 引导类加载器/启动类加载器 Bootstap Classloader:用C++编写的,是 JVM自带的类加载器,负责Java平台核心库,用来装载核心类库。该加载器无法直接获取
    2. 扩展类加载器 Extension Classloader:负责 jre/lib/ext目录下的 jar包或 -D java.ext.dirs指定目录下的jar包,装入工作库。开发者可以直接使用标准扩展类加载器。
    3. 系统类加载器/应用程序类加载器 System Classloader: 负责 java-classpath 或 -D java.class.path 所指定的目录下的类与jar包,装入工作,是最常用的加载器。
    4. 自定义类加载器:java.lang.classloder:通过继承 java.lang.classloder
      image

    3. 双亲委派模型

    1. 我们应用程序都是由以上三种类加载器互相配合进行加载的,还可以加入自己定义的类加载器。称为 类加载器的双亲委派模型 ,这里类加载器之间的父子关系一般不会以继承的关系来实现,而是都使用 组合关系 来复用父加载器的。

    4. 双亲委托模型的工作原理

    1. 是当一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。

    5. 使用双亲委派模型好处?(原因)

    1. 第一:可以避免重复加载,当父亲已经加载了该类的时候,子类不需要再次加载。
    2. 第二:考虑到安全因素,如果不使用这种委托模式,那我们就可以随时使用自定义的String来动态替代java核心api中定义类型,这样会存在非常大的安全隐患,而双亲委托的方式,就可以避免这种情况,因为String已经在启动时被加载,所以用户自定义类是无法加载一个自定义的类装载器。

    2. volatile与synchronized的区别

    1. volatile本质是在告诉jvm当前变量在寄存器中的值是不确定的,需要从主存中读取,synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住.
    2. volatile仅能使用在变量级别,synchronized则可以使用在变量,方法.
    3. volatile仅能实现变量的修改可见性,但不具备原子特性,而synchronized则可以保证变量的修改可见性和原子性.
    4. volatile不会造成线程的阻塞,而synchronized可能会造成线程的阻塞.
    5. volatile标记的变量不会被编译器优化,而synchronized标记的变量可以被编译器优化.
    6. synchronized很强大,既可以保证可见性,又可以保证原子性,而volatile不能保证原子性!

    12-13

    1. 同步代码块,同步方法

    public class Test {
        private synchronized void a() {
        }
        private void b() {
            synchronized (this) {
            }
        }
        private synchronized static void c() {
        }
        private void d() {
            synchronized (Test.class) {
            }
        }
    }
    
    1. 修饰非静态方法 锁的是this 对象
    2. 修饰静态方法 锁的是class对象
    • 同步代码块(synchronized(this),synchronized(类实例对象),锁是小括号()中的实例对象)
    • 同步非静态方法(synchronized method),锁的是当前对象的实例对象
    • 获取类锁
    • 同步代码块(synchronized(类.class)),锁是最小括号 () 中的类对象(Class对象)
    • 同步静态方法(synchronized static method),锁是当前对象的类对象(Class 对象)

    总结

    1. 有线程访问对象的同步代码块时,另外的线程可以访问该对象的非同步代码块
    2. 若锁住的是同一个对象,一个线程在访问对象的同步代码块时,另一个访问对象的同步代码块的线程会被阻塞。
    3. 若锁住的是同一个对象,一个线程在访问对象的同步方法时,另一个访问对象的同步方法的线程会被阻塞。
    4. 若锁住的是同一个对象,一个线程在访问对象的同步代码块时,另一个访问对象同步方法的线程会被阻塞,反之亦然。
    5. 同一个类的不同对象的锁互不干扰
    6. 类锁由于也是一种特殊的对象锁,因此表现和上述一致,而由于一个类只有一把对象锁,所以同一个类的不同对象使用类锁将会是同步的类锁和对象锁互不干扰

    2. Servlet的声明周期

    • Servlet的生命周期一般可以用三个方法来表示:
    1. init():仅执行一次,负责在装载Servlet时初始化Servlet对象
    2. service() :核心方法,一般HttpServlet中会有get,post两种处理方式。在调用doGet和doPost方法时会构造servletRequest和servletResponse请求和响应对象作为参数。
    3. destory():在停止并且卸载Servlet时执行,负责释放资源
    4. 初始化阶段:Servlet启动,会读取配置文件中的信息,构造指定的Servlet对象,创建ServletConfig对象,将ServletConfig作为参数来调用init()方法。所以选ACD。B是在调用service方法时才构造的
      image

    3. 假如某个JAVA进程的JVM参数配置如下:

    • -Xms1G -Xmx2G -Xmn500M -XX:MaxPermSize=64M -XX:+UseConcMarkSweepGC -XX:SurvivorRatio=3,

    • 请问eden区最终分配的大小是多少?

    • Xms 起始内存

    • Xmx 最大内存

    • Xmn 新生代内存

    • Xss 栈大小。 就是创建线程后,分配给每一个线程的内存大小

    • -XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4

    • -XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5

    • -XX:MaxPermSize=n:设置持久代大小

    • 收集器设置

      • -XX:+UseSerialGC:设置串行收集器
      • -XX:+UseParallelGC:设置并行收集器
      • -XX:+UseParalledlOldGC:设置并行年老代收集器
      • -XX:+UseConcMarkSweepGC:设置并发收集器
    • 垃圾回收统计信息

      • -XX:+PrintGC
      • -XX:+PrintGCDetails
      • -XX:+PrintGCTimeStamps
      • -Xloggc:filename
    • 并行收集器设置

      • -XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。
      • -XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
      • -XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
    • 并发收集器设置

      • -XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
      • -XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。

    -XX:SurvivorRatio=3 , 就是说Eden区与Survivor区的大小比值为3:1:1
    题目中所问的Eden区的大小是指年轻代的大小,直接根据-Xmn:500M和-XX:SurvivorRatio=3可以直接计算得出
    500M(3/(3+1+1))
    =500M
    (3/5)
    =500M*0.6
    =300M
    所以Eden区域的大小为300M。

    12-14

    1. Java线程池核心参数与工作流程,拒绝策略

    • 线程池中的执行流程:

      1. 当线程数小于核心线程数的时候,使用核心线程数。
      2. 如果核心线程数小于线程数,就将多余的线程放入任务队列(阻塞队列)中
      3. 当任务队列(阻塞队列)满的时候,就启动最大线程数.
      4. 当最大线程数也达到后,就将启动拒绝策略。
        image
    • 回答:有四种拒绝策略

      1. ThreadPoolExecutor.AbortPolicy
        线程池的默认拒绝策略为AbortPolicy,即丢弃任务并抛出RejectedExecutionException异常(即后面提交的请求不会放入队列也不会直接消费并抛出异常);
      2. ThreadPoolExecutor.DiscardPolicy
        丢弃任务,但是不抛出异常。如果线程队列已满,则后续提交的任务都会被丢弃,且是静默丢弃(也不会抛出任何异常,任务直接就丢弃了)。
      3. ThreadPoolExecutor.DiscardOldestPolicy
        丢弃队列最前面的任务,然后重新提交被拒绝的任务(丢弃掉了队列最前的任务,并不抛出异常,直接丢弃了)。
      4. ThreadPoolExecutor.CallerRunsPolicy
        由调用线程处理该任务(不会丢弃任务,最后所有的任务都执行了,并不会抛出异常)

    2. tcp和udp的区别

    回答:TCP 和UDP都是属于运输层的

    1. TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
    2. TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
    3. TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的;UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
    4. 每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
    5. TCP首部开销20字节;UDP的首部开销小,只有8个字节
      image
      image
    6. TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
      image

    追问:TCP和UDP的使用场景?

    • 某些情况下 UDP 确是一种最有效的工作方式(一般用于即时通信),比如: QQ 语音、 QQ 视频 、直播等等。
    • TCP 一般用于文件传输、发送和接收邮件、远程登录等场景。

    TCP是如何保证可靠传输的?

    1. 应用数据被分割成 TCP 认为最适合发送的数据块。
    2. TCP 给发送的每一个包进行编号,接收方对数据包进行排序,把有序数据传送给应用层。
    3. 校验和: TCP 将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP 将丢弃这个报文段和不确认收到此报文段。
    4. TCP 的接收端会丢弃重复的数据。
    5. 流量控制: TCP 连接的每一方都有固定大小的缓冲空间,TCP的接收端只允许发送端发送接收端缓冲区能接纳的数据。当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失。TCP 使用的流量控制协议是可变大小的滑动窗口协议。 (TCP 利用滑动窗口实现流量控制)
    6. 拥塞控制: 当网络拥塞时,减少数据的发送。
    7. ARQ协议: 也是为了实现可靠传输的,它的基本原理就是每发完一个分组就停止发送,等待对方确认。在收到确认后再发下一个分组。
    8. 超时重传: 当 TCP 发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。

    21-14

    1. Java中四种引用类型

    参考连接 https://www.cnblogs.com/liyutian/p/9690974.html

    • 所以在 JDK.1.2 之后,Java 对引用的概念进行了扩充,将引用分为了:强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)4 种,这 4 种引用的强度依次减弱。

    a. 强引用 Strong Reference

    • Java中默认声明的就是强引用,比如:
    Object obj = new Object(); //只要obj还指向Object对象,Object对象就不会被回收
    obj = null;  //手动置null
    
    • 只要强引用存在,垃圾回收器将永远不会回收被引用的对象,哪怕内存不足时,JVM也会直接抛出OutOfMemoryError,不会去回收。如果想中断强引用与对象之间的联系,可以显示的将强引用赋值为null,这样一来,JVM就可以适时的回收对象了

    b. 软引用 Soft Reference

    • 软引用是用来描述一些非必需但仍有用的对象。在内存足够的时候,软引用对象不会被回收,只有在内存不足时,系统则会回收软引用对象,如果回收了软引用对象之后仍然没有足够的内存,才会抛出内存溢出异常。这种特性常常被用来实现缓存技术,比如网页缓存,图片缓存等。
    • 如果一个对象惟一剩下的引用是软引用,那么该对象是软可及的(softly reachable)。垃圾收集器并不像其收集弱可及的对象一样尽量地收集软可及的对象,相反,它只在真正 “需要” 内存时才收集软可及的对象。

    c. 弱引用 Weak Reference

    • 弱引用的引用强度比软引用要更弱一些,无论内存是否足够,只要 JVM 开始进行垃圾回收,那些被弱引用关联的对象都会被回收。在 JDK1.2 之后,用 java.lang.ref.WeakReference 来表示弱引用。

    d. 虚引用 PLhantom Reference

    • 虚引用是最弱的一种引用关系,如果一个对象仅持有虚引用,那么它就和没有任何引用一样,它随时可能会被回收,在 JDK1.2 之后,用 PhantomReference 类来表示,通过查看这个类的源码,发现它只有一个构造函数和一个 get() 方法,而且它的 get() 方法仅仅是返回一个null,也就是说将永远无法通过虚引用来获取对象,虚引用必须要和 ReferenceQueue 引用队列一起使用。

    e. 引用队列 ReferenceRueue

    • 引用队列可以与软引用、弱引用以及虚引用一起配合使用,当垃圾回收器准备回收一个对象时,如果发现它还有引用,那么就会在回收对象之前,把这个引用加入到与之关联的引用队列中去。程序可以通过判断引用队列中是否已经加入了引用,来判断被引用的对象是否将要被垃圾回收,这样就可以在对象被回收之前采取一些必要的措施。
    • 与软引用、弱引用不同,虚引用必须和引用队列一起使用。

    2. tcp的三次握手和四次挥手

    三次握手

    image
    image

    1. 开始,客户端和服务端都处于 CLOSED 状态。先是服务端主动监听某个端口,处于 LISTEN 状态
    2. 客户端会随机初始化序号( client_isn ),将此序号置于 TCP 首部的「序号」字段中,同时把 SYN 标志位置为 1 ,表示 SYN 报文。接着把第一个 SYN 报文发送给服务端,表示向服务端发起连接,该报文不包含应用层数据,之后客户端处于 SYN-SENT 状态。
    3. 服务端收到客户端的 SYN 报文后,首先服务端也随机初始化自己的序号( server_isn ),将此序号填入TCP 首部的「序号」字段中,其次把 TCP 首部的「确认应答号」字段填入 client_isn + 1 , 接着把 SYN和 ACK 标志位置为 1 。最后把该报文发给客户端,该报文也不包含应用层数据,之后服务端处于 SYN-RCVD 状态。
    4. 客户端收到服务端报文后,还要向服务端回应最后一个应答报文,首先该应答报文 TCP 首部 ACK 标志位置为 1 ,其次「确认应答号」字段填入 server_isn + 1 ,最后把报文发送给服务端,这次报文可以携带客户到服务器的数据,之后客户端处于 ESTABLISHED 状态。
    5. 服务器收到客户端的应答报文后,也进入 ESTABLISHED 状态。

    四次挥手

    image

    1. 客户端打算关闭连接,此时会发送一个 TCP 首部 FIN 标志位被置为 1 的报文,也即 FIN 报文,之后客户端进 FIN_WAIT_1 状态。
    2. 服务端收到该报文后,就向客户端发送 ACK 应答报文,接着服务端进入 CLOSED_WAIT 状态。
    3. 客户端收到服务端的 ACK 应答报文后,之后进入 FIN_WAIT_2 状态。
    4. 等待服务端处理完数据后,也向客户端发送 FIN 报文,之后服务端进入 LAST_ACK 状态。
    5. 客户端收到服务端的 FIN 报文后,回一个 ACK 应答报文,之后进入TIME_WAIT 状态
    6. 服务器收到了 ACK 应答报文后,就进入了 CLOSED 状态,至此服务端已经完成连接的关闭。
    7. 客户端在经过 2MSL 一段时间后,自动进入 CLOSED 状态,至此客户端也完成连接的关闭。

    【注意是:主动关闭连接的,才有 TIME_WAIT 状态。】

    追问:为什么是三次?不是两次、四次?

    • 回答:“因为三次才能保证双方具有接收和发送的能力。”,这样回答或许有些片面,可以在回答以下几点。

    • TCP 建立连接时,通过三次握手能防止历史连接的建立,能减少双方不必要的资源开销,能帮助双方同步初始化序列号。序列号能够保证数据包不᯿复、不丢弃和按序传输。

    • 不使用两次和四次的原因:
      两次:无法防止历史连接的建立,会造成双方资源的浪费,也无法可靠的同步双方序列号;
      四次:三次握手就已经理论上最少可靠连接建立,所以不需要使用更多的通信次数。

    追问:为什么 TIME_WAIT 等待的时间是 2MSL?

    • 追问:为什么需要 TIME_WAIT 状态?
    • 主要是两个原因:
    • 防止具有相同「四元组」的「旧」数据包被收到;
    • 保证「被动关闭连接」的一方能被正确的关闭,即保证最后的 ACK 能让被动关闭⽅接收,从而帮助其正常关闭;

    追问:TIME_WAIT 过多有什么危害?

    • 过多的 TIME-WAIT 状态主要的危害有两种:
    • 第一是内存资源占用;
    • 第二是对端口资源的占用,一个 TCP 连接⾄少消耗⼀个本地端口;

    12-16

    1. 浏览器键入一个网址都经历了哪些过程?

    image

    1. 解析URL得到发送给web的信息,并生产 HTTP 请求信息
    2. 查询服务器域名对应的** IP 地址,这个过程中涉及到DNS解析。
    3. 通过 DNS 获取到 IP 后,就可以把 HTTP 的传输⼯作交给操作系统中的协议栈。
    4. 经过TCP三次握手建立连接进行数据传输。
    5. TCP 模块在执行连接、收发、断开等各阶段操作时,都需要委托 IP 模块将数据封装成网络包发送给通信对象。
    6. 生成了 IP 头部之后,接下来网络包还需要在 IP 头部的前面加上 MAC 头部。
    7. 后续还会经过网卡、交换机和路由器到对端,然后就是一个反向的过程。
      image

    追问:DNS解析过程

    image

    1. 浏览器搜索自己的 dns缓存
    2. 若没有,则搜索操作系统的 dns缓存和 host文件
    3. 若没有,则操作系统将域名发送至本地域名服务器,本地域名服务器则查询自己的 nds缓存,查找成功则返回结果,否则依此向根域名服务器,顶级域名服务器,权限域名服务器发起查询请求,最终将 ip地址返回给本地域名服务器。
    4. 本地域名服务器将得到的 ip地址返回给操作系统,同时自己也将 ip地址缓存起来。
    5. 操作系统将 ip地址返回给浏览器,同时字节也将 ip地址缓存起来
    6. 浏览器得到域名对应的ip

    https://blog.csdn.net/weixin_42029733/article/details/105656665
    https://zhuanlan.zhihu.com/p/88260838
    https://www.jianshu.com/p/401f34691dcc

    12-29

    1. 过滤器(Filter)与拦截器(Interceptor)的区别

    https://www.cnblogs.com/luoyun/archive/2013/01/04/2844274.html
    Filter介绍。。。
    Interceptor介绍。。。

    1. Filter是基于函数回调的,而 Interceptor则是基于 Java反射的。
    2. Filter是依赖 Servlet容器,而 Interceptor依赖于 SpringMVC框架。
    3. Filter对几乎所有的请去起作用,而 Interceptor只能对 action请求起作用。
    4. Interceptor可以访问 Action的上下文,值栈里的对象,而 Filter不能。
    5. 在 Action的生命周期里,Interceptor可以被多次调用,而 Filter只能在容器初始化一次。
    6. 拦截器可以获取 IOC容器中的各个bean,而过滤器就不行,在拦截器里注入一个 service,可以调用业务逻辑。

    22

    2-18

    1. SpringBean的作用域之间有什么区别?

    bena的作用域

    • 在Spring中,可以在<bena>元素的scope属性里设置bena的作用域,已决定这个bean是单实例还是多实例的。
    • 默认情况下,sprig只为每个在ioc容器里声明的bean创建一个唯一一个实例,整个 ioc容器范围内都能共享该实例;后续的 getBean()调用和 bean引用都将返回整个唯一的bean实例。该作用域被称为 singleton,它是所有 benan的默认作用域。
    类别 说明
    singleton 在 springIOC容器中仅存在一个Bean实例,Bean以单实例的方式存在
    prototype 每次调用getBean()返回一个新的实例
    request 每次http请求都会创建一个新的Bean,该作用域仅适合于 WebApplicationContext环境
    session 同一个HTTPSession共享一个Bean,不同的HTTPSession使用不同的bean。该作用域仅适用于 WebApplicationContext环境
    • 仅当 bean的作用域为单实例时,spring会在IOC容器对象创建时就创建ben实例。

    2. Spring中的事务传播行为

    • 当事务方法被另一个方法调用时,必须指定事务应该如何传播。列如:方法可能继续在现有的事务中运行,也可能开启一个新事务,并在自己的事务中运行。
    • 事务的传播行为可以由传播属性指定。Spring定义了7中类传播行为。
    传播属性 描述
    required 如果有事务在运行,当前的方法就在这个事务内运行,否则,就启动一个新的事务,并在自己的事务内运行,(使用原来的事务)默认值
    requires_new 当前的方法必须启动新事务,并在他自己的事务内运行,如果有事务正在运行,应该将他挂起(将原来的事务挂起,开启一个新的事务)
    supports 如果有事务在运行,当前的方法就在这个事务内运行,否则他可以不运行在事务中
    no_supportes 当前的方法不应该运行在事务中,如果有运行的事务,将他挂起
    mandatory 当前的方法必须运行在事物内部,如果没有正在运行的事务,就抛出异常
    never 当前的方法不应该运行在事务中,如果有运行的事务,就抛出异常
    nested 如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行,否则,就启动一个新的事务,并在他自己的事务内运行
    • 事务的传播属性可以在@Transactional注解的propagation属性中定义。
    • isolation:用来设置事务的隔离级别
      Isolation.REPEATABLE_READ:可重复读,mysql默认的隔离级别
      Isolation.READ_COMMITTED:读已提交,Oracle默认的隔离级别,开发时通常使用的隔离级别。

    事务属性的种类: 传播行为、隔离级别、只读和事务超时

    传播行为
    • 定义了被调用方法的事务边界。
    传播行为 意义
    PROPERGATION_MANDATORY 表示方法必须运行在一个事务中,如果当前事务不存在,就抛出异常
    PROPAGATION_NESTED 表示如果当前事务存在,则方法应该运行在一个嵌套事务中。否则,它看起来和 PROPAGATION_REQUIRED 看起来没什么俩样
    PROPAGATION_NEVER 表示方法不能运行在一个事务中,否则抛出异常
    PROPAGATION_NOT_SUPPORTED 表示方法不能运行在一个事务中,如果当前存在一个事务,则该方法将被挂起
    PROPAGATION_REQUIRED 表示当前方法必须运行在一个事务中,如果当前存在一个事务,那么该方法运行在这个事务中,否则,将创建一个新的事务
    PROPAGATION_REQUIRES_NEW 表示当前方法必须运行在自己的事务中,如果当前存在一个事务,那么这个事务将在该方法运行期间被挂起
    PROPAGATION_SUPPORTS 表示当前方法不需要运行在一个是事务中,但如果有一个事务已经存在,该方法也可以运行在这个事务中
    隔离级别
    • 在操作数据时可能带来 3 个副作用,分别是脏读、不可重复读、幻读。为了避免这 3 中副作用的发生,在标准的 SQL 语句中定义了 4 种隔离级别,分别是未提交读、已提交读、可重复读、可序列化。而在 spring 事务中提供了 5 种隔离级别来对应在 SQL 中定义的 4 种隔离级别,如下:
    隔离级别 意义
    ISOLATION_DEFAULT 使用后端数据库默认的隔离级别
    ISOLATION_READ_UNCOMMITTED 允许读取未提交的数据(对应未提交读),可能导致脏读、不可重复读、幻读
    ISOLATION_READ_COMMITTED 允许在一个事务中读取另一个已经提交的事务中的数据(对应已提交读)。可以避免脏读,但是无法避免不可重复读和幻读
    ISOLATION_REPEATABLE_READ 一个事务不可能更新由另一个事务修改但尚未提交(回滚)的数据(对应可重复读)。可以避免脏读和不可重复读,但无法避免幻读
    ISOLATION_SERIALIZABLE 这种隔离级别是所有的事务都在一个执行队列中,依次顺序执行,而不是并行(对应可序列化)。可以避免脏读、不可重复读、幻读。但是这种隔离级别效率很低,因此,除非必须,否则不建议使用。
    • Required:必须的。说明必须要有事物,没有就新建事物。supports:支持。说明仅仅是支持事务,没有事务就非事务方式执行。mandatory:强制的。说明一定要有事务,没有事务就抛出异常。required_new:必须新建事物。如果当前存在事物就挂起。not_supported:不支持事物,如果存在事物就挂起。never:绝不有事务。如果存在事物就抛出异常。

    只读

    • 如果在一个事务中所有关于数据库的操作都是只读的,也就是说,这些操作只读取数据库中的数据,而并不更新数据,那么应将事务设为只读模式( READ_ONLY_MARKER ) , 这样更有利于数据库进行优化 。
    • 因为只读的优化措施是事务启动后由数据库实施的,因此,只有将那些具有可能启动新事务的传播行为 (PROPAGATION_NESTED 、 PROPAGATION_REQUIRED 、 PROPAGATION_REQUIRED_NEW) 的方法的事务标记成只读才有意义。
    • 如果使用 Hibernate 作为持久化机制,那么将事务标记为只读后,会将 Hibernate 的 flush 模式设置为 FULSH_NEVER, 以告诉 Hibernate 避免和数据库之间进行不必要的同步,并将所有更新延迟到事务结束。

    事务超时

    • 如果一个事务长时间运行,这时为了尽量避免浪费系统资源,应为这个事务设置一个有效时间,使其等待数秒后自动回滚。与设置“只读”属性一样,事务有效属性也需要给那些具有可能启动新事物的传播行为的方法的事务标记成只读才有意义。

    3. SpringMVC如何解决POST请求中文乱码问题

    • 使用CharacterEncodingFilter过滤器,进行参数设置编码集
    • 如果是get请求乱码,在 server.xml文件中在connector的标签中使用属性 URLEncoding=utf-8设置。

    4. 简单的谈一下SpringMVC的运行流程

    1. 用户向服务器发送请求,请求被 Spring 前端控制 Servelt DispatcherServlet捕获;
    2. DispatcherServlet 对请求 URL 进行解析,得到请求资源标识符(URI)。然后根据该 URI,调用 HandlerMapping 获得该 Handler 配置的所有相关的对象 (包括Handler 对象以及 Handler 对象对应的拦截器),最后以 HandlerExecutionChain 对象的形式返回;
    3. DispatcherServlet 根据获得的 Handler,选择一个合适的 HandlerAdapter。(附注:如果成功获得 HandlerAdapter 后,此时将开始执行拦截器的 preHandler(...)方法)
    4. 提取 Request 中的模型数据,填充 Handler 入参,开始执行 Handler(Controller)。 在填充 Handler 的入参过程中,根据你的配置,Spring 将帮你做一些额外的工作:HttpMessageConveter: 将请求消息(如 Json、xml 等数据)转换成一个对象,将对象转换为指定的响应信息数据转换:对请求消息进行数据转换。如 String 转换成 Integer、Double 等数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等数据验证: 验证数据的有效性(长度、格式等),验证结果存储到 BindingResult或 Error 中
    5. Handler 执行完成后,向 DispatcherServlet 返回一个 ModelAndView 对象;
    6. 根据返回的 ModelAndView,选择一个适合的 ViewResolver视图解析器(必须是已经注册到 Spring 容器中的 ViewResolver)返回给 DispatcherServlet ;
    7. ViewResolver 结合 Model 和 View,来渲染视图
    8. 将渲染结果返回给客户端。

    快速记忆

    • 核心控制器捕获请求,查找 Hander,执行 Handler,选择 ViewResolver,通过ViewResoler 渲染视图并返回

    5. Mybatis中实体类中的属性名和表中的字段名不一样,怎么办

    1. 写sql语句时起别名。
    2. 在MyBatis的全局配置文件中开启驼峰命名规则。
    3. 在Mapper映射文件中使用 resultMap来自定义映射规则。

    6. Lunux系统,常用服务类相关的命令

    • 注册在系统的标准化程序
    • 有方便统一的管理方式(常用的方法)
      systemctl start 服务名(xxx.service)
      systemctl restart 服务名
      systemctl stop 服务名
      systemctl reload 服务名
      systemctl status 服务名
    • 查看服务的方法 /usr/lib/systemd/system
    • 查看服务的命令
      systemctl list-unit-files
      systemctl --type service
    • 通过systemctl命令设置自启动
      自动启动 systemctl enable service_name
      不自启动 systemctl disable service_name

    7. git分支相关命令,实际应用

    创建分支

    • git branch <分支名>
    • git branch -v 查看分支

    切换分支

    • git checkout <分支名>
    • 一步完成:git checkou -b <分支名>

    合并分支

    • 像切换到主干 git checkout master
    • git merge <分支名>

    删除分支

    • 像切换到主干 git checkout master
    • git branch -D <分支名>
      image

    8. redis持久化有几种类型,他们分别是

    • redis提供了两种不同形式的持久化方式。
      • RDB(redis database)
        • 在指定的时间间隔内将内存的数据集快照写入磁盘,也就是行话讲的 snapshot快照,它恢复时是将快照文件直接读到内存里。
        • redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的可能性如果需要进行大规模的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。
        • rdb的优点:节省磁盘空间,恢复速度快
        • red的缺点:虽然redis在fork时使用了写时拷贝技术,但是如果数据庞大时还是比较消耗性能。在备份周期在一定间隔时间做一次备份,所以如果redis意外down掉的话,就会丢失最后一次快照的所欲修改。
      • AOF(append of file)
      • 以日志的形式来记录每个写操作,将redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取改文件重新构建数据,换而言之,redis重启的话就会根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
        • aof的优点:备份机制更稳健,丢失数据概率更低。可读的日志文本,通过操作aof文件,可以处理误操作。
        • aof的缺点:比起red占用更多的磁盘空间。恢复备份要慢。每次读写都同步的话,有一定的性能压力。存在个别bug,造成恢复不能。

    9. MySQL什么时候建索引

    哪些情况需要创建索引

    • 主键自动建立唯一索引
    • 频繁作为查询条件的字段应该建立索引
    • 查询中与其他关联的字段,外键关系建立索引
    • 单键、组合索引的问题,组合索引性价跟高
    • 查询中排序的字段,排序字段若通过索引去访问将大大提交排序速度
    • 查询中统计或者分组字段

    哪些情况不需要创建索引

    • 表记录太少
    • 经常增删改的表或者字段
    • where条件用不到的字段不创建索引
    • 过滤性不好的不适合建立索引

    10 wait和notify区别

    • Java中的多线程是一种抢占式的机制,而不是分时机制。抢占式的机制是有多个线程处于可运行状态,但是只有一个线程在运行。

    共同点

    1. 他们都是在多线程的环境下,都可以在程序的调用处阻塞指定的毫秒数,并返回。
    2. wait()和sleep()都可以通过interrupt()方法 打断线程的暂停状态 ,从而使线程立刻抛出InterruptedException。
      如果线程A希望立即结束线程B,则可以对线程B对应的Thread实例调用interrupt方法。如果此刻线程B正在wait/sleep/join,则线程B会立刻抛出InterruptedException,在catch() {} 中直接return即可安全地结束线程。
      需要注意的是,InterruptedException是线程自己从内部抛出的,并不是interrupt()方法抛出的。对某一线程调用 interrupt()时,如果该线程正在执行普通的代码,那么该线程根本就不会抛出InterruptedException。但是,一旦该线程进入到 wait()/sleep()/join()后,就会立刻抛出InterruptedException 。

    不同点

    1. 每个对象都有一个锁来控制同步访问。Synchronized关键字可以和对象的锁交互,来实现线程的同步。sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。
    2. wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用
    3. sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常
    4. sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。
    5. wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。

    2-22

    1. JVM垃圾回收机制,GC发生在JVM那部分,有几种GC,他们的算法是什么?

    image

    GC是什么(分代收集算法)

    • 次数上频繁收集 Young区:Minor GC
    • 次数上较少收集 Old区:Full GC
    • 基本上不动 Perm区

    GC4大算法

    https://blog.csdn.net/q961250375/article/details/107859902

    2. Redis在项目中的使用场景

    • String
      1.缓存功能:redis作为缓存层,MySQL作为存储层,绝大部分请求的数据都是从 redis中获取。由于 redis具有支撑高并发的特性,所以缓存通常能起到加速读写和降低后端压力的作用。
      1. 计数:许多应用都会使用 redis作为计数的基础工具,它可以实现快速计数,查询缓存的功能,同时数据可以异步落地到其他数据源。列如视频播放次数。
      2. 共享 session:可以使用 redis将用户的 session进行集中管理,这种模式下只要保证 redis是高可用和扩展性的,每次用户或者查询登录信息都直接从 redis中集中获取。
      3. 限速:很多应用处于安全的考虑,会在每次进行登录时,让用户输入手机验证码,从而确定是否是用户本人。但是为了短信接口不被频繁访问,会限制用户每分钟获取验证码的频率。
    • Hash
      1. 存储用户信息:相比使用字符串序列化缓存用户信息,哈希类型变得更加直观,并且在更新操作上更加便捷。可以将每个用户的id定义为键后缀,多对 field-value对应每个用户的属性。

      我们已经能够使用三种方法缓存用户信息,下面给出三种方案的实现方法和优缺点分析、

      1. 原生字符串类型:每个属性一个键。
        优点:简单直观,每个属性都支持更新操作。
        缺点:占用过多的键,内存占用量较大,同时用户信息内聚性比较差,所以此种方案一般不会在生产环境使用。
      2. 序列化字符串类型:将用户信息序列化后用一个键保存。
        优点:简化编程,如果合理的使用序列化可以提高内存的使用效率。
        缺点:序列化和反序列化有一定的开销,同时每次更新属性都需要把全部数据取出进行反序列化,更新后在序列化到 redis中。
      3. 哈希类型:每个用户属性使用一对 field-value,但是只用一个键保存。
        优点:简单直观,如果使用合理可以减少内存空间的使用。
        缺点:要控制哈希在 ziplist和 hashtable两种内部编码的转换,hashtable会消耗更多的内存。
    • List
      1. 消息队列:redis和 lpush+brpop命令组合即可实现阻塞队列,生产客户端使用 lrpush从列表左侧插入元素,多个消费者客户端使用 brpop命令阻塞式的“抢”列表尾部的元素,多个客户端保证了消费的负载均衡和高可用性。
      2. 文章列表:每个用户都属于自己的文章列表,现需要分页展示文章列表。此时可以考虑使用列表,因为列表不但是有序的,同时支持按照索引范围获取元素。

      实际上列表的使用场景很多,在选择时可以参考以下口诀:
      ·lpush+lpop=Stack(栈)
      ·lpush+rpop=Queue(队列)
      ·lpsh+ltrim=Capped Collection(有限集合)
      ·lpush+brpop=Message Queue(消息队列)

    • Set
      1. 标签:列如一个用户可能娱乐,体育比较感兴趣,另一个用户可能对历史,新闻比较感兴趣,这些兴趣点就是标签。
    • Zset
      1. 排行榜系统:列如视频网站需要对用户上传的视频做排行榜,榜单的维度可能是多个方面的:按照时间,按照播放数量,按照获得的赞数。

    3. Elasticsearch 和 solr的区别

    • 背景:他们都是基于 Lucene搜索服务器基础之上开发的,一款优秀的,高性能的企业级搜索服务器。【是因为他们都是基于分词技术构建的倒排索引的方式进行查询】
    • 开发语言:Java语言开发。
    • 诞生时间:Solr:2004年诞生。ES:2010年诞生,ES更新功能却强大。

    区别:

    1. 当实时建立索引的时候,solr会产生 io阻塞,而 es则不会,es查询性能要高于 solr。
    2. 在不断动态台南佳数据的时候,solr的检索效率会变得地下,而 es则没有什么变化。
    3. Solr利用 Zookeeper进行分布式管理,而 ES自身带有分布式系统管理功能。Solr一般都要部署到 web服务器上,比如 tomca。启动 tomcat的时候需要配置 tomcat与 solr的关联。【Solr本质是一个动态web项目
    4. Solr支持更多的格式数据【xml,json,csv等】,而 ES仅支持 json文件格式。
    5. Solr是传统搜索应用的有力解决方案,但是 es更适合新兴的实时搜索应用。
      单纯的对已有数据进行检索的时候,Solr效率更好,高于 ES。
    6. Solr官网提供的功能更多,而 ES本身更注重于核心功能,高级功能多有第三份插件。

    4. 单点登录实现过程

    • 单点登录:一处登录多出使用。前提:单点登录多使用在分布式系统中。
      image
      参观动物园流程
    • 检票员=认证中心模块
    1. 我直接带着大家进动物园,则会被检票员拦住【看我们是否有门票】,没有【售票处买票】,登录=买票。
    2. 我去买票【带着票,带着大家一起准备进入动物园】检查员 check【有票】token=piao
    3. 我们手中有票就可以任意观赏动物的每处景点。
      京东:单点登录,是将 token放入 cookie中的。
      案例:将浏览器的 cooki禁用,则在登录京东失败!无论如何登录不了!

    5. 消息队列在项目中的使用

    • 背景:在分布式系统中,是如何处理高并发的。
      由于在高并发的环境下,来不及同步处理用户发送的请求,则会导致请求发生阻塞。比如说,大量的 insert,update之类的请求同时到达数据库 MySQL,直接导致无数的行锁表锁,甚至会导致请求堆积很多。从而触发 too many connections错误。使用消息队列可以解决【异步通信】
    1. 异步
      写数据库请求并发送消息队列,发送消息队列成功可返回给用户结果
      image
    2. 并行
      image
    3. 排队
      image
    4. 消息队列在电商使用场景
      image
    5. 消息队列弊端:
      消息的不确定性:延迟队列,轮询技术来解决该问题即可
  • 相关阅读:
    undefined reference to `sqrt' 问题
    linux上开发minigui的配置过程
    linxu select 返回值
    Unix/Linux C静态库的使用
    ubuntu 默认pdf阅读器乱码
    文件锁使用原理及其方法
    fileno函数与ftruncate函数
    Linux下select函数的使用
    unix linux 文件锁
    iOS 基础笔试题
  • 原文地址:https://www.cnblogs.com/zk2020/p/15632023.html
Copyright © 2020-2023  润新知