一.JavaSE 面试题
1.自增变量
public static void main(String[] args) {
int i = 1;
i = i++;
int j = i++;
int k = i + ++i * i++;
System.out.println("i=" + i);
System.out.println("j=" + j);
System.out.println("k=" + k);
}
执行 i = i++; 先将i变量压入操作数栈,然再对i变量进行自增,最后把计算结果赋值给 i , i 任然是1
最后一步
结果
i=4
j=1
k=11
个人总结:
2.单例设计模式
单例模式(Singleton)
:在Java中 即指单例设置模式,探视软件开发最常用的设置模式之一,单指唯一,例指实例,即某个类在整个系统中只能有一个实例对象可被获取和使用的代码模式。
几种常见形式
:
饿汉式:
直接创建对象,不存在线程安全问题
直接实例化饿汉式(简洁直观)
枚举式 (最简洁)
静态代码块饿汉式(适合复杂实例化)
懒汉式;
延迟创建对象
线程不安全(使用于单线程)
线程安全(使用于多线程)
静态内部类模式 (适用于多线程)
饿汉式
:
直接实例化饿汉式(简洁直观)
public class Singleton1 {
/**
* 1、构造器私有化
* 2、自行创建,并且用静态变量保存
* 3、向外提供实例
* 4、强调这是一个单例,我们可以用final修改
*/
private Singleton1() {
}
public static final Singleton1 INSTANCE = new Singleton1();
}
枚举式 (最简洁)
public enum Singleton2 {
/**
* 枚举类型:表示该类型是有限的几个
*/
INSTANCE
}
静态代码块饿汉式(适合复杂实例化)
public class Singleton3 {
/**
* 静态代码块
*/
public static final Singleton3 INSTANCE;
private String info;
static {
try {
INSTANCE = new Singleton3("123");
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
private Singleton3(String info) {
this.info = info;
}
}
懒汉式
:
线程不安全(使用于单线程)
public class Singleton4 {
/**
* 1、构造器私有化
* 2、用一个静态变量保存这个唯一的实例
* 3、提供一个静态方法,获取这个实例对象
*/
static Singleton4 instance;
private Singleton4() {}
public static Singleton4 getInstance() {
if (instance == null) {
instance = new Singleton4();
}
return instance;
}
}
线程安全(使用于多线程)
public class Singleton5 {
/**
* 1、构造器私有化
* 2、用一个静态变量保存这个唯一的实例
* 3、提供一个静态方法,获取这个实例对象
*/
static Singleton5 instance;
private Singleton5() {}
public static Singleton5 getInstance() {
if (instance == null) {
synchronized (Singleton5.class) {
if (instance == null) {
instance = new Singleton5();
}
return instance;
}
}
return instance;
}
}
静态内部类模式 (适用于多线程)
public class Singleton6 {
/**
* 1、内部类被加载和初始化时,才创建INSTANCE实例对象
* 2、静态内部类不会自动创建,随着外部类的加载初始化而初始化,他是要单独去加载和实例化的
* 3、因为是在内部类加载和初始化时,创建的,因此线程安全
*/
private Singleton6(){}
public static class Inner{
private static final Singleton6 INSTANCE = new Singleton6();
}
public static Singleton6 getInstance() {
return Inner.INSTANCE;
}
}
3.类初始化和实例初始化
类初始化
一个类要创建实例需要先加载并初始化该类
main方法所在的类需要先加载和初始化
一个子类要初始化需要先初始化父类
一个类初始化就是执行 clinit 方法
clinit 方法由静态类变量显示赋值代码和静态代码块组成
类变量显示赋值代码和静态代码块代码从上到下执行
clinit 方法只调用一次
实例初始化过程
实例初始化就是执行 init() 方法
init () 方法可能重载有多个,有几个构造器就有几个 init() 方法
init() 方法由非静态实例变量显示赋值代码和非静态代码块,对应构造器代码组成
非静态实例变量显示赋值代码和非静态代码块从上到下顺序执行,而对应构造器的代码最后执行
每次创建实例对象,调用对应构造器,执行的就是对应的 ini方法
init 方法的首行是super()和super(实参列表) ,即对应父类的 init 方法
Father.java
package com.atguigu.classLoader;
/**
* 父类初始化<clinit>
* 1、j = method()
* 2、 父类的静态代码块
*
* 父类实例化方法:
* 1、super()(最前)
* 2、i = test() (9)
* 3、子类的非静态代码块 (3)
* 4、子类的无参构造(最后)(2)
*
*
* 非静态方法前面其实有一个默认的对象this
* this在构造器或<init> 他表示的是正在创建的对象,因为咱们这里是正在创建Son对象,所以
* test()执行的就是子类重写的代码(面向对象多态)
*
* 这里i=test() 执行的就是子类重写的test()方法
* @author gcq
* @Create 2020-09-25
*/
public class Father {
private int i = test();
private static int j = method();
static{
System.out.println("(1)");
}
Father() {
System.out.println("(2)");
}
{
System.out.println("(3)");
}
public int test(){
System.out.println("(4)");
return 1;
}
public static int method() {
System.out.println("(5)");
return 1;
}
}
Son.java
package com.atguigu.classLoader;
/**
* 子类的初始化<clinit>
* 1、j = method()
* 2、子类的静态代码块
*
* 先初始化父类 (5)(1)
* 初始化子类 (10) (6)
*
* 子类实例化方法:
* 1、super()(最前
* 2、i = test() (9)
* 3、子类的非静态代码块 (8)
* 4、子类的无参构造(最后)(7)
* @author gcq
* @Create 2020-09-25
*/
public class Son extends Father {
private int i = test();
private static int j = method();
static {
System.out.println("(6)");
}
Son() {
super();
System.out.println("(7)");
}
{
System.out.println("(8)");
}
public int test(){
System.out.println("(9)");
return 1;
}
public static int method() {
System.out.println("(10)");
return 1;
}
public static void main(String[] args) {
Son son = new Son();
System.out.println();
Son son1 = new Son();
}
}
执行结果
(5)
(1)
(10)
(6)
(9)
(3)
(2)
(9)
(8)
(7)
(9)
(3)
(2)
(9)
(8)
(7)
4.方法的参数传递机制
代码:本题考点是方法的参数传递机制,String、包装类等对象的不可变性
package com.atguigu.methodParam;
import java.util.Arrays;
/**
* @author gcq
* @Create 2020-09-28
*/
public class Exam4 {
public static void main(String[] args) {
int i = 1;
String str = "hello";
Integer num = 200;
int[] arr = {1,2,3,4,5};
MyData my = new MyData();
change(i,str,num,arr,my);
// arr my变了
System.out.println("i= " + i);
System.out.println("str= " + str);
System.out.println("num= " + num);
System.out.println("arr= " + Arrays.toString(arr));
System.out.println("my.a= " + my.a);
}
public static void change(int j, String s, Integer n, int[] a, MyData m) {
j += 1;
s += "world";
n += 1;
a[0] += 1;
m.a += 1;
}
}
class MyData {
int a = 10;
}
方法的参数传递机制
:
1.形参是基本数据类型:传递数据值
2.实参是引用数据类型:传递地址值
3.特殊的类型:String、包装类等对象的不可变性
输出结果:
i= 1
str= hello
num= 200
arr= [2, 2, 3, 4, 5]
my.a= 11
小结:
5.递归与迭代
编程题:有n步台阶,一次只能上1步或2步,共有多少种走法?
1.递归:
public class TestStep{
@Test
public void test(){
long start = System.currentTimeMillis();
System.out.println(f(100));//165580141
long end = System.currentTimeMillis();
System.out.println(end-start);//586ms
}
//实现f(n):求n步台阶,一共有几种走法
public int f(int n){
if(n<1){
throw new IllegalArgumentException(n + "不能小于1");
}
if(n==1 || n==2){
return n;
}
return f(n-2) + f(n-1);
}
}
2.循环迭代:
public class TestStep2 {
@Test
public void test(){
long start = System.currentTimeMillis();
System.out.println(loop(100));//165580141
long end = System.currentTimeMillis();
System.out.println(end-start);//<1ms
}
public int loop(int n){
if(n<1){
throw new IllegalArgumentException(n + "不能小于1");
}
if(n==1 || n==2){
return n;
}
int one = 2;//初始化为走到第二级台阶的走法
int two = 1;//初始化为走到第一级台阶的走法
int sum = 0;
for(int i=3; i<=n; i++){
//最后跨2步 + 最后跨1步的走法
sum = two + one;
two = one;
one = sum;
}
return sum;
}
}
小结:
6.成员变量与局部变量
考点
:
1.就近原则
2.变量的分类
* 成员变量:类变量、实例变量
3.局部变量
* 非静态代码块的执行:每次创建实例对象都会执行
4.方法的调用规则:调用一次执行一次
局部变量与成员变量的区别
:
1.声明的位置:
局部变量:方法体{}中,形参,代码块{}中
成员变量:类中方法外
* 类变量:有static修饰
* 实例变量:没有static修饰
2.修饰符
局部变量:final
成员变量:public、protected、private、final、static、volatile、transient
3.值存储的位置:
局部变量:栈
实例变量:堆
类变量:方法区
4.作用域:
局部变量:从声明处开始,到所属的}结束
实例变量:在当前类中“this.”(有时this.可以缺省),在其他类中“对象名.”访问
类变量:在当前类中“类名.”(有时类名.可以省略),在其他类中“类名.”或“对象名.”访问
5.声明周期:
局部变量:每一个线程,每一次调用执行都是新的生命周期
实例变量:随着对象的创建而初始化,随着对象的被回收而消亡,每一个对象的实例变量是独立的
类变量:随着类的初始化而初始化,随着类的卸载而消亡,该类的所有对象的类变量是共享的
当局部变量与xx变量重名时,如何区分
:
局部变量与实例变量重名:在实例变量前面加“this.
局部变量与类变量重名:在类变量前面加“类名.”
题目:
/**
* @author gcq
* @Create 2020-09-29
*/
public class Exam5 {
static int s;// 5
int i; // A-2 B-1
int j;//A-1 B-1
{
int i = 1;
i++; // 就近原则
j++;
s++;
}
public void test(int j) {
j++; // 就近原则 21
i++;
s++;
}
public static void main(String[] args){
Exam5 obj1 = new Exam5();
Exam5 obj2 = new Exam5();
obj1.test(10);
obj1.test(20);
obj2.test(30);
// 2 1 5
System.out.println(obj1.i + "," + obj1.j + "," + obj1.s);
// 1 1 5
System.out.println(obj2.i + "," + obj2.j + "," + obj2.s);
}
}
二.ssm面试题
1.spring Bean的作用域
2.Spring中支持的常用数据库事务传播属性和事务隔离级别
事务的七大传播行为
:当事务方法被另一个事务方法调用时,必须指定事务应该如何传播,列如方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行,事务传播的行为有传播属性指定,Spring定义了7中类传播行为
事务传播属性可以在@Transactional注解的propagation属性中定义
数据库事务并发问题
:设有A,B两个事务并发执行
1.脏读:A修改了数据还没提交,B读取到了A修改后的数据,最后A回滚,造成脏读。
2.幻读:A查询到了10条数据,这时B往表中插入两条数据,A再次查询发现多了两条数据,怀疑人生,于是幻读出现了。
3.不可重复读:A查询数据,还未提交,这时候B修改了数据并且提交,甚至可能还有一个C也修改数据并且提交,最后A提交,会发现读取到的数据是C修改的,和刚开始要读相比较发生了变化,读取的数据不一样。
事务四大隔离级别
:数据库系统必须具有隔离并发运行各个事务的能力,使它们不会相互影响,避免各种并发问题。一个事务与其他事务隔离的程度称为隔离级别。SQL标准中规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性越弱。
读未提交:READ UNCOMMITTED
允许Transaction01读取Transaction02未提交的修改。
读已提交:READ COMMITTED
要求Transaction01只能读取Transaction02已提交的修改。
可重复读:REPEATABLE READ
确保Transaction01可以多次从一个字段中读取到相同的值,即Transaction01执行期间禁止其它事务对这个
字段进行更新。
串行化:SERIALIZABLE
确保Transaction01可以多次从一个表中读取到相同的行,在Transaction01执行期间,禁止其它事务对这个
表进行添加、更新、删除操作。可以避免任何并发问题,但性能十分低下。
3.springmvc解决post的中文乱码问题
4.springmvc的工作流程
5.Mybatis实体类中的属性名和表中的字段不一样解决办法
三.java高级面试题
1.linux的常用服务类命令:https://blog.csdn.net/qq_46334333/article/details/107858884
2.git分支的相关命令:https://blog.csdn.net/qq_46334333/article/details/113388039
3.redis的持久化:https://blog.csdn.net/qq_46334333/article/details/113623666
4.mysql什么时候需要建索引:https://blog.csdn.net/qq_46334333/article/details/113531339
5.JVM的垃圾回收机制
6.redis的实战场景
四.项目面试题
1.单点登录:一处登录多处使用,前提是单点登录多使用在分布式系统中。
Demo:
参观动物园流程:
检票员=认证中心模块
1.我直接带着大家进动物园,则会被检票员拦住【看我们是否有门票】,没有[售票处买票]
登录=买票
2.我去买票【带着票,带着大家一起准备进入动物园】检票员check【有票】
Token=piao
3.我们手中有票就可以任意观赏动物的每处景点。
京东:单点登录,是将token放入到cookie中的。
案例:将浏览器的cookie禁用,则在登录京东则失败!无论如何登录不了!
2.购物车
购物车
:
1.购物车跟用户的关系?
a)一个用户必须对应一个购物车【一个用户不管买多少商品,都会存在属于自己的购物车中。】
b)单点登录一定在购物车之前。
2.跟购物车有关的操作有哪些?
a)添加购物车
i.用户未登录状态
1.添加到什么地方?未登录将数据保存到什么地方?
a)Redis? --- 京东
b)Cookie? --- 自己开发项目的时候【如果浏览器禁用cookie】
ii.用户登录状态
1.Redis 缓存中 【读写速度快】
a)Hash :hset(key,field,value)
i.Key:user:userId:cart
ii.Hset(key,skuId,value);
2.存在数据库中【oracle,mysql】
b)展示购物车
i.未登录状态展示
1.直接从cookie 中取得数据展示即可
ii.登录状态
1.用户一旦登录:必须显示数据库【redis】+cookie 中的购物车的数据
a)Cookie 中有三条记录
b)Redis中有五条记录
c)真正展示的时候应该是八条记录
3.消息队列:详细看我的ActiveMQ:https://blog.csdn.net/qq_46334333/article/details/112972839
背景
:在分布式系统中是如何处理高并发的。
由于在高并发的环境下,来不及同步处理用户发送的请求,则会导致请求发生阻塞。比如说,大量的insert,
update之类的请求同时到达数据库MYSQL,直接导致无数的行锁表锁,甚至会导致请求堆积很多。从而触发 too
many connections 错误。使用消息队列可以解决【异步通信】
1.异步
2.并行
3.排队
消息队列电商使用场景:
消息队列的弊端:
消息的不确定性:延迟队列,轮询技术来解决该问题即可!
推荐大家使用activemq!环境都是java。