多益一面

1.面向对象基础

1.1 面向对象三大特点

  封装、继承、多态

  面向过程是将解决问题的过程拆分为一个个方法执行;面向对象是先抽象出对象由对象执行方法的方式解决问题

1)封装:将一个对象的属性封装在对象内部不允许外部对象直接访问对象内部信息

2)继承:不同类型对象,相互之间经常有一定共同点。具体表现为子类继承父类

  1. 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法)。但是父类的私有属性和方法,子类只能拥有,无法访问
  2. 子类可以拥有自己的属性和方法,即子类可对父类进行拓展。
  3. 子类可以自己方式实现父类方法(子类可重写父类方法)

3)多态:一个对象具有多种状态,即提供一个统一的接口使不同类型的对象可以实现同一个操作或方法。具体表现为父类引用指向子类实例。如Map<Integer, Integer>map = new HashMap<>();

  父类引用是指使用父类类型声明一个变量map(map可引用父类实例或子类实例),父类引用存放在栈中子类实例是指子类创建的具体对象如new HashMap(),子类实例存放在内存中。故声明的父类引用map指向子类实例new HashMap。

  对象实例的相等表现为内存存放的内容是否相同;引用类型的相等表现为指向的内存地址是否相同

  特点:

  1. 对象类型引用类型之间存在(继承)类/(实现)接口的关系;
  2. 父类引用可访问子类对象中继承父类的属性和方法,但父类无法访问子类特有的属性和方法
  3. 编译器只识别父类引用不考虑实际引用对象运行期间根据实际引用对象确定具体执行方法和属性;
  4. 如果子类重写父类方法,真正执行的是子类覆盖的方法,若子类没有覆盖父类方法,执行的是父类的方法。

1.2 构造方法

1)如果一个类没有声明构造方法,程序能正确运行吗

  可以的,类的构造方法是为了初始化对象。类即使没有声明构造方法,也会有默认的不带参数的构造方法。

2)构造方法有什么特点?是否可被@override

  特点:

  1. 名字与类相同
  2. 无返回值,不能使用void声明构造函数
  3. 生成类的对象时自动执行,无需调用

  构造方法不能被@override,但能被重载,如一个类中可以有多个构造函数

1.3 重写和重载的区别

  重写是指子类将父类本身的方法重写,在方法名返回值类型参数列表相同的情况下,子类对父类方法体进行重写。子类方法的访问修饰符不能低于父类。

  访问修饰符等级排序:public > protected(同一包或不同包的子类都能访问) > default(同一包的所有类都能访问) > private(只能在当前类中被访问)

  重载是指同一个类中,同名的方法有不同的参数列表参数类型参数个数参数顺序等不同)。

1.4 接口和抽象类的区别

  1)共同点

  1. 不能被实例化
  2. 都能拥有抽象方法
  3. 都拥有默认方法(可理解为实现方法)Java8中通过default关键字在接口中定义默认方法),如MybatisPlus中Mapper接口中默认的增删改查方法,使得其存在部分实现方法,提高代码复用

  2)不同点

  1. 接口是对类的行为进行约束,即实现某接口的类型需要实现接口所有的方法,从而具备对应的行为能力;抽象类主要用于代码复用,强调所属关系(即抽象类和子类之间的继承关系,子类是抽象类的具体表现);
  2. 一个类只能继承一个父类,但可以实现多个接口;
  3. 接口中成员变量只能是public static final 类型的,不能被修改且必须有初始值;抽象类中成员变量默认是default,可在子类中重新定义可被重新赋值

  final修饰的变量可称为常量,为保证常量的值在后续代码中不被修改,Java编译器要求其在声明时构造方法时进行初始化赋值,否则会导致编译报错

 正确方式

1)声明时赋值

2)构造方式赋值

1.5 动态绑定和静态绑定

  1. 动态绑定:在运行时才能确定调用哪个函数或方法。当引用变量是一个基类类型,实际引用类型是派生类类型,动态绑定会根据对象实际类型调用对应方法或函数。动态绑定的作用是实现多态
  2. 静态绑定:编译时即可确定调用哪个函数或方法。如一个类中有一个静态方法,在编译时就可确定调用该方法的地址,因此静态方法在编译时就确定的

1.6 深拷贝、浅拷贝、引用拷贝

浅拷贝:在堆上创建一个新的对象,如果对象内部是引用类型,则复制内部对象的引用地址,拷贝对象和原对象共用同一个内部对象;当其中一个对象修改了内部数据另一个对象也会受到影响

定义一个Address类和Person类实现Cloneable接口,重写clone()方法实现浅拷贝。

package com.ku.test.basic;

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class Person implements Cloneable{
    private Address address;
    @Override
    public Person clone() throws CloneNotSupportedException {
        Person person = (Person)super.clone();
        return person;
    }
}

package com.ku.test.basic;

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class Address implements Cloneable{
     private String name;

     @Override
     public Address clone() throws CloneNotSupportedException {
         return (Address)super.clone();//强转为Address类型
     }
}

package com.ku.test.basic;

public class TestCopy {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person = new Person(new Address("武汉"));
        Person personCopy = person.clone();
        System.out.println(person.getAddress() == personCopy.getAddress());
    }
}
浅拷贝

深拷贝:完全复制整个对象,包含对象所包含的内部对象;拥有各自的对象以及内存地址,两个对象相互独立。

package com.ku.test.basic;

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
/**
 * 深拷贝
 */
public class Person1 implements Cloneable{
    private Address address;

    @Override
    public Person1 clone() throws CloneNotSupportedException {
        Person1 person1 = (Person1) super.clone();
        person1.setAddress(person1.getAddress().clone());
        return person1;
    }
}
深拷贝

引用拷贝:两个不同的引用指向同一个对象

1.7 “==”和equals方法的区别

  Java参数传递方式是基于值传递(引用拷贝,两个引用指向同一个对象),不管是基本数据类型还是引用数据类型,==比较的都是值,不过引用类型变量存储的值是内存地址

区别:

  • 对于基本数据类型,==比较的是值;
  • 对于引用数据类型,==比较的是内存地址;

equals方法不能用于基本数据类型的比较,只能用于引用数据类型的比较。equals方法是Object类中的方法,Object是所有类的直接父类或间接父类,因此每个类中都存在equals方法。

public boolean equals(Object obj){
    return   (this == obj);
}

equals存在两种使用情况:

  • 类如果没有重写equals方法,那么equals方法相当于==,比较的是两个对象的内存地址
  • 类如果重写了equals方法,那么equals方法比较的是对象的值

创建String类型的对象时,虚拟机会在常量池中查找是否存在值与创建的值相同的对象;如果存在将其赋予当前引用,如果不存在则在常量池中创建一个新的String对象

public boolean equals(Object anObject) {
    if (this == anObject) {//地址相等则值相等,引用拷贝
        return true;
    }
    if (anObject instanceof String) {//如果每个字符相等则值相等
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}
String中重写的equals方法

1.8 hashcode()方法

hashcode()的作用获取哈希码(int 整形),也被称为散列码。哈希码是确定对象在哈希表中的索引位置

   hashcode()方法是Object中的本地方法,即所有类都存在hashcode()方法。

  散列表存储的是键值对(key-value),特点是能够根据键快速检索出值。其中利用到了哈希码

为什么要拥有hashcode()方法呢?

  以HashSet检查重复为例,当对象加入HashSet时,HashSet会计算对象的hashcode判断对象插入的位置,同时也会将其与已存在对象的hashcode比较。如果不相等,则根据hashcode将对象散列到HashSet中;如果存在重复的hashcode,那么通过equals方法检查hashcode相等的两个对象是否相同,若相同则Hash不会让其加入;若不同则散列到其他位置。由于equals方法是通过重写来实现对象值的比较,而对象值的比较是需要比较对象每个成员变量的值;若成员变量较多或比较逻辑较复杂,那么equals方法开销较大。这样大大减少equals()方法比较的次数,提高了执行速度

  1. 两个对象的hashcode不相等,两个对象一定不相等;
  2. 两个对象的hashcode相等,两个对象不一定相等(哈希冲突)
  3. 两个对象的hashcode相等且equals方法返回true,那么两个对象相等

为什么重写equals()方法一定要重写hashcode()方法?

  1. 如果两个对象相等,那么他们的hashcode一定相等。如果两个对象的equals方法返回true,那么两个对象的hashcode相等。
  2. 如果重写equals方法()时,没有重写hashcode方法,则equals()方法判断相等的对象,它们的hashcode不相等

1.9 字符串常量池

  字符串常量池是JVM提升性能减少内存消耗针对字符串(String类)专门开辟的一块区域,目的是避免字符串的重复创建

 String s = new String("abc")会创建几个字符串对象?

  • 如果创建的字符串在字符串中不存在指向它的引用,那么首先在字符串常量池中创建一个指向该字符串的引用,然后在堆中通过new String()创建一个对象。一共创建两个对象。
  • 如果创建的字符串在字符串常量池中存在指向它的引用,那么new String()会在堆中创建一个对象。

 String 中intern()方法的作用

intren()方法是一个native(本地)方法,作用是将指定字符串对象的引用保存在字符串常量池中,分为两种情况:

  1. 如果字符串常量池中保存了指定字符串的引用,则直接返回
  2. 如果字符创常量池中没有保存指定字符串的引用,那么在字符串常量池中创建一个指向指定字符串的引用并返回

2.异常

  这里引用JavaGuide的一张异常类层次结构图

 2.1 Exception和Error的区别

  在Java中,所有的异常类有一个共同的父类Throwable存在于java.lang包中。Throwable类有两个子类:

  • Exception:程序中可以处理的异常,可通过catch进行捕获;Exception可分为Checked Exception(受检查异常,必须处理)(RuntimeException及其子类)Unchecked Exception(不受检查异常,可不处理)
  • Error:程序本身无法处理的异常;如虚拟机运行异常(Virtual MachineError)、内存溢出异常(OutOfMemory)、类定义错误(NoClassDefFoundError)。

  FileNotFoundException是一种受检查异常,是Exception异常类的子类

2.2 Throwable类常见的方法

  1. String getMessage():返回异常发生时的简要描述;
  2. String toString:返回异常时的详细信息;
  3. String getLocalizedMessage():返回异常对象的本地化信息。如果Throwable的子类覆盖该方法,可生成本地化信息。若没有则返回getMessage()方法信息;
  4. void printStackTrace():控制台打印Throwable对象封装的异常信息

2.3 try-catch-finally 如何使用

try块:捕获异常。可接0个或多个catch块,如果没有catch块,一定要有finally块

catch块:处理try捕获到的异常;

finally块:无论是否捕获或处理异常,finally块的语句都会被执行;当遇到try或catch中存在return时,会在其之前执行

代码实例

注意:不要在 finally 语句块中使用 return!try 语句和 finally 语句中都有 return 语句时try 语句块中的 return 语句会被忽略。这是因为 try 语句中的 return 返回值会先被暂存在一个本地变量中,当执行到 finally 语句中的 return 之后,这个本地变量的值就变为了 finally 语句中的 return 返回值

 2.4 finally一定会执行吗

  不一定,在某些情况下,finally不会执行,如终止正在运行的虚拟机(System.exit(1))关闭CPU

代码实例和展示

 2.5 异常使用需要注意的地方

  • 不要把异常定义为静态变量,因为这样会导致异常栈信息错乱。每次手动抛出异常,我们都需要手动 new 一个异常对象抛出。
  • 抛出的异常信息一定要有意义
  • 建议抛出更加具体的异常比如字符串转换为数字格式错误的时候应该抛出NumberFormatException而不是其父类IllegalArgumentException
  • 使用日志打印异常之后就不要再抛出异常了(两者不要同时存在一段代码逻辑中)。

3.泛型

3.1 什么是泛型?有什么作用?

  泛型(Generics)是JDK5引入的特性,它是一种实现参数化类型的机制。即在定义接口方法时使用参数表示类型,在实际使用时指定具体的类型。使用泛型参数可增强代码得可读性及稳定性。

编译器对泛型参数进行检测,并通过泛型参数指定传入的对象类型实现自动转换类型

代码实例

static class MyGenericsClass<T>{
        private T value;

        private MyGenericsClass(T value){
            this.value = value;
        }

        public void setValue(T value){
            this.value = value;
        }

        public T getValue(){
            return value;
        }
    }

    //声明为一个泛型方法,T表示支持不同类型的参数
    public static <T> void printValue(MyGenericsClass<T> obj){
        System.out.println(obj.getValue());
    }

    public static void main(String[] args) {
        MyGenericsClass<String>stringInstance = new MyGenericsClass<>("Generics");
        MyGenericsClass<Integer>integerInstance = new MyGenericsClass<>(5);

        printValue(stringInstance);
        printValue(integerInstance);
    }
泛型类

 3.2 泛型有哪几种?

  泛型有泛型类泛型接口泛型方法三种

3.2.1 泛型类

public class Generics<T>{
    private T value;
    private Generics(T value){
        this.value = value;
    }
    public void setValue(T Value){
        this.value = value;
    }
    public T getVallue(){
        return value;
    }
}    

实例化

Generics<String> generics = new Generics<>("123456");

3.2.2 泛型接口

public interface Generics<T>{
public T method(); }

实现泛型接口,不指定类型,如果不是null类型,那么会报编译错误。当返回值为null时,表示空引用,因此不会报错。

//T即不指定类型
public
class GenericsClass<T> implements Generics<T>( @Override public T method(){ retutn 5; } )

   可以通过强制转换将其转换为T类型避免报错

代码实例

//创建一个泛型接口
    public interface GenericsInterface<T>{
        T method();
    }

    public static class GenericsClass<T> implements GenericsInterface<T>{
        @Override
        public T method() {
            return (T)Integer.valueOf(5);
        }
    }
强转为T类型

实现泛型接口,指定类型

//T即不指定类型
public class GenericsClass<T> implements Generics<String>(
    @Override
    public String method(){
        retutn "6";
    }
)

3.2.3 泛型方法

public static <E> void printArray(E[] array){
for (E element:array) {
System.out.println(element);
}
}

注意:public static <E> coid printArray(E[] array)一般被称为静态泛型方法;在Java中,泛型是一个占位符,必须在传递类型后才能使用类在实例化时才能真正的传递参数,静态方法加载与编译器,先于运行期加载的类实例化对象。即类中的泛型还没有真正传递类型参数时,静态方法已加载完。因此静态方法无法使用类上的泛型只能使用自己的泛型<E>

3.3 项目中泛型的用处

  • 自定义接口通用返回结果 CommonResult<T> 通过参数 T 可根据具体的返回类型动态指定结果的数据类型
  • 定义 Excel 处理类 ExcelUtil<T> 用于动态指定 Excel 导出的数据类型
  • 构建集合工具类(参考 Collections 中的 sort, binarySearch 方法)。

4. 反射

4.1 概念

   反射运行时分析类以及执行类中方法的机制。通过反射可以获取任意一个类的所有属性和方法,还可以调用这些属性和方法

4.2 优缺点

  • 优点:灵活,为Spring/Spring Boot、Mybatis等框架提供开箱即用的功能提供了便利;
  • 缺点:其在运行时分析类以及执行类中方法能力的同时,增加了安全性问题;如无视泛型参数的安全检查(泛型参数安全检查发生在编译时)。反射性能稍差。

4.3 new一个对象和反射生成一个对象的差异

  反射是在运行时动态操作、监测修改类、对象、方法和属性的机制。使用“new”关键字创建对象是在编译器确定的静态操作

  • 创建对象成本:反射创建对象需要动态解析构造函数、检查访问权限,这些操作在性能上需要额外开销。使用“new”关键字可直接调用构造函数进行对象实例化,成本较低。

4.4 反射应用场景

  在Spring/SpringBoot、Mybatis框架中大量使用了反射机制。这些框架大量使用了动态代理模式,而动态代理模式依赖于反射

  JDK实现动态代理,其中使用反射类的Method方法调用指定的方法

public class DebugInvocationHandler implements InvocationHandler{

        private final Object target ;//final修饰的变量必须在声明时或构造函数中进行初始化赋值,不然会编译报错

        private DebugInvocationHandler(Object target){
            this.target = target;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("before method" + method.getName());
            Object result = method.invoke(target, args);
            System.out.println("after method" + method.getName());
            return result;
        }
    }
JDK实现动态代理

注意:为什么使用 Spring 的时候 ,一个@Component注解就声明了一个类为 Spring Bean 呢?为什么你通过一个 @Value注解就读取到配置文件中的值呢?究竟是怎么起作用的呢?

  因为可以基于反射分析类,然后获取到类/属性/方法/方法的参数上的注解。你获取到注解之后,就可以做进一步的处理

4.4.1 静态代理

 

4.4.2 动态代理

 

4.5 反射实战

4.5.1 获取Class对象的四种方式

  反射动态获取类信息,需要使用Class对象Class类对象将一个类的方法、属性等信息告诉运行的程序。Java提供四种方式获取Class对象。

1.知道具体类情况下使用:

@Data
    static class User{
        private String name;

    }

    public static void main(String[] args) {
        Class<User> userClass = User.class;
        System.out.println(userClass);
    }

  我们一般是不知道具体类的,基本都是通过遍历包下面的类来获取 Class 对象,通过此方式获取 Class 对象不会进行初始化

由于我创建的User是一个静态内部类,所以加载的结尾为$User

2.通过Class.forName()传入类的全路径获取

Class<?>aClass = Class.forName("com.ku.test.basic.TestReflect");
System.out.println(aClass);

3.通过instance.getClass()获取

User user = new User();
Class userClass2 = user.getClass();

4.通过类加载器XXXClassLoader传入类路径获取

Class<?> aClass = ClassLoader.getSystemClassLoader("com.ku.test.basic.TestReflect");

 4.5.2 反射的一些基本操作

public class TargetObject {
    private String value;

    private TargetObject(){
        this.value = "小库";
    }

    public void publicMethod(String s){
        System.out.println("I love" + s);
    }

    public void privateMethod(){
        System.out.println("love is" + value);
    }
}
TargetObject类
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        //反射基本操作
        //获取TargetClass的Class对象,并创建TargetObject类实例
        Class<?> targetClass = Class.forName("class com.ku.test.basic.TargetObject");
        //通过Class.newInstance创建一个对象强转为TargetObject类型
        TargetObject targetObject = (TargetObject)targetClass.newInstance();

        //获取TargetObject中定义的所有方法
        Method[] declaredMethods = targetClass.getDeclaredMethods();
        for (Method method:declaredMethods) {
            System.out.println(method.getName());
        }

        //获取指定方法并调用
        //传入String.class为参数类型
        Method publicMethod = targetClass.getDeclaredMethod("publicMethod", String.class);
        //动态调用
        publicMethod.invoke(targetObject, "xiaoku");

        //获取指定参数,并对参数进行修改
        Field field = targetClass.getDeclaredField("value");
        field.setAccessible(true);//为对类中参数进行修改取消安全检查
        field.set(targetClass, "xiaoku");

        //执行private方法
        Method privateMethod = targetClass.getDeclaredMethod("privateMethod");
        privateMethod.setAccessible(true);
        privateMethod.invoke(targetObject);
    }
具体操作类

注意:反射操作涉及多个受检测异常,我这里统一在方法后面抛出。

 

JVM

  • OOM(内存溢出)检查是否存在内存溢出,及时释放不再使用的对象(通过GC Root引用链查找)可达性分析;调整堆大小
  • StackOverflow(无限递归或方法调用层次多)检查是否存在无限递归;增加JVM栈大小,增加栈容量

7)创建对象的方式,new与反射哪个效率高
方式:new 、clone、反射、序列化和反序列化

 比较:new关键字创建对象的效率比使用反射机制高。“new”关键字在创建对象时,编译器在编译时已经确定具体的类和构造函数生成的可执行代码直接调用构造函数创建对象。省去了在运行时进行额外的查找和解析开销。而使用反射机制创建对象时,需要在运行时通过类的

全限定名获取类对象再通过类对象获取构造函数最后通过构造函数来创建对象。该过程涉及到类的查找、解析和动态方法调用等操作。因此相对于直接使用new关键字,反射机制的创建对象过程会增加额外的开销,从而导致效率降低。

4.MYSQL

4.1 一条SQL语句查询过程

  一条SQL执行过过程会经过连接器、查询缓存、解析器、执行器以及存储引擎等组件,执行过程图(仿小林coding)如下:

  MYSQL架构分为Server层存储引擎层Server层负责建立连接分析和执行SQL存储引擎负责数据的存储和提取

连接器:TCP三次握手建立连接校验用户名和密码校验权限

查询缓存:SQL需分毫不差才能从查询缓存中查询结果

词法解析:检查SQL中关键词是否存在,如from、where、group by 、having、select、order by、limit

语法解析:检查SQL语句是否存在问题

预处理器:确定表或字段是否存在;将select *中的* 扩展为表上的所有列

优化器:确定SQL语句的执行方案,即表中存在多个索引时,决定使用哪个索引。可在查询语句前加上explain查看SQL语句执行过程中,possible keykey以及type字段的值。进而分析SQL语句查询过程中的执行计划和优化过程

执行器:主键索引查询全表扫描索引下推

4.2 MySQL中一行记录如何存储

4.2.1 MySQL数据存放在哪个文件

  在Linux系统中,每创建一个数据库就会在/var/lib/mysql/目录里面创建一个以 database命名的目录,然后保存表的结构和数据到该目录。创建的数据库中包含三个文件,分别是db.opt(保存表默认的字符集和字符校验规则)、.frm结尾文件(保存表的元数据,主要包含表的结构)、.ibd(独占表空间)或.ibdata1(共享表空间)结尾文件(保存表的数据)。

 

8)MYSQL的快照读和当前读

MYSQL Innodb引擎默认级别虽是可重复读,但它能在很大程度上避免幻读(并不是完全解决),解决方式分别是快照读当前读
快照读(普通select语句):通过MVCC方式方式解决幻读事务执行过程中看到的数据,一直跟这个事务启动时看到数据一致,即使中途有其他事务插入一条数据,是查询不到该数据的,很好避免了幻读。
当前读(select...for update等语句)通过next-key lock(记录锁+间隙锁)解决幻读当执行select...for update语句时,会加上next-key lock如果有其他事务在next-key lock锁范围内插入一条记录,那么这个插入语句会被阻塞,无法成功插入,可很好避免幻读。

9)索引类型可以分为几类,聚簇索引

  1. 按照数据结构分类分为B+tree索引hash索引Full-text索引
  2. 按照物理结构分为聚簇索引和二级索引
  3. 按照字段属性分类,分为主键索引唯一索引前缀索引
  4. 按照字段个数分类:单列索引联合索引
  5. 聚簇索引主键索引二级索引辅助索引

10)如何查看索引是否有效
通过explain关键字查看查询是否使用索引,其中possiblekey字段表示可能用到的索引type表示表中找到所需行的方式真实是否使用索引,其中至少达到range以上最好达到ref。其中range>index>all。key列显示Mysql实际决定使用的键(索引)包含FORCE INDEXUSE INDEXIGNORE INDEX。

11)分页查询limit注意点
深度分页,即指定分页大小前面的偏移量

12)Redis缓存和MYSQL数据一致性(旁路缓存)
先更新数据库再删缓存,缓存设置一个缓存时间,图形如下所示:
13)Redis性能:高性能(单线程处理任务,其余线程分别处理关闭文件,AOF刷盘、释放内存)、高可用(集群、主从复制)
14)缓存雪崩、缓存击穿、缓存穿透是什么以及它们的解决方案

缓存雪崩大量缓存数据在同一时间过期(失效)时,如果此时有大量用户请求都无法在Redis中处理,于是全部请求都直接访问数据库导致数据库压力剧增严重时会造成数据库宕机,从而形成一系列连锁反应,造成整个系统崩溃。
解决方案:1)将缓存时间随机打散,即在原有失效时间上面增加一个1到10分钟的随机值。这样缓存时间不会都重复,降低缓存集体失效概率。2)设置缓存不失效,即后台服务器更新缓存数据
缓存穿透:在缓存和数据库中大量数据查不到
缓存击穿缓存中某个热点数据过期,大量请求访问该热点数据,无法从缓存中获取,直接访问数据库,数据库很容易被高并发请求冲垮。其实缓存雪崩跟缓存击穿很类似,一个是缓存数据一个是热点数据
解决方案:1)互斥锁:同一时间只有一个业务线程请求缓存,未能获取互斥锁的请求,需要等待锁释放后重新读取缓存,要么返回空值或默认值。2)不给热点数据设置缓存时间,由后台异步更新缓存。或在热点数据失效前,提前通知后台线程更新缓存或重新设置过期时间。
15)Web安全
①xss是什么,如何避免

XSS(跨脚本攻击)是种常见的Web应用程序安全漏洞攻击者通过受信任的网站上注入恶意脚本,使用户在浏览器中执行这些脚本,从而实现攻击目的,如窃取用户敏感信息会话劫持
解决方法:

  1. 输入验证和过滤,服务器对用户输入内容进行严格地输入验证和过滤,确保只接受合法的输入数据,拒绝或转义可能含有恶意代码的特殊字符,如HTML标签,JavaScript代码。
  2. 输出/编码/转义:将用户输入的数据输出到HTML页面中时,使用合适的编码方式对数据进行转义防止恶意脚本被浏览器执行。常用转义方法包括HTML实体编码,JavaScript转义等。
  3. 使用安全框架,有些框架内置安全机制,能够有效过滤和转义用户输入内容,减少XSS攻击。
  4. 设置合适的HTTP头设置HTTP响应头的Content-Security-Policy(CSP)X-XSS-Prottion字段,帮助浏览器自动组织XSS攻击。

②cors是什么,如何避免

cors(跨域资源共享)是一种浏览器的安全机制,允许Web应用程序在不同域名之间共享资源。跨域请求指是在浏览器的同源策略下,当一个网页上的脚本试图访问另一个域名下的资源会被阻止
避免方式:

  1. 服务器设置CORS响应头空值跨域资源共享,在响应头添加“Access-Control-Allow-Origin”指定允许访问资源的域名,也可设置为“”,允许任何域名访问,或设置为特定域名,只允许指定的域名访问。
  2. 设置其他CORS相关响应头,如 Access-Control-Allow-Methods(指定允许使用的请求方法)Access-Control-Allow-Headers(指定允许的请求头字段)
  3. 使用WebSocket代替AJAX,是一种全双工通信协议,可在不同域名之间建立实时的双向通信

2.编程题
给一个不重读集合,求所有子集

思路:递归调用,首先判断索引是否与数组长度相等,这里有两个功能。第一个是如果数组长度为空,直接返回空列表;第二个功能是当前索引值越界时,将当前列表t中存储的列表添加到最后输出结果列表ans中。

然后将当前列表最后一个数组值移除,再次进入递归计算。

参考链接

Java基础常见面试题总结(中) | JavaGuide(Java面试 + 学习指南)

小林coding (xiaolincoding.com)

List<Integer>t = new ArrayList<>();
List<List<Integer>>ans = new ArrayList<>();
public List<LIst<Integer>>subsets(int[] nums){
    dfs(0, nums);
    return ans;          
}
public void dfs(int cur, int[] nums){
    if(cur == nums.length){
        ans.add(t);
        return;
    }  
    t.add(nums[cur]);
    dfs(cur+1, nums);
    t.remove(t.size()-1);//移除指定索引位置元素
    dfs(cur+1, nums);
}
子集
posted @ 2023-10-31 10:32  求知律己  阅读(16)  评论(0编辑  收藏  举报