面试中常见的问题类型
第一类:答案是 (是/否) 类型的问题
Tips:此类问题肯定都有隐含的问题:为什么?
> 案例:String类能不能被继承?
> 初级回答:直接给出明确的答案:不能
> 正常回答:阐述为什么:String是final修饰的类
> 优秀回答:最好做到问一答三(除非面试官明确表示,不让你多说)
第二类:罗列型问题
> Tips: 你的回答必须有条目有顺序。
> 最重要的条目请首先回答多使用有前后顺序的词汇(首先,其次,再次......)
> 案例: get请求和post请求有什么区别?
> 第一:GET请求多用来获取数据,POST请求多用来提交数据
> 第二:GET请求参数......POST......
第三类:纯粹的阐述类问题
> Tips:议论文的写作方式(总分总)
> 案例:你是怎么理解多态的?(什么是多态?)
> ①明确给出一个定义:同一种类型的不同表现形态。
> ②逐步阐述其中的细节:
(1)在程序中的体现就是 父类的引用指向子类的实例。
(2)多态的出现必须有继承和方法重写。
(3)举例说明........
> ③再次总结(作用):多态可以让我们程序对扩展开放,对修改关闭
Java基础部分
一、基本概念
- 1、什么是java?
Java是一门面向对象编程语言
java语言不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程 。
- 2、什么是JVM?
JVM是一个可以运行java字节码文件的假想计算机。
JVM是解释运行字节码文件的软件和工具,也是java语言跨平台的基本支持。java语言是运行在JVM上的,而JVM是运行在各种不同的平台上的,那么java程序的运行和平台本身就解耦了,那么自然也就跨平台了。
- 3、JDK和JRE有什么区别?
JDK是java开发工具包。JRE是java的运行环境。
JDK包含了JRE和所有的java开发工具,可以编译和运行java程序。而JRE只是运行环境,只能解释运行字节码文件。
- 4、什么是跨平台性?原理是什么
跨平台就是Java程序的字节码文件可以在任何平台上直接运行。基本支持就是JVM。Java源程序通过编译器编译成字节码文件,而字节码文件是运行在JVM上的,每个不同的平台上都有对应的JVM,那么java的字节码文件就和平台完全无关了,所以就可以跨平台了。
二、基础语法
- 5、 你怎么理解变量
变量是存储数据的最小单元。
变量是一块命名的内存区域,以便程序进行访问,变量用来存储数据,随着程序的执行,存储的数据也可能跟着改变。
- 6、 Java有哪些数据类型
java中的数据类型分为基本类型和引用类型。
基本类型有:byte,short,char,int,long,float,double,boolean。
其余的都是引用类型。
- 7、switch 是否能作用在byte上,是否能作用在long上,是否能作用在String上?
可以作用在byte上,不能作用在long上,JKD1.7之间不能作用在String上,1.7之后就可以作用在String上了。
switch只能做等值判断,所以我们自定义的引用类型,除过常量和枚举之外,都不能作为switch的判断表达式。
- 8、访问修饰符 public,private,protected,以及不写(默认)时的区别
Java中,可以使用访问修饰符来保护对类、成员变量、成员方法、成员内部类和构造方法的访问。Java 支持 4 种不同的访问权限。
private : 在同一类内可见。可以用来修饰成员变量、成员方法和成员内部类。 注意:不能修饰类(外部类)
default (即缺省,什么也不写,不使用任何关键字): 在同一包内可见,不使用任何修饰符。可以修饰类、接口、成员变量、成员方法。
protected : 对同一包内的类和所有子类可见。可以修饰成员变量、成员方法。 注意:不能修饰类(外部类)。
public : 对所有类可见。可以修饰类、接口、成员变量、方法。
- 9、&和&&的区别
&运算符有两种用法:(1)按位与;(2)逻辑与,但是没有短路作用。
&&运算符是短路与运算。逻辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是true 整个表达式的值才是 true。&&之所以称为短路运算,是因为如果&&左边的表达式的值是 false,右边的表达式会被直接短路掉,不会进行运算。
- 10、什么是赋值
赋值运算符就是将运算符右边的表达式的值赋值给左边的变量。
如果右边是具体的数值,则是将数值直接赋值给变量,如果基本类型的变量就是变量的值的副本赋值给左边的变量,如果右边是应用类型,则是将引用对象的地址的副本赋值给左边的变量。
- 11、break、continue 、return 的区别
break 跳出总上一层循环,不再执行循环(结束当前的循环体)
continue 跳出本次循环,继续执行下次循环(结束正在执行的循环 进入下一个循环条件)
return 程序返回,不再执行下面的代码(结束当前的方法 直接返回)
- 12、char型变量中能不能存贮一个中文汉字?为什么?
char型变量是用来存储Unicode编码的字符的,unicode编码字符集中包含了汉字,所以,char型变量中当然可以存储汉字。不过,如果某个特殊的汉字没有被包含在unicode编码字符集中,那么,这个char型变量中就不能存储这个特殊汉字。补充说明:unicode编码占用两个字节,所以,char类型的变量也是占用两个字节。
- 13、什么是值传递,什么是引用传递?
Java中没有值传递和引用传递的概念,都是值传递。
基本类型传递时就是值的拷贝,因为基本数据类型的值就是存储在栈内存中。
引用数据类型传递时,传递的是引用地址的拷贝,因为引用数据类型在栈中只存储引用地址。
三、面向对象
- 14、 类是什么 ?
类就是一类具有相同或者相似的特征和行为的事物的抽象描述。
在程序中,一个类就是一个新的数据类型,类中主要描述这类数据的属性和方法,我们可以根据类创建对应的数据对象。
- 15、对象是什么
“万物皆对象”。对象就是一个类型的具体存在的实例。
在程序中就是通过类的构造方法创建的类的实例。
- 16、类和对象是什么关系
类是一个抽象的描述,对象是根据类创建的实例。
在程序中,一个类就是一个数据类型,对象就是根据这个类在内存中开辟的空间,当然这个空间中缓存了这个类申明的相关属性和方法。
- 17、 面向对象的特征都有那些?
封装:就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。封装的目的就是代码重用、影藏细节、方便调用。
继承:可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。目的是代码重用。也是多态的前提。
多态:同一种类型的不同表现形态。就是将子类型对象赋值给父类型的引用,导致父类型的引用在运行期表现出子类型的状态。
- 18、 成员变量的默认值
所有的成员变量都是有默认值的。
成员变量的默认值和数组的默认值一致:
所有的整数类型都是0或者0L
float是0.0F
double是0.0
char是空字符\u000
boolean是 false
其他类型全部都是null
- 19、方法重载
重载是我们可以在一个类中定义同名的方法,通过不同的参数类区分这些方法,在调用的时候,JVM会根据传入的参数自动调用对应的方法。
所谓参数不同,有三种情况:
参数的类型不同。
参数的个数不同。
参数的顺序不同。
方法重载和方法的返回值无关。
静态方法和非静态方法之间的情况不能“构成”重载。
- 20、构造方法
在每一个类中都有构造方法,构造方法就是用来创建这个类的对象的。
构造方法的要求:
构造方法的方法名和类名完全一致。
构造方法没有返回值。也不能写void。
和访问修饰符无关,和参数无关。
- 21、 this关键字
- 详解
this关键字可以表示当前类对象或者调用当前类的其他构造方法。
this在当前类的构造方法中或者当前类的实例方法中可以表示当前对象。
this()可以在构造方法中调用本类的其他的重载的构造方法,这时this()必须放在构造方法的第一行
this不能在静态方法或者静态块中使用
- 22、 类继承
在java中继承发生在两个类之间或者两个接口之间。比如A类继承了B类,A类会自动拥有B类的财富(属性和方法)。
继承者称之为子类,被继承者称之为父类或者基类。
父类只能有一个。 这个概念其实就是java中单继承的概念。
所谓单继承就是:任何一个类最多只能有一个直接父类。 但是一个父类可以有多个子类。
- 23、 构造方法是否可以被继承
在类的继承中,构造方法是不能继承的。
任何一个类的默认的构造方法中都默认的调用类父类的无参数的构造方法
当在子类的构造方法中使用super调用父类的构造方法时。必须写在第一行。
- 24、 父子类之间的类型转换
任何需要父类的地方,都可以使用子类代替。这也是所谓里氏替换原则。
子类对象赋值给父类引用,不需要类型转换。
父类对象赋值给子类引用,需要强制类型转换,如果父类对象本身不是子类的实例会出现类型转换异常。
- 25、什么是方法重写?
所谓方法重写就是在子类中重新定义父类的方法
要求子类中的方法的名称,参数列表必须和父类的一致。访问修饰符应该大于等于父类访问修饰符范围。返回值类型要和父类一致或者是父类方法返回值类型的子类。
方法重写是实现多态的基本要求。
- 26、 super关键字
super关键在子类中使用
super在子类中可以表示父类对象,当super作为父类对象使用的时候,可以调用父类被覆盖的方法或者没有被覆盖的方法。但是不能调用父类私有的任何成员。
super还可以在子类的构造方法中调用父类的构造方法, 当super用来调用父类的构造方法的时候,必须写在子类构造方法的第一行。
- 27、多态
所谓多态就是同一种类型的不同的表现形态,就是我们将不同的子类对象当作父类来看,这时这些对象表现的是子类本身的形态。
多态产生的条件就是:第一要继承(实现),第二要重写方法。
我们一般所说的多态都是运行时多态。
有时我们也会将方法重载称之为静态多态。
多态可以提高程序的可扩展性。
- 28、java中实现多态的机制是什么
靠的是父类或者接口定义的引用变量可以指向子类或者具体实现类的实例对象,而程序调用的方法在运行期才动态绑定,就是引用变量所指向的具体的实例对象的方法,也就是内存里正在运行的那个对象的方法,而不是引用变量的类型中定义的方法。
- 29、 static修饰符
static修饰符可以修饰:成员变量,方法,成员内部类,静态代码块。
static修饰的成员可以使用类名直接调用。
static修饰的任何成员都是属于类的。不是属于某一个对象的。所以才可以使用类名直接调用,当然了也可以使用对象调用,但是这种方式是不合理。
在任何的静块(方法)中都不能使用非静态的任何引用。
static修饰的代码块为静态代码块,静态代码块会在类加载的时候执行一次,之后就不再执行了。静态代码块往往被用来做一些初始化工作。
- 30、final修饰符
final修饰符表示是最终的。
final修饰的成员变量必须在申明的同时赋值,而且不能被重新赋值。final修饰的成员变量一般都是常量。final修饰的局部变量只能赋值一次,不能重复赋值。
final修饰的方法不能被子类重写。
final修饰的类不能被继承。默认情况下final修饰的类中的所的方法都是final的。
- 31、 抽象类
抽象类是使用abstract修饰的。
抽象类不能实例化对象。
抽象类中可以定义抽象方法和非抽象方法。
抽象类就是为了让其他的类来继承。
抽象类可以继承非抽象类。
当一个类继承了抽象类之后,这个类必须实现抽象类中的抽象方法。当然如果这个子类也是抽象,就可以选择不实现。
- 32、 抽象方法
抽象方法是必须使用abstract修饰的。
抽象方法必须写在抽象类或者接口中。
抽象方法是没有方法体的。
抽象方法不能使用static或者final修饰。
- 33、抽象类和接口有什么区别
- 意义上:
- 抽象类本质上是一个类,具有类的所有的特征和意义。类是用来描述一类事物的。接口并不具备类的特征和意义。
- 语法上:
(1)抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
(2)抽象类要被子类继承,接口要被类实现。
(3)接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现
(4)接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
(5)抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。
(6)抽象方法只能申明,不能实现,接口是设计的结果 ,抽象类是重构的结果
(7)抽象类里可以没有抽象方法
(8)如果一个类里有抽象方法,那么这个类只能是抽象类
(9)抽象方法要被实现,所以不能是静态的,也不能是私有的。
(10)接口可继承接口,并可多继承接口,但类只能单根继承
应用场景上:
抽象类往往用来做模板类,比如模版方法设计模式中的模板类就是抽象类。而接口往往是用来申明规范和标准。
- 34、为什么java中的方法不能根据返回值类型来区分重载。
因为调用时,编译器无法根据返回值类型判断你要调用哪个方法。
方法重载指的是方法的名称相同,参数列表不同。当参数不同时编译器可以根据参数类型调用对应的方法。而无法根据返回值类型判断调用那个方法。
- 35、 抽象方法是否可以定义为static的,是否可以定义为native,是否可以定义为synchronized
都不能,抽象方法是需要子类实现的,而静态方法时不能被子类重写的,而且静态方法可以使用类名直接调用,抽象方法没有方法体,无法调用,所以是矛盾的。 Native修饰的方法是本地方法,需要使用其他语言实现,而抽象方法是没有方法实现的,也是矛盾的。Synchronized和方法的实现细节有关,而抽象方法没有实现,也是矛盾的。
- 36、静态变量和实例变量的区别
语法区别:静态变量需要static关键字修饰,实例变量不需要。
程序运行时的区别:静态变量从属于类,实例变量从属于对象。实例变量必须在创建了实例对象之后,其中的实例变量才会被分配空间,才能使用这个实例变量;静态变量即类变量,只要程序加载了类的字节码,静态变量就会被分配空间,即可使用。
- 37、 int 和 Integer 有什么区别,Integer的值缓存范围
1、Integer是int的包装类,int则是java的一种基本数据类型
2、Integer变量必须实例化后才能使用,而int变量不需要
3、Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值 int数据是在栈中的。 Integer是在堆中的。
4、Integer的默认值是null,int的默认值是0
Integer的值缓存范围是-128~127
- 38、equals与==的区别
==使用来比较两个数据是否一致的,基本数据类型比较的就是数值。引用数据类型比较的是两个对象的地址。
equals是Object类中定义的方法,也是比较当前对象和传入的参数对象的。默认情况下也是使用==做比较的,所以默认情况下和==是一样的效果。一般都会重写equals,重写之后就按照重写的程序执行。 String的equals就是重写的,比较的就是两个字符串的字符序列。
- 39、 静态内部类和非静态内部类有什么区别
内部类是申明在一个类的内部的类
当内部类使用static修饰时,就是静态内部类,静态内部类在外部类没有实例化的情况下就可以实例化,而非静态内部类必须是外部类实例化之后才能实例化。
四、异常
- 40、什么是异常
异常由于非合理的操作或者使用导致的程序运行的意外情况。
java中的意外情况体系为: Throwable、Error 、Exception
Throwable为例外的老祖宗,Error一般是指一些严重的错误导致程序无法继续运行。而Exception就是我们常说的异常,是可以捕获处理,程序可以继续运行的情况。
我们处理异常就是为了让程序继续运行。
- 41、Java中的检查型异常(checked exception)和非检查型异常(RuntimeException)有什么区别
检查型异常(CheckedException):也叫编译期异常,在Java中所有不是RuntimeException(运行时异常)子类的Exception都是检查型异常。当方法中存在抛出检查型异常的操作时,该方法的声明中必须包含throws语句。调用该方法的其他方法也必须对该异常进行处理,如不进行处理则必须在调用函数上声明throws语句。
非检查型异常(UncheckedException):也叫运行时异常,在Java中所有RuntimeException的子类都是非检查型异常,与检查型异常对比,非检查型异常可以不在方法声明中添加throws语句,调用方法上也不需要强制处理。
- 42、 Try-catch-finally的执行过程
(1)try{}语句块中放的是要检测的java代码,可能有会抛出异常,也可能会正常执行
(2)catch(异常类型){}块是当java运行时系统接收到try块中所抛出异常对象时,会寻找处理这一异常catch块来进行处理(可以有多个catch块)
(3)finally{}不管系统有没有抛出异常都会去执行,一般用来释放资源。除了在之前执行了System.exit(0)强制终止JVM。
- 43、 如果执行finally代码块之前方法返回了结果,或者JVM退出了,finally块中的代码还会执行吗?
只有在try里面是有System.exit(0)来退出JVM的情况下finally块中的代码才不会执行。否则finally块中的代码都会执行。
- 44、 Exception、Error有什么区别?
1、Exception 和 Error 都是继承了 Throwable 类,在 Java 中只有 Throwable 类型的实例才可以被抛出(throw)或者捕获(catch),它是异常处理机制的基本组成类型。
2、Exception 是程序正常运行中,可以预料的意外情况,可能并且应该被捕获,进行相应处理。
3、Error 是指在正常情况下,不大可能出现的情况,绝大部分的 Error 都会导致程序(比如 JVM 自身)处于非正常的、不可恢复状态。既然是非正常情况,所以不便于也不需要捕获,常见的比如 OutOfMemoryError 之类,都是 Error 的子类。
- 45、 处理异常的方法
java中的两种异常处理方式
1、try{可能出现异常的代码}catch(可能出现的异常类型 异常对象){处理异常的代码 }
2、在方法签名的后面使用throws 申明这个方法可能抛出的异常,由方法的调用者处理。
五、常用类
- 46、String的“”赋值和new String的不同
当使用“”赋值的时候,会首先检查内存中是否已经存在对应的字符串字样,如果存在就直接指向已经存在的字符串,如果不存在才会开辟新的空间存储对应的字符串。
当使用new String()创建String对象的时候,无论如何都会开辟新的空间。
- 47、String的不可变性
我们常说的String是常量,就是String不可变性,String的不可变性是说String一旦创建存储在内存中的对应的字符序列是不能改变的。但是存储在栈中的引用地址是可以修改的。
- 48、StringBuilder和StringBuffer
StringBuilder和StringBuffer都是可以变的字符串。
我们可以通过它们的API直接修改这些对象在内存中存储的对应的字符序列。
StringBuilder是线程不安全的效率较高,StringBuffer是线程安全的效率较低。
- 49、 是否可以继承String
不能,因为String类是final修饰的,final修饰的类不能被继承,final修饰的方法不能被重写,final修饰的类中的方法默认都是final的,final修饰的量不能被修改
六、集合
- 50、ArrayList
ArrayList底层使用数组实现。
使用无参数的构造创建的ArrayList,在创建初期内部只是一个空数组,只有当放入第一个元素的时候才会创建一个容量为10的数组。
当ArrayList中元素个数和容量相同时,再添加元素就会扩容,扩容为之前的1.5倍。
扩容会创建新的数组,所以扩容是比较耗资源的,如果对ArrayList有大量而频繁的增删改操作可以考虑使用LinkedList。
- 51、 List和Set有什么区别?
List和Set都是Conllection接口的子接口。List中的元素是有顺序的,可以重复的。Set中的额元素是没有顺序的,不可以重复的。
- 52、HashMap和HashSet有什么区别?
(1) HashMap实现的是Map接口。HashSet实现的是Set接口。
(2)HahsMap是以键值对的形式存储数据,HashSet只是存储对象。
(3) HashMap使用键值对来计算HashCode,HashSet使用对象来计算HashCode。
(4) HashSet的底层是使用HashMap实现的。
- 53、 List 和 Map 区别
List:是存储单列数据的集合,存储的数据是有序并且是可以重复的
Map:存储双列数据的集合,通过键值对存储数据,存储 的数据是无序的,Key值不能重复,value值可以重复
- 54、 Arraylist 与 LinkedList 区别
(1)ArrayList底层使用数组实现。LinkedList底层使用链表实现。
(2)对于随机访问 get和set ArrayList的优于LinkedList,因为LinkedList查询时需要指针移动。
(3)对应增删改操作 LinkedList优于ArrayList,因为ArrayList需要创建新数组拷贝数据,元素数量越多效率差距越大。
- 55、 ArrayList 与 Vector 区别
(1) ArrayList和Vector都是list的实现类。
(2)ArrayList和Vector底层都是数组实现的。
主要区别:
(1)ArrayList的所有方法都不是线程安全的效率较高,Vector的所有方法都是线程安全的效率偏低。
(2)ArrayList和Vector的空间增长方式不同,ArrayList按照1.5被扩容,Vector当扩容容量大于0时,新数组的长度为原数组的长度+扩容容量。否则新数组的长度为原数组的两倍。
- 56、 HashMap 和 Hashtable 的区别
核心区别:
(1) HashMap的方法不是同步的,Hashtable的方法是同步的。
(2)HashMap允许value为null,允许有一个key为null。Hashtable不允许出现为null的key和value。
次要区别:
(1) HashMap和Hashtable继承的父类不同。
(2)两个Api也略有不同,
(3)Hashtable默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap默认的初始化大小为16。之后每次扩充,容量变为原来的2倍
- 57、 HashMap 和 ConcurrentHashMap 的区别
(1) HashMap不是线程安全的,ConcurrentHashMap是线程安全的。
(2)ConcurrentHashMap对整个桶数组进行了分段,而HashMap则没有
- 58、 HashMap 的工作原理及代码实现
原理:HashMap是基于hashing的原理,我们使用put(key, value)存储对象到HashMap中,使用get(key)从HashMap中获取对象。当我们给put()方法传递键和值时,我们先对键调用hashCode()方法,返回的hashCode用于找到bucket位置来储存Entry对象。
- 59、 java中有序的Map
1. LinkedHashMap 利用一个链表保存元素的添加顺序。
2. TreeMap 利用一个比较器对元素排序,或者使用元素的自然顺序进行升序排序。
- 60、 HashSet是如何判断元素是否相同的
HashSet的底层是HashMap实现的, 所以比较方式和HashMap的key的比较方式相同。
先比较key的hashcode值,如果hashcode不同就认为不同,如果hashcode相同再比较equals,如果equals返回true,就表示相同,否则就表示不同。
七、IO
- 61、 java中有几种流?
按照流的方向可以分为:输入流和输出流。
按照流处理数据的单位可以分为:字节流和字符流。
- 62、字节流如何转换为字符流
字节输入流可以通过InputStreamReader流转换,InputStreamReader流的构造方法的输入参数就是InputStream。
字节输出流可以通过OuputStringWriter流转换,OutputStreamWroiter流的构造方法的输入参数就是OutputStream。
- 63、 什么是序列化,java中如何实现序列化
为了便于存储或者传输,将一个对象转换为另一种形式的过程就是序列换,反之就是反序列化。类似于商家将商品打包(序列化),方便快递送货,到货后买家将商品拆包(反序列化)。
Java中通过ObjectOuputStrean将对象序列化,要求对象类必须实现java.io.Serialization接口。通过ObjectInputStream将对象反序列化。
- 64、字节流和字符流的区别
节流在操作时本身不会用到缓冲区(内存),是文件本身直接操作的,而字符流在操作时使用了缓冲区,通过缓冲区再操作文件。
字符流处理的单元为2个字节的Unicode字符,分别操作字符、字符数组或字符串,而字节流处理单元为1个字节,操作字节和字节数组。
字节流可用于任何类型的对象,包括二进制对象,而字符流只能处理字符或者字符串;
八、多线程
- 65、什么是线程安全?如何保证线程安全
线程安全:就是多线程访问同一代码,不会产生不确定结果。(比如死锁)
如何保证呢:
(1)使用线程安全的类
(2)使用synchronized同步代码块,或者用Lock锁
(3)多线程并发情况下,线程共享的变量改为方法局部级变量
- 66、什么是同步,什么是异步
Java中常说的同步有两类:一类是说方法,一类是说请求。
(1)方法调用:
同步方法调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为
异步方法调用更像一个消息传递,一旦开始,方法调用就会立即返回,调用者就可以继续后续的操作。而异步方法通常会在另外一个线程中,“真实”地执行着。整个过程,不会阻碍调用者的工作。
(2)请求:
同步交互:指发送一个请求,需要等待返回,然后才能够发送下一个请求,有个等待过程;
异步交互:指发送一个请求,不需要等待返回,随时可以再发送下一个请求,即不需要等待。
- 67、sleep和wait有什么区别
使用上
从使用角度看,sleep是Thread线程类的方法,而wait是Object顶级类的方法。
sleep可以在任何地方使用,而wait只能在同步方法或者同步块中使用。
CPU及资源锁释放
sleep,wait调用后都会暂停当前线程并让出cpu的执行时间,但不同的是sleep不会释放当前持有的对象的锁资源,到时间后会继续执行,而wait会放弃所有锁并需要notify/notifyAll后重新获取到对象锁资源后才能继续执行。
异常捕获
sleep需要捕获或者抛出异常,而wait/notify/notifyAll不需要。
- 68、 创建线程的方式及实现
继承Thread类创建线程
public class MyThread extends Thread{//继承Thread类
public void run(){
//重写run方法
}
}
实现Runnable接口创建线程
public class MyThread2 implements Runnable {//实现Runnable接口
public void run(){
//重写run方法
}
}
- 69、线程和进程的概念、并行和并发的概念
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。线程是程序中一个单一的顺序控制流程。
并行:多个线程可以同时执行,每一个时间段,可以有多个线程同时执行。
并发:多个线程同时竞争一个位置,竞争到的才可以执行,每一个时间段只有一个线程在执行。
- 70、说说线程的几个状态
(1)新建状态(New):新创建了一个线程对象。
(2)就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于“可运行线程池”中,变得可运行,只等待获取CPU的使用权。即在就绪状态的进程除CPU之外,其它的运行所需资源都已全部获得。
(3)运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
(4)阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。
(5)死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
- 71、什么情况下会死锁
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。
死锁产生的四个必要条件
(1)互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用
(2)不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
(3)请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
(4)循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。
- 72、 如何预防死锁?
答:根据产生死锁的四个必要条件,只要使其中之一不能成立,死锁就不会出现。为此,可以采取下列三种预防措施:
1、采用资源静态分配策略,破坏"部分分配"条件;
2、允许进程剥夺使用其他进程占有的资源,从而破坏"不可剥夺"条件;
3、采用资源有序分配法,破坏"环路"条件。
- 73、 如何检测死锁?
解决死锁的另一条途径是死锁检测方法,这种方法对资源的分配不加限制,即允许死锁的发生。但系统定时地运行一个"死锁检测"程序,判断系统是否已发生死锁,若检测到死锁发生则设法加以解除。
- 74、 如何解除死锁?
(1)资源剥夺法
(2)撤消进程法
- 75、 启动一个线程是使用start方法还是run方法
启动一个线程是调用start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行。这并不意味着线程就会立即运行。run()方法可以产生必须退出的标志来停止一个线程。
Run方法是线程启动后的回调方法。
- 76、锁的相关的概念
乐观锁(轻量级锁)自旋锁:轻操作,并发很少
悲观锁(重量级锁):并发多,操作重。
公平锁: AQS,按顺序来。 效率低,但是每个线程都能运行。
非公平锁:不按照顺序来,按照最高的效率来。 效率高,有的线程可能会被“饿死”。
读写锁(读共享):虽然加锁,但是可以读数据。
重入锁:所谓锁的重入就是同一个线程获取到锁之后,再次获取锁。
- 77、java中的4中线程池
-
(1)newCachedThreadPool
创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行 很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调用 execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并 从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资 源。
(2)newFixedThreadPool
创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。在任意点,在大多数 nThreads 线程会处于处理任务的活动状态。如果在所有线程处于活动状态时提交附加任务,则在有可用线程之前,附加任务将在队列中等待。如果在关闭前的执行期间由于失败而导致任何线程终止,那么一个新线程将代替它执行后续的任务(如果需要)。在某个线程被显式地关闭之前,池中的线程将一直存在。
(3)newScheduledThreadPool
创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
(4)newSingleThreadExecutor
Executors.newSingleThreadExecutor()返回一个线程池(这个线程池只有一个线程),这个线程池可以在线程死后(或发生异常时)重新启动一个线程来替代原来的线程继续执行下去.
78、synchronized的锁升级
synchronized在jdk1.6之前只区分轻量级锁和重量级锁。在JDK1.6之后进行了优化,就有了四种不同的情况:无锁,偏向锁,轻量级锁和重量级锁。
(1)无锁状态就是一个资源可以同时被多个线程方法, 执行效率很高,如果对资源进行修改可能会出现线程安全问题。
(2)偏向锁状态就是使用synchronized修饰的资源,只有一个线程在使用,没有产生资源竞争,这时线程不会主动释放锁,第二次执行同步资源的时候会判断是否是之前的线程,如果是就直接执行,这样就节省了释放所和获取锁的开销,提高了效率。
(3)轻量级锁也是我常说的自旋锁,自旋锁就是通过循环不断的尝试获取锁,这样就省去了唤醒线程的开销,效率较高。但是,当然轻量级锁只适合在少量竞争的情况下使用
轻量级锁会在一下两种情况下产生:
①JVM关闭了偏向锁,偏向锁是可以通过JVM的配置关闭的,如果关闭了,一开始就是轻量级锁。
②当有偏向锁的资源产生了资源竞争就会自动升级为轻量级锁。
(4)重量级锁就是当资源产生大量竞争,这个竞争限度可以通过JVM参数设置。一个对象如果是重量级锁状态,如果有一个线程获取锁之后,其余所有等待获取该锁的线程都会处于阻塞状态。
以上就是在JDK1.6之后的synchronized的锁升级的情况。
九、反射
- 79、 说说反射的用途及实现
所谓反射就是jdk提供的一种可以在运行期动态获取类的相关信息的机制。
(1)Java反射框架主要提供以下功能:
a.在运行时判断任意一个对象所属的类;
b.在运行时构造任意一个类的对象;
c.在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);
d.在运行时调用任意一个对象的方法
(2)主要用途 :
反射最重要的用途就是开发各种通用框架。
(3)基本反射功能的实现(反射相关的类一般都在java.lang.relfect包里):
a.获得Class对象使用Class类的forName静态方法,直接获取某一个对象的class, 调用某个对象的getClass()方法
b.判断是否为某个类的实例用instanceof关键字来判断是否为某个类的实例
c.创建实例使用Class对象的newInstance()方法来创建Class对象对应类的实例先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例。
d.获取方法getDeclaredMethods()
e.获取构造器信息 getDeclaredMethods() getMethods() getMethod()
f.获取类的成员变量(字段)信息 getFiled: 访问公有的成员变量 getDeclaredField:所有已声明的成员变量。但不能得到其父类的成员变量 getFileds和getDeclaredFields用法
g.调用方法 invoke()
h.利用反射创建数组 Array.newInstance()
- 80、说说自定义注解的场景及实现
跟踪代码的依赖性,实现代替配置文件的功能。比较常见的是Spring等框架中的基于注解配置。还可以生成文档常见的@See@param@return等。如@override放在方法签名,如果这个方法 并不是覆盖了超类方法,则编译时就能检查出。使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节,在定义注解时,不能继承其他注解或接口。
十、JVM
- 81、介绍一下GC
GC是java的垃圾回收机制,这也是java和C++的最大区别。Java的垃圾回收机制,会在不定的时间将垃圾对象进行回收。所谓垃圾对象就是没有被任何引用继续引用的对象。垃圾回收机程序员无法主动调用,由JVM根据情况在不确定的时刻自动调用。
- 82、基本类型和引用类型在内存中的情况
所有的基本类型它的引用和值都是存储在方法栈中的。
所有的引用类型在栈中存储的只是引用和地址。具体的数据是存储在堆中。
基本类型作为成员变量或者数组的时候,默认值都是各自不同的。而所有的引用类型的默认值都是null。
JDBC
- 83、简述原生的JDBC操作数据的流程
(1)加载驱动类,通过Class.forname();
(2)通过DriverManger.getConnection获取数据库链接
(3)通过connection获取statement/preparedStatement对象
(4)执行sql语句。增删改使用update。查询使用query
(5)如果是增删改则没有第五步。如果是查询,则需要处理上一步query返回的resultSet
(6)关闭结果集,关闭回话,关闭链接。
字节输入流可以通过InputStreamReader流转换,InputStreamReader流的构造方法的输入参数就是InputStream。字节输出流可以通过OuputStringWriter流转换,OutputStreamWroiter流的构造方法的输入参数就是OutputStream。
- 84、 statement和PreparedStatement区别
(1)preparedStatment是Statement的子类。
(2) preparedStatment可以预编译sql语句。执行速度要高于statement。
(3)preparedStatment可以通过占位符传入参数,可以有效的防止sql注入。
(4)任何时候都不要使用statement的。
- 85、什么是数据库连接池
数据库连接池就是预先创建一部分可用的链接存入集合(池)中。当需要使用时可以直接从池中获取链接,用完之后再还回池中。 这样可以提高数据库的访问速度。
连接池可以根据需求自动增长链接数量,但是一定有上限,当达到上限是,再有需求就只能等待。可以自动减少链接数量。
javaWEB
- 86、 什么是servlet
按照一种约定俗成的称呼习惯,通常我们也把实现了servlet接口的java程序,称之为Servlet
servlet不是我们自己创建的,也不是通过main方法启动运行,servlet是由servlet容器(tomcat)加载并且运行的。
servlet容器通过反射机制加载到servlet并且调用其中的方法。
- 87、 servlet生命周期
(1)加载和实例化
Servlet容器负责加载和实例化Servlet。当Servlet容器(Tomcat)启动时,或者在容器检测到需要这个Servlet来响应第一个请求时,创建Servlet实例。
(2)初始化
在Servlet实例化之后,容器将调用Servlet的init()方法初始化这个对象。
(3)请求处理
Servlet容器调用Servlet的service()方法对请求进行处理。
(4)服务终止
当需要释放内存或者容器关闭时,容器就会调用Servlet实例的destroy()方法。在destroy()方法调用之后,容器会释放这个Servlet实例,该实例随后会被Java的垃圾收集器所回收。
- 88、 servlet是否是线程安全的
servlet不是线程安全的。
servlet是由servlet容器实例化和管理的,每个servlet都是单利存在的。sevlet中的service方法也不是线程安全的。
如果servlet中的操作要线程安全,则可以使用同步代码块或者使用异步的servlet。
- 89、重定向和转发有什么区别?
重定向和转发都可以跳转到其他的资源(页面或者其他的servlet)
重定向是给客户端给出响应,让客户端再次请求其他的资源。这样会导致客户端重新请求一次。重定向是通过response.sendRedirect(String path) 实现的。
转发是服务器内部转发,就是从当前资源直接跳转到服务器内部的其他资源,最终响应客户端。客户端在整个过程中只请求一次。转发的实现:
reuqest.RequestDispatcher(String path).forward(request,response)
- 90、 doGet和doPost区别
(1)GET请求往往是从服务器端获取数据,POST请求往往是将客户端的数据提交到服务器端。
(2)GET请求中的参数是携带在url后面的。POST请求的参数实在一个独立的数据区域中。
(3)GET请求数据量长度有限制,一般不超过2kb。POST请求适合大规模的数据传送。因为是以实体的方式传送的。
(4)GET请求安全性较差一些,因为参数就在URL地址中。POST请求相对安全一些。
- 91、 Session与Cookie区别
Session和Cookie是用来解决会话跟踪的问题的。
Session是存储在服务器端的,用来记录用户的访问状态。
cookie是存储在客户端的,长度和类型都有一定的限制。
数据库
- 92、 数据库中drop、truncate、 delete区别
这三个关键字都是用来做删除的。
drop是用来删除数据表,列,索引等这些数对象的。
DELETE语句执行删除的过程是每次从表中删除一行,并且同时将该行的删除操作作为事务记录在日志中保存以便进行进行回滚操作。
TRUNCATE TABLE 则一次性地从表中删除所有的数据并不把单独的删除操作记录记入日志保存,删除行是不能恢复的。并且在删除的过程中不会激活与表有关的删除触发器。执行速度快。
如果只需删除表中的部分记录,只能使用delete语句并用where条件过滤。
- 93、数据库的索引有什么优点和缺点?
(1)数据库中创建索引的优点
a.创建唯一性索引,保证数据库表中每一行数据的唯一性。
b.加快数据的检索速度,这也是创建索引的最主要的原因。
c.减少磁盘IO(向字典一样可以直接定位)。
d.通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。
e.加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。
(2)数据库中创建索引的缺点
a.创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。
b.索引需要占用物理空间,特别是聚集索引,需要较大的空间。
c.当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度
- 94、 唯一约束和主键有什么异同
(1)相同点:主键和唯一索引都保重了数据不重复。
(2)不同点
a.每张表只能有一个主键,而唯一所以可以有多个。
b.唯一所以可以有一个记录为null,而主键不行。
c.主键可以作为其他表的外键,而唯一索引不一定行。
- 95、唯一索引和普通索引哪个性能更好
-
理论上所有的索引都会提高查询的性能,相对的降低对数据的增删改的速度。
唯一索引和普通索引的性能从查询和修改的两个方面来说:
对于查询操作而言,两个索引的性能几乎没有什么区别。
对于增删改而言,唯一索引对操作的性能影响会更大一些,因为唯一索引在修改索引的时候还要对索引的唯一性进行对比,索引影响就更大一些。
- 96、视图有什么好处
(1)视图能简化用户操作
(2)视图使用户能以多种角度看待同一数据
(3)视图对重构数据库提供了一定程序的逻辑独立性
(4)视图能够对机密数据提供安全保护
(5)适当的利用视图可以更清晰的表达查询
- 97、 数据库三大范式是什么
所谓第一范式(1NF)是指在关系模型中,对于添加的一个规范要求,所有的域都应该是原子性的,即数据库表的每一列都是不可分割的原子数据项,而不能是集合,数组,记录等非原子数据项。
第二范式(2NF)要求数据库表中的每个实例或记录必须可以被唯一地区分。选取一个能区分每个实体的属性或属性组,作为实体的唯一标识。
第三范式(3NF)要求一个关系中不包含已在其它关系已包含的非主关键字信息。
- 98、union和union all有什么不同
union和union all的区别是,union会自动压缩多个结果集合中的重复结果,而union all则将所有的结果全部显示出来,不管是不是重复。
- 99、 说说什么是事务和事务的四大特性
事务指访问并可能更新数据库中各种数据项的一个程序执行单元,简单的说就是将多个修改数据库中数据的操作作为一个整体。要么一起成功,要么一起失败。
事务四大特性 :(一元持久隔离)
原子性,要么执行,要么不执行 一个事务就是一个整体,不能被打断的。
隔离性,所有操作全部执行完以前,其它会话不能看到过程。 每个事务之间是相互隔离的。
一致性,事务前后,数据总额一致 事务总是让数据从一个一致性状态转换到另外一个一致性状态。
持久性,一旦事务提交,对数据的改变就是永久的。
- 100、 Mysql 中MyISAM和InnoDB的区别有哪些
InnoDB支持事务, MyISAM不支持
InnoDB支持外键,而MylSAM不支持。对一个包含外键的InnoDB表转为MYISAM会失败;
InnoDB是聚集索引,数据文件是和索引绑在一起,必须要有主键,通过主键索引效率高。
InnoDB不保存 表的具体行数,执行select count(*) from table时需要全表扫描。
Innodb不支持全文索引,而MyISAM支持全文索引,查询效率上MyISAM要高;
- 101、 inner join和left join区别(SQL查询中的内连接查询和外链接查询有什么区别)
一般情况下,我们的链接查询的查询条件都是基于外键关系的。
内连接查询的结果是两张数据表中符合关联关系的数据集合。内连接是通过`inner join`实现的,链接条件通过`on`设置。
而外连接会分为左外链接和右外链接,左外链接通过`left join` 或者 `out left join`实现,链接条件也是通过`on`设置。左外链接查询的结果是左边数据表的数据全部显示,右边数据表符合条件的数据会和左边数据表对应的数据对应显示,如果左边表中的数据在右边数据表中没有对应的数据就是null代替。 右外链接和左外链接正好相反,使用`right join`或者`out right join`实现。右边数据表数据要全部显示,左边的数据表根据情况显示对应的数据或者显示null。
- 102、 count和sum的区别,以及count(*)和count(列名)的区别
count和sum区别:求和用累加sum(),求行的个数用累计count
count(*)包括了所有的列,在统计结果的时候不会忽略列值为null
count(列名)只包括列名那一项,会忽略列值为空的计数
- 103、什么是脏读
脏读就是指当一个事务正在访问数据,并且对数据进行了修改,但是还没有来得及提交到数据库中,这时,另一个事务也访问这个数据,然后使用了这个数据。
简单来说,就是一个事务读取到了其他事务尚未提交的数据。没有提交(确定)的数据就是脏数据。
104、什么是不可重复读
在一个事务中,多次读取相同的数据,但是读取的数据不同。基本是因为在多次读取期间被其他的事务修改了数据导致的。
- 105、什么是幻读
幻读是指在一次读取操作之后,读取的结果无法支持后续的操作。 大概率是因为一个事务在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的数据行。
幻读是有特定的场景的:
我们要添加一个用户数据,名字不能重复,我们在添加之前先查询这个数据,查询不到,就开始添加,但是在添加的时候却发现名字重复了。这就是最经典的幻读。
106、事务的隔离级别
读未提交READ_UNCOMMITTED: 会产生脏读、幻读和不可重复读。
读提交READ_COMMITTED: 会产生幻读和不可重复读。
可重复读REPEATABLE_READ: 会产生幻读。
序列化SERIALIZABLE:会解决所有问题,但是效率特别低,一般都不使用。
Java框架
- 107、MVC设计思想
MVC是model、view、controller的简称。它是一中软件的设计思想,将应用的一个输入、处理、输出按照模型层,视图层,控制层进行分层设计。
(1)模型: 业务逻辑包含了业务数据的加工与处理以及相应的基础服务(为了保证业务逻辑能够正常进行的事务、安全、权限、日志等等的功能模块)
(2)视图:展现模型处理的结果;另外,还要提供相应的操作界面,方便用户使用。
(3)控制器:视图发请求给控制器,由控制器来选择相应的模型来处理;模型返回的结果给控制器,由控制器选择合适的视图。 主要作用是视图和业务分离。
- 108、什么是IOC
IOC,Inversion of Control,控制反转,指将对象的控制权转移给Spring框架,由 Spring 来负责控制对象的生命周期(比如创建、销毁)和对象间的依赖关系。
最直观的表达就是,以前创建对象的时机和主动权都是由自己把控的,如果在一个对象中使用另外的对象,就必须主动通过new指令去创建依赖对象,使用完后还需要销毁(比如Connection等),对象始终会和其他接口或类耦合起来。而 IOC 则是由专门的容器来帮忙创建对象,将所有的类都在 Spring 容器中登记,当需要某个对象时,不再需要自己主动去 new 了,只需告诉 Spring 容器,然后 Spring 就会在系统运行到适当的时机,把你想要的对象主动给你。也就是说,对于某个具体的对象而言,以前是由自己控制它所引用对象的生命周期,而在IOC中,所有的对象都被 Spring 控制,控制对象生命周期的不再是引用它的对象,而是Spring容器,由 Spring 容器帮我们创建、查找及注入依赖对象,而引用对象只是被动的接受依赖对象,所以这叫控制反转。
- 109、什么是DI
IOC 的一个重点就是在程序运行时,动态的向某个对象提供它所需要的其他对象,这一点是通过DI(Dependency Injection,依赖注入)来实现的,即应用程序在运行时依赖 IoC 容器来动态注入对象所需要的外部依赖。而 Spring 的 DI 具体就是通过反射实现注入的,反射允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性
- 110、什么是AOP
AOP,一般称为面向切面,作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,提高系统的可维护性。可用于权限认证、日志、事务处理。
AOP实现的关键在于 代理模式,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。
(1)AspectJ是静态代理,也称为编译时增强,AOP框架会在编译阶段生成AOP代理类,并将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。
(2)Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理
- 111、 什么是ORM
对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。ORM框架是连接数据库的桥梁,只要提供了持久化类与表的映射关系,ORM框架在运行时就能参照映射文件的信息,把对象持久化到数据库中。
ORM框架:为了解决面型对象与关系数据库存在的互不匹配的现象的框架。
- 112、Spring是什么框架?
Spring一个开源的轻量级框架。使用Spring可以轻松的将简单组件组装成一个复杂应用。
Spring有两个核心思想:IOC和AOP。
其中IOC是Spring的根本。我们可以将类配置到spring中,spring通过反射创建类对象,并且可以根据配置完成对象的初始化和属性注入。所以我们也说spring是一个对象工厂,或者对象仓库。
spring主要作用解耦。
- 113、什么SpringMVC框架,简答描述SpringMVC的工作流程
-
首先MVC是一个经典得的程序架构模式,MVC将程序分为Model,View,Controller。
SpringMVC是对MVC的经典实现。
SpringMVC利用DispatcherServlet作为前端控制器。
客户端请求到达服务器之后,首先由前端控制器DispatcherServlet处理,解析请求的url,根据url将请求分发给对应的后端控制器,也就是我们自己写的controller,在后端控制器中根据业务调用对应的业务处理程序,处理之后根据处理结果生成对应的ModelAndView,返回给前端控制器DispatcherServlet,前端控制器根据根据ModelAndView生成或者调用对应的视图响应客户端。
- 114、什么是MyBatis框架?
MyBatis是一个半自动化的ORM框架。
ORM说的是对象关系映射,就是将我们java对象和关系型数据库的表结构进行映射。
MyBatis将SQL从java代码中分离出来,然后根据ResultMap的配置实现对象和关系的映射。利用反射将JDBC的复杂操作进行封装,大幅度简化了持久化的操作。
- 115、什么是SpringBoot框架?
SpringBoot是基于Spring实现的一个简化Spring系列框架配置的框架。
SpringBoot将我们开发需要的场景整合为一个场景启动器。我们需要任何场景只需加入对应的场景启动器,SpringBoot就按照默认大于配置的思路完成场景需求配置。每一个场景启动器主要做两件事:
- 给当前工程添加场景需要的依赖
- 使用自动配置类完成当前场景需要的基础配置。
- 116、Spring框架常用注解:
@Component:泛指各种组件
@Controller、@Service、@Repository都可以称为@Component
@Autowired:由Spring提供 表示属性自动装配
@Resource:由JSR-250提供 表示属性自动装配
@Configuration:声明当前类为配置类
@Bean:注解在方法上,声明当前方法的返回值为一个bean,并且将这个bean加入Spring容器
@ComponentScan:用于对Component进行扫描
@Scope设置类型包括:设置Spring容器如何新建Bean实例
@Value注解:注入普通字符、注入操作系统属性、注入表达式结果、注入其它bean属性、注入文件资源、注入网站资源、注入配置文件
@Aspect:声明一个切面
@After:在方法执行之后执行(方法上)
@Before:在方法执行之前执行(方法上)
@Around:在方法执行之前与之后执行(方法上)
@PointCut:声明切点
- 117、SpringMVC常见注解:
EnableWebMvc:在配置类中开启Web MVC的配置支持
RequestMapping:用于映射web请求,包括访问路径和参数
ResponseBody:支持将返回值放到response内,而不是一个页面,通常用户返回json数据
RequestBody:允许request的参数在request体中,而不是在直接连接的地址后面
PathVariable:用于接收路径参数,比如@RequestMapping(“/hello/{name}”)声明的路径,将注解放在参数前,即可获取该值,通常作为Restful的接口实现方法。
RestController:该注解为一个组合注解,相当于@Controller和@ResponseBody的组合,注解在类上意味着,该Controller的所有方法都默认加上了ResponseBody
- 118、MyBatis的接口绑定的是什么?有几种实现方式?
-
在使用动态代理Mapper的情况下,MyBatis的mapper接口中的方法我们需要绑定对应的SQL语句。
具体的绑定方式有两种:
第一种是使用xml文件绑定,我们可以在对应的xml文件中使用`insert`、`select`这些标签给Mapper接口中的方法绑定SQL语句。MyBatis会使用动态代理的方式实现这些方法,并且执行对应的SQL语句。
第二种是使用注解的方式绑定,我们可以在Mapper接口的方法上方使用`@insert`、`@select`这些注解完成SQL的绑定,MyBatis依然是动态代理的方式实现这些方法。
119、什么是微服务
简单来说,微服务就是微小的服务。
服务一般说的是项目中的功能模块,一般都是用来解决一些问题或者提供一组功能。
微服务的特点就是就是服务体积更小,业务的复杂度更低,功能更加单一。一般就是一个微服务就干一件事。
微服务都是可以独立的开发,独立部署,独立发展的。
我们现在常说的微服务架构就是将一个复杂的项目拆分为多个小型的微服务。这些微服务之间通过轻量级的通信机制进行通信,现在基本都是HTTP。
SpringCloud框架就是一个微服务框架。
- 120、什么是SpringCloud
Spring Cloud是一个基于Spring框架的微服务架构的开源框架。它提供了一系列的工具和技术,用于开发、部署和管理分布式系统,帮助开发者构建微服务架构。
SpringCloud是基于SpringBoot的,使用Spring Boot开发微服务架构变得非常简单,大幅度的提高了开发效率。
SpringCloud包含了一套几乎完整的微服务工具集,比如:服务的注册与发现,配置管理,负载均衡,断路器,限流,路由等。
通过使用Spring Cloud,开发者可以轻松地构建分布式系统,而无需关心复杂的分布式系统的底层实现。
所以SpringCloud无疑是开发微服务架构项目的首选。
121、什么是缓存击穿、缓存穿透、缓存雪崩?
**缓存穿透**:指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,进而给数据库带来压力。
常见解决方案:
(1)如果是非法请求,我们在API入口,对参数进行校验,过滤非法值。
(2)如果查询数据库为空,我们可以给缓存设置个空值,或者默认值。
缓存雪奔: 指缓存中数据大批量到过期时间,而查询数据量巨大,请求都直接访问数据库,引起数据库压力过大。
常见解决方案: 可通过均匀设置过期时间或者讲一些热点数据设置为缓存永久有效来解决。
缓存击穿:指热点key在某个时间点过期的时候,而恰好在这个时间点对这个Key有大量的并发请求过来,从而大量的请求打到数据库,导致数据库压力过大。
常见解决方案:
(1)使用互斥锁方案。缓存失效时,不是立即去加载db数据,而是先使用某些带成功返回的原子操作命令,如(Redis的setnx)去操作,成功的时候,再去加载db数据库数据和设置缓存。否则就去重试获取缓存。
(2)“永不过期”,是指没有设置过期时间,但是热点数据快要过期时,异步线程去更新和设置过期时间。
- 122、MySQL与Redis 如何保证双写一致性
-
常见的解决方案就是延迟双删。
延迟双删的流程是:
(1)先删除缓存
(2)再更新数据库
(3)休眠一会(比如1秒),再次删除缓存。
这个休眠时间 = 读业务逻辑数据的耗时 + 几百毫秒。为了确保读请求结束,写请求可以删除读请求可能带来的缓存脏数据。
为了确保安全可以再加上删除缓存重试机制:
因为延时双删可能会存在第二步的删除缓存失败,导致的数据不一致问题。可以使用这个方案优化。
删除缓存重试流程:
(1)写请求更新数据库
(2)缓存因为某些原因,删除失败
(3)把删除失败的key放到消息队列
(4)消费消息队列的消息,获取要删除的key
(5)重试删除缓存操作