java基础
1.什么是java
答:Java是一门面向对象编程语言
2.Jdk和Jre和JVM的区别
答:Jdk中包括了Jre,Jre中包括了JVM
3. 什么是跨平台性?原理是什么
答:主要依靠虚拟机JVM,不同平台有不同的JVM,他们使用共同的字节码
4. Java应用程序与小程序之间有那些差别?
答:简单说应用程序是从主线程启动(也就是main()方法)。applet小程序没有main方法,主要是嵌在浏览器页面上运行(调用init()线程或者run()来启动)
5.什么是Java程序的主类?应用程序和小程序的主类有何不同
答:一个程序中可以有多个类,但只能有一个类是主类,应用程序的主类不一定要求是public类,但小程序的主类要求必须是public类。主类是Java程序执行的入口点。
6.Java和C++的区别
答:Java不提供指针来直接访问内存,程序内存更加安全,Java有自动内存管理机制,不需要程序员手动释放无用内存,Java的类是单继承的,C++支持多重继承
7.java中的数据类型
答:类型上分,数值型,字符型,布尔型,共8种,整数类型(byte,short,int,long) 浮点类型(float,double)字符型(char)布尔型(boolean)
8.switch的作用范围
答:java8后除了long都可以
9.java的默认数据类型
答:整数int,小数double
因此会有float f=3.4是不正确的问题
10.java语言采用什么编码方案
答:Java语言采用Unicode编码标准
11.访问修饰符都有什么
答: public,private,protected,以及default(默认)
12.&和&&的区别
答:单个逻辑与& 短路与&&
13.final 有什么用
答:用于修饰类、属性和方法,强调其不可变
14.final finally finalize区别
final可以修饰类、变量、方法,finally一般作用在try-catch代码块中,finalize是一个方法一般由垃圾回收器来调
15.static存在的主要意义
答:即使没有创建对象,也能使用属性和调用方法,用来形成静态代码块以优化程序性能,只会在类加载的时候执行一次
16.静态使用方法
答: 静态只能访问静态,非静态既可以访问非静态的,也可以访问静态的
17.break ,continue ,return 的区别及作用
答:break结束当前的循环体 ,continue跳过本次循环 ,return 结束方法返回
18.什么是抽象类
答:抽象类是用来捕捉子类的通用特性从设计层面来说,抽象类是对类的抽象,是一种模板设计
19.什么是接口
答:接口是抽象方法的集合。接口是行为的抽象,是一种行为的规范
20.抽象类和接口的对比与联系
答:接口和抽象类都不能实例化都位于继承的顶端,用于被其他实现或继承都包含抽象方法,其子类都必须覆写这些抽象方法
抽象类:抽象类使用abstract关键字声明,子类使用extends关键字来继承抽象类,一个类最多只能继承一个抽象类
接口:接口使用interface关键字声明,子类使用implements关键字来实现接口,一个类可以实现多个接口
21.在Java中定义一个不做事且没有参数的构造方法的作用
答:如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用super()来调用父类中特定的构造方法,则编译时将发生错误
22.构造方法有哪些特性
答:名字与类名相同;没有返回值,但不能用void声明构造函数;生成类的对象时自动执行,无需调用。
23.静态方法和实例方法有何不同
答:调用静态方法可以无需创建对象
24.内部类的分类
答:内部类可以分为四种:成员内部类、局部内部类、匿名内部类(重点)和静态内部类
25.重载(Overload)和重写(Override)的区别
答:方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性
重载:发生在同一个类中,方法名相同参数列表不同(参数类型不同、个数不同、顺序不同),与方法返回值和访问修饰符无关,即重载的方法不能根据返回类型进行区分
重写:发生在父子类中,方法名、参数列表必须相同,返回值小于等于父类,抛出的异常小于等于父类,访问修饰符大于等于父类(里氏代换原则);如果父类方法访问修饰符为private则子类中就不是重写。
26.== 和 equals 的区别是什么
答:如果equals没有被重写,两种比较是相同的,如果没有重写”==“ 比较引用,equals 比较值
27. hashCode 与 equals 的重写
答:当你需要将该对象存入到底层为散列表结构的集合中时,是先判断hashcode值,碰到相同值时再通过equals进一步判断。所以两个方法都需要重写,保证如果两个对象是相等的,它们的 equals() 方法应该要返回 true,它们的 hashCode() 需要返回相同的结果
但这其实是针对当该类会在HashSet, Hashtable, HashMap等等这些本质是散列表的数据结构中用到的时候这种情况
28.对象的相等与指向他们的引用相等,两者有什么不同?
答:对象的相等 比的是内存中存放的内容是否相等而 引用相等 比较的是他们指向的内存地址是否相等。
29.为什么 Java 中只有值传递
答:方法不能修改传递给它的任何参数变量的内容
30.java 中 IO 流分为几种
答:
按照流的流向分,可以分为输入流和输出流;
按照操作单元划分,可以划分为字节流和字符流;
按照流的角色划分为节点流和处理流。
31.什么是反射?
答:在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制
32.反射机制优缺点
答:
优点: 运行期类型的判断,动态加载类,提高代码灵活度。
缺点: 反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的java代码要慢很多。
33.String真的是不可变的吗?
答:String类利用了final修饰的char类型数组存储字符,通过反射是可以修改所谓的“不可变”对象,不可变但不代表引用不可以变
34.String和StringBuffer、StringBuilder的区别是什么
答:string对象是不可变的。StringBuilder与StringBuffer都可变,修改string是指针指向新的String 对象,后两者是对对象本身操作
对于三者使用的总结
如果要操作少量的数据用 = String
单线程操作字符串缓冲区 下操作大量数据 = StringBuilder,线程不安全,数组越界
多线程操作字符串缓冲区 下操作大量数据 = StringBuffer
35.引用类型
答:强引用,软引用,弱引用,虚引用
- 强引用: 被强引用关联的对象不会被回收。一般采用 new 方法创建强引用。
- 软引用:被软引用关联的对象只有在内存不够的情况下才会被回收。一般采用 SoftReference 类来创建软引用。
- 弱引用:垃圾收集器碰到即回收,也就是说它只能存活到下一次垃圾回收发生之前。一般采用 WeakReference 类来创建弱引用。
- 虚引用: 无法通过该引用获取对象。唯一目的就是为了能在对象被回收时收到一个系统通知。虚引用必须与引用队列联合使用
类间关系
1.类间关系的强弱表现?
答:继承 > 实现 > 组合 > 聚合 > 关联 > 依赖。各种关系之间并不是有非常明确的界限,要根据实际场景去确定类之间的关系
2.什么是继承关系?
答:通过部分相同的功能,实现不同的结果。
继承关系就是is-a 关系,一个类(子类、子接口)继承另外的一个类(父类、父接口)的功能,并可以增加它自己的新功能的能力,如果几个类存在部分相同功能,此时就可以抽象出一个父类来,将相同的部分由父类实现,让他们都继承这个类
关于继承规则:
子类拥有父类非 private 的属性和方法。
子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
子类可以用自己的方式实现父类的方法。
3.什么是实现关系?
答:接口由子类自定义实现的过程就是“实现”关系。
如果几个类对外处理的结果是一致的,但得到这种结果的方式不一样,此时就可以定义一个统一的接口,让这几个类都以自己的方式来实现,我们称这种方式为接口处理
4.什么是组合关系?
答:是一种整体和部分的关系,部分对象的创建、存在、和消亡都是和整体一起的,就是组合
组合是一种强聚合的关系,部分对象的创建、存在、和消亡都是和整体一起的,所谓同生共死的关系,通常在构造整体对象时创建部分对象,并在整体对象销毁时销毁部分对象,比如ATM机由读卡器、吐钞器、凭条打印等组成,他们就构成一个组合关系,创建一个ATM机类对象,就必须构造读卡器对象。
5.什么是聚合关系?
答:是一种整体和部分的关系,关系较弱,部分实例可以添加到聚合整体,也可以从聚合整体中移出,单独存在,就是聚合
聚合关系是一种has-a关系,聚合相较于组合,关系要弱一些,但也是整体和部分的关系,并非同生共死,部分实例可以添加到聚合整体,也可以从聚合整体中移出。比如大学里的学院,其部分包括:管理办公室、系和研究中心,系可以创建和撤销,研究中心也可以独立于学院而存在。
6.什么是关联关系?
答:关联关系是长期的(类属性关联而非方法)、平等的。关联可以是单向,也可以是双向的
关联关系是一种长期的关系,主体现在成员变量,无论是否调用方法这种关系都存在。
7.什么是依赖关系?
答:被依赖方作为依赖方的方法参数,也可能作为依赖方的方法返回值,使用与被使用关系就是依赖关系
依赖关系即use-a关系,一个类A使用到了另一个类B,但是这种使用关系是具有偶然性的、临时性的、非常弱的,但是类B的变化会影响到类A,通常,类B作为类A的方法的参数(或者局部变量)存在
8.关联和依赖的区别?
答:依赖关系是一种临时的关系,依赖关系主要通过函数参数或局部变量来实现,当调用方法时才有关系,关联关系是一种长期的关系,主体现在成员变量,无论是否调用方法这种关系都存在。
9. 聚合和组合的异同?
答:都是整体和部分的关系,但组合更强,组合部分的生命周期同整体,聚合关系中部分离开整体仍可存活,组合关系中部分离开整体没有意义
10.聚合和关联的区别?
答:关联关系双方是平级的,是个体和个体的关系,聚合关系双方不是平级的,是整体和部分的关系。
面向对象
1.什么是面向对象(Object Oriented Programming)?
答:面向对象的底层是面向过程,把面向过程抽象成类,然后封装,方便我们使用的就是面向对象了
面向过程是一种自顶而下的编程模式。
把问题分解成一个一个步骤,每个步骤用函数实现,依次调用即可。
2.面向对象的三大特征
答:封装继承和多态
封装:把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果属性不想被外界访问,我们大可不必提供方法给外界访问。但是如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了。
继承:见类间关系
多态:父类或接口定义的引用变量可以指向子类或具体实现类的实例对象。提高了程序的拓展性。在Java中有两种形式可以实现多态:继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口中同一方法)。
3.什么是抽象
答:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。
4.什么是多态
答:多态分为编译时多态和运行时多态。其中编辑时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的函数,通过编辑之后会变成两个不同的函数,在运行时谈不上多态。而运行时多态是动态的,它是通过动态绑定来实现的,也就是我们所说的多态性
5.Java语言是如何实现多态的
答:Java实现多态有三个必要条件:继承、重写、向上转型
继承:在多态中必须存在有继承关系的子类和父类。
重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备技能调用父类的方法和子类的方法。
java的设计模式
1.什么是设计模式
答:抽象思维是设计模式的内核,是为了让写出的代码简洁、易扩展,可以重复使用的通用解决方案
2.设计模式分类
答:创建型模式用于对象的创建,包括单例、工厂等模式,结构型模式用于描述对象之间的组合关系,包括代理、享元等模式,行为型模式用于描述对象之间的通信和责任分配。包括模版方法、观察者、责任链等模式
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享 元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
3.设计模式原则
答:单一职责原则,最少知道原则,接口隔离原则,依赖倒转原则,里氏代换原则,开放封闭原则
里氏代换原则:子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法
优点:增加程序的健壮性,即使增加了子类,原有的子类还可以继续运行,互不影响。
依赖倒转原则:高层模块不应该直接依赖于低层模块,两者都应该依赖于抽象
即详细应该依赖于抽象。在调用链上,调用者属于高层,被调用者属于低层。例如controller层不能直接依赖于service层,他们都应该依赖于抽象,即service接口。
依赖倒置原则的核心思想是面向接口编程
依赖倒转原则要求我们在程序代码中传递参数时或在关联关系中,尽量引用层次高的抽象层类,
这个是开放封闭原则的基础,具体内容是:对接口编程,依赖于抽象而不依赖于具体。
开放封闭原则:软件对扩展开放,对修改关闭。提高扩展性和可维护性。
原则思想:尽量通过扩展软件实体来解决需求变化,而不是通过修改已有的代码来完成变化
接口隔离原则:类之间的依赖关系应该建立在最小的接口上。
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的 意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级 和维护方便。所以上文中多次出现:降低依赖,降低耦合。
例如:实现类只能用到接口的部分方法,为了降低冗余提高可维护性,对原接口进行合理的拆分和组合。
迪米特法则(最少知道原则):一个类对自己依赖的类知道的越少越好,只与直接的朋友(成员变量,方法参数,方法返回值的类)通信
大概意思就是一个类尽量减少自己对其他对象的依赖,原则是低耦合,高内聚,只有使各个模块之间的耦合尽量的低,才能提高代码的复用率。
单一职责原则:一个类应该只负责一项职责。提高可读性维护性、降低耦合。
合成复用原则:尽量使用聚合或组合的方式,而不是使用继承。
聚合:也就是把需要用到的类作为本类的参数、成员变量、局部变量
组合:
4.什么是单例模式
答:保证一个类只有一个实例,并且提供一个访问该全局访问点
单例模式的实现方式
答:饿汉式,懒汉式
饿汉式单例类:饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的
懒汉式单例类:通过私有构造方法避免了类在外部被实例化,但是线程不安全。
工厂模式
什么是工厂模式?
答:通过使用一个共同的接口来指向新创建的对象。实现了创建者和调用者分离,只需要知道名称,不用new对象
工厂模式类型?
答:简单工厂模式、工厂方法模式和抽象工厂模式
简单工厂的缺点?
答:违反开放-封闭原则,单一职责原则问题,不易扩展
6.什么是代理模式
答:原有代码乃至原业务流程都不修改的情况下,直接在业务流程中切入新代码
通过代理控制对象的访问,可以在这个对象调用方法之前、调用方法之后去处理/添加新的功能。(也就是AO的P微实现)
代理在原有代码乃至原业务流程都不修改的情况下,直接在业务流程中切入新代码,增加新功能, 这也和Spring的(面向切面编程)很相似
7.什么是门面模式
答:和封装类似,将多个方法封装进一个方法中
8.什么是原型模式
答:简单来说就是克隆
9.什么是策略模式
答:简单来说就是不同的实现重写
Spring
基础
什么是spring?
答:Spring是一个轻量级Java开发框架,Spring负责基础架构
spring的设计理念?核心?
答:使应用面向接口开发,核心是IoC容器和AOP模块
Spring框架的优缺点?
答:优点是简化开发,方便集成,可以面向切面编程,缺点是依赖反射影响性能
spring的应用场景?
答:JavaEE企业应用开发,包括SSH、SSM等
Spring由哪些主要模块组成?
答:核心容器,AOP,设备支持,数据访问与集成,Web,消息,Test
什么是核心模块?
答:提供spring 框架的基础功能,BeanFactory 是 任何以spring为基础的应用的核心。Spring 框架建立在此模块之上,它使Spring成为一个容器。
Spring 框架中都用到了哪些设计模式?
答:BeanFactory是工厂模式,Bean默认为单例模式,可以说切面编程的底层体现了代理模式
spring的常用注解?
答:
web方面:Controller,RestController,Requestmapping,ResponseBody,RequestBody,RequestParam
容器类:
Component,Service,Repository,Autowired,Configuration,Value
AOP切面类:
After,Before.Around,PointCut
事务注解:Transactional
IOC
什么是IOC?
答:控制反转是一种思想,控制是指IoC 容器控制了对象,就是主要控制了外部资源获取,反转是指依赖对象的获取被反转了,通过IOC实现解耦
IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。
什么是IOC(Inversion of Control-控制反转)容器?
答:IOC容器就是帮使用者创建对象、管理对象(通过依赖注入(DI)、装配对象、配置对象,并且管理这些对象的整个生命周期
什么是DI(Dependency Injection-依赖注入)?
答:容器动态的将某个依赖关系注入到组件之中,因为应用程序依赖于IoC容器,所以IoC容器注入应用程序依赖的对象需要的外部资源(包括对象、资源、常量数据)
Spring IoC 的实现机制?
答:Spring 中的 IoC 的实现原理就是工厂模式加反射机制
AOP
什么是AOP(aspect-oriented programming-切面编程)?
答:也是一种封装,将可重复使用的功能形成切面,需要的时候引入即可
@Transactional 事务注解就是典型的AOP应用
spring的容器、web容器、springmvc的容器之间的区别?
答:Spring容器负责管理和创建Bean。Web容器是运行Web应用的服务器环境。SpringMVC容器是在Spring容器的基础上扩展,专注于处理Web请求
Spring Boot
基础
什么是springboot
答:spring boot是简化spring应用开发,约定大于配置,去繁从简,just run就能创建一个独立的,产品级别的应用
Springboot 有哪些优点?
答:快速开发,快速整合,配置简化、内嵌服务容器
SpringBoot与SpringCloud 区别
答:SpringBoot是快速开发的Spring框架,SpringCloud是完整的微服务框架,SpringCloud依赖于SpringBoot。
Spring Boot、Spring MVC 和 Spring 有什么区别
答:spring boot就是一个大框架里面包含了许许多多的东西,其中spring就是最核心的内容之一,当然就包含spring mvc。spring mvc 是只是spring 处理web层请求的一个模块
运行 Spring Boot 有哪几种方式?
答:打包用命令或者放到容器中运行,用 Maven/ Gradle 插件运行,直接执行 main 方法运行
Spring Boot 需要独立的容器运行吗?
答:可以不需要,内置了 Tomcat/ Jetty 等容器。
Spring Boot项目如何热部署?
答:引入DevTools 模块
Spring boot的核心注解是哪个?
答:@SpringBootApplication,主要包含@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan
@SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。
@EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能:
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。
@ComponentScan:Spring组件扫描。
什么是Spring boot Stater?
答:可以理解为启动器,它包含了一系列可以集成到应用里面的依赖包
自动配置原理?
答:Spring Boot框架会自动读取配置META-INF/spring.factories配置文件中EnableAutoConfiguration所配置的配置类,然后将其中所定义的bean根据条件注解所指定的条件来决定是否需要将其导入到Spring容器中
配置文件
什么是YAML?
答:YAML 是一种简单的数据序列化语言,YAML 具有分层配置数据
Spring Boot 是否可以使用 XML 配置?
答:可以通过@ImportResource 注解可以引入一个 XML 配置
配置加载顺序?
答:启动命令行,操作系统环境变量,指定配置文件,默认配置文件(properties(最高)> yml > yaml(最低)包外部>包内部文件)
spring boot 核心配置文件是什么?
答:bootstrap 和application
跨域
什么是跨域问题?
答:出于浏览器的同源策略限制,请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域
Spring Boot 中如何解决跨域问题 ?
答:添加@CrossOrigin
注解;添加CORS过滤器;实现WebMvcConfigurer
,重写addCorsMappings
方法
添加CORS过滤器:增加配置类,增加@Configuration,@Bean
Spring Boot 打成的 jar 和普通的 jar 有什么区别?
答:Spring Boot 项目最终打包成的 jar 是可执行 jar ,这种 jar 可以直接通过 java -jar xxx.jar 命令来运行,这种 jar 不可以作为普通的 jar 被其他项目依赖,即使依赖了也无法使用其中的类。
Spring MVC
基础
什么是Spring MVC?
答:Spring MVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架
MVC
什么是MVC?
答:mvc是一种设计模式,模型(model)-视图(view)-控制器(controller)
MVC设计模式的好处?
答:分层设计,解耦,有利于扩展开发
Tomcat
基础
什么是tomcat?
答:web服务器,或者说web容器,Servlet容器
servlet和Tomcat和spring框架的关系?
答:客户端的请求直接到tomcat,它监听端口,请求过来后,根据url等信息,确定要将请求交给哪个servlet去处理,然后调用那个servlet的service方法,service方法返回一个response对象,tomcat再把这个response返回给客户端。
redis
基础
redis的基本数据类型?
答:String,Hash,List,Set,Sorted Set
String:最大512MB,适用于缓存用户信息、计数器(如网站访问量、在线人数等)
Hash:存储对象或者映射表,如用户的个人信息、用户属性等。
List:按照插入顺序排序,适用于实现消息队列、时间线功能,如社交网络中的最新消息排行榜
Set:无序集合,适用于存储不允许重复的数据,如统计网站访问的 IP 地址、社交网络中的好友关系等。
Sorted Set:适用于需要排序的数据集合,如排行榜、带权重的消息队列等
缓存穿透
什么是缓存穿透?
答:大量请求请求到没有key的数据,相当于直接访问数据库
解决办法
答:1.对请求参数判断2.对于找不到的数据在redis中增加虚假数据3.使用布隆过滤器
缓存击穿
什么是缓存击穿?
答:用户访问一个热点的redis的key,但是这个key突然失效,失效瞬间,大量请求数据库就打进来了,直接打崩数据库
解决办法
答:1.此热点key设置永不过期2.接口限流与熔断,降级3.使用布隆过滤器4.增加互斥锁
互斥锁的设计:
从缓存读数据,缓存中不存在时,去获取锁获取成功去数据库查询,查询到数据更新缓存,释放锁,如果获取锁失败,暂停后再次查询或返回失败
缓存雪崩
什么是缓存雪崩?
答:指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机
解决办法
答:1.过期时间增加随机值2.分布式可以将热点数据分不到不同的缓存中3.时间永不过期
使用场景
缓存
把频繁访问的数据放在内存中
分布式锁
日常开发中,我们经常会使用Redis做为分布式锁。可以在分布式系统中协调多节点对共享资源的访问,确保操作的原子性。
排行榜
redis 经常用来做排行榜,比如游戏积分实时排名、直播送礼排名等等。
基于Sorted Set来实现,每个键值对有一个排序值
计数器
为了处理高并发场景可以使用Redis来实现限流和计数
基于String来实现,每个计数器有一个唯一键,值为字符串表示的整数
消息队列
用于消息传递、任务队列,延迟队列(订单超时处理)等场景
基于List和Sorted Set实现,
布隆过滤器
什么是布隆过滤器
答:它实际上是一个很长的二进制向量和一系列随机映射函数,可以把布隆过滤器理解为一个set集合,我们可以通过add往里面添加元素,通过contains来判断是否包含某个元素。
原理
答:通过key的N个无偏hash函数计算出的N个值,修改数组对应位置为1,之后请求就判断对应位置全部是1,只要有0就是不存在
因为无法完全避免hash冲突,所以一定存在误判,设置参数误判率越低,占用空间越大,其实就是增加无偏函数个数,多算出几个结果,key的结果就不容易一样了
布隆过滤器的优点
答:时间复杂度低,保密性强,存储空间小
布隆过滤器的缺点
答:有点一定的误判率,无法获取元素本身,很难删除
使用场景
答:解决Redis缓存穿透问题
redission
redisson 的加锁原理,保证加锁的原子性
答:最终加锁是通过一段lua脚本来实现加锁的,redis在执行lua脚本的时候是可以保证加锁的原子性的
先通过RedissonClient,传入锁的名称,拿到一个RLock,然后通过RLock实现加锁和释放锁 lock方法会调用重载的lock方法,传入的leaseTime为-1,调用到这个lock方法,之后会调用tryAcquire实现加锁的逻辑 tryAcquire最后会调到tryAcquireAsync方法,传入了leaseTime和当前加锁线程的id。 tryAcquire和tryAcquireAsync的区别就是tryAcquireAsync是异步执行,而tryAcquire是同步等待tryAcquireAsync的结果,也就是异步转同步的过程。 tryAcquireAsync方法会根据leaseTime是不是-1来判断使用哪个分支加锁,其实不论走哪个分支,最后都是调用tryLockInnerAsync方法来实现加锁, 只不过是参数不同罢了。但是我们这里的leaseTime其实就是-1,所以会走下面的分支,尽管传入到tryAcquireAsync的leaseTime是-1, 但是在调用tryLockInnerAsync方法传入的leaseTime参数是internalLockLeaseTime,默认是30s。 通过tryLockInnerAsync方法的实现可以看出,最终加锁是通过一段lua脚本来实现加锁的,redis在执行lua脚本的时候是可以保证加锁的原子性的, 所以Redisson实现加锁的原子性是依赖lua脚本来实现的。其实对于RedissonLock这个实现来说,最终实现加锁的逻辑都是通过tryLockInnerAsync来实现的。 if (redis.call('exists', KEYS[1]) == 0) then redis.call('hincrby', KEYS[1], ARGV[2], 1); redis.call('pexpire', KEYS[1], ARGV[1]); return nil; end; if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then 可重入加锁逻辑 redis.call('hincrby', KEYS[1], ARGV[2], 1); redis.call('pexpire', KEYS[1], ARGV[1]); return nil; end; return redis.call('pttl', KEYS[1]); KEYS[1]:就是锁的名称,对于我们的demo来说,就是myLock ARGV[1]:就是锁的过期时间,不指定的话默认是30s ARGV[2]:代表了加锁的唯一标识,由UUID和线程id组成。一个Redisson客户端一个UUID,UUID代表了一个唯一的客户端。 所以由UUID和线程id组成了加锁的唯一标识,可以理解为某个客户端的某个线程加锁。
lua 脚本是如何加锁
答:脚本中,使用了if条件和组合各种redis.call命令来判断以及实现加锁
先调用redis的exists命令判断加锁的key存不存在,如果不存在的话,那么就进入if。不存在的意思就是还没有某个客户端的某个线程来加锁,
第一次加锁肯定没有人来加锁,于是第一次if条件成立。
然后调用redis的hincrby的命令,设置加锁的key和加锁的某个客户端的某个线程,加锁次数设置为1,加锁次数很关键,
是实现可重入锁特性的一个关键数据。用hash数据结构保存。hincrby命令完成后就形成如下的数据结构。
myLock:{
"b983c153-7421-469a-addb-44fb92259a1b:1":1
}
最后调用redis的pexpire的命令,将加锁的key过期时间设置为30s。
3.为什么需要默认30s
答:防止宕机等原因引起的死锁
4.那不设置时间是如何一直锁定的
答:利用看门口机制,启动一个定时任务,来定时续约
看门狗机制
在客户端通过tryLockInnerAsync方法加锁成功之后,如果你没有指定锁过期的时间,
那么客户端会起一个定时任务,来定时延长加锁时间,默认每10s执行一次。所以watchdog的本质其实就是一个定时任务
注意:
因为有了看门狗机制,所以说如果你没有设置过期时间并且没有主动去释放锁,那么这个锁就永远不会被释放,因为定时任务会不断的去延长锁的过期时间,造成死锁的问题。但是如果发生宕机了,是不会造成死锁的,因为宕机了,服务都没了,那么看门狗的这个定时任务就没了,也自然不会去续约,等锁自动过期了也就自动释放锁了,跟上述说的为什么需要设置过期时间是一样的。
手动设置过期时间但是程序没有执行完,也会自动释放,所以要判断逻辑执行时间,来手动设置时间
5.可重入加锁是如何实现的
答:lua脚本中判断了加锁的名称和唯一id,当重复加锁时,加锁参数+1
可重入加锁的意思就是同一个客户端同一个线程也能多次对同一个锁进行加锁
也就是同时可以执行多次 lock方法,流程都是一样的,最后也会调用到lua脚本,所以可重入加锁的逻辑最后也是通过加锁的lua脚本来实现的
可重入加锁成功之后,加锁key和对应的值可能是这样。
myLock:{
"b983c153-7421-469a-addb-44fb92259a1b:1":2
}
解锁就-1知道解完为止
6.主动释放锁和避免其他线程释放了自己加的锁是如何实现的
答:在lua脚本中,锁是由名称,uuid+线程,加锁次数,组成的,解锁会比对前两个参数。
当业务执行完成之后,肯定需要主动释放锁,那么为什么需要主动释放锁呢?
第一,假设你任务执行完,没有手动释放锁,如果没有指定锁的超时时间,那么因为有看门狗机制,势必会导致这个锁无法释放,那么就可能造成死锁的问题。
第二,如果你指定了锁超时时间(锁超时自动释放逻辑后面会说),虽然并不会造成死锁的问题,但是会造成资源浪费的问题。假设你设置的过期时间是30s,
但是你的任务2s就完成了,那么这个锁还会白白被占有28s的时间,这28s内其它线程都无法成功加锁。
所以任务完成之后,一定需要主动释放锁。以及最终finally 要释放
那么Redisson是如何主动释放锁和避免其它线程释放了自己加的锁?
主动释放锁是通过unlock方法来完成的,接下来就分析一下unlock方法的实现。unlock会调用unlockAsync,传入释放线程的id,代表了当前线程来释放锁,
unlock其实也是将unlockAsync的异步操作转为同步操作。
也是执行一段lua脚本。
1)先判断来释放锁的线程是不是加锁的线程,如果不是,那么直接返回nil,所以从这里可以看出,主要是通过一个if条件来防止线程释放了其它线程加的锁。
2)如果来释放锁的线程是加锁的线程,那么就将加锁次数减1,然后拿到剩余的加锁次数 counter 变量。
3)如果counter大于0,说明有重入加锁,锁还没有彻底的释放完,那么就设置一下锁的过期时间,然后返回0
4)如果counter没大于0,说明当前这个锁已经彻底释放完了,于是就把锁对应的key给删除,然后发布一个锁已经释放的消息,然后返回1。
7.超时自动释放是如何实现
答:是依赖redis自动过期,指定时间执行redis脚本创建锁后自动过期,不指定时间会启动看门狗自动续约不过期
8.如何实现不同线程加锁互斥
答:因为lua脚本加锁的逻辑同时只有一个线程能够执行(redis是单线程的原因)
所以一旦有线程加锁成功,那么另一个线程来加锁,前面两个if条件都不成立,最后通过调用redis的pttl命令返回锁的剩余的过期时间回去。
这样,客户端就根据返回值来判断是否加锁成功,因为第一次加锁和可重入加锁的返回值都是nil,而加锁失败就返回了锁的剩余过期时间。
所以加锁的lua脚本通过条件判断就实现了加锁的互斥操作,保证其它线程无法加锁成功。
9.如何实现阻塞等待加锁
答:lock()方法,tryLock()方法不指定时间
RLock的lock方法实现中,会循环尝试获取锁,直到获取成功
指定阻塞等待时间
tryLock()方法,增加等待时间参数
实现中使用了循环加锁,增加了超时处理
10.什么是公平锁
答:公平锁就是指线程成功加锁的顺序跟线程来加锁的顺序是一样,实现了先来先成功加锁的特性,所以叫公平锁。就跟排队一样,不插队才叫公平。
缺点是,执行性能不高
11.什么是非公平锁
答:谁获取到锁就是谁的,随机性强,执行效率高
缺点是可能会出现线程饿死
12.公平锁如何实现的
答:当线程来加锁的时候,如果加锁失败了,那么会将线程扔到一个set集合中,这样就按照加锁的顺序给线程排队
getFairLock()方法
set集合的头部的线程就代表了接下来能够加锁成功的线程。当有线程释放了锁之后,其它加锁失败的线程就会来继续加锁,
加锁之前会先判断一下set集合的头部的线程跟当前要加锁的线程是不是同一个,如果是的话,那就加锁成功,如果不是的话,那么就加锁失败,这样就实现了加锁的顺序性
13.如何实现读写锁
答:加锁成功之后会在redis中维护一个hash的数据结构,存储加锁线程和加锁次数。在读写锁的实现中,会往hash数据结构中多维护一个mode的字段,来表示当前加锁的模式
getReadWriteLock()方法
在实际的业务场景中,其实会有很多读多写少的场景,那么对于这种场景来说,使用独占锁来加锁,在高并发场景下会导致大量的线程加锁失败,阻塞,对系统的吞吐量有一定的影响,
为了适配这种读多写少的场景,Redisson也实现了读写锁的功能。
所以能够实现读写锁,最主要是因为维护了一个加锁模式的字段mode,这样有线程来加锁的时候,就能根据当前加锁的模式结合读写的特性来判断要不要让当前来加锁的线程加锁成功。
14.如何实现批量加锁
答:getMultiLock()方法,其实现就是循环加锁
15.redis分布式锁存在的问题
答:可能会出现延迟同步,数据不一致
对于单Redis实例来说,如果Redis宕机了,那么整个系统就无法工作了。所以为了保证Redis的高可用性,一般会使用主从或者哨兵模式。
但是如果使用了主从或者哨兵模式,此时Redis的分布式锁的功能可能就会出现问题。
那就是客户端对原先的主节点加锁,加成之后还没有来得及同步给从节点,主节点宕机了,从节点变成了主节点,
此时从节点是没有加锁信息的,如果有其它的客户端来加锁,是能够加锁成功的,也就是延迟同步问题
16.什么是RedLock,其原理如何实现
答:是为了解决redis分布式锁存在的问题的一种算法,简单来说就是同时向多个redis中存储备份保证,有宕机也可以使用RedissonRedLock底层其实也就基于RedissonMultiLock实现的RedissonMultiLock要求所有的加锁成功才算成功,
RedissonRedLock要求只要有N/2 + 1个成功就算成功(所以至少有3个)
实现流程
1.获取当前Unix时间,以毫秒为单位。
2.依次尝试从N个Master实例使用相同的key和随机值获取锁(假设这个key是LOCK_KEY)。
当向Redis设置锁时,客户端应该设置一个网络连接和响应超时时间,这个超时时间应该小于锁的失效时间。例如你的锁自动失效时间为10秒,
则超时时间应该在5-50毫秒之间。这样可以避免服务器端Redis已经挂掉的情况下,客户端还在死死地等待响应结果。
3.如果服务器端没有在规定时间内响应,客户端应该尽快尝试另外一个Redis实例。
客户端使用当前时间减去开始获取锁时间(步骤1记录的时间)就得到获取锁使用的时间。当且仅当从大多数的Redis节点都取到锁,并且使用的时间小于锁失效时间时,锁才算获取成功。
4.如果取到了锁,key的真正有效时间等于有效时间减去获取锁所使用的时间(步骤3计算的结果)。
5.如果因为某些原因,获取锁失败(没有在至少N/2+1个Redis实例取到锁或者取锁时间已经超过了有效时间),
客户端应该在所有的Redis实例上进行解锁(即便某些Redis实例根本就没有加锁成功)。
kafka
1.设计结构是怎样的
答:由生产者,消费者,Broker 代理组成,代理内部有多个Topic,每个Topic内又有多个有序队列(分区),将消息以topic为单位进行归纳
•Producer :消息生产者,就是向 kafka broker 发消息的客户端。
•Consumer :消息消费者,向 kafka broker 取消息的客户端。
•Topic (主题):可以理解为一个队列,一个 Topic 又分为一个或多个分区,
•Consumer Group:这是 kafka 用来实现一个 topic 消息的广播(发给所有的 consumer)和单播(发给任意一个 consumer)的手段。一个 topic 可以有多个 Consumer Group。
•Broker (代理):一台 kafka 服务器就是一个 broker。一个集群由多个 broker 组成。一个 broker 可以容纳多个 topic。
•Partition(物理分区):为了实现扩展性,一个非常大的 topic 可以分布到多个 broker上,每个 partition 是一个有序的队列。partition 中的每条消息都会被分配一个有序的id(offset)。将消息发给 consumer,kafka 只保证按一个 partition 中的消息的顺序,不保证一个 topic 的整体(多个 partition 间)的顺序。
即 AB在一个队列上,c在一个队列上,那顺序可能是ACB 或者 ABC
•Offset:kafka 的存储文件都是按照 offset.kafka 来命名,用 offset 做名字的好处是方便查找。例如你想找位于 2049 的位置,只要找到 2048.kafka 的文件即可。当然 the first offset 就是 00000000000.kafka。
如果某个 Topic 下有 n 个Partition 且集群有 n 个Broker,那么每个 Broker会存储该 Topic 下的一个 Partition
如果某个 Topic 下有 n 个Partition 且集群中有 m+n 个Broker,那么只有 n 个Broker会存储该Topic下的一个 Partition
如果某个 Topic 下有 n 个Partition 且集群中的Broker数量小于 n,那么一个 Broker 会存储该 Topic 下的一个或多个 Partition,这种情况尽量避免,会导致集群数据不均衡
Replica:Partition 的副本,用来保障Partition的高可用性。
Controller: Kafka 集群中的其中一个服务器,用来进行Leader election以及各种 Failover 操作。
Zookeeper:Kafka 通过Zookeeper来存储集群中的 meta 消息
2.特点是什么
答:高吞吐、低延迟,可扩展性,持久性、可靠性,容错性,高并发,依赖zookeeper进行元数据管理
优点
高吞吐、低延迟:kafka每秒可以处理几十万条消息,它的延迟最低只有几毫秒,每个topic可以分多个partition(分区), consumer group 对partition进行consume操作。
•可扩展性:kafka集群支持热扩展
•持久性、可靠性:消息被持久化到本地磁盘,并且支持数据备份防止数据丢失
•容错性:允许集群中节点失败(若副本数量为n,则允许n-1个节点失败)
•高并发:支持数千个客户端同时读写
缺点:
由于是批量发送,数据并非真正的实时;
•对于mqtt协议不支持;
•不支持物联网传感数据直接接入;
•仅支持统一分区内消息有序,无法实现全局消息有序;
•监控不完善,需要安装插件;
•依赖zookeeper进行元数据管理;
3.应用场景
答:日志收集,消息系统,用户活动跟踪,流式处理,流量削峰,异步处理
日志收集:
一个公司可以用Kafka可以收集各种服务的log,通过kafka以统一接口服务的方式开放给各种consumer,例如hadoop、HBase、Solr等。
消息系统:
解耦和生产者和消费者、缓存消息等
用户活动跟踪:
Kafka经常被用来记录web用户或者app用户的各种活动,如浏览网页、搜索、点击等活动,这些活动信息被各个服务器发布到kafka的topic中,然后订阅者通过订阅这些topic来做实时的监控分析,或者装载到hadoop、数据仓库中做离线分析和挖掘。
运营指标:
Kafka也经常用来记录运营监控数据。包括收集各种分布式应用的数据,生产各种操作的集中反馈,比如报警和报告。
流式处理:
比如spark streaming和 Flink
流量削峰:
一般用于秒杀或抢购活动中,来缓冲网站短时间内高流量带来的压力
异步处理:
通过异步处理机制,可以把一个消息放入队列中,但不立即处理它,在需要的时候再进行处理
4.高性能原因
答:利用了 PageCache 缓存,磁盘顺序写,零拷贝技术,pull 拉模式
5.文件高效存储原理
答:
- Kafka把Topic中一个Partition大文件分成多个小文件段,通过多个小文件段,就容易定期清除或删除已经消费完成的文件,减少磁盘占用
- 通过索引信息可以快速定位Message和确定response的最大大小
- 通过将索引元数据全部映射到 memory,可以避免 Segment 文件的磁盘I/O操作
- 通过索引文件稀疏存储,可以大幅降低索引文件元数据占用空间大小
6.为什么要分区
答:
- 方便在集群中扩展,每个 Partition 可用通过调整以适应它所在的机器,而一个Topic又可以有多个Partition组成,因此整个集群就可以适应任意大小的数据了
- 可以提高并发,因为可以以Partition为单位进行读写.
- 实现负载均衡。分区对于消费者来说,可以提高并发度,提高效率
7.分区原则
答:
- 指明Partition的情况下,直接将指明的值作为Partition值
- 没有指明Partition值但有 key 的情况下,将 key 的 Hash 值与 topic 的Partition值进行取余得到Partition值
- 既没有Partition值又没有 key 值的情况下,第一次调用时随机生成一个整数(后面每次调用在这个整数上自增),将这个值与Topic可用的Partition总数取余得到Parittion值,也就是常说的 round-robin 算法
8.消息封装方式
答:按数量,按时间间隔,按大小
9.数据传输的事务有几种?
答:最多一次,最少一次,精确一次
数据传输的事务定义通常有以下三种级别:
(1)最多一次: 消息不会被重复发送,最多被传输一次,但也有可能一次不传输
(2)最少一次: 消息不会被漏发送,最少被传输一次,但也有可能被重复传输.
(3)精确的一次(Exactly once): 不会漏传输也不会重复传输,每个消息都传输被
10.消息的消费模式
答:producer将消息推送到broker,consumer从broker拉取消息
- 一些消息系统采用了push模式,将消息推送到下游的consumer。这样做有好处也有坏处:由broker决定消息推送的速率,对于不同消费速率的consumer就不太好处理了。消息系统都致力于让consumer以最大的速率最快速的消费消息,但不幸的是,push模式下,当broker推送的速率远大于consumer消费的速率时,consumer恐怕就要崩溃了。最终Kafka还是选取了传统的pull模式
- Pull模式的另外一个好处是consumer可以自主决定是否批量的从broker拉取数据。Push模式必须在不知道下游consumer消费能力和消费策略的情况下决定是立即推送每条消息还是缓存之后批量推送。如果为了避免consumer崩溃而采用较低的推送速率,将可能导致一次只推送较少的消息而造成浪费。Pull模式下,consumer就可以根据自己的消费能力去决定这些策略
- Pull有个缺点是,如果broker没有可供消费的消息,将导致consumer不断在循环中轮询,直到新消息到t达。为了避免这点,Kafka有个参数可以让consumer阻塞知道新消息到达(当然也可以阻塞知道消息的数量达到某个特定的量这样就可以批量发
11.Zookeeper的作用
答:leader选举和follower信息同步
Kafka每个topic的partition有N个副本,其中N是topic的复制因子。Kafka通过多副本机制实现故障自动转移,当Kafka集群中一个Broker失效情况下仍然保证服务可用。在Kafka中发生复制时确保partition的预写式日志有序地写到其他节点上。N个replicas中。其中一个replica为leader,其他都为follower,leader处理partition的所有读写请求,与此同时,follower会被动定期地去复制leader上的数据。
Kafka 的各 Broker 在启动时都要在Zookeeper上注册,由Zookeeper统一协调管理。如果任何节点失败,可通过Zookeeper从先前提交的偏移量中恢复,因为它会做周期性提交偏移量工作。同一个Topic的消息会被分成多个分区并将其分布在多个Broker上,这些分区信息及与Broker的对应关系也是Zookeeper在维护。
AR
:分区中所有副本称为 ARISR
:所有与主副本保持一定程度同步的副本(包括主副本)称为 ISROSR
:与主副本滞后过多的副本组成 OSR
ISR是由leader维护,follower从leader同步数据有一些延迟,超过相应的阈值会把 follower 剔除出 ISR, 存入OSR(Out-of-Sync Replicas )列表,新加入的follower也会先存放在OSR中。AR=ISR+OSR。
LEO:是 LogEndOffset 的简称,代表当前日志文件中下一条
•HW:水位或水印(watermark)一词,也可称为高水位(high watermark),通常被用在流式处理领域(比如Apache Flink、Apache Spark等),以表征元素或事件在基于时间层面上的进度。在Kafka中,水位的概念反而与时间无关,而是与位置信息相关。严格来说,它表示的就是位置信息,即位移(offset)。取 partition 对应的 ISR中 最小的 LEO 作为 HW,consumer 最多只能消费到 HW 所在的位置上一条信息。
•LSO:是 LastStableOffset 的简称,对未完成的事务而言,LSO 的值等于事务中第一条消息的位置(firstUnstableOffset),对已完成的事务而言,它的值同 HW 相同
•LW:Low Watermark 低水位, 代表 AR 集合中最小的 logStartOffset 值。
12.数据有序性
答:一个消费者组里它的内部是有序的,消费者组与消费者组之间是无序的
13.消费者如何消费数据
答:消费者每次消费数据的时候,消费者都会记录消费的物理偏移量(offset)的位置,等到下次消费时,他会接着上次位置继续消费
分布式
基础
什么是分布式系统?
答:是由多个节点组成的系统,相互的操作会有协同,解决高并发的问题
常见的分布式?
答:分布式缓存(redis),分布式锁(redis),分布式服务(springCloud),分布式消息队列(kafka)
JVM
基础
1.简述JVM内存模型
线程私有的运行时数据区: 程序计数器、Java 虚拟机栈、本地方法栈。
线程共享的运行时数据区:Java堆、方法区。
2.简述JVM中的堆
堆主要作用是存放对象实例,Java 里几乎所有对象实例都在堆上分配内存,堆也是内存管理中最大的一块。Java的垃圾回收主要就是针对堆这一区域进行。 可通过 -Xms 和 -Xmx 设置堆的最小和最大容量。
堆会抛出 OutOfMemoryError异常
JVM调优
注意:99%的情况下,基本用不到 JVM 调优,要认识到JVM调优不是常规手段,性能问题一般第一选择是优化程序,最后的选择才是进行JVM调优
1.调优原则
答:一定是根据数据,性能,需求调优,不能盲目调整参数
2.什么情况需要调优
答:Heap内存(老年代)持续上涨达到设置的最大内存值,Full GC 次数频繁,GC 停顿时间过长(超过1秒),应用出现OutOfMemory 等内存异常,系统吞吐量与响应性能不高或下降
3.JVM如何调优
答:
1.一般增加一个xms和xmx参数就够了
2.按照官方建议设置,年轻代:老年代=1:2,eden:survivor=8:1,堆内存设置为物理内存的3/4左右
3.实际使用时,升级垃圾回收器最有效。CMS 升级到 G1,甚至 ZGC
必须要有具体优化时的实际流程:
- 分析系统系统运行情况:分析GC日志及dump文件,判断是否需要优化,确定瓶颈问题点;
- 确定JVM调优量化目标;
- 确定JVM调优参数(根据历史JVM参数来调整);
- 依次确定调优内存、延迟、吞吐量等指标;
- 对比观察调优前后的差异;
- 不断的分析和调整,直到找到合适的JVM参数配置;
- 找到最合适的参数,将这些参数应用到所有服务器,并进行后续跟踪。
算法
什么是算法?
答:对于某一类问题的固定解决办法
什么是时间复杂度?
答:表示一个算法的运行时间
执行总次数的和,并假设当n趋于无穷大,去掉影响小的就是时间复杂度
3*+4*n+5->
。也可以理解为取式子最高阶
采用大 O 记法表示算法的执行时间
上面最终就是O()
什么是空间复杂度?
答:表示一个算法占用空间的大小
与时间复杂度的表示方法一样,空间复杂度也采用大 O 记法表示。算法空间复杂度的估算方法是:
- 如果算法中额外申请的内存空间不受用户输入值的影响(是一个固定值),那么该算法的空间复杂度用 O(1) 表示;
- 如果随着输入值 n 的增大,算法申请的存储空间成线性增长,则程序的空间复杂度用 O(n) 表示;
- 如果随着输入值 n 的增大,程序申请的存储空间成 n2 关系增长,则程序的空间复杂度用 O(n2) 表示;
- 如果随着输入值 n 的增大,程序申请的存储空间成 n3 关系增长,则程序的空间复杂度用 O(n3) 表示;
常见算法
递归算法
自身的直接调用或间接调用,注意:必须有一个结束否则会死循环
数据库
数据模型
概念-逻辑-物理
由抽象到具体
Shiro
基础
什么是Shiro?
答:安全框架
主要特征?
答:身份验证,授权,会话管理,加密,缓存,多租户支持
linux
常用命令
free
free:显示系统中物理上的空闲和已用内存,还有交换内存,同时,也能显示被内核使用的缓冲和缓存
top
实时动态地查看系统的整体运行情况
tail
读取文件
ll
查看文件情况,罗列出当前文件或目录的详细信息,含有时间、读写权限、大小、时间等信息
ls
查看当前目录下的文件名或目录名
sz
下载文件
rz
上传文件
vim
编辑文件
rm -f
删除文件
mkdir
创建文件夹