java基础(慢慢更新)

1.1. Array 和 List 的区别是什么?

Array和List都属于顺序表。

  • Array是一段连续的存储结构,List则是不连续的存储结构。 List的每个节点都有着一个Next属性,这个属性则记录着他的下一个节点的地址。
  • 数组必须要在初始化时分配固定的大小,List由于空间不必连续,所以无须指定初始大小。

当需要大量的查找操作时,最好使用Array。 当需要进行频繁的插入,删除操作时,最好使用List代替Array。

1.2. == 和 equals 、equals 和 hashcode 的区别是什么?

「== 解读」

对于基本类型和引用类型 == 的作用效果是不同的,如下所示:

  • 基本类型:比较的是值是否相同;
  • 引用类型:比较的是引用是否相同;

「equals 解读」

equals 本质上就是 ==,只不过 String 和 Integer 等重写了 equals 方法,把它变成了值比较。.

总结 :== 对于基本类型来说是值比较,对于引用类型来说是比较的是引用;而 equals 默认情况下是引用比较,只是很多类重写了 equals 方法,比如 String、Integer 等把它变成了值比较,所以一般情况下 equals 比较的是值是否相等。

///

「hashcode 解读」

hashCode() 的作用是获取哈希码,也称为散列码。实际上是返回一个int整数。

  • 如果两个对象相等(equals),它们的hashcode一定相同;
  • 反过来,如果两个对象有相同的hashcode,它们不一定相等(equals)。

拓展:在map中,数据是一般用数组+链表的形式存储,通过计算key的hashcode来确定value存放在数组中的位置(下标)。如果存在哈希冲突,则在对应下标位置使用链表存储存在冲突的元素。即,两个元素具有相同的hashcode值只能说明两个元素在存在同一个数组下标,但是没法保证是否是同一个对象,因为可能有哈希冲突,元素在链表中的位置不确定。而equals相等表示元素在链表中位置也一致。

1.3. final 在 Java 中有什么作用?

  • final 修饰的类叫最终类,该类不能被继承。
  • final 修饰的方法不能被重写。
  • final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改。

1.4. Java 中浮点型取整的方式有哪4种?

  • Math.ceil(),ceil是天花板的意思,表示向上取整。如1.9就返回2,-1.9就返回-1,返回的总是大于等于原数
  • Math.floor(),floor是地板的意思,表示向下取整。如1.9就返回值为1,-1.9就返回-2,返回的总是小于等于原数
  • Math.rint(),表示接近取整,顾名思义就是接近哪个取整哪个。如1.6接近2,所以就取2;1.4接近1,所以就取1;那么1.5呢,1.5跟1和2都很接近,这时候就取偶数。
  • Math.round(),执行的就是数学上的四舍五入(+0.5向下取整)。
Math.ceil(1.1); // 2

Math.floor(1.6); // 1

Math.rint(1.5); // 2
Math.rint(2.5); // 2

Math.round(1.5); // 2
Math.round(-1.6); // 等价于 Math.floor(-1.6 + 0.5) = -2

1.5. Java 中基本数据类型有哪些?String属于基本数据类型吗?

Java 中有 8 种基本数据类型,分别为:

  • 6 种数字类型:
    • 4 种整数型:byteshortintlong
    • 2 种浮点型:floatdouble
  • 1 种字符类型:char
  • 1 种布尔型:boolean

String 不属于基础类型,String 属于对象。

1.6. Java 中操作字符串都有哪些类?它们之间有什么区别?

操作字符串的类有:StringStringBufferStringBuilder

String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象
StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。

StringBuffer 和 StringBuilder 最大的区别在于:StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。

1.7. String str=“i” 和 String str=new String(“i”)一样吗?

不一样,因为内存的分配方式不一样。String str="i"的方式,Java 虚拟机会将其分配到常量池中;而 String str=new String(“i”) 则会被分到堆内存中。

1.8. 如何将字符串反转?

使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。

// StringBuffer reverse
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("abcdefg");
System.out.println(stringBuffer.reverse()); // gfedcba

// StringBuilder reverse
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("abcdefg");
System.out.println(stringBuilder.reverse()); // gfedcba

1.9. String 类的常用方法都有那些?

  • indexOf():返回指定字符的索引。
  • charAt():返回指定索引处的字符。
  • replace():字符串替换。
  • trim():去除字符串两端空白。
  • split():分割字符串,返回一个分割后的字符串数组。
  • getBytes():返回字符串的 byte类型数组。
  • length():返回字符串长度。
  • toLowerCase():将字符串转成小写字母。
  • toUpperCase():将字符串转成大写字符。
  • substring():截取字符串。
  • equals():字符串比较。

1.10. 抽象类必须要有抽象方法吗?

不需要。
抽象类不一定非要有抽象方法,例如:

abstract class Cat{
    
    
    public static void sayHi(){
    
    
        System.out.println("hi~");
    }
}

1.11. 普通类和抽象类有哪些区别?

  • 普通类不能包含抽象方法,抽象类可以包含抽象方法。
  • 抽象类不能直接实例化,普通类可以直接实例化。
// 抽象类可以通过子类继承,实例化子类的时候抽象类也会被实例化
abstract class B {
    
    
    private String str;

    public B(String tips) {
    
    
        this.str = tips;
        System.out.println("父类已经实例化:" + str);
    }

    public abstract void play();
}

public class A extends B {
    
    

    public A(String tips) {
    
    
        super(tips);
        System.out.println("子类已经实例化");
    }

    @Override
    public void play() {
    
    
        System.out.println("我实现了父类的方法");
    }

    public static void main(String[] args) {
    
    
        B aa = new A("6666");
    }
}

// 父类已经实例化:6666
// 子类已经实例化

1.12. 抽象类能使用 final 修饰吗?

不能。
定义抽象类就是让其他类继承的,如果定义为 final 该类就不能被继承,这样彼此就会产生矛盾,所以 final 不能修饰抽象类。

1.13. 接口和抽象类有什么区别?

实现:抽象类的子类使用 extends 来继承;接口必须使用 implements 来实现接口。
构造函数:抽象类可以有构造函数,接口不能有。
实现数量:一个类可以实现很多个接口,但是只能继承一个抽象类。
访问修饰符:接口中的方法默认使用 public 修饰,抽象类中的方法可以是任意访问修饰符。

1.14. 什么是重写和重载?

  • 重写(override):子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。
  • 重载(overlord):在一个类里面,多个方法具有相同的方法名,而参数不同。返回类型可以相同也可以不同。
public class TestCs {
    
    
    public int test(){
    
    
        System.out.println("test1");
        return 1;
    }
    public void test(int a){
    
    
        System.out.println("test2");
    }
    //以下两个参数类型顺序不同
    public String test(int a,String s){
    
    
        System.out.println("test3");
        return "returntest3";
    }
    public String test(String s,int a){
    
    
        System.out.println("test4");
        return "returntest4";
    }
    public static void main(String[] args){
    
    
        TestCs o = new TestCs(); // test1
        System.out.println(o.test()); // 1
        o.test(1); // test2
        System.out.println(o.test(1,"test3")); // test3 // returntest3
        System.out.println(o.test("test4",1)); // test4 // returntest4
    }
}

1.15. 解释Java的三大特性:封装、继承、多态?

封装:将类的某些信息隐藏在类的内部,不允许外部程序进行直接的访问调用。通过该类提供的方法来实现对隐藏信息的操作和访问。

// 使用private修饰符,表示最小的访问权限。
// 对成员变量的访问,统一提供setter,getter方法。
public class Student {
    
     
    // 属性
    private String name;
 
    // 无参构造函数
    public Student() {
    
    
    }
 
    // 有参构造函数
    public Student(String name) {
    
    
        this.name = name;
    } 
    
    // getter setter 方法
    public String getName() {
    
    
        return name;
    } 
    
    public void setName(String name) {
    
    
        this.name = name;
    } 
    
    // toString 方法
    @Override
    public String toString() {
    
    
        return "TestStudent{" +
                "name='" + name + '\'' +
                '}';
    }
}

继承:​ 可以使得子类具有父类的属性和方法,同时还可以在子类中重新定义以及追加属性和方法。class B extends A

多态:多态的前提:继承 + 重写。多态是同一个行为具有多个不同表现形式或形态的能力。

class Animal{
    
    
    public Animal() {
    
     // 子类会默认使用父类的无参构造
        System.out.println("我是父类的无参构造");
    }

    public void eat(){
    
     // 定义父类的方法
        System.out.println("动物会吃东西");

    }

}
// 创建Animal的子类Cat 
class Cat extends Animal{
    
        
    //子类可以重写父类的方法
    public void eat(){
    
    
        System.out.println("小猫喜欢吃小鱼干!");
    }
    // 定义子类的私有方法
    private void jump(){
    
    
        System.out.println("小猫会跳");
    }
}

// Animal cat = new Cat();

1.16. 单例模式是什么?怎么实现?

单例模式有以下特点:

  1. 单例类只能有一个实例。
  2. 单例类必须自己创建自己的唯一实例。
  3. 单例类必须给所有其他对象提供这一实例。

单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

//懒汉式单例类.在第一次调用的时候实例化自己 
public class Singleton {
    
    
    private Singleton() {
    
    }
    private static Singleton single=null;
    //静态工厂方法 
    public static Singleton getInstance() {
    
    
         if (single == null) {
    
      
             single = new Singleton();
         }  
        return single;
    }
}

容器

2.1. Java 中容器都有哪些?

Java 容器分为 CollectionMap 两大类,其下又有很多子类,如下所示:

  • Collection
    • List (有序集合)
      • ArrayList
      • LinkedList
      • Vector
        • Stack
    • Set (不重复集合)
      • HashSet
        • LinkedHashSet
      • TreeSet
  • Map
    • HashMap
      • LinkedHashMap
    • TreeMap
    • ConcurrentHashMap
    • Hashtable在这里插入图片描述

2.2. Collection 和 Collections 有什么区别?

  • Collection 是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法,所有集合都是它的子类,比如 List、Set 等。
  • Collections 是一个包装类,包含了很多静态方法,不能被实例化,就像一个工具类,比如提供的排序方法: Collections.sort(list)。

2.3. List、Set、Map 之间的区别是什么?

List、Set、Map 的区别主要体现在两个方面:元素是否有序是否允许元素重复
三者之间的区别,如下表:
在这里插入图片描述

2.4. HashMap 和 Hashtable 有什么区别?

  • 存储:HashMap 允许 key 和 value 为 null,而 Hashtable 不允许。
  • 线程安全:Hashtable 是线程安全的,而 HashMap 是非线程安全的。

推荐使用:在 Hashtable 的类注释可以看到,Hashtable 是保留类不建议使用,推荐在单线程环境下使用 HashMap 替代,如果需要多线程使用则用 ConcurrentHashMap 替代。

2.5. 如何决定使用 HashMap 还是 TreeMap?

  • 对于在 Map 中插入、删除、定位一个元素这类操作,HashMap 是最好的选择,因为相对而言 HashMap 的插入会更快;
  • 但如果你要对一个 key 集合进行有序的遍历,那 TreeMap 是更好的选择。

2.6. 说一下 HashMap 的实现原理?

HashMap 基于 Hash 算法实现的,我们通过 put(key,value)存储,get(key)来获取。
当传入 key 时,HashMap 会根据 key.hashCode() 计算出 hash 值,根据 hash 值将 value 保存在 bucket 里。
当计算出的 hash 值相同时,我们称之为 hash 冲突,HashMap 的做法是用链表和红黑树存储相同 hash 值的 value。当 hash 冲突的个数比较少时,使用链表否则使用红黑树

bucket:所谓bucket就是对于 HashMap 及其子类而言,它们采用 Hash 算法来决定集合中元素的存储位置。当系统开始初始化 HashMap 时,系统会创建一个长度为 capacity 的 Entry 数组,这个数组里可以存储元素的位置被称为“桶(bucket)”,每个 bucket 都有其指定索引,系统可以根据其索引快速访问该
bucket 里存储的元素。

2.7. 说一下 HashSet 的实现原理?

HashSet 是基于 HashMap 实现的,HashSet 底层使用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层 HashMap 的相关方法来完成,HashSet不允许重复的值

2.8. ArrayList 和 LinkedList 的区别是什么?

  • 数据结构实现:ArrayList是动态数组的数据结构实现,而LinkedList是双向链表的数据结构实现。
  • 随机访问效率:ArrayList 比 LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。
  • 增加和删除效率:在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为 ArrayList 增删操作要影响数组内的其他数据的下标。

综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList,而在插入和删除操作较多时,更推荐使用 LinkedList。

2.9. 如何实现数组和 List 之间的转换?

  • 数组转 List:使用 Arrays.asList(array) 进行转换。
  • List 转数组:使用 List 自带的 toArray() 方法。

示例代码:

// 数组转list
String[] array = new String[]{
    
    "薄荷分享","bhshare.cn"};
Arrays.asList(array);

// list转数组
List<String> list = new ArrayList<String>();
list.add("薄荷分享");
list.add("bhshare.cn");
list.toArray();

2.10. ArrayList 和 Vector 的区别是什么?

  • 线程安全:Vector 使用了 Synchronized 来实现线程同步,是线程安全的,而 ArrayList 是非线程安全的。
  • 性能:ArrayList 在性能方面要优于 Vector。
  • 扩容:ArrayList 和 Vector 都会根据实际的需要动态的调整容量,只不过在 Vector 扩容每次会增加 1 倍,而 ArrayList 只会增加 50%。

2.11. Array 和 ArrayList 有何区别?

  • Array可以存储基本数据类型和对象,ArrayList只能存储对象。
  • Array是指定固定大小的,而 ArrayList 大小是自动扩展的。
  • Array内置方法没有ArrayList多,比如 addAll、removeAll、iteration 等方法只有 ArrayList 有。

2.12. Queue 中 poll()和 remove()有什么区别?

  • 相同点:都是返回第一个元素,并在队列中删除返回的对象。
  • 不同点:如果没有元素 poll()会返回 null,而 remove()会直接抛出 NoSuchElementException 异常。

代码示例:

Queue<String> queue = new LinkedList<String>();
queue.offer("string"); // add
System.out.println(queue.poll());
System.out.println(queue.remove());
System.out.println(queue.size());

2.13. 哪些集合类是线程安全的?

  • 线程安全:VectorHashTableStackenumerationjava.util.concurrent包下的所有集合类,如:ConcurrentHashMapCopyOnWriteSet
  • 非线程安全:HashMap、ArrayList

2.14. 迭代器 Iterator 是什么?

Iterator 接口提供遍历任何 Collection 的接口。我们可以从一个 Collection 中使用迭代器方法来获取迭代器实例。
迭代器取代了 Java 集合框架中的 Enumeration,迭代器允许调用者在迭代过程中移除元素。

2.15. Iterator 怎么使用?有什么特点?

Iterator 使用代码如下:

List<String> list = new ArrayList<>();
... // add list
Iterator<String> it = list.iterator();
while(it.hasNext()){
    
    
  String obj = it.next();
  System.out.println(obj);
}

Iterator 的特点是更加安全,因为它可以确保,在当前遍历的集合元素被更改的时候,就会抛出 ConcurrentModificationException异常。

2.16. Iterator 和 ListIterator 有什么区别?

  • Iterator可以遍历 Set 和 List 集合,而 ListIterator 只能遍历 List。
  • Iterator 只能单向遍历,而 ListIterator 可以双向遍历(向前/后遍历)。
  • ListIterator 从 Iterator 接口继承,然后添加了一些额外的功能,比如添加一个元素、替换一个元素、获取前面或后面元素的索引位置。

2.17. 怎么确保一个集合不能被修改?

可以使用 Collections.unmodifiableCollection(Collection c) 方法来创建一个只读集合,这样改变集合的任何操作都会抛出 Java.lang.UnsupportedOperationException 异常。

示例代码:

List<String> list = new ArrayList<>();
list.add("x");
Collection<String> clist = Collections.unmodifiableCollection(list);
clist.add("y"); // 运行时此行报错
System.out.println(list.size());

多线程

3.1. 并行和并发有什么区别?

  • 并发(concurrency):把任务在不同的时间点交给处理器进行处理。在同一时间点,任务并不会同时运行。
  • 并行(parallelism):把每一个任务分配给每一个处理器独立完成。在同一时间点,任务一定是同时运行。

图解:
在这里插入图片描述


数据结构

4.1. 常见的排序算法有哪些?

在这里插入图片描述

  • 冒泡排序:把最大的换到最后一位,然后把第二大的换到倒数第二位…

     public static void bubbleSort(int[] args) {
          
          
          int len = args.length;
          while (len > 0) {
          
          
              for (int i = 0; i < len - 1; i++) {
          
          
                  int next = i + 1;
                  if (args[i] > args[next]) {
          
          
                      int temp = args[next];
                      args[next] = args[i];
                      args[i] = temp;
                  }
              }
              len--;
          }
      }
    
  • 快速排序:从数列中取出一个值,将比这个值大的放在它的右边,将比这个值小的放在它的左边,再在左右两个区域重复(递归)这个过程,直到各个区域只有一个数。

    public void quickSort(int[] target, int left, int right) {
          
          
          if (left >= right) {
          
          
              return;
          }
          int pivot = target[left];// 基准点
          int lo = left;
          int hi = right;
          while (lo < hi) {
          
          
    
              while (target[hi] >= pivot && lo < hi) {
          
          
                  hi--;
              }
              //把右边受阴元素和左边换
              target[lo] = target[hi];
    
              while (target[lo] <= pivot && lo < hi) {
          
          
                  lo++;
              }
              //把左边受阴元素和右边换
              target[hi] = target[lo];
          }
          //把拿出来的元素放回去
          target[lo] = pivot;
          
          quickSort(target, left, lo - 1);
          quickSort(target, lo + 1, right);
      }
    

4.2. 二叉树的层序遍历?

  • 思想:运用广度优先搜索(BFS)的思想,进行一层一层的遍历。

  • 实现

      1. 创建一个队列,初始时将根节点入队。
    
      2. 如果队列不为空,执行以下操作:
    
      	2.1 弹出队首元素,即当前层要遍历的结点。
    
      	2.2 如果当前结点有左儿子,则将其左儿子入队。
    
      	2.3 如果当前结点有右儿子,则将其右儿子入队。
    
      	2.4 将当前结点的值输出。
      	
      3. 重复步骤2,直到队列为空。
    
  • Java代码

    class TreeNode {
          
          
        int val;
        TreeNode left;
        TreeNode right;
    
        public TreeNode(int val) {
          
          
            this.val = val;
        }
    }
    // ----------
    public List<List<Integer>> levelOrder(TreeNode root) {
          
          
        List<List<Integer>> result = new ArrayList<>();
        if (root == null) {
          
          
            return result;
        }
    
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()) {
          
          
            int levelSize = queue.size(); // 当前层的节点数量
            List<Integer> level = new ArrayList<>(); // 当前层的节点值列表
            for (int i = 0; i < levelSize; i++) {
          
          
                TreeNode node = queue.poll();
                level.add(node.val);
                if (node.left != null) {
          
          
                    queue.add(node.left);
                }
                if (node.right != null) {
          
          
                    queue.add(node.right);
                }
            }
            result.add(level);
        }
    
        return result;
    }
    
    public static void main(String[] args) {
          
          
        TreeNode root = new TreeNode(0);
        root.left = new TreeNode(1);
        root.right = new TreeNode(10);
        root.left.left = new TreeNode(15);
        root.right.right = new TreeNode(5);
        System.out.println(levelOrder(root)); // [[0], [1, 10], [15, 5]]
    }
    
    

后端开发

5.1. hibernate和mybatis的区别?

  • MyBatis 是一个小巧、方便、高效、简单、直接、半自动化的持久层框架,Hibernate 是一个强大、方便、高效、复杂、间接、全自动化的持久层框架。

对于性能要求不太苛刻的系统,比如管理系统、ERP 等推荐使用 Hibernate,而对于性能要求高、响应快、灵活的系统则推荐使用 MyBatis。

  • 最大区别:针对高级查询Mybatis需要手动编写SQL语句,以及ResultMap。而Hibernate有良好的映射机制,开发者无需关心SQL的生成与结果映射,可以更专注于业务流程。
    针对简单逻辑,Hibernate和MyBatis都有相应的代码生成工具,可以生成简单基本的DAO层方法。
  • 开发难度:Hibernate的开发难度要大于Mybatis。主要由于Hibernate比较复杂、庞大,学习周期较长。而Mybatis则相对简单一些,并且Mybatis主要依赖于sql的书写,让开发者感觉更熟悉。
  • SQL书写:Mybatis的SQL是手动编写的,可以按需求指定查询的字段。不过没有自己的日志统计,所以要借助log4j来记录日志。Hibernate也可以自己写SQL来指定需要查询的字段,但这样就破坏了Hibernate开发的简洁性。不过Hibernate具有自己的日志统计。
  • 数据库扩展性:Mybatis由于所有SQL都是依赖数据库书写的,所以扩展性,迁移性比较差。Hibernate与数据库具体的关联都在XML中,所以HQL对具体是用什么数据库并不是很关心。
  • 缓存机制:因为Hibernate对查询对象有着良好的管理机制,用户无需关心SQL。所以Hibernate在使用二级缓存时如果出现脏数据,系统会报出错误并提示。而MyBatis在这一方面,使用二级缓存时需要特别小心。如果不能完全确定数据更新操作的波及范围,避免Cache的盲目使用。否则,Mybatis脏数据的出现会给系统的正常运行带来很大的隐患

5.2. 谈谈对数据库索引的理解、建立索引的优缺点?

  • 建立索引的优点: 可以大大提高系统的性能。

    1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
    2. 可以大大加快数据的检索速度,这也是创建索引的最主要的原因。
    3. 可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。
    4. 在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间
    5. 通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。
  • 建立索引的缺点

    1. 创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。
    2. 索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间。如果要建立聚簇索引,那么需要的空间就会更大。
    3. 当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度
  • 创建索引的方法

    1. 创建索引: create index <索引的名字> on table_name (列的列表);
    2. 修改表: alter table table_name add index [索引的名字] (列的列表);
    3. 创建表的时候指定索引:create table table_name ( […], INDEX [索引的名字] (列的列表) );
  • 查看索引的方法show index from table_name;

  • 索引的类型

    1. PRIMARY KEY (主键索引):mysql> alter table table_name add primary key ( ``column``)
    2. UNIQUE 或 UNIQUE KEY (唯一索引) :mysql> alter table table_name add unique (``column``)
    3. FULLTEXT (全文索引):mysql> alter table table_name add fulltext (``column``)
    4. INDEX (普通索引):mysql> alter table table_name add index index_name ( ``column``)
    5. 多列索引 (聚簇索引):mysql> alter table ``table_name`` add index index_name ( ``column1``, ``column2``, ``column3`` )

5.3. 什么样的字段适合/不适合创建索引?

  • 适合创建索引
    1. 在经常需要搜索的列上,可以加快搜索的速度;
    2. 在作为主键的列上,强制该列的唯一性和组织表中数据的排列结构;
    3. 在经常用在连接的列上,这些列主要是一些外键,可以加快连接的速度;
    4. 在经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的;
    5. 在经常需要排序的列上创建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间;
    6. 在经常使用在WHERE子句中的列上面创建索引,加快条件的判断速度。
  • 不适合创建索引:
    1. 对于那些在查询中很少使用或者参考的列不应该创建索引。这是因为,既然这些列很少使用到,因此有索引或者无索引,并不能提高查询速度。相反,由于增加了索引,反而降低了系统的维护速度和增大了空间需求。
    2. 对于那些只有很少数据值的列也不应该增加索引。这是因为,由于这些列的取值很少,例如人事表的性别列,在查询的结果中,结果集的数据行占了表中数据行的很大比例,即需要在表中搜索的数据行的比例很大。
    3. 对于那些定义为text, image和bit数据类型的列不应该增加索引。这是因为,这些列的数据量要么相当大,要么取值很少。
    4. 当修改性能远远大于检索性能时,不应该创建索 引。这是因为,修改性能和检索性能是互相矛盾的。

5.4. MySQL索引的数据结构、索引失效的原因?

  • 数据结构:B+树
    • MySQL 默认的存储引擎是 InnoDB,它采用 B+树作为索引的数据结构。
    • MySQL 的 MyISAM 存储引擎支持多种索引数据结构,比如 B+ 树索引、R 树索引、Full-Text 索引。MyISAM 存储引擎在创建表时,创建的主键索引默认使用的是 B+树索引。
  • 索引失效:
    1. 当我们使用左或者左右模糊匹配的时候,也就是 like %xx 或者 like %xx%这两种方式都会造成索引失效;
    2. 当我们在查询条件中对索引列使用函数,就会导致索引失效。
    3. 当我们在查询条件中对索引列进行表达式计算,也是无法走索引的。
    4. MySQL 在遇到字符串和数字比较的时候,会自动把字符串转为数字,然后再进行比较。如果字符串是索引列,而条件语句中的输入参数是数字的话,那么索引列会发生隐式类型转换,由于隐式类型转换是通过 CAST 函数实现的,等同于对索引列使用了函数,所以就会导致索引失效。
    5. 联合索引要能正确使用需要遵循最左匹配原则,也就是按照最左优先的方式进行索引的匹配,否则就会导致索引失效。
    6. 在 WHERE 子句中,如果在 OR 前的条件列是索引列,而在 OR 后的条件列不是索引列,那么索引会失效。

5.5. mybatis中 ${} 和 #{} 有什么区别?

  1. #{} 是预编译处理,${} 是字符串替换。
  2. MyBatis在处理 #{} 时,会将SQL中的 #{} 替换为 ? 号,使用 PreparedStatementset 方法来赋值;
  3. MyBatis在处理 ${} 时,就是把 ${} 替换成变量的值。
  4. 使用 #{} 可以有效的防止SQL注入,提高系统安全性。

5.6. 数据库事务的四大特性ACID?

数据库事务的四大特性(ACID)指的是原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability),它们是保证数据库操作正确性和数据完整性的关键因素。

  1. 原子性:事务中的所有操作要么全部成功执行,要么全部失败回滚。也就是说,一个事务中的多个操作必须作为一个不可分割的原子单元来处理,不能只执行其中的一部分操作而放弃另外一些操作。这样可以确保数据库在任何情况下都能保持一致性状态。

  2. 一致性:事务执行前后,数据库的状态必须保持一致。也就是说,在事务开始和结束时,数据库必须处于一致的状态。如果事务执行过程中发生了错误,那么必须撤销已经完成的操作,使得数据回到事务开始之前的状态。

  3. 隔离性:多个事务并发执行时,它们之间应该相互隔离。也就是说,一个事务执行的结果不能被其他事务看到,直到该事务提交之后。这种隔离性能够防止并发事务之间的干扰,确保每个事务都可以独立执行。

  4. 持久性:事务完成后,对数据库的修改必须被永久保存。也就是说,事务提交后,对数据库的修改应该被保存到磁盘上,而不是只在内存中保存。这种持久性能够确保修改对其他事务和系统故障都是可见的。

5.7 SSH框架和SSM框架的区别?

  1. SSH框架:将Spring作为业务逻辑层框架、将Struts2作为Web层框架、Hibernate作为持久化层框架的一种组合方式。
  2. SSM框架:将Spring作为整个应用的核心框架,SpringMVC作为Web层框架,MyBatis作为持久化层框架的一种组合方式。

待续:

5.8 spring?

springMVC、gc




参考文章:
https://blog.csdn.net/Jay112011/article/details/79955593
https://blog.csdn.net/uuqaz/article/details/123502779
https://www.bbsmax.com/A/QV5Z1GoVJy/
https://blog.csdn.net/m0_70051776/article/details/127878918
https://blog.csdn.net/qq_44333980/article/details/126020150
https://javaforall.cn/143988.html
https://blog.csdn.net/qq_52297656/article/details/127736721

猜你喜欢

转载自blog.csdn.net/qq_40738764/article/details/127677086