Java基础
Java的平台版本
- JavaME(Java Platform Micro Edition)小型版;一般用于手机端和嵌入式的程序开发
- JavaSE(Java Platform Standard Edition)标准版;一般用于桌面端的应用程序开发
- JavaEE(Java Platform Enterprise Edition)企业版;一般用于服务器网站的开发
Java 语言的特点
- 简单性(Java语言的语法和C语言和C++很相似,相对C++来说比较简单,屏蔽掉了指针)
- 面向对象(封装、继承、多态)
- 分布式
- 跨平台性(Java的虚拟机(jvm)可以实现平台无关性)
- 多线程
- 动态性
- 健壮性(Java语言的强类型机制、异常处理、垃圾的自动收集等)
- 安全性
Java跨平台性原理
什么是跨平台?
通过Java语言编写的应用程序在不同的系统平台上都可以运行
跨平台的原理
只要在需要运行java应用程序的操作系统上,先安装一个Java虚拟机(JVM Java Virtual Machine)即可。由JVM来负责Java程序在该系统中的运行。
JDK环境变量配置以及其功能
- JAVA_HOME:用于配置JDK安装路径,用于提供第三方软件支持
- CLASSPATH:jdk1.5以后默认配置,无需配置(如果配置不要配错),配置源代码编译后生成位置
- PATH:使bin下工具可以在任意路径下使用
JAVA编译执行流程
Java程序开发运行流程
开发Java程序,需要三个步骤:编写源程序-编译程序-运行程序。
注释的分类与功能
什么是注释:注释是对代码的解释和说明文字,可以提高程序的可读性。
注释的分类
- 单行注释格式://后都是注释的的内容: // 单行注释
- 多行注释格式: 多行注释的格式是使用/* 和 */将一段较长的注释括起来。
- 文档注释:文档注释以
/**开始,以*/;补:结束文档注释可以书写相应的文档标签@autor 作者 @date 时间通过javadoc指令可以生成文档(现在已经不用了)
- 注意:注释会在编译期间自动忽略(不会编译到class文件中)、多行注释不能嵌套使用。
什么是关键字
概念:关键字是指被java语言赋予了特殊含义的单词
关键字的特点:
- 常用的关键字51+2个保留字(const go to) 。
- 关键字是单词 ; 关键字是字母全部小写。
- 常用的代码编辑器对关键字都有高亮显示,比如现在我们能看到的public、class、static等。
Java中数据的分类
- 基本数据类型
| 类型 | 关键字 | 包装类 | 占用内存 | 取值范围 | 默认值 |
|---|---|---|---|---|---|
| 整型 | byte | Byte | 1 | -128~127 | 0 |
| short | Short | 2 | 0 | ||
| int | Integer | 4 | 0 | ||
| long | Long | 8 | 0L | ||
| 浮点型 | float | Float | 4 | 0.0F | |
| double | Double | 8 | 0.0D | ||
| 布尔型 | boolean | Boolean | 1 | true / false | '\u0000' |
| 字符型 | char | Character | 2 | 0~65535 | false |
- 引用数据类型
-
- 类(class)
- 接口(interface)
- 数组([])
- 基本数据类型-- 四类八种 :
-
- 整数类 byte(字节型)、int(整型)、long(长整型)、short(短整型)
- 浮点类 float(单精度浮点型)、double(双精度浮点型)
- 字符类 char(字符型)
布尔型 boolean(布尔类型)
-
- 2.引用数据类型:类、数组、接口。
小扩展: Java 中的默认类型:整数类型是 int 、浮点数类型是 double。
标识符命名规则与规范
- 标识符: 标识符是用户编程时使用的名字,用于给类、方法、变量、常量等命名。
标识符命名规则与规范
标识符的命名规则
由字母、数字、下划线、美元符组成,(不能以数字开头,不能使用java中的关键字作为标识符,标识符区分大小写)。
命名规范
- 见名知意:根据名字知道标识符对应功能
- 驼峰命名法:如果标识符由多个单词组成除首个单词首字母外其余单词首字母大写 passWord
类名
一个单词: 首字母大写 Hello
多个单词: 每一个单词的首字母都要大写 HelloWorld (大驼峰)
变量名和方法
一个单词: 全部小写 value get()
多个单词: 从第二个单词的首字母开始,每一个单词都要大写 maxValue getValue() (小驼峰)
包
一个单词: 全部小写 cn com
多个单词: 全部小写中间用
.分割 cn.yunhe
公司的域名倒着写 : www.baidu.com-->com.baidu.xxx
自定义常量
一个单词: 全部大写 MAX VALUE
多个单词: 全部大写 中间用 _ 隔开 MAX_VALUE
由字母、数字、下划线、美元符组成,(不能以数字开头,不能使用java中的关键字作为标识符,标识符区分大小写)。
流程控制语句
流程控制语句分类
顺序结构:按照代码的先后顺序,从上到下依次执行.
分支结构(if, switch)
循环结构(for, while, do…while)
if与switch的区别
相同点
● if和switch都是Java里的分支语句,它俩都属于条件分支语句;
不同点
● if和switch判断条件的数据类型不同,if的判断条件数据类型是布尔类型,switch的判断条件数据类型一般是int类型。
● if elseif 流程语句中可以允许有多个判断条件为true但只会执行第一个判断条件为true的语句体,switch语句中case条件取值是不能重复的。
● 写法上,当if 流程语句中只有一个判断条件的时候语句体的{}可以省略不写,switch语句中只有一个case条件语句体的{}也不能省略,当switch语句有多个case时,每个case后面的break关键字不能省略,不然可能会发生向下穿透的问题。
while和do...while
执行流程不同
- while先判断循环条件后进行循环体执行,do...while先执行循环体后进行条件判断
循环体执行次数不同
- while循环体可能由于条件不满足一次都不执行,do..while循环体至少执行一次
for循环和while的区别
变量作用域不同
- for循环声明的变量只有当前循环可用,while在外声明可以直接使用
使用场景不同
- 已知次数的循环使用for循环,未知次数的循环使用while循环
break、continue、return的区别
- break 结束当前的循环体
- continue 结束本次循环,进入下一次循环
- return 结束当前方法,并返回方法指定类型的值
- 注意:break和continue只能在循环中进行使用,单独使用无任何意义!!
什么是数组
- 用于存储多个同一数据类型的元素的容器就是数组
- 特点:长度固定。存储类型相同
面向对象和面向过程的区别
- 面向过程
-
- 优点:性能比面向对象高,因为类调用时需要实例化,开销较大,比较消耗资源。
- 缺点:没有面向对象易维护、易复用、易扩展。
- 面向对象
-
- 优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护
- 缺点:性能比面向过程低
类与对象的关系
- 类:具有相同特性和行为的对象组成的集合就是类
- 对象:是类的具体实例。
- 类与对象的关系:抽象与类的实例化就是对象,对象的共性特征抽象出来就是类。
面向对象的三大特征
- 封装
-
- 封装就是隐藏对象的属性和方法,用于提高程序的安全性,一般使用private进行修饰,然后对外提供公开的方法进行查看和修改。
- 封装的优点:隐藏事物的实现细节,提高了代码的安全性与复用性。
- 继承
-
- 通过extends关键字子类继承父类,继承父类所有非私有属性与行为。
- 继承的优点: 提高代码的复用性 类与类之间产生了关系,是多态的前提。
- 多态(重要)
-
- 多态是同一个行为具有多个不同表现形式或形态的能力。
- 多态的前提 :
-
- 要有继承或实现关系
- 要有方法的重写
- 要有父类引用指向子类对象
构造方法概述
构造方法也叫构造器,其实就是个方法,方法名和类名形同,可以被重载,一般用于为属性初始化赋值。
super和this 的区别
- super:代表父类的存储空间标识(可以理解为父类的引用)。
- this:代表当前对象的引用(谁调用就代表谁).
this
- this关键字可用来引用当前类的实例变量。主要用于形参和成员名字重名,用this来区分。
- this关键字可以用来调用当前类方法。
this()可以用来调用当前类的构造函数。(this()一定放在构造函数的第一行,否则编译不通过)
- super
- super可以用来引用直接父类的实例变量。主要用于区分父类和子类中相同的名的属性。
- super可以用来调用直接父类构造函数。
- super可以用来调用直接父类方法。
注意:
- 子类的每个构造方法中均有默认的super(),调用父类的空参构造。手动调用父类构造会覆盖默认的super()。
- super()和this()都必须是在构造方法的第一行,所以不能同时出现。
Overload和Override的区别
Overload方法重载:在同一个类中,方法名相同,参数列表不同(个数、类型、顺序),与返回值类型无关;称之为发生了方法的重载,或多个方法互为重载。
Override方法重写:发生在继承过程中,方法名、参数列表相同。 存在于子父类中;或者子父接口中。子类继承父类非私有方法。父类方法运行不能满足子类需求,子类重写父类方法(修饰符大于或等于父类、返回值,方法名,参数列表必须与父类相同、方法体按照需求修改)
位置区别:Overload方法重载发生在同一个类中,Override方法重写发生在存在继承关系的父类与子类中
可修改区别:Overload方法重载可以任意修改修饰符 返回值类型 参数列表 方法体,Override方法重写一般只修改方法体
校验区别:Overload方法重载是在书写完毕后,编译时进行校验,Override方法重写通过书写Override注解进行限制
抽象类与接口的区别
- 抽象方法概念:只有方法的声明,没有方法体的方法,就是抽象方法
- 抽象类概念:抽象方法所在的类必定是一个抽象类
- 接口概念:接口就是一种公共的规范标准。
抽象类和接口的区别
-
成员区别
-
- 抽象类 :变量,常量;有构造方法;有抽象方法,也有非抽象方法
- 接口:静态常量;抽象方法
-
关系区别
-
- 类与类:继承,单继承
- 接口与接口:实现,单实现,多实现
- 类与接口:实现,可以单实现,也可以多实现
-
设计理念区别
-
- 抽象类:为了继承而来,让子类强制重写父类中的抽象方法
- 接口:对行为抽象,主要是行为,主要用于功能的扩展
多态的前提、向下转型的前提
- 多态概念: 同一个对象,在不同时刻表现出来的形态是不同的
多态的前提
- 要有继承或实现关系
- 要有方法的重写
- 要有父类引用指向子类对象
多态的转型
向上转型概念:多态本身是子类类型向父类类型向上转换的过程,这个过程是默认的。
向下转型的前提
向下转型的概念:一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。
- 向下转型成功的前提:该对象曾经发生过向上转型(该对象保存的就是对应类型的数据)
- 父类类型向子类类型向下转换的过程,这个过程是强制的。
- 使用格式:子类类型 变量名 = (子类类型) 父类变量名;
例如: Cat c =(Cat) a;
子类类型 变量名 = (子类类型) 父类变量名;
如:Cat c =(Cat) a;
static关键字
static关键字概念:static关键字的主要用途就是方便在没有创建对象时调用方法和变量和优化程序性能。
static变量(静态变量)
用static修饰的变量被称为静态变量,也被称为类变量,可以直接通过类名来访问它。静态变量被所有的对象共享,只会在类初次加载时会被初始化。
static方法(静态方法)
static方法不依赖于任何对象就可以进行访问,在static方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都是必须依赖具体的对象才能被调用,但是在非静态成员方法中是可以访问静态成员方法/变量的。
statiac代码块(静态代码块)
静态代码块的主要用途是可以用来优化程序的性能,因为它只会在类加载时加载一次,很多时候会将一些只需要一次的初始化操作都放在static代码块中进行。如果程序中有多个static块,在类初次被加载的时候,会按照static块的顺序来执行每个static块。
可以通过this访问静态成员变量吗?(可以)
this代表当前对象,可以访问静态变量,而静态方法中是不能访问非静态变量,也不能使用this引用。
static总结
- 被static修饰的内容
-
- 是随着类的加载而加载的,且只加载一次。
- 存储于一块固定的内存区域(静态区),所以,可以直接被类名调用。
- 它优先于对象存在,所以,可以被所有对象共享。
- 被static修饰的方法只能使用被static修饰的属性与static修饰的方法
- 未被static修饰的方法都可以使用
- static修饰的方法中不能使用this与super
注意事项
- 静态方法可以直接访问静态变量和静态方法。
- 静态方法不能直接访问普通成员变量或成员方法。反之,成员方法可以直接访问静态变量或静态方法。
- 静态方法中,不能使用this关键字。
final关键字
- 简介final关键字
- final代表最终的、最后的。用于修饰程序运行中不允许改变的内容,可以用来修饰类 方法 变量。
-
- final修饰的类称之为最终类 不能被继承
- final修饰的方法称之为最终方法 可以被继承 但不能被重写
final修饰的变量称之为常量,仅能赋值一次,不允许修改(常量命名规范)
-
- 修饰局部变量,可以先声明后进行一次赋值,修饰的引用类型,变量不允许修改,但是引用类型的属性值可以修改,修饰全局变量,必须进行初始化赋值,或定义唯一构造方法为常量赋值
equals与==的区别
- == :对于基本数据类,==比较的是值;对于引用数据类型,==比较的是内存地址。
- equals :对于没有重写equals方法的类,equals方法和==作用相同;对于重写过equals方法的类,equals比较的是值。
equals方法与 ==运算符的区别
- ==是比较运算符概述: 用于基本数据类型的值对比、引用类型的地址对比
- equasl概述:equals是object类提供的方法;底层就是==;对于没有重写equals方法的类,equals方法和==作用相同;对于重写过equals方法的类,equals比较的是值。
- 区别本质的区别: equals是方法; ==是运算符比较数据的区别 ; equals只能比较引用数据类型; ==既可以比较基本数据类型也可以比较引用数据类型
- 比较内容的区别 :object类提供的equals方法本质就是== ; 比较的是引用类型的地址;重写的equals就变成比较内容的方法比较的内容的区别:==是比较运算符;它比基本数据类型时;它比较的是值;而比较引用类型时就比较的是内容。
为什么重写equals方法需要重写hashCode方法
equals
- 如果没有重写equals方法,那么它是和==的作用相同,都是比较对象的地址值。
重写的equals会依次比较内存地址、对象类型、以及值。内存地址相同,equals一定返回true,对象类型和值相同,equals方法一定返回true。
- hashCode
hashCode方法返回对象的散列码,返回值是int类型的散列码。散列码的作用是确定该对象在哈希表中的索引位置。
关于hashCode的一些约定:
- 两个对象相等,hashCode一定相同
- 两个对象hashCode相同,它们不一定相等
- hashCode()方法默认是对堆上的对象产生独特值如果没有重写hashCode()方法则该类的两个对象的hashCode()值肯定不同
为什么重写equals方法后要重写hashCode?
- 根据规定,两个对象相等,hashCode值也要相同,所以重写equals方法后,hashCode方法也必须重写(面试官肯定不想听这个)
- 因为hashCode在具有哈希机制的集合中有着非常关键的作用,比如HashMap、HahsSet等。以HashSet为例,HashSet的特点是存储元素时无序且不可重复,在向HashSet中添加对象时,首先会计算对象的HashCode值来确定对象的存储位置,如果该位置没有其它对象,直接将该对象添加到该位置;如果存储位置有存储其它对象(新添加的对象和该存储位置的hashCode相同),则调用equals方法判断两个对象是否相同,如果相同,则添加对象失败,如果不同,则会将该对象重新散列到其它位置。所以重写euquals方法后,HashCode方法不重写的话,会导致所有对象的HashCode值都不相同,都能添加成功了,那么HashSet中会出现很多重复元素,HashMap也是同理(因为HashSet的底层就是通过HashMap实现的),会出现大量的Key(HashMap中的key是唯一的,但不同的key可以对应相同的value)。所以重写equals方法后,HashCod方法也必须要重写。同时因为两个对象的HashCode值不同,则它们一定不相等,所以先计算的HashSet值可以在一定程度上判断两个对象是否相等,提高了集合的效率。
- 总结:第一,在HashSet等集合中,不重写hashCode方法会导致其功能出现问题;第二,可以提高集合的效率。
String、StringBuffer、Stringbuilder的区别
- 可变String不可变,StringBuilder和StringBuffer是可变的
- 线程安全性: String由于是不可变的,所以线程安全。StringBuffer对方法添加了同步锁,所以线程是安全的。而StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
- 性能: StringBuilder > StringBuffer > String
| 是否可变 | 是否安全 | 性能 | |
|---|---|---|---|
| String | 不可变 | 安全 | 低 |
| StringBuilder | 可变 | 不安全 | 高 |
| StringBuffer | 可变 | 安全 | 高 |
场景:经常需要改变字符串内容时使用StringBuffer和StringBuider,其中优先使用StringBuilder,如果是多线程并且使用共享变量时使用StringBuffer
什么是包装类?什么是自动拆装箱?
什么是包装类?
包装类就是将基本数据类型包装成引用类型
什么是自动拆装箱?
装箱:就是将基本类型用包装器类型包装起来
拆箱:就是将包装器类型转换为基本类型
请说出几种常见的异常,异常的分类?处理异常的方式有哪些?
异常的分类?
- 编译时异常
- 运行时异常
8、列出编程过程中的常见异常以及如何处理它们。
常见异常
| NullPointerException | 空指针异常,操作一个null对象的方法或者属性的时候触发 |
|---|---|
| OutOfMemoryError | 内存异常 |
| IOException | IO异常(输入输出异常) |
| FileNotFoundException | 文件找不到异常 |
| ClassNotFoundException | 类找不到异常 |
| StackOverflowError | 栈溢出异常;简单说就是死循环了 |
| IllegalAccessException | (安全权限异常) |
| IndexOutOfBoundsException | (数组越界) |
处理异常的方式
- 声明抛出异常
-
- throw
- throws
- 区别:throw用于方法内部,抛出的是异常对象;throws用于方法头,表示的是异常的声明
- try-catch 捕获异常 并处理不会停止执行
- Jvm 是默认处理异常;就是向上一直抛;Jvm的处理方式就是打印异常并停止运行。
final、finally、finlize的区别
- final是个关键字,主要用于修饰类、属性和方法的;修饰的类不能被继承,修饰的变量不能重新赋值,修饰的方法不能被重写。
- finally:Java的一种异常处理机制;一般配合try-catch使用。是异常处理语句结构的一部分,表示总是执行。finally代码块中的代码表示不管是否出现异常,里面的代码块都会执行,一般用来存放一些关闭资源的代码。
- finalize是一个属于object类的一个方法,该方法一般由垃圾回收器来调用,当我们调用System.gc()方法的时候,有垃圾回收器调用finalize()方法,回收垃圾。
ArrayList与LinkedList的区别
- 是否线程安全:ArrayList和LinkedList都是不保证线程安全的
- 底层的实现:ArrayList的底层是动态数组,LinkedList的底层是双向链表
- 内存占用:ArrayList会存在一定的空间浪费,因为每次扩容都是之前的1.5倍,而LinkedList中的每个元素都存放其前后的数据地址,所以对每个元素的存储都要比ArrayList花费更多的空间。
- 应用场景:
- ArrayList更适合用于多读,少增删的场景。
- LinkedList适用于多增删,少读写的场景。
- ArrayList:基于动态数组,连续内存存储,适合下标访问
- LinkedList:基于链表,可以存储在分散的内存中,适合做数据插入及删除操作
List接口与Set接口的区别
| List | 有序,按对象进入的顺序保存对象,可重复,允许多个Null元素对象,可以使用Iterator获取元素,再逐一遍历,还可以使用get(int index)获取指定下标的元素 |
|---|---|
| Set | 无序,不可重复,最多允许有一个Null元素对象,取元素时只能用Iterator接口取得所有元素,再逐一遍历各个元素 |
HashSet存储流程
- HashSet是java编程语言中的一个集合类,它实现了Set接口。HashSet的底层是HashMap,HashSet的值存放与HashMap的key上,所以实现了元素的无序且唯一,工作原理是基于哈希表的数据结构
- 当在HashSet中添加一个元素时,它首先会计算该元素的哈希码,以这个哈希码来确定元素在数组中的存储位置。
- 然后使用equsal来比较链表存储的数据,判断是否存在相等值,如果没有就可以添加,如果有就覆盖。
HashMap初始容量?扩容倍数?负载因子?(为什么负载因子是0.75 其他可不可以)
初始容量:16
扩容倍数:2.0
负载因子:0.75
- 在JDK中默认长度是初始容量16,并且默认长度和扩容后的长度都必须是2倍
- 负载因子是用来控制哈希表的装填程度的,当负载因子超过一定阈值时,哈希表会自动进行扩容操作,以保持较低的哈希冲突率和较高的性能。
- 可以但不建议,因为会频繁扩容比较浪费空间。0.75比较合理,因为这个数和2的幂乘积结果都是整数
创建线程的方式?
- 继承Thread类,重写run方法
- 实现runnable接口,实现run方法,交由线程对象运行
- 实现callable接口,通过包装器FutrueTask创建线程(有返回值)
- 使用线程池,例如Executor框架
Thread和Runnable的区别
Thread
Thread是java中表示线程的类,它提供了内置的方法用来管理线程,比如启动,暂停,恢复,停止等,要使用Thread类创建一个线程,需要重写它的run()方法,run()方法包含了线程要执行的代码
Runnable
Runnable是java中的一个接口,定义了一个可运行的任务,它只有一个run()方法,用于定义线程执行的代码。为了创建一个线程,可以实现runnable接口,并将实现Runnable接口的任务传递给Thread类的构造函数
实现Runnable接口比继承Thread类所具有的优势
1.适合多个相同的程序代码的线程去共享同一个资源。
2.可以避免java中的单继承的局限性。
3.增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立
- 4.线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。
线程的生命周期
线程状态的划分并不唯一,但是都大同小异,这里参考《Java并发编程的艺术》,主要由以下几种状态:
| 状态 | |
|---|---|
| NEW | 初始状态,注意此时还未调用start()方法 |
| RUNNABLE | 运行状态,包含就绪和运行中两种状态 |
| BLOCKED | 阻塞状态 |
| WAITING | 等待状态 |
| TIME_WAITING | 超时等待状态,和等待状态不同的是,它可以在制定的时间自行返回 |
| TERMINATED | 终止状态,线程运行结束 |
- 创建状态-->new
- 就绪状态-->start
- 运行状态-->run
- 阻塞状态-->lock
- 等待状态-->wart
- 死亡状态-->Terminated
线程同步的方式
- synchronized关键字
- 使用Volatie关键字实现线程同步
- 使用重入锁实现线程同步
- 使用局部变量实现线程同步
- 使用阻塞队列实现线程同步
- 使用原子变量实现线程同步
- wait()方法与notify()方法
死锁的产生方式与解决方法
死锁的产生方式
- 竞争不可抢占性资源(p1已经打开F1,想去打开F2,p2已经打开F2,想去打开F1,但是F1和F2都是不可抢占的,这是发生死锁。)
- 竞争可消耗资源引起死锁(进程间通信,如果顺序不当,会产生死锁,比如p1发消息m1给p2,p1接收p3的消息m3,p2接收p1的m1,发m2给p3,p3,以此类推,如果进程之间是先发信息的那么可以完成通信,但是如果是先接收信息就会产生死锁。)
进程推进顺序不当(进程在运行过程中,请求和释放资源的顺序不当,也同样会导致产生进程死锁。)
- 避免死锁的方法
- 破坏“请求和保持”条件
- 破坏“不可抢占”条件
- 破坏“循环等待”条件
wait与sleep方法的区别
相同点:
- wait()方法和sleep()方法都可以使得线程进入到阻塞状态。
wait()方法和sleep()方法都是可中断方法,被中断后都会收到中断异常。
- 不同点:
- wait()是Object的方法,sleep()是Thread的方法。
- wait()必须在同步方法中进行,sleep()方法不需要。
- 线程在同步方法中执行sleep()方法,不会释放monitor的锁,而wait()方法会释放monitor的锁。
- sleep()方法在短暂的休眠之后会主动阻塞,而wait()方法在没有指定wait时间的情况下需要被其他线程中断才可以退出阻塞。
run方法与start方法的区别
调用方不同:start方法由开发人员调用开启线程,run方法由系统分配资源后自动调用
生命周期不同:start方法会将线程由初始态转换为就绪态,run方法会将线程由就绪态转换为运行态
执行方式不同:start方法由开发人员手动调用才能执行,run方法时系统存在资调度后自动执行,且run方法也可以直接执行,但是直接执行就是在当前线程直接执行代码,不会创建新的线程
执行次数不同:start方法在线程创建后只能执行一次,run方法可能被执行多次
什么是设计模式?设计模式的分类?简述你知道的设计模式有哪些?
什么是设计模式?
设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案
设计模式本身是一种思想,用于解决某些简单常用需求的固定思路或代码,称之为设计模式
设计模式的分类
- 创建型模式
工厂方法模式 抽象工厂模式 单例模式 建造者模式 原型模式
- 结构型模式
适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式
- 行为性模式
策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式
网络编程三要素
协议
ip
端口号
TCP与UDP的区别
| 是否面向连接 | 可靠性 | 传输形式 | 传输效率 | 消耗资源 | 应用场景 | 首部字节 | |
|---|---|---|---|---|---|---|---|
| TCP | 面向连接 | 可靠 | 字节流 | 慢 | 多 | 文件/邮件传输 | 20~60 |
| UDP | 无连接 | 不可靠 | 数据报文段 | 快 | 多 | 视频/语音传输 | 8 |
sql语句的分类
- DQL 数据查询语言
- DML 数据操作语言
- DDL 数据定义语言
- DCL 数据安全控制语言
- TCL 事务控制语言
数据库引擎InnoDB与MyISAM的区别?
- InnoDB:InnoDB是MySQL的默认存储引擎,支持事务、行锁和外键等操作。
- MyISAM:是MySQL5.5版本前的默认存储引擎,MyISAM的并发性比较差,不支持事务和外键等操作,默认的锁和粒度为表现锁。
| InnoDB | MyISAM | |
|---|---|---|
| 外键 | 支持 | 不支持 |
| 事务 | 支持 | 不支持 |
| 锁 | 支持表锁和行锁 | 支持表锁 |
| 可恢复性 | 根据事务日志进行恢复 | 无事务日志 |
| 表结构 | 数据和索引是集中存储的, .ibd和 .frm | 数据和索引是分开存储的,数据 .MYD,索引 .MYI |
| 查询性能 | 一般情况相比于MyISAM较差 | 一般情况相比于InnoDB好些 |
| 索引 | 聚簇索引 | 非聚簇索引 |
行表锁: InnoDB支持数据行锁定,MyISAM不支持行锁定,只支持锁定整个表。
char与varchar的区别
存储方式、存储容量、存储效率
- char的长度是固定的,varchar是可变的。当所插入的字符超过他们的长度时,在严格模式下,会拒绝插入并提示错误信息,在一般模式下,会截取后插入。如char(5),无论插入的字符长度是多少,长度都是5,插入字符长度小于5,则用空格补充。对于varchar(5),如果插入的字符长度小于5,则存储的字符长度就是插入字符的长度,不会填充。
- 存储容量不同,对于char来说,最多能存放的字符个数为255。对于varchar,最多能存放的字符个数是65532。
- 存储速度不同,char长度固定
事务的ACID特性分别指的是什么?
ACID代表的是事务的四大特性
- 原子性(Atomicity):原子性是指包含事务的操作要么全部执行成功,要么全部失败回滚。
- 一致性(Consistency):一致性指事务在执行前后状态是一致的。
- 隔离性(Isolation):一个事务所进行的修改在最终提交之前,对其他事务是不可见的。
- 持久性(Durability):数据一旦提交,所做的修改将永久地保存到数据库中。
SQL查询语句的执行顺序
- FROM: 对SQL语句执行查询时,首先对关键字两边的表以笛卡尔积的形式执行连接,并产生一个虚拟表V1。虚拟表就是视图,数据会来自多张表的执行结果。
- ON: 对FROM连接的结果进行ON过滤,并创建虚表V2
- JOIN: 对ON过滤后的左表添加进来,并创建新的虚拟表V3
- WHERE: 对虚拟表V3进行WHERE筛选,创建虚拟表V4
- GROUP BY: 对V4中的记录进行分组操作,创建虚拟表V5
- HAVING: 对V5进行过滤,创建虚拟表V6
- SELECT: 对V6中的结果按照SELECT进行筛选,创建虚拟表V7
- DISTINCT:对7表中的结果进行去重操作,创建虚拟表V8,如果使用GROUP BY子句则无需使用DISTINCT,因为分组的时候 是将列中唯一的值分成一组,并且每组只返回一行记录,所以所有的记录都是不同的。
ORDER BY: 对V8表中的结果进行排序
- limit
delete from 表名与truncate 表名 都可以删除表中所有数据区别在哪?
| drop | delete | truncate | |
|---|---|---|---|
| 速度 | 快 | 逐行删除,慢 | 较快 |
| 类型 | DDL | DML | DDL |
| 回滚 | 不可回滚 | 可回滚 | 不可回滚 |
| 删除内容 | 删除整个表,数据行、索引都会被删除 | 表结构还在,删除表的一部分或全部数据 | 表结构还在,删除表的全部数据 |
一般来说,删除整个表使用drop,删除表的部分数据使用delete,保留表结构删除表的全部数据使用truncate(truncate是先删除掉原表,再创建一个结构相同的表)
数据库建库建表三范式指的是什么?
- 第一范式:确保每列保持原子性,数据表中的所有字段值都是不可被分割的原子值。
- 第二范式:确保表中的每列都和主键相关
- 第三范式:确保每列都和主键列直接相关而不是间接相关
数据库三大范式
什么叫范式
为了建立冗余较小、结构合理的数据库,设计数据库时必须遵循一定的规则。在关系型数据库中这种规则就称为范式。
目前关系数据库有六种范式
第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式和第五范式(5NF)。
而通常我们用的最多的就是第一范式(1NF)、第二范式(2NF)、第三范式(3NF),也就是数据库设计的“三大范式”。
三大范式也就是常用范式
第一范式(1NF):要求数据库表的每一列都是不可分割的原子数据项。
简单理解就是: 例原子;意思就是不可分割。
大白话就是说: 每一列不能存储多余数据 ;只能对应的数据 。
例:性名例只保存性名;不保存无关数据
第二范式(2NF):在1NF的基础上,非码属性必须完全依赖于候选码(在1NF基础上消除非主属性对主码的部分函数依赖)
第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)
简单理解:第二范式必须要以第一范式为前提;然后就是说每个表中必须有主键例;用来获得其他所有相关数据 。
大白话就说:每个表必须设置主键例;这样就可以通过主键例得到对应的数据的所有相关数据 。
例:如果在一个学生表中;把学号设置成主键例那么你就可以通过学号得到这个学生的所有数据 如年龄、学校、班级、住址等对应相关信息。
第三范式(3NF):在2NF基础上,任何非主属性不依赖于其它非主属性(在2NF基础上消除传递依赖)
第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关
简单理解 就是说:在2NF基础上; 非主键非关联系、除了主键都没有关联。 不符合表内容的;不要进行存放。
where与having的区别
- where是在group by分组和聚合函数之前对数据进行过滤
- having是对group by分组和聚合函数之后的数据进行过滤
什么是视图?视图的作用是什么?有什么优缺点?
什么是视图?
- 在MySQL中,视图就是一个命名的虚拟表,它是由一个SQL查询来定义,可以当做表来使用。与持久表不同的是,视图中的数据没有实际的物理存储。
- 视图的作用?
- 提高重用性
- 便于数据库重构
- 提高安全性
- 数据更清晰
有什么优缺点?
- 优点:
-
- 简化查询
- 数据安全性
- 数据一致性
- 缺点:
-
- 性能问题
-
- 视图是基于查询语句生成的,可能会导致查询性能下降,特别是当视图涉及复杂的连接和过滤操作时。
- 更新限制
-
- 视图通常只能读取数据,对于包含聚合函数,分组或连接等的视图,可能无法直接进行数据的插入,更新和删除操作
- 维护复杂性
-
- 当底层表结构发生变化时,可能需要对相关的视图进行相应的修改,增加了维护的复杂性
什么是索引?索引的优缺点?索引失效的场景有哪些?
什么是索引?
- MySQL官方定义: 索引(index)是帮助MySQL高效获取数据的数据结构。
- 本质:索引是数据结构。是一张描述索引列的列值与元表中记录行之间一一对应关系的序表。
索引的优缺点?
- 优点
-
- 大大加快数据检索的速度。
- 加速表与表之间的连接
- 将随机I/O变成顺序I/O(因为B+树的叶子节点是连接在一起的)
- 缺点
-
- 从空间角度考虑,建立索引需要占用物理空间
从时间角度考虑,创建和维护索引都需要花费时间,例如对数据进行增删改的时候都需要维护索引。
索引失效的场景?
- 对非索引列进行查询
- 对索引列进行函数操作
- 数据分布不均匀
- 使用 “or”操作符
- %like
- 数据库表中的数据类型不匹配,导致索引失效
- 数据库表中存在大量的空值,导致索引失效
- 数据频繁更新
sql优化
- 避免出现select *
- 避免出现不确定结果的函数
- 多表关联查询时,小表在前,大表在后
- 使用表的别名
- 用where子句替换having
调整where子句中的连接顺序
- SQL优化—MSDN
- 基础Sql优化
-
- 查询SQL尽量不要使用select *,而是具体字段
- 避免在where子句中使用or来连接条件
- 使用varchar代替char
- 尽量使用数值替代字符串类型
- 查询尽量避免返回大量数据
- 使用explain分析你SQL执行计划
- 是否使用了索引及其扫描类型
- 创建name字段的索引
- 优化like语句:
- 字符串怪现象
- 索引不宜太多,一般5个以内
- 索引不适合建在有大量重复数据的字段上
- where限定查询的数据
- 避免在索引列上使用内置函数
- 避免在where中对字段进行表达式操作
- 避免在where子句中使用!=或<>操作符
- 去重distinct过滤字段要少
- where中使用默认值代替null
- 高级SQL优化
-
- 批量插入性能提升
- 批量删除优化
- 伪删除设计
- 提高group by语句的效率
- 复合索引最左特性
- 排序字段创建索引
- 删除冗余和重复的索引
- 不要有超过5个以上的表连接
- inner join 、left join、right join,优先使用inner join
- in子查询的优化
- 尽量使用union all替代union
mvc与三层架构的区别
MVC是软件工程中的一种软件架构模式,分为Model(模型)、View(视图)、Controller(控制器)
- MVC架构模式分别有
-
- 视图层(view):用于连接用户提交请求和现实的代码
- 控制器(Controller):用于将用户请求转发给相应的Model进行处理,并根据Model的计算结果向用户提供相应的响应
- Model(模型):承载数据,并对用户提交请求进行计算的模块
- 三层架构分别是 (表现层/业务逻辑层/数据访问层),采用面向抽象编程,更好的降低各层间的耦合度
-
- 视图(view) 用于显示数据和接收用户输入的数据,为用户提供一种交互式操作的界面。主要作用是界面展示,接收请求,发布请求
- 服务层(Service):系统的业务逻辑主要在这里完成
- 持久层(Dao):直接操作数据库的代
-
- 码
- MVC是一种架构模式,可以用它来创建在域对象和UI表示层对象之间的区
- 分
- 它们同样是架构级别的,相同的地方在于他们都有一个表现层,但是他们不同的地方在于其他的两个层
- 三层架构是基于业务逻辑来分的,而mvc是基于页面来分的
- MVC模式是一种复合设计模式,一种解决方案
- 三层是软件架构,通过接口实现编程
- 三层模式是体系结构模式,MVC是设计模式
什么是泛型 泛型的书写与注意事项
- 什么是泛型
-
- 泛型是JDK5中引入的一个新特性,泛型提供了编译时类型安全检测机制,程序员在编译时可以检测到非法的数据类型。
- 泛型的书写
-
- 泛型类型参数通常用大写字母表示,以便与普通的类和变量区分开来
- 注意事项
-
- 泛型类型参数可以有多个
- 在实例化泛型类时,需要提供具体的类型参数
- 泛型类型参数可以用于类、接口、方法的定义和使用
- 修饰泛型类的时候去书写在类的前面
什么是递归 递归的书写与注意事项
- 递归就是书写方法,并自己调用自己陷入循环递归中
- 递归分为两种,直接递归和间接递归
- 直接递归称为方法自身调用自己
- 间接递归可以A方法调用B方法,B方法调用C方法,C方法调用A方法
- 注意事项
- 递归函数必须有让递归停下来的限制条件;否则会发生栈内存溢出.
- 递归函数必须有让递归停下来的限制条件,随着递归函数的不断运行,必须越来越接近这个限制条件,以避免无限递归或错误的结果
- 否则会发生栈内存溢出
事务的隔离级别
- 读未提交
-
- 脏读:一个事物可以读取未提及的数据
- 一个事务可以读取其他事务未提交的数据。这种隔离级别最低,可能导致脏读问题
- 读已提交
-
- 不可重复读: 一个事务范围内两个相同的查询却返回不同数据
- 一个事务只能读取其他事务以及提交的数据。这种隔离级别可以避免脏读问题,但可能导致不可重复读问题
- 可重复读
-
- 幻读:一个事物范围内两个相同的查询却返回了不同数据。但是对应的是插入操作
- 一个事务在执行期间多次读取同一数据时,能够看到相同的数据值。这个隔离级别可以避免脏读和不可重复问题,但可能导致幻读问题
- 串行化
-
- 最高的隔离级别,确保每个事务完全独立执行,避免了脏读、不可重复读和幻读问题。但是串行化级别可能会导致并发性能下降。
HTTP协议与TCP协议的区别
- 层级不同:
-
- TCP:在OSI模型的传输层(第4层)
- HTTP:在OSI模型的应用层(第七层)
- 目的不同:
-
- TCP: 它是一种传输层协议,负责在两个设备之间建立可靠和面向连接的通信通道。它确保数据的完整性、顺序和流量控制
- HTTP: 它是一种应用层协议,用于在万维网上传输超文本文档。它定义了客户端和服务器之间通信的格式和规则
- 功能不同:
-
- TCP: 提供可靠、有序和经过错误检查的数据传输。它处理分段、重组和拥塞控制
- HTTP:定义了客户端和服务器之间请求和响应的结构
- 连接不同:
-
- TCP:在数据传输之前,它在两个设备之间建立连接,并确保数据的可靠传递
- HTTP:它有自己的传输协议也可以用使用TCP作为底层传输协议,在客户端和服务器之间建立连接,允许它们交换HTTP消息
servlet生命周期
- 构造servlet,然后使用init方法将其初始化
-
- 第一次发送请求的时候。执行一次
- loadOnstartUp 默认-1 修改为0可以让init方法在刚开运行时执行一次
- 处理来自客户端的对service方法的所有调用
-
- 客户端发送一次请求,调用service方法一次。可以执行多次
- 从服务中取出servlet,然后使用destroy方法销毁它,最后进行垃圾回收并终止它
-
- tomcat服务器关闭的时候销毁
转发与重定向区别
- 转发可以请求WEB-INF下的内部资源
- 转发地址栏不会发生改变,但是重定向会发生改变
- 转发的状态码是2xx,重定向是3xx
- 转发可以使用request域对象传递数据,但是重定向必须将请求与响应对象传递
- 转发是一次请求和一次响应,重定向是多次请求与响应
- 转发可以携带数据;重定向并不能携带数据。
-
实现在项目中的实现;在项目中起到什么作用?
get请求与post请求区别
- get请求发送数据在url地址传输,post请求在请求体中传输
- get请求没有post请求安全
- get请求可以被书签保存,post不可以
- get请求在浏览器页面后退时无影响,post可能导致表单的重复提交
- get请求页面编码与后台编码相同不会乱码,post可能会乱码
- get请求一次响应一次,post请求是先建立请求,所以get请求是被动请求
Cookie和Session区别
- 存储位置:Cookie是将数据存储在客户端,Session将数据存储在服务端
- 安全性:Cookied不安全,Session安全
- 数据大小:Cookie最大3kB,Session几乎无大小限制
- 存储时间:Cookie可以长期存储,Session默认30分钟
- 服务器性能:Cookie不占服务器资源,Session占用服务器资源
jsp九大内置对象
- request
-
- 一次请求
- session
-
- 一次会话
- application
-
- 代表一次应用
pageContext
-
- 当前页面
- response
-
- 保存请求页面响应信息
- config
-
- 保存当前servlet对应的配置文件
- page
-
- 代码当前的jsp对应的servlet对象
- out
-
- 可以直接在页面输出内容
- exception
-
- 用于存储当前页面的异常信息
双亲委派原则
- 当前ClassLoader首先从自己已经加载的类中查询是否此类已经加载,如果已经加载则直接返回原来已经加载的类。每个类加载器都有自己的加载缓存,当一个类被加载了以后就会放入缓存,等下次加载的时候就可以直接返回了。
- 当前classLoader的缓存中没有找到被加载的类的时候,委托父类加载器去加载,父类加载器采用同样的策略,首先查看自己的缓存,然后委托父类的父类去加载, 一直到bootstrp ClassLoader,引导类加载器,去jre.home下的lib包中找jar包和配置
- 当所有的父类加载器都没有加载的时候,再由当前的类加载器加载,并将其放入它自己的缓存中,以便下次有加载请求的时候直接返回
这样做的好处是什么?
- 避免重复加载。当父类已经加载了该类的时候,就没有必要子ClassLoader再加载一次。
- 为了安全。避免核心类被替换,比如String被替换。
for与foreach的区别
- for循环在遍历集合时使用下标来定位集合中的元素
- foreach循环适用于数组或实现了iterator(迭代器)的集合类。foreach就是使用iterator(迭代器)接口来实现对集合的遍历的
- foreach循环遍历一个集合时,不能改变集合中的元素,比如增删改
git常用指令
- git clone 从远程仓库克隆
- git add 将文件加入暂存区
- git reset 将指定的文件取消暂存
- git commit 将暂存区的文件修改提交到本地仓库
- git rm 删除文件
- git branch 查看分支
- git beanch 创建分支
- git checkout 切换分支
- git push 地址 推送分支
Mybatis与Hibernate区别
- Hibernate是全ORM框架,Mybatis是半ORM框架,其实它连半个都不算因为它是基于JPA接口书写的简易的
- Hibernate实现了JPA接口,操作数据库无需书写sql语句
- Mybatis没有实现JPA接口,需要书写原生的sql语句进行数据库操作
- 因为Mybatis没有实现JPA接口,编写的是原生的Sql语句,所以效率高
- Hibernate内部封装完善,就是因为封装这一步骤导致它的效率没有mybaits高,还有就是它的学习成本高
- Mybatis甚至不用管对象与对象的关系映射
#{}与${}的区别
- 都是对指定的参数进行获取填入
- #{}使用的是占位符的形式进行填值,可以有效的解决sql注入
- ${}使用的是字符串拼接形式 需要注意sql语句的书写规则,不能防止sql注入
mysql与oracle的区别
- mysql开源免费,而oracle使用免费,但有些服务收费
- mysql轻量型数据库,oracle重量型数据库
- mysql在innodb存储引擎的行级锁的情况下才支持事务;orcal完全支持事务。
- mysql默认自动提交事务;orcal需要用户手动提交事务
- orcal对sql语句要求更加严格,而mysql有许多方便的扩展limt更加灵活,对sql语句没有orcal严格
- mysql软件体积小,安装使用简单且易于维护,维护成本低;
- oracle对硬件要求很高、管理维护麻烦一些、操作比较复杂,需要一定技术含量
spring实例化对象的方式有哪些
- 默认使用无参构造方法实例化对象:通过在容器中配置bean的构造函数参数来实例化对象
- 使用静态工厂方法实例化对象:通过在容器中配置bean的静态工厂方法名和方法参数来实例化对象
- 使用工厂的实例方法实例化对象:通过在容器中配置bean的实例工厂方法名和方法参数来实例化对象
- 使用注解实例化对象:使用注解(如@Component、@Service、@Controller、@Repository等)标注一个类并在容器中自动扫描并实例化对象
spring依赖注入的方式有哪些
- 构造器注入
-
- 构造方法注入相比于其他的两种注入方法,它可以注入不可变对象,并且它只会执行一次,也不存在像 Setter 注入那样,被注入的对象随时被修改的情况
- Setter注入
-
- 不能注入不可变对象
- 注入对象可以被修改
- 接口注入 注解注入 == 自动注入
@Autowired与@Resource的区别
- @Autowired 是由spring提供的
-
- 结合使用根据类型与名称进行属性的注入
- 先通过类型匹配进行注入 如果没有相应的spring管理对象的类型 通过名称进行赋值
- @Resource 是由jdk提供的
-
- 按照名称与类型的形式进行属性注入
- 先通过变量的名字进行注入 如果没有与变量名相同的bean 则通过类型进行赋值
jdk动态代理与cglib动态代理的区别
- 动态代理技术是实现在不修改源码的情况下对功能的增加
- JDK创建代理对象效率较高,执行效率较低
- cglib创建代理对象效率较低,执行效率高
- jdk动态代理机制是委托机制,只能对实现接口的类生成代理,通过反射动态实现接口类;
-
- JDK代理:基于接口的动态代理技术
- cglib则使用的继承机制,针对类实现代理,被代理的类和代理类是继承关系
-
- cglib代理:基于父类的动态代理技术