Java基础知识扫盲

1、JDK、JRE、JVM

2、final

final修饰类(不可被继承),方法(不可重写)、变量(不可重新赋值)

3、String、StringBuffer、StringBuilder区别及其使用场景

        String底层是final char[]来存储数据的,是final修饰的,不可变,每次操作都会产生新的String对象

        StringBuffer和StringBuilder底层都是 char[]来存储数据的,都是在原对象上操作

        StringBuffer是线程安全的,它的方法都是用synchronized修饰

        性能:StringBuilder > StringBuffer > String

        需要经常改变字符串的内容使用后面两个

4、重载和重写

        重载:发生在同一个类中,方法名必须相同,参数类型不同,个数不同,顺序不同,方法返回值和访问修饰符可以不同,发生在编译的时候

        重写:发生在父子类中,方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类;如果父类方法访问修饰符为private则子类就不能重写该方法

5、接口和抽象类的区别

        抽象类可以存在普通成员函数,而接口中只能存在public abstract方法

        抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的

        抽象类只能继承一个,接口可以实现多个

        接口的设计目的是对类的行为进行约束,也就是提供一个机制,可以强制要求不同的类具有相同的行为。它只约束了行为的有无,但不对如何实现行为进行限制;

        抽象类的设计是为了代码复用,抽象类将不同类的相同行为提取出来,让这些类都派生于一个抽象类,抽象类不能实例化;

        抽象类是对类本质的抽象,表达的是is a的关系,如BMW is a Car。抽象类包含并实现子类的通用特性,将子类存在差异化的特性进行抽象,交由子类去实现。

        接口是对类行为的抽象,表达的是like a的关系,如Bird like a Aircraft。但其本质是Bird is a Bird。接口的核心是定义行为,即实现类可以做什么,至于实现类主体是谁,是如何实现的,接口并不关心

6、list和set的区别

        list:有序,按对象进入的顺序保存对象,可重复,允许多个null元素对象,可以使用iterator取出所有元素,再逐一遍历,还可以用get()方法获取指定下标的元素

        set:无序,不可重复,最多允许有一个null元素对象,取元素时,只能用iterator接口取所有元素,再逐一遍历各个元素

7、hashcode

        hashcode()的作用时获取哈希码,也称为散列码;它实际上是返回一个int整数;这个哈希码的作用是确定该对象在哈希表中的索引位置。hashcode()定义在JDK的Object.java中,Java中的任何类都包含hashcode()方法。散列表存储的是键值对(key-value),特点是:能根据键快速的检索出对应的值。这其中就利用到了散列码

        如果两个对象相等,则hashcode一定相同

        如果两个对象相等,对两个对象分别调用equals方法都返回true

        两个对象有相同的hashcode值,他们也不一定相等

        hashcode和equals方法一样,必须被覆盖

        hashcode()的默认行为是对堆上的对象产生独特值。如果没有重写hashcode,则该Class的两个对象无论如何都不会相等,即使这两个对象指向相同的数据

8、ArrayList和LinkedList

        ArrayList:基于动态数组,连续内存存储,适合下标访问(随机访问),数组的扩容机制:数组长度固定,超出长度存数据时需要新建数组,然后将老数组的数据拷贝到新数组,如果不是尾部插入数据还会涉及到元素的移动,使用尾部插入法并指定初始容量可以极大提升性能

        LinkedList:基于链表,每个节点存储数据和指向下一个节点的指针,可以存储在分散的内存中,适合做数据插入和删除操作,不适合查询:需要逐一遍历LinkedList必须使用Iterator,不能使用for循环,因为每次for循环体内通过get(i)方法取得某一元素时都需要对list重新进行遍历,性能消耗极大

9、HashMap 和 HashTable

        HashTable里面的方法都是由synchronized关键字修饰的,是线程独占的和安全的,效率较低

        HashMap线程不安全 数组+链表

10、对线程安全的理解

        不是线程安全,应该是内存安全,堆是共享内存,可以被所有线程访问

        当多个线程访问一个对象时,如果不进行额外的同步控制或其他的协调操作,调用这个对象的行为可以获得正确的结果,我们就说这个对象是线程安全的。

        堆是进程和线程共有的空间,分为全局堆和局部堆。全局堆就是所有没有分配的空间,局部堆就是用户分配的空间,堆在操作系统对进程初始化的时候分配,运行过程中也可以向系统要额外的堆,但是用完了要还给操作系统,不然会造成内存泄露。

        栈是每个线程独有的,保存其运行状态和局部变量的,栈在线程开始的时候初始化,每个线程的栈相互独立,因此栈是线程安全的。操作系统在切换线程的时候会自动切换栈,栈空间不需要在Java中显示的分配和释放。

        目前主流操作系统都是多任务的,即多个进程同时运行。为了保证安全,每个进程只能访问分配给自己的内存空间,而不能访问别的进程的,这是由操作系统保障的。

        在每个进程的内存空间中都会有一块特殊的公共区域,通常称为堆(内存),进程内的所有线程都可以访问到该区域,这就是造成问题的潜在原因。

11、对守护线程的理解

        任何一个守护线程都为整个JVM中所有用户线程服务。GC垃圾回收线程是一个经典的守护线程,当GC线程是JVM上仅剩的线程时,GC线程会自动离开。它始终在低级别的状态中运行,用于实时监控和管理系统中的可回收资源。

        thread.setDaemon(true)可以将线程设置为守护线程,但是此操作必须在thread.start()之前进行设置,否则程序会抛出异常,即不能将一个正在运行的线程设置为守护线程;守护线程不能用于访问固有资源;使用后台线程就不能使用线程池;

12、ThreadLocal的理解和使用场景

ThreadLocal叫做线程变量,意思是ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的,也就是说该变量是当前线程独有的变量。ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。

ThreadLocal与synchronized有本质的区别:

1、Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。

2、Synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。

而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。

13、ThreadLocal内存泄露原因和如何避免

        不会再使用的对象或者变量占用的内存不能被回收,就是内存泄露。内存泄露为程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但是内存泄露堆积后果很严重,无论多少内存,迟早会被占光

        OOM Out Of Memry 内存溢出

        强引用:使用最普遍的引用(new),一个对象具有强引用,不会被垃圾回收器回收。当内存空间不足,Java虚拟机宁愿抛出OOM错误,是使程序异常终止,也不会回收这种对象。

        如果想取消强引用和某个对象之前的关联,可以显示地将引用赋值为null,这样可以使JVM在合适的时间就会回收该对象。

        弱引用:JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在Java中,用java.lang.ref.WeakReference类来表示。可以在缓存中使用弱引用。

        ThreadLocal的实现原理:每一个Thread维护一个ThreadLocalMap,key为使用弱引用的ThreadLocal实例,value为线程变量的副本

        ThreadLocal内存泄露的根源:由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应的key就会导致内存泄露,而不是因为弱引用

ThreadLocal正确使用方法

        每次使用完ThreadLocal都调用它的remove()方法清除数据

        将ThreadLocal变量定义为private static,这样就一直存在ThreadLocal的强引用,也就能保证任何时候都能通过ThreadLocal的弱引用访问到Entry的value值,进而清除掉

14、并发、并行、串行的区别

串行:任务在时间上不可能发生重叠,前一个任务没完成,下一个任务必须等待

并行:任务在时间上是重叠的,两个任务在同一时间互不干扰的同时执行

并发:允许两个任务彼此干扰。同一时间点,只有一个任务运行,多个任务交替执行

15、并发的三大特性---保证线程安全

原子性

        在一个操作中CPU不可以在中途暂停然后再调度,即不被中断操作,要不全部执行完成,要不都不执行。例子:银行转账

有序性

        程序执行的顺序按照代码的先后顺序执行。虚拟机在进行代码编译时,对于那些改变顺序之后不会对最终结果造成影响的代码,虚拟机不一定会按照我们写的代码的顺序来执行,有可能将他们重新排序。实际上,对于有些代码进行重排序之后,虽然对变量的值没有造成影响,但有可能会出现线程安全问题。

可见性

        当一个线程修改了共享变量的值,其他线程能够看到修改的值。

16、为什么使用线程池?解释一下线程池参数?

        线程池创建 

        ①ThreadPoolExecutor

        ② ExecutorService pool= Executors.newCachedThreadPool(); // Executors是ThreadPoolExecutor的子类

        线程池的核心线程数为5,如果这5个线程都被一个个的任务占用,后面还有任务被提交,此时,这些任务会被放入任务队列中,假如初始化的时候设置任务队列的长度为10。如果后续没有任务提交或是提交的任务数小于等于10,则任务队列中的任务会等待核心线程中的线程执行完成后排队执行。如果超过10个,那么此时就会创建最大线程数,如果最大线程数是10,那么就会创建5个线程处理其他任务,如果还有源源不断的任务被提交,就需要考虑拒绝策略了;后续没有那么多任务了,空闲的线程就会被回收掉,但是核心线程不会被释放;

即:5(corePoolSize) + 10(workQueue) + 5(maximumPoolSize - corePoolSize) + 1 + 拒绝策略

        线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。

        线程池是运用场景最多的并发框架,几乎所有需要一步或者并发执行任务的程序都可以使用线程池。使用线程池一般有以下三个好处:

①降低资源的消耗,通过重复利用已经创建的线程降低线程创建和销毁造成的消耗。

②提高响应速度,当任务到达的时候,任务可以不需要等到线程创建就能立刻执行。

③提高线程的可管理性,线程是稀缺资源,使用线程池可以统一的分配、调优和监控。

        线程池的七大参数

(1)corePoolSize 核心线程数

        核心线程一开始就会有,是常驻线程。线程池中会维护一个最小的线程数量,即使这些线程处理空闲状态,他们也不会被销毁,除非设置了allowCoreThreadTimeOut。这里的最小线程数量就是corePoolSize。任务提交到线程池后,首先会检查当前线程数是否达到了corePoolSize,如果没有达到的话,则会创建一个新线程来处理这个任务。

(2)maximumPoolSize 最大线程数

        表示最大允许被创建的线程数量。当前线程数达到corePoolSize后,如果继续有任务被提交到线程池,会将任务缓存到工作队列中。如果队列已满,则会去创建一个新线程来处理任务。线程池不会无限制地创建新线程,有一个最大线程数的限制,这个数由maximunPoolSize指定。

(3)keepAliveTime 线程存活时间

        线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁,这里的指定时间由keepAliveTime设定。

(4)unit 线程存活时间单位

        keepAliveTime的计量单位

(5)workQueue 工作队列

        用来存放待执行的任务。新任务被提交后,会先进入到工作队列中,任务调度时再从队列中取出任务。jdk中提供了四种工作队列:

        ①ArrayBlockingQueue

        基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经满了,则创建一个新线程来处理新任务,如果线程数量已经达到maximumPoolSize,则会执行拒绝策略。

        ②LinkedBlockingQuene

        基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而基本不会去创建新线程直到maxPoolSize(很难达到Interger.MAX这个数),因此使用该工作队列时,参数maxPoolSize其实是不起作用的。

        ③SynchronousQuene

        一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。

        ④PriorityBlockingQueue

        具有优先级的无界阻塞队列,优先级通过参数Comparator实现。

(6)threadFactory 线程工厂

        创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等

(7)handler 拒绝策略

        当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略,就是解决这个问题的,jdk中提供了4中拒绝策略:

①CallerRunsPolicy

        该策略下,在调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,则直接抛弃任务。

②AbortPolicy

        该策略下,直接丢弃任务,并抛出RejectedExecutionException异常。

③DiscardPolicy

        该策略下,直接丢弃任务,什么都不做。

④DiscardOldestPolicy

        该策略下,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列

17、简述线程池处理流程

18、线程池中阻塞队列的作用?为什么是先添加队列而不是先创建最大线程? 

(1)线程池中阻塞队列的作用:
        普通队列只能保证作为⼀个有限长度的缓冲区,如果超出了缓冲长度,就无法保留当前的任务了,阻塞队列通过阻塞可以保留住当前想要继续入队的任务,保持任务的活力,当前面有任务解决,阻塞任务会被唤醒,进入缓冲区。
        阻塞队列可以保证任务队列中没有任务时阻塞获取任务的线程,使得线程进⼊wait状态,释放cpu资源。
        阻塞队列自带阻塞和唤醒的功能,不需要额外处理,无任务执⾏时,线程池利⽤阻塞队列的take⽅法挂起,从⽽维持核⼼线程的存活、不⾄于⼀直占⽤cpu资源
(2)为什么是先添加列队而不是先创建最大线程?
        在创建新线程的时候,是要获取全局锁的,这个时候其它线程的都得阻塞,影响整体效率。

19、线程池中线程复用原理

        线程池可以把线程和任务进行解耦,线程归线程,任务归任务,摆脱了之前通过 Thread 创建线程时的一个线程必须对应一个任务的限制。

        在线程池中,同一个线程可以从 BlockingQueue 中不断提取新任务来执行,其核心原理在于线程池对 Thread 进行了封装,并不是每次执行任务都会调用 Thread.start() 来创建新线程,而是让每个线程去执行一个“循环任务”,在这个“循环任务”中,不停地检查是否还有任务等待被执行,如果有则直接去执行这个任务,也就是调用任务的 run 方法,把 run 方法当作和普通方法一样的地位去调用,相当于把每个任务的 run() 方法串联了起来,所以线程数量并不增加。

20、spring是什么

spring是一个轻量级的控制反转和面向切面的容器框架

        --从大小和开销两方面而言spring都是轻量级的

        --通过控制反转的技术达到松耦合的目的

        --提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务进行内聚性的开发

        --包含并管理应用对象的配置和声明周期,这个意义上是一个容器

        --将简单的组件配置,组合成为复杂的应用,这个意义上是一个框架

21、谈谈你对AOP的理解

        AOP (面向切面编程):即面向规则编程,找出多个类中有一定规律的代码,开发时拆开,运行时再合并。本质:解耦,专人做专事。

目的:

(1)、把核心业务和非核心业务进行分离    

(2)、把多个非核心业务处理(点)变成一个切面(之前的非核心业务代码需要写多次变成只需要写一次。)

要求:

        只有当核心类中有多个业务需要进行处理的时候使用AOP较为合理,如果只有一个业务需要被处理那么就没有必要使用AOP了。

应用场景:
1、用于日志打印

2、用于全局异常处理拦截

3、返回值统一处理

4、多数据源切换
 

22、Springbean生命周期

        简单点说就是:bean的实例化–>bean的属性赋值–>bean的初始化–>bean的销毁(包含bean的使用)

        实例化和属性赋值对应构造方法和setter方法的注入,初始化和销毁是用户能自定义扩展的两个阶段。

        在这四步之间穿插的各种扩展点,分为两类,第一类是影响多个BEAN的,第二类是只调用一次的接口。在实例化和初始化阶段,都有beanPostProcessor参与,其中AOP的逻辑就是在instantiationAwareBeanPostProcessor实现的。第二类分为aware类型的接口和生命周期接口。

        Aware类型的接口分为直接获取资源的和通过beanPostProcessor获取的applicationContext类型的aware。

生命周期接口只有初始化和销毁,因为实例化和属性赋值都是Spring帮助我们做的。

23、谈谈IOC的理解

        IOC容器实际上就是一个map,里面存放的是各种对象(在xml里配置的bean节点,@repository、@service、@controller、@component),在项目启动的时候会读取配置文件里面的bean节点,根据全限定类名使用反射创建对象放到map里,扫描到打上上述注解的类,通过反射创建对象放到map里。

        这个时候map里面就有各种对象了,接下来我们在代码里需要用到里面的对象时,再通过ID注入(@autowired、@resource,xml文件里的bean节点内的ref属性,项目启动的时候会读取xml节点ref属性,根据id注入,也会扫描这些注解,根据类型或id注入;id就是对象名)

24、谈谈对反射的理解
(1)正常的类加载过程和反射

(2) 通过反射创建对象

1)使用Class对象的newInstance()方法来创建Class对象对应类的实例。

Class<?> c = String.class;
Object str = c.newInstance();

2)先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建对象,这种方法可以用指定的构造器构造类的实例。

//获取String的Class对象
Class<?> str = String.class;
//通过Class对象获取指定的Constructor构造器对象
Constructor constructor=c.getConstructor(String.class);
//根据构造器创建实例:
Object obj = constructor.newInstance(“hello reflection”);
 25、BeanFactory和ApplicationContext的区别

ApplicationContext是继承BeanFactory的子接口

(1)概述
        BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。其中ApplicationContext是BeanFactory的子接口。

BeanFactory

        BeanFactory是Spring里面最底层的接口,是Ioc的核心,定义了Ioc的基本功能,包含了各种Bean的定义、加载、实例化,依赖注入和生命周期管理;

ApplicationContext

        ApplicationContext接口作为BeanFactory的子类,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:
        ①继承MessageSource,因此支持国际化
        ②资源文件访问,如URL和文件(ResourceLoader)
        ③载入多个(有继承关系)上下文(及同时加载多个配置文件),使得每一个上下文都专注于一个特定的层次
        ④提供在监听器中注册bean的事件;

(2)二者区别


        ①BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在运行时使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化,这样,我们就不能发现一些存在的Spring的配置问题。而ApplicationContext则相反,它是在容器启动时,在编译期间,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误。 相对于基本的BeanFactory,ApplicationContext 唯一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。

        ②BeanFacotry延迟加载,如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常;而ApplicationContext则在初始化自身时检验,这样有利于检查所依赖属性是否注入;所以通常情况下我们选择使用 ApplicationContext。应用上下文则会在上下文启动后预载入所有的单实例Bean。通过预载入单实例bean ,确保当你需要的时候,你就不用等待,因为它们已经创建好了。

        ③BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。(Applicationcontext比 BeanFactory 加入了一些更好使用的功能。而且 BeanFactory 的许多功能需要通过编程实现而 Applicationcontext 可以通过配置实现。比如后处理 bean , Applicationcontext 直接配置在配置文件即可而 beanFactory 这要在代码中显示的写出来才可以被容器识别。 )

        ④BeanFactory主要是面对与 spring 框架的基础设施,面对 spring 自己。而 Applicationcontex 主要面对与 spring 使用的开发者。基本都会使用 Applicationcontex 并非 BeanFactory 。

        ⑤因此,在实际开发中,通常都选择使用 ApplicationContext,而只有在系统资源较少时,才考虑使用 BeanFactory。

26、解释一下spring支持的几种bean的作用域        

Spring框架支持以下五种bean的作用域:

        ① singleton (单例模式):bean在每个Spring IOC 容器中只有一个实例。

        ② prototype(原型模式):一个bean的定义可以有多个实例。

        ③ request(HTTP请求):每次http请求都会创建一个bean,该作用域仅在基于web的Spring ApplicationContext情形下有效。

        ④ session(会话):在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。

        ⑤ global-session(全局会话):在一个全局的HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。

27、spring框架中的单例bean是线程安全的吗?

        单例bean是线程不安全的,spring中的bean默认是单例模式的,框架并没有对bean进行多线程的封装处理。如果bean是有状态的,就需要我们自己来进行线程安全的保证。最简单的方法就是改变bean的作用域,把单例模式改为原型模式,这样每次请求bean就相当于是new Bean(),这样就可以保证线程安全了;

        有状态就是有数据存储功能

        无状态就是没有数据存储功能

28、spring框架中都用到了哪些设计模式

(1)工厂模式:Spring使用工厂模式,通过BeanFactory和ApplicationContext来创建对象

(2)单例模式:Bean默认为单例模式

(3)策略模式:例如Resource的实现类,针对不同的资源文件,实现了不同方式的资源获取策略

(4)代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术

(5)模板方法:可以将相同部分的代码放在父类中,而将不同的代码放入不同的子类中,用来解决代码重复的问题。比如RestTemplate, JmsTemplate, JpaTemplate

(6)适配器模式:Spring AOP的增强或通知(Advice)使用到了适配器模式,Spring MVC中也是用到了适配器模式适配Controller

(7)观察者模式:Spring事件驱动模型就是观察者模式的一个经典应用。

(8)桥接模式:可以根据客户的需求能够动态切换不同的数据源。比如我们的项目需要连接多个数据库,客户在每次访问中根据需要会去访问不同的数据库

28、描述一下spring bean的生命周期

        ①解析类得到BeanDefinition,如果有多个构造方法则要推断构造方法

        ②确定好构造方法后,进行实例化得到一个对象

        ③对对象中的加入了@Autowired注解的属性进行属性填充

        ④回调Aware方法,比如BeanNameAware,BeanFactoryAware

        ⑤调用BeanPostProcessor的初始化前的方法

        ⑥调用初始化方法

        ⑦调用BeanPostProcessor的初始化后的方法,在这里进行AOP

        ⑧如果当前创建的bean是单例的则会把bean放入单例池

        ⑨使用bean

        ⑩spring容器关闭时调用DisposableBean中destroy()方法

29、spring事务的实现方式和原理以及隔离级别

spring事务的实现方式

        在使用Spring框架时,有两种使用事务的方式。一种是编程式的,一种是声明式(@Transactional注解)。

        首先,事务这个概念是数据库层面的,Spring只是基于数据库中的事务进行了扩展,以及提供了一些能让程序员更加方便操作事务的方式。        

        比如我们可以通过在某个方法上面增加@Transactional注解,就可以开启事务,这个方法中所有的sql都会在一个事务中执行,统一成功或失败。

        在一个方法上加了@Transactional注解后,Spring会基于这个类生成一个代理对象,会将这个代理对象作为bean。

        当在使用这个代理对象的方法时,如果这个方法上存在@Transactional注解,那么代理逻辑会先把事务的自动提交设置为false,然后再去执行原本的业务逻辑方法,如果执行中没有出现异常,那么代理逻辑就会将事务提交,如果出现了异常,那么就会将事务进行回滚。

        当然,针对哪些异常回滚事务是可以配置的,可以利用@Transactional注解中的rollbackFor属性进行配置,默认情况会对RuntimeExceptionError进行回滚。

spring事务隔离级别

        spring事务隔离级别就是数据库的隔离级别:外加一个默认级别

                read uncommitted 未提交读

                read committed 提交读,不可重复读

                repeatable read 可重复读

                serializable 可串行化

数据库配置的隔离级别是read committed,而Spring配置的隔离级别是repeatable read,请问这时隔离级别以哪个为准?

以Spring配置的为准,如果spring设置的隔离级别数据库不支持,效果取决于数据库

30、聚集索引和非聚集索引

        聚集索引就是基于主键创建的索引,除了主键以外的索引统一称为非聚集索引(二级索引)

        在InnoDB的引擎里面,一张表的数据对应的物理文件本身就是按照B+树来组织的,而聚集索引就是按照每张表的主键来构建这样一个B+树,然后叶子节点里面存储了这个表里面的每一行数据记录,所以基于InnoDB这样一个特征,聚集索引并不仅仅是一种索引类型,还代表了一种数据的存储方式,同时也意味着每个表里面必须要有一个主键,如果没有主键,InnoDB会默认选择或者添加一个隐藏列,作为主键索引来存储这个表的数据行。

        一般情况是建议使用自增id作为主键,这样的话id本身是具有连续性,使得对应的数据,也会按照顺序存储在磁盘上,写入性能和检索性能都很高,否则的话使用uuid这种随机id,那么在频繁插入数据的时候就会导致随机磁盘IO,从而会导致性能下降。

        需要注意的是InnoDB里面只能存在一个聚集索引,如果存在多个聚集索引,那么就意味着这个表里面的数据会存在多个副本,不仅会造成磁盘空间浪费,还会导致数据的维护困难。由于在InnoDB里面,主键索引是存储一个表的完整数据,所以如果是基于非聚集索引来查询一条数据的时候,那么最终还是得需要访问主键索引来进行检索。

31、spring事务什么时候会失效?

(1)发生自调用,类使用的this调用本类的方法,此时this对象不是代理类,而是类本身

(2)方法不是public,@Transactional只能用于public的方法上,否则事务会失效;如果要用在非public的方法上,可以开启AspectJ代理模式

(3)数据库不支持事务

(4)没有被spring管理,该类根本就没有用注解,不会在spring启动的时候产生bean

(5)异常被吃掉(try catch),事务不会回滚,或者抛出的异常没有被定义

32、什么是bean的自动装配,有哪些方式

<bean id="cutomer" class="com.xx.xxx.Cumtor" autowire="">

<bean id="cutomer" class="com.xx.xxx.Cumtor" autowire="byName">

<bean id="cutomer" class="com.xx.xxx.Cumtor" autowire="byType">

<bean id="cutomer" class="com.xx.xxx.Cumtor" autowire="constructor">

<bean id="cutomer" class="com.xx.xxx.Cumtor" autowire="autodetect">

autodetect = constructor + byType

33、#{} 和 ${}的区别

        #{}是预编译处理,${}是字符串替换。

(1)mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值。

(2)mybatis在处理时,把{}替换成变量的值。

(3)使用#{}可以有效的防止SQL注入,提高系统安全性。原因在于:预编译机制。预编译完成之后,SQL的结构已经固定,即便用户输入非法参数,也不会对SQL的结构产生影响,从而避免了潜在的安全风险。

(4)预编译是提前对SQL语句进行预编译,而其后注入的参数将不会再进行SQL编译。我们知道,SQL注入是发生在编译的过程中,因为恶意注入了某些特殊字符,最后被编译成了恶意的执行操作。而预编译机制则可以很好的防止SQL注入。

1){} 方式

#{}: 解析为SQL时,会将形参变量的值取出,并自动给其添加引号。 例如:当实参username="Amy"时,传入下Mapper映射文件后

<select id="findByName" parameterType="String" resultMap="studentResultMap">
    SELECT * FROM user WHERE username=#{value}
</select>

SQL将解析为:

SELECT * FROM user WHERE username="Amy"

2)${} 方式

${}: 解析为SQL时,将形参变量的值直接取出,直接拼接显示在SQL中

例如:当实参username="Amy"时,传入下Mapper映射文件后

<select id="findByName" parameterType="String" resultMap="studentResultMap">
    SELECT * FROM user WHERE username=${value}
</select>

SQL将解析如下:

SELECT * FROM user WHERE username=Amy

34、spring MVC工作流程

35、spring MVC的主要组件

36、springboot自动配置原理

37、如何理解springboot中的starter

38、什么是嵌入式服务器?为什么要使用嵌入式服务器?

39、mybatis的优缺点

40、mybatis和Hiberabate对比

ORM Object Relational Mapping 对象关系映射

Hiberabate是ORM框架;mybatis不是ORM框架,其通过SQL连接数据库

41、#{}和${}的区别是什么

#{}是预编译处理,是占位符,${}是字符串替换,是拼接符.

#{value} --> 'value'

${}存在SQL注入问题

${value} --> value

42、简述mybatis的插件运行原理;如何编写一个插件;

43、Java中的Handler

        Handler主要用于异步消息的处理:当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外一个部分在消息队列中逐一将消息取出,然后对消息进行处理,也就是发送消息和接收消息不是同步的处理。 这种机制通常用来处理相对耗时比较长的操作。

44、索引的基本原理

45、B树和B+树

B树中,数据存放在key中,占用了索引的key的空间

B+树将数据全部放到叶子节点,留下更多的空间放 key, key 越多,宽度越宽,同样的数据量,宽度越大,高度越小。查找次数就越小。

46、MySQL聚簇和非聚簇索引的区别

47、MySQL索引的数据结构,各自优劣

48、索引设计的原则

查询更快、占用空间更小

49、MySQL锁的类型有哪些

50、MySQL的执行计划(SQL优化)

explain + select语句

51、事务的基本特性和隔离级别

52、怎么处理慢查询

        如果是使用主键查询还是比较慢,那可能是数据量实在太大,没有优化余地,需要考虑的是分表,将数据拆分到多个表里面

53、ACID靠什么保证的?

54、什么是MVCC?

多版本并发控制

55、MySQL主从同步原理

56、简述MyISAM和InnoDB的区别

57、简述MySQL中索引类型对数据库性能的影响

58、RDB和AOF机制

59、Redis的过期键的删除策略

60、Redis线程模型,单线程快的原因

61、缓存雪崩、缓存穿透、缓存击穿

62、简述Redis事务实现

63、redis的集群方案

64、redis主从复制的核心原理

65、Java面向对象的特征

        封装继承多态,增加了代码的复用性,可移植性,健壮性和灵活性

66、高并发中的集合有哪些问题

67、内存区域类型

1.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制;

1. 栈:存放基本类型 的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new出来的对象)或者常量池中(字符串常量对象存放的常量池中),局部变量【注意:(方法中的局部变量使用final修饰后,放在堆中,而不是栈中)】

2.堆:存放使用new创建的对象,全局变量

3. 静态域:存放静态成员(static定义的);

4. 常量池:字符串常量和基本类型常量(public static final)。有时,在嵌入式系统中,常量本身会和其他部分分割离开(由于版权等其他原因),所以在这种情况下,可以选择将其放在ROM中 ;

5. 非RAM存储:硬盘等永久存储空间

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值