记2021秋招补录开始面试Java后端岗位记录

Java面试题

请介绍一下Java里内存泄漏和溢出的区别

  • 内存溢出 out of memory(OOM)

    是指程序在申请内存时,没有足够的内存空间供其使用。

    比如定义一个long型变量需要8个字节,但内存只有4个字节,就会造成内存溢出。

  • 内存泄露 memory leak

    就是内存够用,但是程序在申请内存后,无法释放已申请的内存空间。

    是垃圾回收机制中,有些对象已经没有用了,但垃圾回收机制的不够完善,无法进行垃圾回收,这些无法进行回收的垃圾就z造成内存泄露。

  • 内存溢出 ( OOM ) 原因:

    内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
    集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
    代码中存在死循环或循环产生过多重复的对象实体;
    使用的第三方软件中的BUG;
    启动参数内存值设定的过小

  • 内存溢出 ( OOM ) 的解决方案:

    第一步,修改JVM启动参数,直接增加内存。(-Xms,-Xmx参数一定不要忘记加。)

    第二步,检查错误日志,查看 “OutOfMemory” 错误前是否有其 它异常或错误。

    第三步,对代码进行走查和分析,找出可能发生内存溢出的位置。

    Jpofiler 工具分析

java多线程面试题整理及答案

1) 什么是线程?

​ 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。

2) 线程和进程有什么区别?

​ 线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。

​ 不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。

​ 每个线程都拥有单独的栈内存用来存储本地数据。

3) 如何在Java中实现线程?
  • 继承Thread 类,覆盖Thread类中的run方法。

    class MyThread extends Thread{
        @Override
        public void run(){
            ...
        }
    }
    MyThread myThread=new MyThread();
    myThread.start();
    
  • 声明一个实现 Runnable接口的类, 该类然后实现 run 方法。 实例化 Thread 类然后传入实现Runnable接口的类。

    class MyThreard implements Runnable {
        @Override
        public void run() {
            ...
        }
    }
    MyThreard p = new MyThreard();
    new Thread(p).start();
    
  • lambda 表达式

    new 资源类() 不继承 Thread 类 ,不实现 Runnable 接口,降低耦合性。
    new Thread( ()->{ 资源类.操作方法()}, "线程名" ).start();
    
4) 用Runnable还是Thread?

​ Java不支持类的多重继承,但允许你调用多个接口。所以一般实现Runnable接口。(使用 lambda表表达式降低耦合性)

6) Thread 类中的start() 和 run() 方法有什么区别?

在这里插入图片描述

​ start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果不一样。

调用run()方法只会在原来的线程中调用,没有新的线程启动.

start()方法会启动新线程。

7) Java中Runnable和Callable有什么不同?

​ 主要区别是Callable的 call() 方法可以返回值和抛出异常,而Runnable的run()方法没有这些功能。Callable可以返回装载有计算结果的Future对象

8) Java中CyclicBarrier 和 CountDownLatch有什么不同?
public class CountDownLatchTest {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(6);

        for (int i = 1; i <= 6; i++) {
            new Thread(()->{
                countDownLatch.countDown();
                System.out.println(Thread.currentThread().getName()+" go");
            }, String.valueOf(i)).start();
        }

        countDownLatch.await();     // 等待计数器 结束

        System.out.println("Close");

    }
}
public class CyclicBarrierDemo {
    public static void main(String[] args) {

        CyclicBarrier cyclicBarrier = new CyclicBarrier(7, ()->{
            System.out.println("召唤神龙");
        });

        for (int i = 1; i <= 7 ; i++) {
            final int temp = i;     // jdk1.8 不用final
            new Thread(()->{
                System.out.println("收集第"+ temp + "颗龙珠!");
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }, String.valueOf(i)).start();
        }
    }
}

CyclicBarrier 和 CountDownLatch 都可以用来让一组线程等待其它线程。

与 CyclicBarrier 不同的是,CountdownLatch 不能重新使用。

  • **CountDownLatch:**一个或者多个线程,等待其他多个线程完成某件事情之后才能执行;

  • **CyclicBarrier:**多个线程互相等待,直到到达同一个同步点,再继续一起执行。

9) Java内存模型是什么? Java Memory Model ( JMM )

​ 因为在不同的硬件生产商和不同的操作系统下,内存的访问逻辑有一定的差异,Java内存模型,就是为了屏蔽系统和硬件的差异,让一套代码在不同平台下能到达相同的访问结果。

在这里插入图片描述

内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的

  • lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态
  • unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
  • read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用
  • load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中
  • use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令
  • assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中
  • store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用
  • write  (写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中

在这里插入图片描述

JMM 对这八种指令的使用,制定了如下规则:

  • 不允许read和load、store和write操作之一单独出现。即使用了read必须load,使用了store必须write ( 成对使用 )

  • 不允许线程丢弃他最近的assign操作,即工作变量的数据改变了之后,必须告知主存 (工作内存变量改变及时通知主存

  • 不允许一个线程将没有assign的数据从工作内存同步回主内存 ()

  • 一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是对变量实施use、store操作之前,必须经过assign和load操作

  • 一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解锁

  • 如果对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新load或assign操作初始化变量的值

  • 如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量

  • 对一个变量进行unlock操作之前,必须把此变量同步回主内存

关于JMM的一些同步的约定∶

1、线程解锁前,必须把共享变量立刻刷回主存。

2、线程加锁前,必须读取主存中的最新值到工作内存中!

3、加锁和解锁是同一把锁.

JMM 模型特征

**原子性:**例如上面八项操作,在操作系统里面是不可分割的单元。被synchronized关键字或其他锁包裹起来的操作也可以认为是原子的。

**可见性:**每个工作线程都有自己的工作内存,所以当某个线程修改完某个变量之后,在其他的线程中,未必能观察到该变量已经被修改。**volatile关键字要求被修改之后的变量要求立即更新到主内存,每次使用前从主内存处进行读取。**因此volatile可以保证可见性。synchronized和final也能实现可见性。

**有序性:**java的有序性跟线程相关。如果在线程内部观察,会发现当前线程的一切操作都是有序的。如果在线程的外部来观察的话,会发现线程的所有操作都是无序的。

10)Java volatile关键字解析

​ volatile在Java语言中是一个关键字,用于修饰变量。

​ 被volatile修饰的变量后,表示这个变量在不同线程中是共享,编译器与运行时都会注意到这个变量是共享的,因此不会对该变量进行重排序。

​ 1、保证可见性 (变量在不同线程中是共享

​ 2、不保证原子性

​ 3、禁止指令重排 (加内存屏障)

11) 什么是线程安全?Vector是一个线程安全类吗?

当多个线程访问某一个类(对象或方法)时,对象对应的公共数据区始终都能表现正确,那么这个类(对象或方法)就是线程安全的。

Vector 是用同步方法来实现线程安全的, 而和它相似的ArrayList不是线程安全的。

13) Java中如何停止一个线程?

​ Java提供了很丰富的API但没有为停止线程提供API。

​ 当run() 或者 call() 方法执行完的时候线程会自动结束。

​ 如果要手动结束一个线程,你可以用volatile 布尔变量来退出run()方法的循环或者是取消任务来中断线程。

14) 一个线程运行时发生异常会怎样?

​ 如果异常没有被捕获该线程将会停止执行。

15) 如何在两个线程间共享数据?

​ (涉及到在两个线程间共享对象)用wait和notify方法实现了生产者消费者模型。

16) Java中notify 和 notifyAll有什么区别?

notify()方法不能唤醒某个具体的线程,所以只有一个线程在等 待的时候它才有用武之地。(只随机唤醒一个 wait 线程)

notifyAll()唤醒所有线程并允许他们争夺锁确保了至少有一个线程能继续运行(唤醒所有 wait 线程)

24) Java中堆和栈有什么不同?
  • ​ 运行时动态分配的。但缺点是,由于需要在运行时动态分配内存,所以存取速度较慢。

  • ​ 栈的优势是,存取速度比堆快,栈数据可以共享。但缺点是,存放在栈中的数据占用多少内存空间需要在编译时确定下来,缺乏灵活性。

25) 什么是线程池? 为什么要使用它?

​ 创建线程要花费昂贵的资源和时间,如果任务来了才创建线程那么响应时间会变长,而且一个进程能创建的线程数有限。

​ 为了避免这些问题,在程序启动的时 候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程。

27) 如何避免死锁?

​ 死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。

​ (多个线程互相抱着对方需要的资源,然后形成僵持)

​ 死锁的发生必须同时具备以下四个条件:

  • **互斥条件:**一个资源每次只能被一个进程使用。
  • **请求与保持条件:**一个进程因请求资源而阻塞时,对已获得的资源保持不放。
  • **不剥夺条件:**进程已获得的资源,在末使用完之前,不能强行剥夺。
  • **循环等待条件:**若干进程之间形成一种头尾相接的循环等待资源关系。

「如何预防死锁?」

  • 加锁顺序(线程按顺序办事)

  • 加锁时限 (线程请求所加上权限,超时就放弃,同时释放自己占有的锁)

  • 死锁检测

29) 怎么检测一个线程是否拥有锁?

​ 在java.lang.Thread中有一个方法叫holdsLock(),如果它返回true当且仅当当前线程拥有某个具体对象的锁。

31) JVM中哪个参数是用来控制线程的栈堆栈小的

​ -Xss参数用来控制线程的堆栈大小。

32) Java中synchronized 和 ReentrantLock 有什么不同?

​ 1、Synchronized内置的Java关键字,Lock是一个Java类

​ 2、Synchronized无法判断获取锁的状态,Lock 可以判断是否获取到了锁

​ 3、Synchronized 会自动释放锁,lock 必须要手动释放锁! 如果不释放锁,死锁

​ 4、Synchronized 线程1(获得锁,阻塞)、线程2(等待,傻傻的等) ; Lock锁就不一定会等待下去;

​ 5、Synchronized可重入锁,不可以中断的,非公平; Lock ,可重入锁,可以判断锁,非公平(可以自己设置);

​ 6、Synchronized适合锁少量的代码同步问题,Lock适合锁大量的同步代码!

33) 有三个线程T1,T2,T3,怎么确保它们按顺序执行?

​ 在多线程中有多种方法让线程按特定顺序执行,你可以用线程类的join()方法在一个线程中启动另一个线程,另外一个线程完成该线程继续执行。

​ 为了确保三个线程的顺序你应该先启动最后一个(T3调用T2,T2调用T1),这样T1就会先完成而T3最后完成。

34) Thread类中的yield方法有什么作用?

Yield方法可以暂停当前正在执行的线程对象,让其它有相同优先级的线程执行。

44) Java中的ReadWriteLock是什么?

​ 读写锁是用来提升并发程序性能的锁分离技术的成果。

​ 一个ReadWriteLock维护一对关联的锁,一个用于只读操作一个用于写。

​ 在没有写线程的情况下一个读锁可能会同时被多个读线程持有写锁是独占的

49) 如何在Java中创建线程安全的Singleton?

​ 利用枚举类型来创建Singleton

// double check lock 双重检测锁模式
public class DCLLazyMan {

    private DCLLazyMan(){
        System.out.println(Thread.currentThread().getName() + "ok");
    }

    private volatile static DCLLazyMan dclLazyMan;

    public static DCLLazyMan getInstance(){
        if (dclLazyMan == null) {
            synchronized (DCLLazyMan.class) {
                if (dclLazyMan == null) {
                    return dclLazyMan = new DCLLazyMan();  // 不是一个原子性操作 要加 volatile 禁止指令重排
                }
            }
        }
        return dclLazyMan;
    }
}
53) Java多线程中调用wait() 和 sleep()方法有什么不同?

wait()方法用于线程间通信,如果等待条件为真且其它线程被唤醒时它会释放锁

sleep()方法仅仅释放CPU资源或者让当前线程停止执行一段时间,但不会释放锁。

)CAS (compare and swap)

如果期望值达到了, 那么就更新,否则不更新,CAS是CPU并发原语。

CAS:比较当前工作内存中的值,如果这个值是期望的,则执行操作!如果不是就一直循环(底层是自旋锁)

)Synchronized

synchronized 翻译为中文的意思是同步,也称之为”同步锁“。

synchronized的作用是保证在同一时刻, 被修饰的代码块或方法只会有一个线程执行,以达到保证并发安全的效果。

Synchronized的使用

1.synchronized的3种使用方式

  • 修饰实例方法:作用于当前实例加锁
  • 修饰静态方法:作用于当前类对象加锁
  • 修饰代码块:指定加锁对象,对给定对象加锁

在这里插入图片描述

Thread的几种状态

在这里插入图片描述

Mysql面试题

1、能说下 myisam 和 innodb 的区别吗?

​ myisam 引擎是5.1版本之前的默认引擎,支持全文检索,但是不支持事务、行级锁、外键,并且索引和数据是分开存储的

​ innodb 是基于聚簇索引建立的,不支持全文检索,但是支持事务、行级锁、外键,并且通过MVCC来支持高并发,索引和数据存储在一起

myisaminnodb
事务支持不支持支持
数据行锁定不支持支持
外键约束不支持支持
全文索引支持不支持
表空间大小较小较大

常规使用操作:

  • myisam 节约空间,速度较快
  • Innodb 安全性高,事务的处理,多表多用户操作
2、说下mysql的索引有哪些吧,聚簇和非聚簇索引又是什么?

​ 索引按照数据结构来说主要包含B+树索引和Hash索引

InnoDB默认使用B+树索引

InnoDB有两大类索引:

  • 聚簇索引(聚集索引 clustered index)

    聚集索引叶子节点存储行记录,因此, InnoDB必须要有,且只有一个聚集索引:

    ​ (1)如果表定义了主键(Primary Key ),则主键就是聚集索引;

    ​ (2)如果表没有定义PK,则第一个not NULL unique列是聚集索引;

    ​ (3)否则,InnoDB会创建一个隐藏的row-id作为聚集索引;

  • 普通索引(二级索引 secondary index)

    普通索引的叶子节点存储主键值

在这里插入图片描述

什么是聚簇索引和非聚簇索引?

B+树是左小右大的顺序存储结构,节点只包含id索引列,而叶子节点包含索引列和数据,这种数据和索引在一起存储的索引方式叫做聚簇索引,一张表只能有一个聚簇索引。

非聚簇索引(二级索引)保存的是主键id值。

B+Tree索引

B+Tree是mysql使用最频繁的一个索引数据结构,是Inodb和Myisam存储引擎模式的索引类型。相对Hash索引,B+Tree在查找单条记录的速度比不上Hash索引,但是因为更适合排序等操作,所以它更受欢迎。毕竟不可能只对数据库进行单条记录的操作。

带顺序访问指针的B+Tree

B+Tree所有索引数据都在叶子节点上,并且增加了顺序访问指针,每个叶子节点都有指向相邻叶子节点的指针。

这样做是为了提高区间效率,例如查询key为从18到49的所有数据记录,当找到18后,只要顺着节点和指针顺序遍历就可以以此向访问到所有数据节点,极大提高了区间查询效率。

大大减少磁盘I/O读取

数据库系统的设计者巧妙利用了磁盘预读原理,将一个节点的大小设为等于一个页,这样每个节点需要一次I/O就可以完全载入。

B+树

在这里插入图片描述

mysql使用了 B+索引:

B树:有序数组+平衡多叉树;
B+树:有序数组链表+平衡多叉树

B+树的关键字全部存放在叶子节点中,非叶子节点用来做索引,而叶子节点中有一个指针指向一下个叶子节点。做这个优化的目的是为了提高区间访问的性能。而正是这个特性决定了B+树更适合用来存储外部数据。

B+树只要遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作(或者说效率太低)。

索引的最左匹配特性(即从左往右匹配):当b+树的数据项是复合的数据结构,比如(name,age,sex)的时候,b+数是按照从左到右的顺序来建立搜索树的,比如当(张三,20,F)这样的数据来检索的时候,b+树会优先比较name来确定下一步的所搜方向,如果name相同再依次比较age和sex,最后得到检索的数据;但当(20,F)这样的没有name的数据来的时候,b+树就不知道下一步该查哪个节点,因为建立搜索树的时候name就是第一个比较因子,必须要先根据name来搜索才能知道下一步去哪里查询。比如当(张三,F)这样的数据来检索时,b+树可以用name来指定搜索方向,但下一个字段age的缺失,所以只能把名字等于张三的数据都找到,然后再匹配性别是F的数据了, 这个是非常重要的性质,即索引的最左匹配特性。

这也是经常考察的,比如 我定义了 A,B,C的联合索引,如果 我只传递了 A,B 能走索引吗?答案是能,因为最左侧原理

1、联合索引是两个或更多个列上的索引。

对于联合索引:Mysql从左到右的使用索引中的字段,一个查询可以只使用索引中的一部份,但只能是最左侧部分。

例如索引是key index (a,b,c). 可以支持a 、 a,b 、 a,b,c 3种组合进行查找,但不支持 b,c进行查找 .当最左侧字段是常量引用时,索引就十分有效。

索引使用注意事项

1,不要滥用索引

​ ①,索引提高查询速度,却会降低更新表的速度,因为更新表时,mysql不仅要更新数据,保存数据,还要更新索引,保存索引

​ ②,索引会占用磁盘空间

2,索引不会包含含有NULL值的列

​ 复合索引只要有一列含有NULL值,那么这一列对于此符合索引就是无效的,因此我们在设计数据库设计时不要让字段的默认值为NULL。

3,MySQL查询只是用一个索引

如果where子句中使用了索引的话,那么order by中的列是不会使用索引的

4,like

​ like '%aaa%'不会使用索引而like "aaa%"可以使用索引

对比

1.B树和B+树同样适用于高度越低,查询越快。

2.B树查找节点,B+树只需要查询所有节点(索引),B树查询索引和数据。虽然可能第一个就找到,但在极端情况下,需要全查询索引和数据,不如B+树稳定。

3.B+树和B树比,B+树的硬盘空间更少,io的读写代价更低。因为B+树节点只有索引,占位更少。在查询的情况下硬盘指针移动更低

哈希索引和B+树索引的区别

从上面的图来看,B+树索引和哈希索引的明显区别是:

  • 如果是等值查询,那么哈希索引明显有绝对优势,因为只需要经过一次算法即可找到相应的键值;当然了,这个前提是,键值都是唯一的。如果键值不是唯一的,就需要先找到该键所在位置,然后再根据链表往后扫描,直到找到相应的数据;
  • 从示意图中也能看到,如果是范围查询检索,这时候哈希索引就毫无用武之地了因为原先是有序的键值,经过哈希算法后,有可能变成不连续的了,就没办法再利用索引完成范围查询检索;
  • 同理,哈希索引也没办法利用索引完成排序,以及like ‘xxx%’ 这样的部分模糊查询(这种部分模糊查询,其实本质上也是范围查询);
  • 哈希索引也不支持多列联合索引的最左匹配规则
  • B+树索引的关键字检索效率比较平均,不像B树那样波动幅度大,在有大量重复键值情况下,哈希索引的效率也是极低的,因为存在所谓的哈希碰撞问题

解决哈希冲突:

开放地址法、链地址法、再散列法(再哈希法)

3、什么是覆盖索引**(Covering index)**?

​ 覆盖索引指的是在一次查询中,如果一个索引包含或者说覆盖所有需要查询的字段的值,我们就称之为覆盖索引,而不再需要回表查询。

explain 的输出结果 Extra 字段为Using index时,能够触发索引覆盖。

​ 只需要在一棵索引树上就能获取SQL所需的所有列数据,无需回表,速度更快。

create` `table` `user` `(
  ``id ``int` `primary` `key``,
  ``name` `varchar``(20),
  ``sex ``varchar``(5),
  ``index``(``name``)
)engine=innodb;
select` `id,``name` `from` `user` `where` `name``=``'shenjian'``; 

能够命中name索引,索引叶子节点存储了主键id,通过name的索引树即可获取id和name,无需回表,符合索引覆盖,效率较高。

select` `id,``name``,sex ``from` `user` `where` `name``=``'shenjian'``;

能够命中name索引,索引叶子节点存储了主键id,但sex字段必须回表查询才能获取到,不符合索引覆盖,需要再次通过id值扫码聚集索引获取sex字段,效率会降低。

如果把(name)单列索引升级为联合索引(name, sex)就不同了。

create` `table` `user` `(
  ``id ``int` `primary` `key``,
  ``name` `varchar``(20),
  ``sex ``varchar``(5),
  ``index``(``name``, sex)
)engine=innodb;

可以看到:

select` `id,``name` `... ``where` `name``=``'shenjian'``;
select` `id,``name``,sex ... ``where` `name``=``'shenjian'``;

都能够命中索引覆盖,无需回表。

联合索引

联合索引是指对表上的多个列进行索引,联合索引也是一棵B+树,不同的是联合索引的键值数量不是1,而是大于等于2.

最左匹配原则

在这里插入图片描述

假定上图联合索引的为(a,b)。联合索引也是一棵B+树,不同的是B+树在对索引a排序的基础上,对索引b排序。所以数据按照(1,1),(1,2)…顺序排放。

对于selete * from table where a=XX and b=XX,显然是可以使用(a,b)联合索引的,

对于selete * from table where a=XX,也是可以使用(a,b)联合索引的。因为在这两种情况下,叶子节点中的数据都是有序的。

但是,对于b列的查询,selete * from table where b=XX。则不可以使用这棵B+树索引。可以发现叶子节点的b值为1,2,1,4,1,2。显然不是有序的,因此不能使用(a,b)联合索引。

selete * from table where b=XX and a=XX,也是可以使用到联合索引的,你可能会有疑问,这条语句并不符合最左匹配原则。这是由于查询优化器的存在,mysql查询优化器会判断纠正这条sql语句该以什么样的顺序执行效率最高,最后才生成真正的执行计划。所以,当然是我们能尽量的利用到索引时的查询顺序效率最高咯,所以mysql查询优化器会最终以这种顺序进行查询执行。

优化:在联合索引中将选择性最高的列放在索引最前面。

查询与索引优化分析

在优化MySQL时,通常需要对数据库进行分析,常见的分析手段有慢查询日志,EXPLAIN 分析查询,profiling分析以及show命令查询系统状态及系统变量,通过定位分析性能的瓶颈,才能更好的优化数据库系统的性能。

explain分析查询

使用 EXPLAIN 关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理你的SQL语句的。这可以帮你**分析你的查询语句或是表结构的性能瓶颈。**通过explain命令可以得到:

– 表的读取顺序

– 数据读取操作的操作类型

– 哪些索引可以使用

– 哪些索引被实际使用

– 表之间的引用

– 每张表有多少行被优化器查询

4、什么是事务?

​ 事务是**数据库执行操作的最小单元,可以由一个SQL语句或多个SQL语句组成,由于事务的特性保证了事务要么全成功执行,要么都不执行**。

事务的特性(ACID)

  • 原子性(Atomicity):一个事务的所有操作要么全成功,要么都不成功,不会只完成一部分。

    一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作,这就是事务的原子性

    是利用Innodb的undo log。undo log名为回滚日志,是实现原子性的关键,当事务回滚时能够撤销所有已经成功执行的sql语句,他需要记录你要回滚的相应日志信息。

  • 一致性(Consistency):事务开始之前和结束之后,数据库的完整性没有被破坏

    数据库通过原子性、隔离性、持久性来保证一致性

    一般由代码层面来保证

  • 隔离性(Isolation):数据库允许多个并发事务对数据进行读写,事务的隔离性要求每个读写事务与其他事务相互隔,可以防止多个事务的交叉执行导致数据不一致

    通常来说,一个事务所做的修改在最终提交以前,对其他事务是不可见的。

    利用的是锁和MVCC机制。MVCC,即多版本并发控制(Multi Version Concurrency Control),一个行记录数据有多个版本对快照数据,这些快照数据在undo log中。

    由MVCC来保证

  • 持久性(Durability):事务一旦执行,数据的修改就是永久的,即便系统出现故障也不会丢失该修改。

​ 内存+redo log来保证,mysql修改数据同时在内存和redo log记录这次操作,事务提交的时候通过redo log刷盘,宕机的时候可以从redo log恢复

5、并发问题和隔离级别

脏读:事务读取到了另一个事务未提交的数据

在这里插入图片描述

不可重读:一个事务内的两次相同查询,结果却不同
在这里插入图片描述

正常逻辑:事务A的第一次查询和第二次查询结果应该一样

不可重读和脏读不同的是,事务B提交了事务,也就是说这些数据是有效的,并不是脏数据。

幻读:事务两次查询的的结果集数目不同
在这里插入图片描述

正常逻辑:事务A的第一次查询和第二次查询的结果集数目应该一样

幻读和不可重读有些类似,不过幻读针对的是记录数的差异。同样的,幻读读取到的数据,也是有效数据。

InnoDB提供了四种隔离级别,从上到下:隔离性越高,并发性越低

在这里插入图片描述

所以可重复读是InnoDB的默认隔离级别

6、锁类型

共享锁和排他锁

  • 共享锁,也叫读锁,简称S锁(share lock)

    被加锁的对象只能被持有锁的事务进行读操作,其他事务也只能对该对象加共享锁进行读取,而不能修改该对象

  • 排他锁,也叫写锁,简称X锁(exclusive lock)

    被加了排他锁的对象只能被持有锁的事务读取和修改,其他事务不能读取和修改该对象。

意向锁

  • 意向共享锁,简称 IS 锁(Intention Shared Locks)

    事务准备给在数据行上加S锁之前,会先获取数据行所在表的IS锁;

  • 意向排他锁,简称 IX 锁(Intention Exclusive Locks)

    事务准备给在数据行上加X锁之前,会先获取数据行所在表的IX锁

这两种锁都是表级锁,是InnoDB自己维护的,并不需要人为加锁。

悲观锁主要分为共享锁和排他锁

悲观锁

具有强烈的独占和排他特性。它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态.(摘自百度百科)
传统关系型数据库里面的很多锁就是采用的这种机制,例如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。java里面的synchronize和ReentrantLock等重入锁就是采用的这种机制;

乐观锁

**总是认为不会产生并发问题,每次去取数据的时候总认为不会有其他线程对数据进行修改,因此不会上锁,但是在更新时会判断其他线程在这之前有没有对数据进行修改,一般会使用版本号机制或CAS操作实现。**乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。

在这里插入图片描述

悲观锁

悲观锁(Pessimistic Lock),顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。

Java synchronized 就属于悲观锁的一种实现,每次线程要修改数据时都先获得锁,保证同一时刻只有一个线程能操作数据,其他线程则会被block。

乐观锁

乐观锁(Optimistic Lock),顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在提交更新的时候会判断一下在此期间别人有没有去更新这个数据。乐观锁适用于读多写少的应用场景,这样可以提高吞吐量。

乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。

乐观锁一般来说有以下2种方式:

使用数据版本(Version)记录机制实现,这是乐观锁最常用的一种实现方式。何谓数据版本?即为数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据。

使用时间戳(timestamp)。乐观锁定的第二种实现方式和第一种差不多,同样是在需要乐观锁控制的table中增加一个字段,名称无所谓,字段类型使用时间戳(timestamp), 和上面的version类似,也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是版本冲突。

7、多版本并发控制 MVVC(Multi-Version Concurrency Control)?

​ MVCC叫做多版本并发控制,实际上就是保存了数据在某个时间节点的快照。

举个例子,程序员A正在读数据库中某些内容,而程序员B正在给这些内容做修改(假设是在一个事务内修改,大概持续10s左右),A在这10s内 则可能看到一个不一致的数据,在B没有提交前,如何让A能够一直读到的数据都是一致的呢?

第一种: 基于锁的并发控制,程序员B开始修改数据时,给这些数据加上锁,程序员A这时再读,就发现读取不了,处于等待情况,只能等B操作完才能读数据,这保证A不会读到一个不一致的数据,但是这个会影响程序的运行效率。

​ 还有一种就是:MVCC,每个用户连接数据库时,看到的都是某一特定时刻的数据库快照,在B的事务没有提交之前,A始终读到的是某一特定时刻的数据库快照,不会读到B事务中的数据修改情况,直到B事务提交,才会读取B的修改内容。

​ 一个支持MVCC的数据库,在更新某些数据时,并非使用新数据覆盖旧数据,而是标记旧数据是过时的,同时在其他地方新增一个数据版本。因此,同一份数据有多个版本存储,但只有一个是最新的。

MVCC提供了 时间一致性的 处理思路,在MVCC下读事务时,通常使用一个时间戳或者事务ID来确定访问哪个状态的数据库及哪些版本的数据。读事务跟写事务彼此是隔离开来的,彼此之间不会影响。假设同一份数据,既有读事务访问,又有写事务操作,实际上,写事务会新建一个新的数据版本,而读事务访问的是旧的数据版本,直到写事务提交,读事务才会访问到这个新的数据版本。

MVCC有两种实现方式,第一种实现方式是将数据记录的多个版本保存在数据库中,当这些不同版本数据不再需要时,垃圾收集器回收这些记录。这个方式被PostgreSQL和Firebird/Interbase采用,SQL Server使用的类似机制,所不同的是旧版本数据不是保存在数据库中,而保存在不同于主数据库的另外一个数据库tempdb中。第二种实现方式只在数据库保存最新版本的数据,但是会在使用undo时动态重构旧版本数据,这种方式被Oracle和MySQL/InnoDB使用。

​ InnoDB的MVCC实现,是通过保存数据在某个时间点的快照来实现的。一个事务,不管其执行多长时间,其内部看到的数据是一致的。也就是事务在执行的过程中不会相互影响。

InnoDB的MVCC,通过在每行记录后面保存两个隐藏的列来实现:一个保存了行的创建时间,一个保存行的过期时间(删除时间),当然,这里的时间并不是时间戳,而是系统版本号,每开始一个新的事务,系统版本号就会递增

十八、在MVCC并发控制中,读操作可以分成两类:

  1. 快照读 (snapshot read):读取的是记录的可见版本 (有可能是历史版本),不用加锁(共享读锁s锁也不加,所以不会阻塞其他事务的写)
  2. 当前读 (current read):读取的是记录的最新版本,并且,当前读返回的记录,都会加上锁,保证其他事务不会再并发修改这条记录
8、 什么是索引?

​ 索引是一种数据结构,可以帮助我们快速的进行数据的查找.

三、优点:

  1. 通过创建唯一性索引,可以保证数据库表中的每一行数据的唯一性。
  2. 可以加快数据的检索速度
  3. 可以加速表与表之间的连接
  4. 在使用分组和排序进行检索的时候,可以减少查询中分组和排序的时间

四、缺点

  1. 创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。

  2. 索引需要占用物理空间,数据量越大,占用空间越大

  3. 会降低表的增删改的效率,因为每次增删改索引,都需要进行动态维护

会导致索引失效

1、一些关键字会导致索引失效,例如 or, != , not in,is null ,is not unll

2、like查询是以%开头

3、隐式转换会导致索引失效。

4、对索引应用内部函数,索引字段进行了运算。

9、索引的分类
  • 主键索引 (Primary Key)
  • 唯一索引 (UNIQUE Key)
  • 常规索引 (Key/Index)
  • 全文索引 ( FullText )
10、 Hash索引和B+树所有有什么区别或者说优劣呢?

​ B+树是一个平衡的多叉树,从根节点到每个叶子节点的高度差值不超过1,而且同层级的节点间有指针相互链接,是有序的

在这里插入图片描述

哈希索引就是采用一定的哈希算法,把键值换算成新的哈希值,检索时不需要类似B+树那样从根节点到叶子节点逐级查找,只需一次哈希算法即可,是无序的,

在这里插入图片描述

hash索引进行等值查询更快(一般情况下),但是却无法进行范围查询.

hash索引不支持使用索引进行排序

hash索引不支持模糊查询以及多列索引的最左前缀匹配.原理也是因为hash函数的不可预测.AAAAAAAAB的索引没有相关性.

hash索引任何时候都避免不了回表查询数据,而B+树在符合某些条件(聚簇索引,覆盖索引等)的时候可以只通过索引完成查询.

hash索引虽然在等值查询上较快,但是不稳定.性能不可预测,当某个**键值存在大量重复的时候,发生hash碰撞,此时效率可能极差.**而B+树的查询效率比较稳定,对于所有的查询都是从根节点到叶子节点,且树的高度较低.

select、poll、epoll之间的区别

11、 主键使用自增ID还是UUID?

​ 推荐使用自增ID,不要使用UUID.

​ 因为在InnoDB存储引擎中,主键索引是作为聚簇索引存在的,也就是说,主键索引的B+树叶子节点上存储了主键索引以及全部的数据(按照顺序),如果主键索引是自增ID,那么只需要不断向后排列即可,如果是UUID,由于到来的ID与原来的大小不确定,会造成非常多的数据插入,数据移动,然后导致产生很多的内存碎片,进而造成插入性能的下降.

请你简单介绍一下,数据库水平切分与垂直切分

垂直拆分就是要把表按模块划分到不同数据库表中(当然原则还是不破坏第三范式),这种拆分在大型网站的演变过程中是很常见的。当一个网站还在很小的时候,只有小量的人来开发和维护,各模块和表都在一起,当网站不断丰富和壮大的时候,也会变成多个子系统来支撑,这时就有按模块和功能把表划分出来的需求。其实,相对于垂直切分更进一步的是服务化改造,说得简单就是要把原来强耦合的系统拆分成多个弱耦合的服务,通过服务间的调用来满足业务需求看,因此表拆出来后要通过服务的形式暴露出去,而不是直接调用不同模块的表,淘宝在架构不断演变过程,最重要的一环就是服务化改造,把用户、交易、店铺、宝贝这些核心的概念抽取成独立的服务,也非常有利于进行局部的优化和治理,保障核心模块的稳定性。

垂直拆分:单表大数据量依然存在性能瓶颈

水平拆分,上面谈到垂直切分只是把表按模块划分到不同数据库,但没有解决单表大数据量的问题,而水平切分就是要把一个表按照某种规则把数据划分到不同表或数据库里。例如像计费系统,通过按时间来划分表就比较合适,因为系统都是处理某一时间段的数据。而像SaaS应用,通过按用户维度来划分数据比较合适,因为用户与用户之间的隔离的,一般不存在处理多个用户数据的情况,简单的按user_id范围来水平切分。

通俗理解:水平拆分行,行数据拆分到不同表中, 垂直拆分列,表数据拆分到不同表中。

● 请你介绍一下,数据库的三个范式?

第一范式(1NF)
强调的是列的原子性,即列不能够再分成其他几列。
第二范式(2NF)
首先是 1NF,另外包含两部分内容,一是表必须有一个主键;二是没有包含在主键中的列必须完全依赖于主键,而不能只依赖于主键的一部分。
在1NF基础上,任何非主属性不依赖于其它非主属性 [在1NF基础上消除非主属性依赖]
第三范式(3NF)
第三范式(3NF)是第二范式(2NF)的一个子集,即满足第三范式(3NF)必须满足第二范式(2NF)。[在2NF基础上消除传递依赖]。
首先是 2NF,另外**非主键列必须直接依赖于主键,不能存在传递依赖。**即不能存在:非主键列 A 依赖于非主键列 B,非主键列 B 依赖于主键的情况。

11、数据库中的左连接(left join)和右连接(right join)区别

关于左连接和右连接总结性的一句话:

左连接where只影向右表,右连接where只影响左表。

  • Left Join

    select * from tbl1 Left Join tbl2 where tbl1.ID = tbl2.ID

    左连接后的检索结果是显示tbl1的所有数据和tbl2中满足where 条件的数据。

    简言之 Left Join影响到的是右边的表

  • Right Join

    select * from tbl1 Right Join tbl2 where tbl1.ID = tbl2.ID

    检索结果是tbl2的所有数据和tbl1中满足where 条件的数据。

    简言之 Right Join影响到的是左边的表。

  • inner join

    select * FROM tbl1 INNER JOIN tbl2 ON tbl1.ID = tbl2.ID

    功能和 select * from tbl1,tbl2 where tbl1.id=tbl2.id相同。

4.一般要使得数据库查询语句性能好点遵循一下原则:

​ 在做表与表的连接查询时,大表在前,小表在后
​ 不使用表别名,通过字段前缀区分不同表中的字段
​ 查询条件中的限制条件要写在表连接条件前
​ 尽量使用索引的字段做为查询条件

在使用left jion时,on和where条件的区别如下:

1、 on条件是在生成临时表时使用的条件,它不管on中的条件是否为真,都会返回左边表中的记录。
2、where条件是在临时表生成好后,再对临时表进行过滤的条件。这时已经没有left join的含义(必须返回左边表的记录)了,条件不为真的就全部过滤掉

  • 左外连接:LEFT OUTER JOIN, 以左表为主,先查询出左表,按照ON后的关联条件匹配右表,没有匹配到的用NULL填充,可以简写成LEFT JOIN
  • 右外连接:RIGHT OUTER JOIN, 以右表为主,先查询出右表,按照ON后的关联条件匹配左表,没有匹配到的用NULL填充,可以简写成RIGHT JOIN

表1 left join(right join) 表2 on 条件

on: 生成临时表,左表(右表)的每一行匹配 右表(左表)中的所有行,如果存在则接入右表中的值,不存在则接入右表中所有字段都为null值的记录

在这里插入图片描述

1.INNER JOIN (内连接)
内连接是一种一一映射关系,就是两张表都有的才能显示出来

在这里插入图片描述

2.LEFT JOIN (左连接)
左连接是左边表的所有数据都有显示出来,右边的表数据只显示共同有的那部分,没有对应的部分只能补空显示,所谓的左边表其实就是指放在left join的左边的表

在这里插入图片描述

3.RIGHT JOIN(右连接)
右连接正好是和左连接相反的,这里的右边也是相对right join来说的,在这个右边的表就是右表

在这里插入图片描述

一、Mysql索引主要有两种结构:B+Tree索引和Hash索引

(a) Inodb存储引擎 默认是 B+Tree索引

(b) MyISAM 存储引擎 默认是Fulltext索引;

©Memory 存储引擎 默认 Hash索引;

SQl 中的 # 和 $

一、主要区别如下:

1、# 将传入的参数都当成一个字符串,会对自动传入的数据加一个双引号如:order by #{age},如果传入的值是18,那么解析成sql时的值为order by "18", 如果传入 age,则会解析为 order by "age"

2、 $ 将传入的参数直接显示生成在sql中,被当成一个对象。如:order by${age},如果传入的值是18,那么解析成sql时的值为order by 18, 如果传入的值是age,则解析成的sql为 order by age

3、# 方式底层采用预编译方式PreparedStatement,能够很大程度**防止sql注入**;$方式底层只是Statement无法防止Sql注入

4、$ 方式一般用于传入数据库对象,例如传入表名

5、一般能用#的就别用$

注意点:MyBatis排序时使用order by 动态参数时需要注意,用$而不是#

说说对SQL语句优化有哪些方法?(选择几条)

(1)Where子句中:where表之间的连接必须写在其他Where条件之前,那些可以过滤掉最大数量记录的条件必须写在Where子句的末尾.HAVING最后。

(2)用EXISTS替代IN、用NOT EXISTS替代NOT IN。

(3) 避免在索引列上使用计算

(4)避免在索引列上使用IS NULL和IS NOT NULL

(5)对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。

(6)应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描

(7)应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描

计算机网络常见面试题

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

TCP(Transmission Control Protocol,传输控制协议)是面向连接的协议,也就是说,**在收发数据前,必须和对方建立可靠的连接。**一个TCP连接必须有三次握手、四次挥手。

UDP(User Data Protocol,用户数据报协议)是一个非连接的协议,传输数据之前源端和终端不建立连接, 当它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。虽然 UDP 不提供可靠交付,但在某些情况下 UDP 确是一种最有效的工作方式(一般用于即时通信),比如: QQ 语音、 QQ 视频 、直播等等

1、TCP的三次握手(Three-Way Handshake)(连接的建立)

在这里插入图片描述

  • 第一次握手(SYN=1, seq=x)

    建立连接。客户端发送连接请求报文段,这是报文首部中的同步位SYN=1,同时选择一个初始序列号 seq=x ,此时,客户端进程进入了 SYN-SENT(同步已发送状态)状态。TCP规定,SYN报文段(SYN=1的报文段)不能携带数据,但需要消耗掉一个序号

  • 第二次握手(SYN=1, ACK=1, seq=y, ACKnum=x+1)

    服务器收到客户端的SYN报文段,如果同意连接,则发出确认报文。确认报文中应该 ACK=1,SYN=1,确认号ACKnum=x+1,同时,自己还要发送SYN请求信息,SYN=1,为自己初始化一个序列号 seq=y,服务器端将上述所有信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时,TCP服务器进程进入了SYN-RCVD(同步收到)状态。这个报文也不能携带数据,但是同样要消耗一个序号

  • 第三次握手(ACK=1,ACKnum=y+1)

    客户端收到服务器的SYN+ACK报文段,再次发送确认包(ACK),SYN 标志位为0,ACK 标志位为1,确认号 ACKnum = y+1,这个报文段发送完毕以后,客户端和服务器端都进入ESTABLISHED(已建立连接)状态,完成TCP三次握手。

1、建立连接,客户端发送SYN=1的SYN报文(不能携带数据,但需要消耗一个序号),然后客户端进程进入SYN-SENT(同步已发送状态)

2、服务器收到客户端的SYN报文,如果同意连接,则发出确认报文(SYN+ACK报文段),TCP服务器进程进入了SYN-RCVD(同步收到)状态。(报文也不能携带数据,但是同样要消耗一个序号)

3、客户端收到服务器的SYN+ACK报文段,再次发送确认包(ACK),这个报文段发送完毕以后,客户端和服务器端都进入ESTABLISHED(已建立连接)状态,完成TCP三次握手。

客户端发送连接请求 		(发送带有 SYN 标志的数据包)       第一次握手
服务端发送确认报文 		(发送带有 SYN/ACK 标志的数据包)   第二次握手
客户端再次发送确认报文    (发送带有带有 ACK 标志的数据包)   第三次握手
2、TCP的四次挥手(Four-Way Wavehand)(连接的释放)

在这里插入图片描述

  • 第一次挥手(FIN=1,seq=x)

    主机1(可以使客户端,也可以是服务器端),设置seq=x,向主机2发送一个FIN报文段;此时,主机1进入FIN_WAIT_1状态;这表示主机1没有数据要发送给主机2了;

  • 第二次挥手(ACK=1,ACKnum=x+1)

    主机2收到了主机1发送的FIN报文段,向主机1回一个ACK报文段,Acknnum=x+1,主机1进入FIN_WAIT_2状态;主机2告诉主机1,我“同意”你的关闭请求;

  • 第三次挥手(FIN=1,seq=y)

    主机2向主机1发送FIN报文段,请求关闭连接,同时主机2进入LAST_ACK 状态

  • 第四次挥手(ACK=1,ACKnum=y+1)

    主机1收到主机2发送的FIN报文段,向主机2发送ACK报文段,然后主机1进入TIME_WAIT状态;主机2收到主机1的ACK报文段以后,就关闭连接;此时,主机1等待2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,主机1也可以关闭连接了,进入 CLOSED 状态。

1、主机1(可以使客户端,也可以是服务器端)向主机2发送一个 FIN报文段,表示主机1没有数据要发送给主机2了,主机1进入FIN_WAIT_1状态。

2、主机2收到主机1发送的FIN报文,向主机1大发送一个ACK报文,主机1进入FIN_WAIT_2状态。

3、主机2向主机1发送FIN报文,请求关闭连接,同时主机2进入LAST_ACK 状态

4、 主机1收到主机2发送的FIN报文段,向主机2发送ACK报文段,然后主机1进入TIME_WAIT状态;

主机2收到主机1的ACK报文段以后,就关闭连接;此时,主机1等待2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,主机1也可以关闭连接了,进入 CLOSED 状态。

  • 服务器-收到这个 FIN,它发回一 个 ACK,确认序号为收到的序号加1 。和 SYN 一样,一个 FIN 将占用一个序号
  • 服务器-关闭与客户端的连接,发送一个FIN给客户端
  • 客户端-发回 ACK 报文确认,并将确认序号设置为收到序号加1
Clenit: 我们分手吧
Server: 你要走了吗? 
Server: 你真的真的要走了吗?
Clenit: 我真的走了
3、 为什么要三次握手?

三次握手的目的是建立可靠的通信信道,说到通讯,简单来说就是数据的发送与接收,而三次握手最主要的目的就是双方确认自己与对方的发送与接收是正常的。

第一次握手:Client 什么都不能确认;Server 确认了对方发送正常,自己接收正常

第二次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:对方发送正常,自己接收正常

第三次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:自己发送、接收正常,对方发送、接收正常

所以三次握手就能确认双发收发功能都正常,缺一不可。

4、 为什么要四次挥手?

任何一方都可以在数据传送结束后发出连接释放的通知,待对方确认后进入半关闭状态。当另一方也没有数据再发送的时候,则发出连接释放通知,对方确认后就完全关闭了TCP连接。

举个例子:A 和 B 打电话,通话即将结束后,A 说“我没啥要说的了”,B回答“我知道了”,但是 B 可能还会有要说的话,A 不能要求 B 跟着自己的节奏结束通话,于是 B 可能又巴拉巴拉说了一通,最后 B 说“我说完了”,A 回答“知道了”,这样通话才算结束。

5、URI和URL的区别是什么?
  • URI(Uniform Resource Identifier) 是统一资源标志符,可以唯一标识一个资源。

  • URL(Uniform Resource Location) 是统一资源定位符,可以提供该资源的路径。它是一种具体的 URI,即 URL 可以用来标识一个资源,而且还指明了如何定位这个资源。

URI:表示的是web上每一种可用的资源,如 HTML文档、图像、视频片段、程序等都由一个URI进行标识的。

URL:URL是URI的一个子集。采用URL可以用一种统一的格式来描述各种信息资源,包括文件、服务器的地址和目录等。URL是URI概念的一种实现方式。

**举例: **

​ URI可以唯一的标识某一资源, 比如学号可以唯一标识学生, 身份证号可以唯一标识一个人等等。

​ URL是URI的子集, 不仅可以唯一标识一个资源,还能告诉你他在哪。 比如某学生在5号公寓楼328寝5床, 这就是一个URL。

6、Cookie的作用是什么?和Session有什么区别?

​ cookie 是浏览器的一种缓存机制,它可用于维持客户端与服务器端之间的会话。

​ session 是一种维持客户端与服务器端会话的机制。session 只是一种会话机制,在许多 web 应用中,session 机制就是通过 cookie 来实现的

session和cookie有什么区别

​ cookie会将会话保存在客户端,session 把会话保留在服务器端。

1、cookie数据存放在客户的浏览器上,session数据放在服务器上。

2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗考虑到安全应当使用session。

3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用COOKIE。

4、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。

Cookie禁用了,Session还能用吗?

Cookie与 Session,一般认为是两个独立的东西,Session采用的是在服务器端保持状态的方案,而Cookie采用的是在客户端保持状态的方案。

但为什么禁用Cookie就不能得到Session呢?

因为Session是用Session ID来确定当前对话所对应的服务器Session,而Session ID是通过Cookie来传递的,禁用Cookie相当于失去了Session ID,也就得不到Session了。

是不是Cookie让禁用了,Session就一定不能用了呢?答案是否定的,即使cookie禁用,session还可以借助于url来和客户端交互。

7、从输入网址到获得页面的过程
  1. 浏览器查询 DNS,获取域名对应的IP地址:具体过程包括浏览器搜索自身的DNS缓存、搜索操作系统的DNS缓存、读取本地的Host文件和向本地DNS服务器进行查询等。对于向本地DNS服务器进行查询,如果要查询的域名包含在本地配置区域资源中,则返回解析结果给客户机,完成域名解析(此解析具有权威性);如果要查询的域名不由本地DNS服务器区域解析,但该服务器已缓存了此网址映射关系,则调用这个IP地址映射,完成域名解析(此解析不具有权威性)。如果本地域名服务器并未缓存该网址映射关系,那么将根据其设置发起递归查询或者迭代查询;
  2. 浏览器获得域名对应的IP地址以后,浏览器向服务器请求建立链接,发起三次握手
  3. TCP/IP链接建立起来后,浏览器向服务器发送HTTP请求
  4. 服务器接收到这个请求,并根据路径参数映射到特定的请求处理器进行处理,并将处理结果及相应的视图返回给浏览器;
  5. 浏览器解析并渲染视图,若遇到对js文件、css文件及图片等静态资源的引用,则重复上述步骤并向服务器请求这些资源;
  6. 浏览器根据其请求到的资源、数据渲染页面,最终向用户呈现一个完整的页面

DNS TCP/IP HTTP

IP寻址 ARP

通过二层交换机直连的两台主机之间的数据传输(在同一个局域网内的两台主机)

1、 A开始只知道B的IP地址,但是不知道B的Mac地址,此时A会发起一个ARP广播:我的IP是xxx,Mac地址是xxx,想知道IP为B的Mac地址是多少,这个广播会被本局域网内所有主机收到,但是只有B会响应,并且向A后回复一个ARP响应。

2、交换机收到ARP广播后,将它转发到所有端口(网口),并且记录该广播源MAC地址(A的MAC地址)到mac地址列表,B收到广播发现和自己IP匹配就会向A发送ARP响应

3、交换机收到B的响应 将响应帧目标MAC与自己mac地址表对比 发现对应的端口(网口是F0/1)便将响应帧转发到F0/1 同时 将响应帧的源mac地址B的MAC地址添加到mac地址列表

4、A收到B的回复帧后 ,得知B ip地址对应的mac地址 于是将信息保存到本地ARP高速缓存 同时以B的mac地址为目标地址封装成帧 发送出去 交换机再次收到A的数据 发现目标的MAC地址是B 对应端口(网口)F0/2 于是将帧转发到F0/2

5、B收到A发出的数据

8、HTTP和HTTPS的区别?
  1. https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
  2. http是超文本传输协议,信息是明文传输https则是具有安全性的ssl加密传输协议
  3. http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
  4. http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

1) https协议要申请证书到ca,需要一定经济成本;

2) http是明文传输,https是加密的安全传输;

3) 连接的端口不一样,http是80,https是443;

4)http连接很简单,没有状态;https是ssl加密的传输,身份认证的网络协议,相对http传输比较安全。

、HTTPS 连接过程?

在这里插入图片描述

  • 在使用HTTPS是需要保证服务端配置正确了对应的安全证书
  • 客户端发送请求到服务端
  • 服务端返回公钥和证书到客户端
  • 客户端接收后会验证证书的安全性,如果通过则会随机生成一个随机数,用公钥对其加密,发送到服务端
  • 服务端接受到这个加密后的随机数后会用私钥对其解密得到真正的随机数,随后用这个随机数当做私钥对需要发送的数据进行对称加密
  • 客户端在接收到加密后的数据使用私钥(即生成的随机值)对数据进行解密并且解析数据呈现结果给客户
  • SSL加密建立

https://www.pianshen.com/article/1027875259/

在这里插入图片描述

9、请你解释一下TCP为什么可靠一些

考察点:TCP

参考回答:

三次握手,超时重传,滑动窗口,拥塞控制。

请你谈谈DNS的寻址过程。

(1)检查浏览器缓存、检查本地hosts文件是否有这个网址的映射,如果有,就调用这个IP地址映射,解析完成。

(2)如果没有,则查找本地DNS解析器缓存是否有这个网址的映射,如果有,返回映射,解析完成。

(3)如果没有,则查找填写或分配的首选DNS服务器,称为本地DNS服务器。服务器接收到查询时:

如果要查询的域名包含在本地配置区域资源中,返回解析结果,查询结束,此解析具有权威性。

如果要查询的域名不由本地DNS服务器区域解析,但服务器缓存了此网址的映射关系,返回解析结果,查询结束,此解析不具有权威性。

(4)如果本地DNS服务器也失效:

如果未采用转发模式(迭代),本地DNS就把请求发至13台根DNS,根DNS服务器收到请求后,会判断这个域名(如.com)是谁来授权管理,并返回一个负责该顶级域名服务器的IP,本地DNS服务器收到顶级域名服务器IP信息后,继续向该顶级域名服务器IP发送请求,该服务器如果无法解析,则会找到负责这个域名的下一级DNS服务器(如http://baidu.com)的IP给本地DNS服务器,循环往复直至查询到映射,将解析结果返回本地DNS服务器,再由本地DNS服务器返回解析结果,查询完成。

如果采用转发模式(递归),则此DNS服务器就会把请求转发至上一级DNS服务器,如果上一级DNS服务器不能解析,则继续向上请求。最终将解析结果依次返回本地DNS服务器,本地DNS服务器再返回给客户机,查询完成。

TCP 和 UDP 的区别

UDP协议全称是用户数据报协议,在网络中它与TCP协议一样用于处理数据包,是一种无连接的协议。在OSI模型中,在第四层——传输层,处于IP协议的上一层。UDP有不提供数据包分组、组装和不能对数据包进行排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。

1. 面向无连接

首先 UDP 是不需要和 TCP一样在发送数据前进行三次握手建立连接的,想发数据就可以开始发送了。并且也只是数据报文的搬运工,不会对数据报文进行任何拆分和拼接操作。

具体来说就是:

  • 在发送端,应用层将数据传递给传输层的 UDP 协议,UDP 只会给数据增加一个 UDP 头标识下是 UDP 协议,然后就传递给网络层了
  • 在接收端,网络层将数据传递给传输层,UDP 只去除 IP 报文头就传递给应用层,不会任何拼接操作

2. 有单播,多播,广播的功能

UDP 不止支持一对一的传输方式,同样支持一对多,多对多,多对一的方式,也就是说 UDP 提供了单播,多播,广播的功能。

3. UDP是面向报文的

发送方的UDP对应用程序交下来的报文,在添加首部后就向下交付IP层。UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。因此,应用程序必须选择合适大小的报文

4. 不可靠性

首先不可靠性体现在无连接上,通信都不需要建立连接,想发就发,这样的情况肯定不可靠。

并且收到什么数据就传递什么数据,并且也不会备份数据,发送数据也不会关心对方是否已经正确接收到数据了。

再者网络环境时好时坏,但是 UDP 因为没有拥塞控制,一直会以恒定的速度发送数据。即使网络条件不好,也不会对发送速率进行调整。这样实现的弊端就是在网络条件不好的情况下可能会导致丢包,但是优点也很明显,在某些实时性要求高的场景(比如电话会议)就需要使用 UDP 而不是 TCP。

JVM面试题

1、JVM 位置

在这里插入图片描述

2、JVM 体系结构

在这里插入图片描述

在这里插入图片描述

3、什么是类加载器,类加载器有哪些?

实现通过类的权限定名获取该类的二进制字节流的代码块叫做类加载器。

主要有一下四种类加载器:

  • 启动类加载器(Bootstrap ClassLoader)用来加载java核心类库,无法被java程序直接引用。

  • 扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。

  • 系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。

  • 用户自定义类加载器,通过继承 java.lang.ClassLoader类的方式实现。

说一下类装载的执行过程?

类装载分为以下 5 个步骤:

  • 加载:根据查找路径找到相应的 class 文件然后导入;
  • 验证:检查加载的 class 文件的正确性;
  • 准备:给类中的静态变量分配内存空间;
  • 解析:虚拟机将常量池中的符号引用替换成直接引用的过程。符号引用就理解为一个标示,而在直接引用直接指向内存中的地址;
  • 初始化:对静态变量和静态代码块执行初始化工作。
4、什么是双亲委派模型?

双亲委派模型:如果一个类加载器收到了类加载的请求,它首先不会自己去加载这个类,而是把这个请求委派给父类加载器去完成,每一层的类加载器都是如此,这样所有的加载请求都会被传送到顶层的启动类加载器中,只有当父加载无法完成加载请求(它的搜索范围中没找到所需的类)时,子加载器才会尝试去加载类。

当一个类收到了类加载请求时,不会自己先去加载这个类,而是将其委派给父类,由父类去加载,如果此时父类不能加载,反馈给子类,由子类去完成类的加载。

在这里插入图片描述

类加载器分类:

  • 启动类加载器(Bootstrap ClassLoader),是虚拟机自身的一部分,用来加载Java_HOME/lib/目录中的,或者被 -Xbootclasspath 参数所指定的路径中并且被虚拟机识别的类库;
  • 其他类加载器:
  • 扩展类加载器(Extension ClassLoader):负责加载\lib\ext目录或Java. ext. dirs系统变量指定的路径中的所有类库;
  • 应用程序类加载器(Application ClassLoader)。负责加载用户类路径(classpath)上的指定类库,我们可以直接使用这个类加载器。一般情况,如果我们没有自定义类加载器默认就是用这个加载器。
5、堆内存

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hPdqokQi-1641473858255)(D:\Typora\Java面试题.assets\image-在这里插入图片描述
.png)]

新生区

  • 类:诞生和成长的地方,甚至死亡。
  • 伊甸园区:所有的对象都在 伊甸园区 new 出来
  • 幸存区(0,1)

养老区

​ 幸存区满了就会到养老区

永久代(元空间)

在这里插入图片描述

6、GC(Gabage Collection)垃圾回收机制?

在这里插入图片描述

GC两种类型:

  • 轻GC(普通的GC)

  • 重GC(全局GC)

说一下 JVM 有哪些垃圾回收算法?

  • **标记-清除算法:**标记无用对象,然后进行清除回收。缺点:效率不高,无法清除垃圾碎片。
  • **复制算法:**按照容量划分二个大小相等的内存区域,当一块用完的时候将活着的对象复制到另一块上,然后再把已使用的内存空间一次清理掉。缺点:内存使用率不高,只有原来的一半。
  • **标记-整理算法:**标记无用对象,让所有存活的对象都向一端移动,然后直接清除掉端边界以外的内存。
  • **分代算法:**根据对象存活周期的不同将内存划分为几块,一般是新生代和老年代,新生代基本采用复制算法,老年代采用标记整理算法。

引用计数法

在这里插入图片描述

复制算法

在这里插入图片描述

在这里插入图片描述

标记-清除算法:

在这里插入图片描述

标记-整理算法:

在这里插入图片描述

分代收集算法

当前商业虚拟机都采用分代收集的垃圾收集算法。分代收集算法,顾名思义是根据对象的存活周期将内存划分为几块。一般包括年轻代、老年代 和 永久代

Java集合框架

在这里插入图片描述

1、List,Set,Map三者的区别?
  • List(对付顺序的好帮手): List里存放的对象是有序的,同时也是可以重复的,List关注的是索引,拥有一系列和索引相关的方法,查询速度快。因为往list集合里插入或删除数据时,会伴随着后面数据的移动,所有插入删除数据速度慢。 (有序、可重复)
  • Set(注重独一无二的性质): 不允许重复的集合。不会有多个元素引用相同的对象。(无序、不能重复)
  • Map(用Key来搜索的专家): Map集合中存储的是键值对,键不能重复,值可以重复。根据键得到值,对map集合遍历时先得到键的set集合,对set集合进行遍历,得到相应的值。(键值对、键唯一、值不唯一)
2、Arraylist 与 LinkedList 区别?

1. 是否保证线程安全: ArrayListLinkedList 都是不同步的,也就是不保证线程安全;

2. 底层数据结构: Arraylist 底层使用的是 Object 数组LinkedList 底层使用的是 双向链表 数据结构

3. 插入和删除是否受元素位置的影响:

  • ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。
  • LinkedList 采用链表存储,所以对于add(E e)方法的插入,删除元素时间复杂度不受元素位置的影响,近似 O(1),如果是要在指定位置i插入和删除元素的话((add(int index, E element)) 时间复杂度近似为o(n))因为需要先移动到指定位置再插入。

4. 是否支持快速随机访问:

LinkedList 不支持高效的随机元素访问,而 ArrayList 支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于get(int index)方法)。

5. 内存空间占用:

  • ArrayList的空间浪费主要体现在在list列表的结尾会预留一定的容量空间
  • LinkedList的空间花费则体现在它的每一个元素都需要消耗比ArrayList更多的空间(因为要存放直接后继和直接前驱以及数据)。

ArrayList必须是连续内存的,而LinkedList不要求连续内存。

ArrayList查询快,增加和删除慢;LinkedList增加和删除快,查询慢。

ArrayList 底层为动态数组,所以查询时是直接通过访问下标,查询效率高。而增加而删除时,为了保证内存的连续,增加和删除某一位置后,后方元素都得向前移动一位,最坏情况就是删除第一个元素,则后面第2个到第n个元素都得往前移动一位。所以增加删除慢。

LinkedList底层为双向链表,不必保证内存上的连续,所以增删快,而查询时必须要经历从头到尾的遍历,所以查询慢。

下面再总结一下 list 的遍历方式选择:

  • 实现了 RandomAccess 接口的list,优先选择普通 for 循环 ,其次 foreach,
  • 未实现 RandomAccess接口的list,优先选择iterator遍历(foreach遍历底层也是通过iterator实现的),大size的数据,千万不要使用普通for循环
3、ArrayList 与 Vector 区别呢?为什么要用Arraylist取代Vector呢?

Vector的所有方法都是同步的。可以由两个线程安全地访问一个Vector对象、但是一个线程访问Vector的话代码要在同步操作上耗费大量的时间。 Arraylist不是同步的,所以在不需要保证线程安全时建议使用Arraylist。

4、HashMap 和 Hashtable 的区别

相同点: 实现原理相同,功能相同,底层都是哈希表结构,查询速度快,在很多情况下可以互用

不同点:

1、Hashtable是早期提供的接口,HashMap是新版JDK提供的接口。

2、Hashtable继承自Dictionary类,而HashMap继承自AbstractMap类。但二者都实现了Map接口。

3、Hashtable线程安全,HashMap线程非安全。

4、Hashtable不允许null值,HashMap允许null值。

5、Hashtable 中的方法是Synchronize的,而HashMap中的方法在缺省情况下是非Synchronize的。

HashMap 多线程解决:

Map m = Collections.synchronizedMap(new HashMap<>());

Map m = ConcurrentHashMap<>();

1、线程是否安全: HashMap 是非线程安全的,HashTable 是线程安全的;HashTable 内部的方法基本都经过synchronized 修饰。(如果你要保证线程安全的话就使用 ConcurrentHashMap 吧!);

2、效率: 因为线程安全的问题,HashMap 要比 HashTable 效率高一点。另外,HashTable 基本被淘汰,不要在代码中使用它;

3、对Null key 和Null value的支持: HashMap 中,null 可以作为键,这样的键只有一个,可以有一个或多个键所对应的值为 null。。但是在 HashTable 中 put 进的键值只要有一个 null,直接抛出 NullPointerException。

4、**底层数据结构:**HashMap 数组+链表+红黑树。

TreeMap的数据结构是红黑树。

HashMap和Hashtable不保证数据有序,LinkedHashMap保证数据可以保持插入顺序,而TreeMap可以按key的大小顺序排序。

LinkedHashMap保证数据可以保持插入顺序

LinkedHashMap在HashMap的基础上多了一个双向链表来维持顺序。

在这里插入图片描述

5、HashMap 和 HashSet区别
public HashSet() {
    map = new HashMap<>(); 
}

HashSet 底层就是基于 HashMap 实现的。(HashSet 的源码非常非常少,因为除了 clone()writeObject()readObject()是 HashSet 自己不得不实现之外,其他方法都是直接调用 HashMap 中的方法。

6、泛型好处:

泛型简单易用

类型安全 泛型的主要目标是实现java的类型安全。 泛型可以使编译器知道一个对象的限定类型是什么,这样编译器就可以在一个高的程度上验证这个类型

消除了强制类型转换 使得代码可读性好,减少了很多出错的机会

Java语言引入泛型的好处是安全简单。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。

泛型的实现原理

泛型的实现是靠类型擦除技术 类型擦除是在编译期完成的 也就是在编译期 编译器会将泛型的类型参数都擦除成它的限定类型,如果没有则擦除为object类型之后在获取的时候再强制类型转换为对应的类型。 在运行期间并没有泛型的任何信息,因此也没有优化。

泛型不考虑继承

ConcurrentHashMap线程安全性问题

1.7版本,采用ReentrantLock+Segment(分段锁)+HashEntry的方式解决多线程安全问题。

1.8版本,采用synchronized+CAS+HashEntry+红黑树方式进行了处理。 利用红黑树结构优化增强链表遍历的效率。

ArrayList 底层

ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存。

ArrayList不是线程安全的,只能用在单线程环境下,多线程环境下可以考虑用Collections.synchronizedList(List l)函数返回一个线程安全的ArrayList类,也可以使用concurrent并发包下的CopyOnWriteArrayList类。

ArrayList实现了Serializable接口,因此它支持序列化,能够通过序列化传输,实现了RandomAccess接口,支持快速随机访问,实际上就是通过下标序号进行快速访问,实现了Cloneable接口,能被克隆。

每个ArrayList实例都有一个容量,该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向ArrayList中不断添加元素,其容量也自动增长。自动增长会带来数据向新数组的重新拷贝,因此,如果可预知数据量的多少,可在构造ArrayList时指定其容量。在添加大量元素前,应用程序也可以使用ensureCapacity操作来增加ArrayList实例的容量,这可以减少递增式再分配的数量。
注意,此实现不是同步的。如果多个线程同时访问一个ArrayList实例,而其中至少一个线程从结构上修改了列表,那么它必须保持外部同步。

Java IO面试题

1、字节流和字符流的区别?

在java.io包中操作文件内容的主要有两大类:字节流、字符流。

两类都分为输入和输出操作。四个都是抽象类

在字节流中输出数据主要是使用OutputStream类完成,输入使的是InputStream类。

在字符流中输出数据主要是使用Writer类完成,输入主要使用Reader类。

字节流读取的时候,读到一个字节就返回一个字节。

字符流使用了字节流读到一个或多个字节(中文对应的字节数是两个,在 UTF-8 码表中是 3 个字节)时。先去查指定的编码表,将查到的字符返回。

字节流可以处理所有类型数据,如:图片,MP3,AVI视频文件,而**字符流只能处理字符数据。**

字节流在操作的时候本身是不会用到缓冲区(内存)的,是与文件本身直接操作的,而字符流在操作的时候是使用到缓冲区的。

3、字节流如何转为字符流?

字节输入流转字符输入流通过 InputStreamReader 实现,该类的构造函数可以传入 InputStream 对象。

5、什么是 java序列化?

序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作时所引发的问题。

4、如何将一个 java 对象序列化到文件里?

在 java 中能够被序列化的类必须先实现 Serializable 接口,该接口没有任何抽象方法只是起到一个标记作用。

10、如何实现对象克隆?

有两种方式

(1)实现 Cloneable 接口并重写 Object 类中的 clone()方法;

(2)实现 Serializable 接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。

HashMap遍历的四种方法

**方法1:**使用For-Each迭代entries

这是最常见的方法,并在大多数情况下更可取的。当你在循环中需要使用Map的键和值时,就可以使用这个方法

Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for(Map.Entry<Integer, Integer> entry : map.entrySet()){
	System.out.println("key = " + entry.getKey() + ", value = " + entry.getValue())
}

方法2 : 使用For-Each迭代keys和values

如果你只需要用到map的keys或values时,你可以遍历KeySet或者values代替entrySet

Map<Integer, Integer> map = new HashMap<Integer, Integer>();
 
//iterating over keys only
for (Integer key : map.keySet()) {
	System.out.println("Key = " + key);
}
 
//iterating over values only
for (Integer value : map.values()) {
	System.out.println("Value = " + value);
}

方法3: 使用Iterator迭代

Map<Integer, Integer> map = new HashMap<Integer, Integer>();
Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator();
while (entries.hasNext()) {
	Map.Entry<Integer, Integer> entry = entries.next();
	System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}

Java

String,StringBuffer,StringBuilder的区别

在这里插入图片描述

​ 拼接效率:StringBuilder > StringBuffer > str + “ ”,StringBuilder线程不安全,StringBuffer线程安全

一、Java String 类——String字符串常量

String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象, 因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,这样不仅效率低下,而且大量浪费有限的内存空间,所以经常改变内容的字符串最好不要用 String 。

在这里插入图片描述

二、StringBuffer 和 StringBuilder 类——StringBuffer、StringBuilder字符串变量

StringBuffer 字符串变量(线程安全)
StringBuilder 字符串变量(非线程安全)

对字符串进行修改的时候,特别是字符串对象经常改变的情况下,需要使用 StringBuffer 和 StringBuilder 类。

由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。

在这里插入图片描述

小结

(1)如果要操作少量的数据用 String;

(2)多线程操作字符串缓冲区下操作大量数据 StringBuffer;

(3)单线程操作字符串缓冲区下操作大量数据 StringBuilder。

4.说说String中hashcode的实现?(此题频率很高)

public int hashCode() {
    int h = hash; // 默认值是0
    if (h == 0 && value.length > 0) {
        char val[] = value; // 字符串对应的char数组

        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];// val[0]*31^(n-1) + val[1]*31^(n-2) + ... + val[n-1]
        }
        hash = h;
    }
    return h;
}
//执行逻辑:
val = value;
val[0] = "l";
val[1] = "i";
val[2] = "u";

h = 31 * 0 + l = l;
h = 31 * (31 * 0 + l) + i = 31 * l + i;
h = 31 * (31 * (31 * 0 + l) + i) + u = 31 * 31 * l + 31 * i + u;
...
val[0]*31^(n-1) + val[1]*31^(n-2) + ... + val[n-1]

String类中的hashCode计算方法还是比较简单的,就是以31为权,每一位为字符的ASCII值进行运算,用自然溢出来等效取模。

哈希计算公式可以计为s[0]31^(n-1) + s[1]31^(n-2) + … + s[n-1]

那为什么以31为质数呢?

主要是因为31是一个奇质数,所以31*i=32*i-i=(i<<5)-i,这种位移与减法结合的计算相比一般的运算快很多。

    //注:字符串常量池从jdk1.7之后已经从方法区,转移到了堆中
    String s = new String("123") ;
    //一共创建2个对象,在String Pool中创建1个字符串对象指向"123"这个字符串字面量(作为String类的构造函数参数)
    //因为是"new"所以自然要在堆中创建1个字符串对象,地址指向s

    String s1 = "123";
    String s2 = new String("123");
    //这时的new创建只创建1个字符串对象,因为在s1 = "123"这一步已经在String Pool中创建了1个123对象
    //这时new String(“123”)的构造函数参数"123"则直接用已经放在String Pool的字符串对象,在堆中创建1个对象

    //考虑下面的问题
    String str1 = new String("ABC");
    String str2 = new String("ABC");
    System.out.println(str1 == str2);//False.
    String str3 = "ABC";
    String str4 = "ABC";
    String str5 = "A" + "BC";
    System.out.println(str3 == str4); //True.
    System.out.println(str3 == str5); //True.
    String a = "ABC";
    String b="AB";
    String c=b+"C";
    System.out.println(a==c); //false

    //字符串常量池(String Pool)保存着所有字符串字面量(literal strings),这些字面量在编译时期就确定。
    //不仅如此,还可以使用 String 的 intern() 方法在运行过程将字符串添加到 String Pool 中.
    //当一个字符串调用 intern() 方法时,如果 String Pool 中已经存在一个字符串和该字符串值相等(使用 equals() 方法进行确定),那么就会返回 String Pool 中字符串的引用;
    //否则,就会在 String Pool 中添加一个新的字符串,并返回这个新字符串的引用。
    String s3 = new String("aaa");
    String s4 = new String("aaa");
    System.out.println(s3 == s4);           // false
    String s5 = s1.intern();
    String s6 = s1.intern();
    System.out.println(s5 == s6);           // true

    //练习题(不考虑字符串在常量池中已存在的情况)
    String a0="123";//创建1个对象
    String a1=new String("123");//2个对象
    String a2="123"+"456";//2个
    String a3="123"+new String("456"); //4个对象
Java集合类里线程安全的有哪些?

Vector:就比ArrayList多了个同步化机制(线程安全)。
Hashtable:就被HashMap多了个线程安全。
ConcurrentHashMap:是一种高效但是线程安全的集合。
Stack:栈,也是线程安全的,继承于Vector,已过时,不建议使用。

深拷贝和浅拷贝的区别

浅克隆(Shallow Clone):当原型对象被复制时,只复制它本身和其中包含的值类型的成员变量,而引用类型的成员变量并没有复制

在这里插入图片描述

深克隆(Deep Clone):除了对象本身被复制外,对象所包含的所有成员变量也将被复制

在这里插入图片描述

进程间通信的方式
1,管道

1,匿名管道:

​ 概念:在内核中申请一块固定大小的缓冲区,程序拥有写入和读取的权利,一般使用fork函数实现父子进程的通信。

2,命名管道:

​ 概念:在内核中申请一块固定大小的缓冲区,程序拥有写入和读取的权利,没有血缘关系的进程也可以进程间通信。

2,消息队列

1,概念:在内核中创建一队列,队列中每个元素是一个数据报,不同的进程可以通过句柄去访问这个队列。

​ 消息队列提供了⼀个从⼀个进程向另外⼀个进程发送⼀块数据的⽅法。

3,信号量

​ 1,概念

​ 在内核中创建一个信号量集合(本质是个数组),数组的元素(信号量)都是1,使用P操作进行-1,使用V操作+1,

​ (1) P(sv):如果sv的值⼤大于零,就给它减1;如果它的值为零,就挂起该进程的执⾏ 。
​ (2) V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运⾏,如果没有进程因等待sv⽽挂起,就给它加1。

​ PV操作用于同一进程,实现互斥。

​ PV操作用于不同进程,实现同步。

4,共享内存

​ 1,概念:

​ 将同一块物理内存一块映射到不同的进程的虚拟地址空间中,实现不同进程间对同一资源的共享。

​ 共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。

final关键字可以用来修饰类、方法和变量(包括成员变量和局部变量)。

1.修饰类
  **当用final修饰一个类时,表明这个类不能被继承。**也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。
  在使用final修饰类的时候,要注意谨慎选择,除非这个类真的在以后不会用来继承或者出于安全的考虑,尽量不要将类设计为final类。

2.修饰方法
  下面这段话摘自《Java编程思想》第四版第143页:
  “使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。“
  因此,如果只有在想明确禁止 该方法在子类中被覆盖的情况下才将方法设置为final的。
  注:类的private方法会隐式地被指定为final方法。

3.修饰变量
  修饰变量是final用得最多的地方,也是本文接下来要重点阐述的内容。首先了解一下final变量的基本语法:

对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改

​ 如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象

抽象类和接口对比
参数抽象类接口
默认的方法实现它可以有默认的方法实现接口完全是抽象的。它根本不存在方法的实现
实现子类使用extends关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明的方法的实现。子类使用关键字implements来实现接口。它需要提供接口中所有声明的方法的实现
构造器抽象类可以有构造器接口不能有构造器
与正常Java类的区别除了你不能实例化抽象类之外,它和普通Java类没有任何区别接口是完全不同的类型
访问修饰符抽象方法可以有publicprotecteddefault这些修饰符接口方法默认修饰符是public。你不可以使用其它修饰符。
main方法抽象方法可以有main方法并且我们可以运行它接口没有main方法,因此我们不能运行它。(java8以后接口可以有default和static方法,所以可以运行main方法)
多继承抽象方法可以继承一个类和实现多个接口接口只可以继承一个或多个其它接口
速度它比接口速度要快接口是稍微有点慢的,因为它需要时间去寻找在类中实现的方法。
添加新方法如果你往抽象类中添加新的方法,你可以给它提供默认的实现。因此你不需要改变你现在的代码。如果你往接口中添加方法,那么你必须改变实现该接口的类。
堆和二叉树的区别

在二叉排序树中,每个结点的值均大于其左子树上所有结点的值,小于其右子树上所有结点的值,对二叉排序树进行中序遍历得到一个有序序列。所以,二叉排序树是结点之间满足一定次序关系的二叉树;
堆是一个完全二叉树,并且每个结点的值都大于或等于其左右孩子结点的值(这里的讨论以大根堆为例),所以,堆是结点之间满足一定次序关系的完全二叉树。

介绍一下 如何实现动态代理?

Java实现动态代理的大致步骤如下:

1.定义一个委托类和公共接口。

2.自己定义一个类(调用处理器类,即实现 InvocationHandler 接口),这个类的目的是指定运行时将生成的代理类需要完成的具体任务(包括Preprocess和Postprocess),即代理类调用任何方法都会经过这个调用处理器类(在本文最后一节对此进行解释)。

3.生成代理对象(当然也会生成代理类),需要为他指定(1)委托对象(2)实现的一系列接口(3)调用处理器类的实例。因此可以看出一个代理对象对应一个委托对象,对应一个调用处理器实例。

4.Java 实现动态代理主要涉及以下几个类:

①java.lang.reflect.Proxy: 这是生成代理类的主类,通过 Proxy 类生成的代理类都继承了 Proxy 类,即 DynamicProxyClass extends Proxy。

②java.lang.reflect.InvocationHandler: 这里称他为"调用处理器",他是一个接口,我们动态生成的代理类需要完成的具体内容需要自己定义一个类,而这个类必须实现 InvocationHandler 接口。

1、定义一个类实现 InvocationHandler 接口重写 invoke() 方法

2、被代理的接口可以通过set方式注入

3、通过Proxy.newProxyInstance() 获得代理类的实例。

4、在 invoke() 调用切面函数。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyInvocationHandler implements InvocationHandler {
    // 被代理的接口
    private Object target;
    public void setTarget(Object target) {
        this.target = target;
    }

    // 生成得到代理的类
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    // 处理代理实例,并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // 动态代理的本质就是使用反射机制
        Object result = method.invoke(target, args);
        return result;
    }
}
谈一谈,JDBC中如何进行事务处理?

Connection提供了事务处理的方法,通过调用setAutoCommit(false)可以设置手动提交事务;当事务完成后用commit()显式提交事务;如果在事务处理过程中发生异常则通过rollback()进行事务回滚。

Math
     //指数函数方法
    System.out.println(Math.exp(8));
    System.out.println(Math.log(8));
    System.out.println(Math.log10(8));
    System.out.println(Math.sqrt(8));

    //取整函数方法,本次笔试考了这个
    System.out.println(Math.ceil(5.2));//6.0
    System.out.println(Math.floor(5.2));//5.0
    System.out.println(Math.rint(5.2));//5.0
    System.out.println(Math.rint(5.6));//6.0rint,个人总结:四舍六入五靠偶
    System.out.println(Math.rint(4.5));//4.0,偶数
    System.out.println(Math.rint(-4.5));//-4.0偶数
    System.out.println(Math.round(5.5f));//6,返回int,参数上加0.5然后进行下取整
    System.out.println(Math.round(5.5));//6,long,参数上加0.5然后进行下取整
    System.out.println(Math.round(-5.5));//-5
    System.out.println(Math.round(-5.6));//-6

    //最值、绝对值函数
    System.out.println(Math.max(4,8));
    System.out.println(Math.min(4,8));
    System.out.println(Math.abs(-5.2));

    //Math.random()随机数方法
    System.out.println(Math.random());//double,0<=double<1
    System.out.println(4+(int)(Math.random()*100));//4<=返回<104
    System.out.println(new Random().nextInt(104));

    //三角函数
    System.out.println(Math.sin(Math.PI/2));//1.0
    System.out.println(Math.acos(Math.sqrt(2)/2));//0.7853981633974483,即π/4
    boolean flag = false;
    if (flag = true){//Test
        System.out.println("Test");
    }else{
        System.out.println("Test2");
    }
    System.out.println(flag);//true

    if (flag = false){//Test4
        System.out.println("Test3");
    }else {
        System.out.println("Test4");
    }
    System.out.println(flag);//false
    //注意:括号内赋值只能是boolean变量
    int a;
    if(a = 4){//Error: java: 不兼容的类型: int无法转换为boolean
        System.out.println("");
    }
位运算

这里顺便复习一下平常很少用的位运算符:

  1. “按位与”运算:&
  2. ”按位或“运算:|
  3. ”按位取反“运算:~
  4. ”按位异或“运算:^
  5. ”移位操作“:<<左移(空位补0),>>右移(最高位0空位补0,最高位1空位补1),>>>无符号右移(空位补0)
代码块

执行顺序:(优先级从高到低)静态代码块>mian方法>构造代码块>构造方法
其中静态代码块只执行一次。构造代码块在每次创建对象是都会执行。

  1. 普通代码块:在方法或语句中出现的{}就称为普通代码块。普通代码块和一般的语句执行顺序由他们在代码中出现的次序决定–“先出现先执行”
    构造代码块
  2. 构造代码块:直接在类中定义且没有static修饰没有名字的{}代码块称为构造代码块。
    构造代码块在每次创建对象时都会被调用,并且构造代码块的执行次序优先于类构造函数。
  3. 静态代码块:static修饰

如果结合了继承的问题,则顺序为

父类静态代码块———子类静态代码块——父类构造代码块——父类构造方法——子类构造代码块——子类构造方法。

需要注意的是,在实例化子类对象时,父类无参构造方法将被自动调用,但有参构造方法并不能被自动调用,只能依赖于super关键字显式地调用父类的构造方法。更需要注意的是,平时我们新建一个类,会自动提供一个无参的构造方法,但如果一个类已经存在一个有参构造方法,则系统不会再隐式为我们提供无参构造方法。

1.Java类的种类有 class、 interface和 —enum— 。

2.字符串 String类中使用 —charAt(int index)— 函数来取得某一个下标位置的字符(只需写出函数名称)。

3.在子类构造函数中使用 —super— 关键字来调用父类的构造函数。

4.Java中回收垃圾的方法名是 —System.GC()—

5.异常类 Exception的父类名是 —Throwable—

6.使用 —static— 修饰符定义的类成员,可以通过类直接访问而不需要创建对象后再访问.

7.在Java中,如果需要对对象的序列化进行深度控制,需要实现-**–Serializable—接口,并且需要实现—writeObject()——readObject()—**方法。 序列号反序列化

8.启动Java线程的方法名是**—start()—**

9.按照标准I/0模型,Java提供3种标准输入或输出,分别是**System.out、System.in 和—System.err—**方法。

10.若a,b为int型变量且已分别赋值为8,10。表达式(a++)+(++b)+a*b的值是 118。

占用字节

byte 1字节

short 2字节

char 2字节

int 4字节

float 4字节

long 8字节

double 8字节

英文字母:

字节数 : 1;编码:GB2312
字节数 : 1;编码:GBK
字节数 : 1;编码:ISO-8859-1
字节数 : 1;编码:UTF-8

中文汉字:

字节数 : 2;编码:GB2312
字节数 : 2;编码:GBK
字节数 : 1;编码:ISO-8859-1
字节数 : 3;编码:UTF-8

transient关键字

将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会被序列化。

静态变量不管是不是transient关键字修饰,都不会被序列化

java 的transient关键字为我们提供了便利,你只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。

Hash表

Hash表也称散列表,也有直接译作哈希表,Hash表是一种根据关键字值(key - value)而直接进行访问的数据结构。它基于数组,通过把关键字映射到数组的某个下标来加快查找速度,但是又和数组、链表、树等数据结构不同,在这些数据结构中查找某个关键字,通常要遍历整个数据结构,也就是O(N)的时间级,但是对于哈希表来说,只是O(1)的时间级。

注意,这里有个重要的问题就是如何把关键字转换为数组的下标,这个转换的函数称为哈希函数(也称散列函数),转换的过程称为哈希化。

根据设定的哈希函数H(key)和处理冲突的方法将一组关键字映像到一个有限的连续的地址集(区间)上,并以关键字在地址集中的“像”作为记录在表中的存储位置,这种便称为哈希表。

Hash表扩容

简单说就是换一个更大的数组重新映射。下面我们讲解下JDK1.8做了哪些优化。经过观测可以发现,我们使用的是2次幂的扩展(指长度扩为原来2倍),所以,元素的位置要么是在原位置,要么是在原位置再移动2次幂的位置

看下图可以明白这句话的意思,n为table的长度,图(a)表示扩容前的key1和key2两种key确定索引位置的示例,图(b)表示扩容后key1和key2两种key确定索引位置的示例,其中hash1是key1对应的哈希与高位运算结果。

在这里插入图片描述

元素在重新计算hash之后,因为n变为2倍,那么n-1的mask范围在高位多1bit(红色),因此新的index就会发生这样的变化:

在这里插入图片描述

Java 重载和重写的区别

重载:在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同甚至是参数顺序不同)则视为重载。同时,重载对返回类型没有要求,可以相同也可以不同,但不能通过返回类型是否相同来判断重载。调用方法时通过传递不同参数个数和参数类型来决定具体使用哪个方法。

重载总结:

  1. 重载Overload是一个类中多态性的一种表现
  2. 重载要求同名方法的参数列表不同(参数类型,参数个数甚至是参数顺序)
  3. 重载的时候,返回值类型可以相同也可以不相同。无法以返回型别作为重载函数的区分标准

重写:是父类与子类之间的多态性,实质是对父类的函数进行重新定义,如果在子类中定义某方法与其父类有相同的名称和参数则该方法被重写,不过子类函数的访问修饰权限不能小于父类的;若子类中的方法与父类中的某一方法具有相同的方法名、返回类型和参数表,则新方法将覆盖原有的方法,如需父类中原有的方法则可使用 super 关键字。

重写总结:

  1. 发生在父类与子类之间
  2. 方法名,参数列表,返回类型(除非子类中方法的返回类型是父类中返回类型的子类)必须相同
  3. 访问修饰符的限制一定要大于被重写方法的访问修饰符(public>protected>default>private)
  4. 重写方法一定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常
&、&&的区别和|、||的区别?

&、&&的区别

1、最终结果一样。

2、&无论左边是false还是true,右边都执行。

3、&&具有短路效果,左边是false,右边不执行。

4、&&效率更高,项目中推荐使用。

|、||的区别

1、最总的结果一样。

2、|无论左边是false还是true,右边都会执行。

3、||具有短路效果,左边是true,右边不执行。

4、||效果效率更高,项目中推荐使用。

&可以用作位运算符,当&两边的表达式不是Boolean类型的时候,&表示按位操作。

Spring IOC 中使用 工厂模式和单例模式。

红黑树

面试常问:什么是红黑树?

左旋

在这里插入图片描述

右旋

在这里插入图片描述

46个经典Linux面试题!(附答案)

1、绝对路径用什么符号表示?当前目录、上层目录用什么表示?主目录用什么表示? 切换目录用什么命令?

答案:
绝对路径:如/etc/init.d
当前目录和上层目录:./ …/
主目录:~/
切换目录:cd

2、怎么查看当前进程?怎么执行退出?怎么查看当前路径?

答案:
查看当前进程:ps
执行退出:exit
查看当前路径:pwd

3、Ls 命令执行什么功能?可以带哪些参数,有什么区别?

答案:
ls 执行的功能:列出指定目录中的目录,以及文件
哪些参数以及区别:a 所有文件l 详细信息,包括大小字节数,可读可写可执行的权限等

4、目录创建用什么命令?创建文件用什么命令?复制文件用什么命令?

答案:
创建目录:mkdir
创建文件:典型的如 touch,vi 也可以创建文件,其实只要向一个不存在的文件输出,都会创建文件
复制文件:cp

5、文件权限修改用什么命令?格式是怎么样的?

文件权限修改:chmod
格式如下:

$ chmod u+x file 给 file 的属主增加执行权限
$ chmod 751 file 给 file 的属主分配读、写、执行(7)的权限,给 file 的所在组分配读、执行(5)的权限,给其他用户分配执行(1)的权限
$ chmod u=rwx,g=rx,o=x file 上例的另一种形式
$ chmod =r file 为所有用户分配读权限
$ chmod 444 file 同上例
$ chmod a-wx,a+r file同上例
$ chmod -R u+r directory 递归地给 directory 目录下所有文件和子目录的属主分配读的权限

6、 Linux 下命令有哪几种可使用的通配符?分别代表什么含义?

答案:
“?”可替代单个字符。

“*”可替代任意多个字符。

方括号“[charset]”可替代 charset 集中的任何单个字符,如[a-z],[abABC]

7、用什么命令对一个文件的内容进行统计?(行号、单词数、字节数)

答案:

wc 命令 - c 统计字节数 - l 统计行数 - w 统计字数。

8、使用什么命令查看磁盘使用空间?空闲空间呢?

答案:

df -hl
文件系统 容量 已用 可用 已用% 挂载点
Filesystem Size Used Avail Use% Mounted on /dev/hda2 45G 19G 24G 44% /
/dev/hda1 494M 19M 450M 4% /boot

9、du 和 df 的定义,以及区别?

答案:

du 显示目录或文件的大小

df 显示每个<文件>所在的文件系统的信息,默认是显示所有文件系统。
(文件系统分配其中的一些磁盘块用来记录它自身的一些数据,如 i 节点,磁盘分布图,间接块,超级块等。这些数据对大多数用户级的程序来说是不可见的,通常称为 Meta Data。) du 命令是用户级的程序,它不考虑 Meta Data,而 df 命令则查看文件系统的磁盘分配图并考虑 Meta Data。
df 命令获得真正的文件系统数据,而 du 命令只查看文件系统的部分情况。

10、ps

列出系统中当前运行的进程
-a 显示所有用户的所有进程(包括其它用户)
-e 显示所有进程,环境变量 f 用树形格式来显示进程;
ps -ef|grep java : 找出所有 java 进程

Linux常用查看CPU信息的命令

(1)top 命令

​ top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器。

(2)ps 命令

​ ps命令用于报告当前系统的进程状态。ps命令是最基本同时也是非常强大的进程查看命令,使用该命令可以确定有哪些进程正在运行和运行的状态、进程是否结束、进程有没有僵死、哪些进程占用了过多的资源等等,总之大部分信息都是可以通过执行该命令得到的。

Linux

命令格式: Commands [options] [arguments]

一、文件操作命令

1、文件处理命令

常用的文件处理命令有:ls、cd、pwd、touch、mkdir、cp、mv、rm、rmdir

(1)ls

​ ls 命令原来的意思为 ‘list’,即“列出”,用于列出参数的属性信息

ls [选项] [参数]

选项说明
-l以详细信息的形式展开当前目录下的文件
-a显示当前目录下的所有文件(包括隐藏文件)
-d查看目录属性
-t按创建时间顺序列出文件
-i输出文件的iNode编号
-R列出当前目录下的所有文件信息,并以递归的方式显示各个子目录中的文件和子目录信息

(2)cd

​ cd 命令原来的意思为 “change directory”,更改目录。

cd 参数

​ cd ~ : 切换到工作路径为当前用户的家目录。

(3)pwd

​ pwd 命令原意为 “print working directory”, 即打印当前工作目录的绝对路径。

​ 直接使用

(4)touch

​ touch 命令主要功能是将已存在的文件的时间标签更新为系统的当前时间。

touch 参数

(5)mkdir

​ mkdir 原意为 “make directory", 即创建目录。

mkdir [选项] 参数
选项说明
-p若路径中的目录不存在,则先创建目录

(6)cp

​ cp 原意为 copy。即复制。将一个或者多个源文件复制到指定的目录。

cp [选择] 源文件或目录 目的目录
选项说明
-R递归处理,将指定目录下的文件及子目录一并处理
-p复制的同时不修改文件属性,包括所有者、所属组、权限和时间
-f强制复制文件或目录,无论目的文件或目录是否已经存在

(7)mv

​ mv 原意为 move,用于移动文件或者目录。

mv 源文件或目录 目的目录

(8)rm

​ rm 原意为 remove, 功能是删除目录总的文件或者目录

rm [选项] 文件或目录

​ 若要使用 rm 命令删除目录,需要在参数前添加 -r 选项。

选项说明
-f强制删除文件或者目录
-rf选项 -r 与 -f 结合,删除目录中所有文件和子目录,并且不会一一确认
-i在删除文件或目录时对删除的内容逐一进行确认(y/n)

(9)rmdir

​ rmdir 原意为 remove directory。它仅用于删除目录。

rmdir [-p] 目录

​ 若在命令中添加参数 -p, 此命令将会在删除指定目录后检查上层目录,若该目录的上层目录已经变成空目录,则将其一并删除。

2、文件查看命令

(1)cat

​ cat 原意为 concatenate and display files, 即连接和显示文件。功能是将文件中的内容打印到输出设备。

cat 文件名

(2)more

​ 用于分页显示文件内容。

more [文件名]

(3)head

​ head 用于查看文件内容,但是可以指定查看文件的前 n 行。

head -n filename

(4)tail

​ tail 用于查看文件内容的后 n 行

tail -n filename
3、权限管理命令

Linux用户大体分为:超级用户 root 和 普通用户

用户与文件之间的关系:文件或者目录拥有者、同组用户、其他组用户和全部用户。

权限对应字符文件目录
读权限(read)r可查看文件内容可以列出目录中的内容
写权限(write)w可以修改文件内容可以在目录中创建、删除文件
执行权限(execute)x可以执行文件可以进入目录

切换到 root 用户根目录: su 若要切换为原用户:exit

输出的序列

-rw--r--r- .....  
1~3 位数字代表 文件所有者权限
4~6 位数字代表 同组用户的权限
7~9 为数字代表 其他用户的权限

0(没有权限) 、1(执行权限)、2(写权限)、4(读权限)、5(4+1,读+执行)、6(4+2,读+写)、7(4+2+1,读+写+执行)

(1)chmod

​ chmod 原意为 change the permissions mode of file, 功能是变更文件或目录的权限。

chmod {augo}{+-=} 文件或目录  // chmod u+x,g+x 目录  chmod 7(用户自己)5(同组用户)4(其他组用户) 目录 

​ 其中 a 表示所有用户,u 表示用户名user,g 表示组名 group,o表示其他;+ 表示添加权限,- 表示取消权限,= 表示设定权限。

(2) chown

​ chown 原意为 chang the ower of file, 功能是更改文件或目录的所有者

chown 用户 文件或目录

(3) chgrp

​ chgrp 原意为 change file group, 功能是更改文件或目录的所属组。

chgrp [组名] [文件或目录]
4、文件搜索命令

(1)which

​ 使用 which 查看命令所在的目录

which 命令

(2) find

​ find 命令可借助搜索关键字查找文件或目录。

find 搜索路径 [选项] 搜索关键字
选项说明
-name根据文件名查找
-size根据文件大小查找
-user根据文件所有者查找

(3)locate

​ locate 命令也可借助搜索关键字查找文件或目录。locate 查找的是 /var/lib/locatedb,而不是 搜索Linux整个目录,所以比find快。 locate 查不到最新变动的文件,可以先使用 updatedb 命令手动更新数据库。

locate [选项] 搜索关键字

(4)grep

​ grep 命令用于在文件中搜索与字符串匹配的行并输出

grep 指定字符 源文件

二、网络管理与通信命令

1、 ifconfig

​ ifconfig 命令愿意为 interface config, 功能为配置和显示Linux内核中网络接口的参数

ifconfig [参数]

​ 参数可以省略,表示查看本机网络配置信息。

2、netstat

​ netstat 用于打印 Linux 系统中网络系统的状态信息。

netstat [选项]
选项说明
-a显示所有端口
-at列出所有 tcp 端口
-au列出所有 udp 端口

3、ping

​ ping 命令用于测试主机之间的网络的连通性,默认情况下该命令会一直打印测试结果(可使用 Ctrl + D 停止打印)。

ping [选项] [参数]
选项说明
-c设置回应次数
-s设置数据包大小
-v详细显示指令的执行过程

4、write

​ write 命令可使当前用户向另一个用户发送信息(Ctrl + D 结束)

write 用户名

5、wall

wall 命令可使用 root 用户向所有用户发送消息,以快捷键Ctrl + D 结束。

wall [message]

三、压缩解压指令

​ Linux 中常见的压缩文件格式为 *.gz、*.zip、*.bz2

(1)gzip/gunzip

​ gzip 命令用于压缩文件,获得 .gz 格式压缩包,压缩后不保存源文件。若同时列出多个文件,则每个文件会被单独压缩。

gzip [选项] 文件

​ 使用 gzip -d file.gz 可以解压 .gz 格式压缩包。

gunzip [选项] 压缩包包名

(2)zip/unzip

​ zip 命名用文件或目录,获得 .zip 格式压缩包,压缩时会保留源文件。

zip [-r] [压缩包包名] 文件或目录

​ zip 命令的选项 -r 表示递归处理指定目录与子目录中的所有文件。

​ 对应的解压缩文件为

unzip [选项] 压缩包包名

(3)bzip2/bunzip2

​ bzip2 命令用于创建和管理(包括解压缩).bz2 格式的压缩包。

bzip2 [选项] 文件

​ bzip2 命令对应的解压命令为 bunzip2

bunzip2 压缩包包名

(4)tar

tar 命令用于打包多个目录或文件,该命令通常与压缩命令一起使用。

tar [选项] 目录
选项说明
-c产生 .tar 打包文
-v打包时显示详细信息
-f指定压缩后的文件名
-z打包,同时通过 gzip 指令压缩备份文件,压缩后的格式为 .tar.gz
-x从打包文件中还原文件
#tar -zcvf newdir.tar.gz newdir  	// 压缩
#tar -zxvf newdir.tar.gz 			// 解压到当前目录

四、帮助命令

(1)man

​ man 命令用于获取 Linux 系统帮助文档 manpage 中的帮助信息。

man [选项] 命令/配置文件     // #man ls
选项说明
-a在所有的 man 帮助手册中搜索
-p指定内容时使用分页程序
-M指定手册搜索的路径

man 帮助文档分为 9 个章节,使用 man CONMAND 命令可以分章节查看整个 man 命令手册。若想要使用 man 查看命令的库函数:

man 章节号 命令名    // #man 3 sleep

(2)info

​ info 命令用于调用Linux下的帮助文档,获取帮助信息。相比 man 文档,该帮助文档更好理解,也更友好。

info [选项] [参数]
选项说明
-d添加包含 info 格式帮助文档的目录
-f指定内容时,使用分页程序
-n指定首先访问的 info 帮助文件的节点
-o输出被选择的结点内容到指定的文件

(3)whatis

​ whatis 命令用于查询命令的功能,并将查询结果打印到终端。

whatis 命令名称

(4)whoami

​ whoami 命令用于打印当前有效的用户名称,即查看当前正在操作的用户的信息

whoami

Redis

redis的原理和优点

redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hashs(哈希类型)

这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的.

在此基础上,redis支持各种不同方式的排序.与memcached一样,为了保证效率,数据都是缓存在内存中.区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步.

Redis的优点:

性能极高 – Redis能支持超过 100K+ 每秒的读写频率。

丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。

原子 – Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。

丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值