Java面试宝典
JDK,JRE,JVM 三者关系?
- JDK 是 JAVA 程序开发时用的开发工具包,其内部也有 JRE 运行环境 JRE。
- JRE 是 JAVA 程序运行时需要的运行环境,就是说如果你光是运行 JAVA 程序而不是去搞开发的话,只安装 JRE 就能运行已经存在的 JAVA 程序了。
- JDk、JRE 内部都包含 JAVA 虚拟机 JVM,JAVA 虚拟机内部包含许多应用程序的类的解释器和类加载器等等。
面向过程和面向对象的区别?
- 两者都是软件开发思想,先有面向过程,后有面向对象。在大型项目中,针对面向过程的不足推出了面向对象开发思想。
- 编程思路不同:面向过程以实现功能的函数开发为主,而面向对象要首先抽象出类、属性及其方法,然后通过实例化类、执行方法来完成功能。
- 封装性:都具有封装性,但是面向过程是封装的是功能,而面向对象封装的是数据和功能。面向对象具有继承性和多态性, 而面向过程没有继承性和多态 性,所以面向对象优势是明显。
Java 有哪些基本数据类型?
定义:Java 语言是强类型语言,对于每一种数据都定义了明确的具体的数据类型,在内存中分配了不同大小的内存空间。
- 数值型:整数类型 (byte,short,int,long)
- 数值型:浮点类型 (float,double)
- 字符型 (char)
- 布尔型 (boolean)
什么 Java 注释?
- 定义:用于解释说明程序的
- 文字分类:
- 单行注释:格式: // 注释文字多
- 行注释:格式: /* 注释文字 */
- 文档注释:格式:/** 注释文字 */
- 作用:在程序中,尤其是复杂的程序中,适当地加入注释可以增加程序的可读性,有利于程序的修改、调试和交流。注释的内容在程序编译的时候会被忽视,不会产生目标代码,注释的部分不会对程序的执行结果产生任何影响。
注意事项:多行和文档注释都不能嵌套使用。
数组和集合有什么区别?
- 数组的长度是固定的,集合的长度是可变的。
- 数组中存储的是一种类型的元素,可以存储任意类型数据。
- 集合存储的都是引用数据类型,如果想存储基本类型数据需要存储对应的包装类型。
final 有什么用?
用于修饰类、属性和方法;
- 被 final 修饰的类不可以被继承
- 被 final 修饰的方法不可以被重写
- 被 final 修饰的变量不可以被改变,被 final 修饰不可变的是变量的引用,而不是引用指向的内容,引用指向的内容是可以改变的
final fially finalize 区别
- final 可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表示该变量是一个常量不能被重新赋值。
- finally 一般作用在 try-catch 代码块中,在处理异常的时候,通常我们将一定要执行的代码方法放在 finally 代码块中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码。
- finalize 是一个方法,属于 Object 类的一个方法,而 Object 类是所有类的父类,该方法一般由垃圾回收器来调用,当我们调用 System.gc () 方法的时候,由垃圾回收器调用 finalize (),回收垃圾,一个对象是否可回收的最后判断。
面向对象三大特性
- 封装:隐藏对象的属性和实现细节,仅对外提供公共访问方式,将变化隔离,便于使用,提高复用性和安全性。
- 继承:继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承可以提高代码复用性。继承是多态的前提。
- 所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。
== 和 equals 的区别是什么?
- == : 它的作用是判断两个对象的地址是不是相等。即判断两个对象是不是同一个对象。(基本数据类型 == 比较的是值,引用数据类型 == 比较的是内存地址)
- equals () : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:
- 情况 1:类没有覆盖 equals () 方法。则通过 equals () 比较该类的两个对象时,等价于通过 “==” 比较这两个对象。
- 情况 2:类覆盖了 equals () 方法。一般,我们都覆盖 equals () 方法来两个对象的内容相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)。
String 类的常用方法都有那些?
-
indexOf()
:返回指定字符的索引。 -
charAt()
:返回指定索引处的字符。 -
replace()
:字符串替换。 -
trim()
:去除字符串两端空白。 -
split()
:分割字符串,返回一个分割后的字符串数组。 -
getBytes()
:返回字符串的 byte 类型数组。 -
length()
:返回字符串长度。 -
toLowerCase()
:将字符串转成小写字母。 -
toUpperCase()
:将字符串转成大写字符。 -
substring()
:截取字符串。 -
equals()
:字符串比较。
String 和 StringBuffer、StringBuilder 的区别是什么?String 为什么是不可变的?
StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
对于三者使用的总结:如果要操作少量的数据用 = String,单线程操作字符串缓冲区 下操作大量数据 = StringBuilder,多线程操作字符串缓冲区 下操作大量数据 = StringBuffer
ChatGPT 的回答:
自动装箱与拆箱
- 装箱:将基本类型用它们对应的引用类型包装起来;
- 拆箱:将包装类型转换为基本数据类型;
int 和 Integer 有什么区别?
Java 是一个近乎纯洁的面向对象编程语言,但是为了编程的方便还是引入了基本数据类型,但是为了能够将这些基本数据类型当成对象操作,Java 为每一个基本数据类型都引入了对应的包装类型(wrapper class),int 的包装类就是 Integer,从 Java 5 开始引入了自动装箱 / 拆箱机制,使得二者可以相互转换。
Java 为每个原始类型提供了哪些包装类型?
- 原始类型:
boolean
,char
,byte
,short
,int
,long
,float
,double
- 包装类型:
Boolean
,Character
,Byte
,Short
,Integer
,Long
,Float
,Double
ArrayList、LinkedList、Vector 的区别?
- ArrayList,Vector 底层是由数组实现,LinkedList 底层是由双线链表实现,从底层的实现可以得出它们的性能问题。
- ArrayList,Vector 插入速度相对较慢,查询速度相对较快,而 LinkedList 插入速度较快,而查询速度较慢。再者由于 Vevtor 使用了线程安全锁,所以 ArrayList 的运行效率高于 Vector。
HashMap 和 Hashtable 的区别?
- 线程是否安全: HashMap 是非线程安全的,HashTable 是线程安全的;HashTable 内部的方法基本都经过 synchronized 修饰。(如果你要保证线程安全的话就使用 ConcurrentHashMap 吧!);
- 效率: 因为线程安全的问题,HashMap 要比 HashTable 效率高一点。另外,HashTable 基本被淘汰,不要在代码中使用它;
- 对 Null key 和 Null value 的支持: HashMap 中,null 可以作为键,这样的键只有一个,可以有一个或多个键所对应的值为 null。但是在 HashTable 中 put 进的键值只要有一个
- 初始容量大小和每次扩充容量大小的不同 : ①创建时如果不指定容量初始值,Hashtable 默认的初始大小为 11,之后每次扩充,容量变为原来的 2n+1。HashMap 默认的初始化大小为 16。之后每次扩充,容量变为原来的 2 倍。②创建时如果给定了 容量初始值,那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为 2 的幂次方大小。也就是说 HashMap 总是使用 2 的幂作为哈希表的大小
- 底层数据结构: JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)时,将链
表转化为红黑树,以减少搜索时间。Hashtable 没有这样的机制。
Synchronized 用过吗,其原理是什么?
- Synchronized 是由 JVM 实现的一种实现互斥同步的一种方式,如果你查看被 Synchronized 修饰过的程序块编译后的字节码,会发现被 Synchronized 修饰过的程 序块,在编译前后被编译器生成了 monitorenter 和 monitorexit 两个字节码指令。
- 这两个指令是什么意思呢?当虚拟机执行到 monitorenter 指令时,首先要尝试获取对象的锁:如果这个对象没有被其他线程锁定,或者当前线程已经拥有了这个对象的锁,那么就将锁的计数器加 1。当执行 monitorexit 指令时,需要释放对象的锁,将锁计数器减 1。
- 如果获取对象的锁失败,当前线程就会被阻塞等待,直到对象的锁被另外一个线程释放为止。在 Java 中,使用 synchronized 关键字来实现同步操作,它通过在对象头部设置标记来达到获取锁和释放锁的目的。
为什么说 Synchronized 是非公平锁?
非公平锁的主要表现在获取锁的行为上,它并不按照申请锁的时间前后给等待线程分配锁,而是每当锁被释放后,任何一个线程都有机会竞争到锁。这样做的目的是为了提高执行性能,但缺点是可能会产生线程饥饿现象。
为什么说 Synchronized 是一个悲观锁?乐观锁的实现原理又是什么?什么是 CAS,它有什么特性?
- Synchronized 显然是一个悲观锁,因为它的并发策略是悲观的:不管是否会产生竞争,任何的数据操作都必须要加锁、用户态和核心态转换、维护锁计数器和检查是否有被阻塞的线程需要被唤醒等操作。
- 随着硬件指令集的发展,我们可以使用基于冲突检测的乐观并发策略。先进行操作,如果没有其他线程征用数据,那操作就成功了;如果共享数据被征用,产生了冲突,那就再进行其他的补偿措施。这种乐观的并发策略的许多实现不需要线程挂起,所以被称为非阻塞同步。
- 乐观锁的核心算法是 CAS(Compare and Swap,比较并交换),它涉及到三个操作数:内存值、期望值、新值。当且仅当期望值和内存值相等时才将内存值修改为新值。这样处理的逻辑是,首先检查某块内存的值是否跟之前我读取时的一样。如果不一样,则表示期间此内存值已经被其他线程更改过,舍弃本次操作;否则说明期间没有其他线程对此内存值操作,可以把新值设置给此块内存。
- CAS 具有原子性,它的原子性由 CPU 硬件指令实现保证,即使用 JNI 调用 Native 方法调用由 C++ 编写的硬件级别指令,JDK 中提供了 Unsafe 类执行这些操作
乐观锁一定就是好的吗?
乐观锁避免了悲观锁独占对象的现象,同时也提高了并发性能,但它也有缺点:
- 乐观锁只能保证一个共享变量的原子操作。如果多一个或几个变量,乐观锁将变得力不从心,但互斥锁能轻易解决,不管对象数量多少及对象颗粒度大小。
- 长时间自旋可能导致开销大。假如 CAS 长时间不成功而一直自旋,会给 CPU 带来很大的开销。
- ABA 问题。CAS 的核心思想是通过比对内存值与预期值是否一样而判断内存值是否被改过,但这个判断逻辑不严谨。假如内存值原来是 A,后来被一条线程改为 B,最后又被改成了 A,则 CAS 认为此内存值并没有发生改变,但实际上是有被其他线程改过的。这种情况对依赖过程值的场景的运算结果影响很大。解决的思路是引入版本号,每次变量更新都把版本号加一。
JDBC 技术
什么是 JDBC,在什么时候会用到它?
JDBC 的全称是 Java Database Connection,也就是 Java 数据库连接,我们可以用它来操作关系型数据库。JDBC 接口及相关类在 java.sql 包和 javax.sql 包里。我们可以用它来连接数据库,执行 SQL 查询,存储过程,并处理返回的结果。JDBC 接口让 Java 程序和 JDBC 驱动实现了松耦合,使得切换不同的数据库变得更加简单。
JDBC 访问数据库的基本步骤是什么?
- 加载(注册)数据库驱动(到 JVM)
- 建立(获取)数据库连接。
- 创建(获取)数据库操作对象。
- 定义操作的 SQL 语句。
- 执行数据库操作。
- 获取并操作结果集。
- 关闭对象,回收数据库资源(关闭结果集–> 关闭数据库操作对象–> 关闭连接)
execute, executeQuery, executeUpdate 的区别是什么?
- Statement 的 execute (String query) 方法用于执行任意的 SQL 查询。如果查询的结果是一个 ResultSet,则该方法返回 true。如果结果不是 ResultSet,例如 insert 或者 update 查询,则该方法返回 false。
- Statement 的 executeQuery (String query) 接口用来执行 select 查询,并且返回 ResultSet。即使查询不到记录返回的 ResultSet 也不会为 null。我们通常使用 executeQuery 来执行查询语句,这样的话如果传进来的是 insert 或者 update 语句的话,它会抛出错误信息为 “executeQuery method can not be used for update” 的 java.util.SQLException。
- Statement 的 executeUpdate (String query) 方法用于执行 insert、update 或 delete(DML)语句。
- 只有当你不确定是什么语句的时候才应该使用 execute () 方法,否则应该使用 executeQuery 或者 executeUpdate 方法。
JDBC 的 PreparedStatement 是什么?
PreparedStatement 对象代表的是一个预编译的 SQL 语句。通过它提供的 setter 方法,可以传入查询的变量。由于 PreparedStatement 是预编译的,可以高效地多次执行对应的 SQL 语句。PreparedStatement 还自动对特殊字符进行转义,避免了 SQL 注入攻击,因此应该尽可能地使用它。
相对于 Statement,PreparedStatement 的优点是什么?
- PreparedStatement 有助于防止 SQL 注入,因为它会自动对特殊字符转义。
- PreparedStatement 可以用来进行动态查询。
- PreparedStatement 执行更快。尤其当你重用它或者使用它的拼量查询接口执行多条语句时。
- 使用 PreparedStatement 的 setter 方法更容易编写面向对象的代码,而使用 Statement 则需要拼接字符串来生成查询语句。如果参数过多,字符串拼接看起来会很丑陋,而且容易出错。
JDBC 的 ResultSet 是什么?
在查询数据库后,会返回一个 ResultSet 对象,它就像是查询结果集的一张数据表。ResultSet 对象维护了一个游标,指向当前的数据行。开始时,游标指向第一行。如果调用 ResultSet 的 next () 方法,游标会下移一行,如果没有更多的数据,next () 方法会返回 false。可以在 for 循环中使用它来遍历数据集。
java.util.Date 和 java.sql.Date 有什么区别?
java.util.Date 包含日期和时间信息,而 java.sql.Date 只包含日期信息,没有具体的时间信息。如果你想在数据库中存储时间信息,可以考虑使用 Timestamp 或者 DateTime 字段。
说说事务的概念,在 JDBC 编程中处理事务的步骤?
事务是作为单个逻辑工作单元执行的一系列操作。一个逻辑工作单元必须有四个属性,称为原子性、一致性、隔离性和持久性 (ACID) 属性,只有这样才能成为一个事务。JDBC 处理事务有如下操作:
- conn.setAutoCommit (false); 设置提交方式为手动提交。
- conn.commit (); 提交事务。
- conn.rollback (); 回滚操作。
提交与回滚只选择一个执行。正常情况下,提交事务。如果出现异常,则回滚。
数据库连接池的原理。为什么要使用连接池?
- 数据库连接是一种关键的、有限的、昂贵的资源。对数据库连接的管理能显著影响整个应用程序的伸缩性和健壮性,进而影响程序的性能指标。数据库连接池正是为解决这个问题而提出的。
- 数据库连接池负责分配、管理和释放数据库连接。它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。同时,它也会释放空闲时间超过最大空闲时间的数据库连接,以避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术可以明显提高对数据库操作的性能。
- 数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,这些数据库连接的数量是由最小数据库连接数来设定的。无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量。连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数。当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中。
什么是 JDBC 的最佳实践?
- 数据库资源是非常昂贵的,用完了应该尽快关闭它。Connection、Statement、ResultSet 等 JDBC 对象都有 close 方法,调用它就好了。
- 养成在代码中显式关闭掉 ResultSet、Statement、Connection 的习惯。如果你用的是连接池的话,连接用完后会放回池里,但是没有关闭的 ResultSet 和 Statement 就会造成资源泄漏。
- 在 finally 块中关闭资源,保证即便出了异常也能正常关闭。
- 尽量使用 PreparedStatement 而不是 Statement,以避免 SQL 注入,同时还能通过预编译和缓存机制提升执行的效率。
- 数据库隔离级别越高性能越差,确保你的数据库连接设置的隔离级别是最优的。
- 如果在 WEB 程序中创建数据库连接,最好通过 JNDI 使用 JDBC 的数据源,这样可以对连接进行重用。
MySQL 数据库技术
数据库 MySQL 分页时用的语句
使用 limit
关键字。Select * from 表名 where 条件 limit 开始位置,结束位置
。通过动态的改变开始和结束位置的值来实现分页。
根据你以往的经验简单叙述一下 MySQL 的优化
- 尽可能使用更小的整数类型(
mediumint
就比int
更合适) - 如果想要清空表的所有记录,建议用
truncate table tablename
而不是delete from tablename
- 尽可能的定义字段为
not null
,除非这个字段需要null
。 - 避免出现
SELECT * FROM table
语句,要明确查出的字段。 - 小心使用
IN
和OR
,需要注意IN
集合中的数据量。建议集合中的数据不超过 200 个。
有两张表;请用 SQL 查询,所有的客户订单日期最新的前五条订单记录。
客户信息表 (c_CUSTOM) 有以下字段:id、name、mobile
客户订单表 (C_ORDER) 有以下字段: id, custom_id, commodity, count, order_date.
1 | SELECT * FROM c_order ORDER BY order_date DESC LIMIT 0,5; |
数据库设计中,一对多如何处理?
数据库外键关系表示的其实是一种一对多关系,所以处理一对多时可以使用外键。
数据库设计中,多对多一般如何处理?
引入中间表,把一个多对多表示为两个一对多。
MySQL 数据库中,常用的数据类型
类型名称 | 说明 |
int(Integer) | 整数类型 |
double | 小数类型 |
decimal(m,d) | 指定整数位与小数位长度的小数类型 |
date | 日期类型,格式为 yyyy-MM-dd,包含年月日,不包含时分秒 |
datetime | 日期类型,格式为 yyyy-MM-dd HH:mm:ss,包含年月日时分秒 |
timestamp | 日期类型,时间戳 |
varchar(M) | 文本类型,M 为 0~65535 之间的整数 |
Student 学生表(学号,姓名、性别、年龄、组织部门),Course 课程表(编号,课程名称),Sc 选课表(学号,课程编号,成绩)
- 写一个 SQL 语句,查询选修了计算机原理的学生学号和姓名
1 | select 学号,姓名 from Student where 学号 in(select 学号 from Sc where 课程编号 in(Select 课程编号 from Course where 课程名称 = ‘计算机原理’)) |
- 写一个 SQL 语句,查询 “周星驰” 同学选修了的课程名字
1 | select 课程名称 from Course where 编号 in (select Sc.课程编号 from Student,Sc where Student.姓名=’周星驰’ and Student.学号 = Sc.学号) |
表结构说明
下面是学生表的(Student)的结构说明
字段名称 | 字段解释 | 字段类型 | 字段长度 | 约束 |
s_id | 学号 | 字符 | 10 | PK |
s_name | 学生姓名 | 字符 | 50 | Not Null |
s_age | 学生年龄 | 数值 | 3 | Not Null |
s_sex | 学生性别 | 字符 | 1 | Not Null |
下面是教师表(Teacher )的结构说明
字段名称 | 字段解释 | 字段类型 | 字段长度 | 约束 |
t_id | 教师编号 | 字符 | 10 | PK |
t_name | 教师姓名 | 字符 | 50 | Not Null |
下面是课程表(Course)的结构说明
字段名称 | 字段解释 | 字段类型 | 字段长度 | 约束 |
c_id | 课程编号 | 字符 | 10 | PK |
c_name | 课程名字 | 字符 | 50 | Not Null |
t_id | 教师编号 | 字符 | 10 | Not Null |
下面是成绩表(SC)的结构说明
字段名称 | 字段解释 | 字段类型 | 字段长度 | 约束 |
s_id | 学号 | 字符 | 10 | PK |
c_id | 课程编号 | 字符 | 10 | Not Null |
score | 成绩 | 数值 | 3 | Not Null |
查询 “001” 课程比 “002” 课程成绩高的所有学生的学号;
1 | select a.s_id from (select s_id,score from SC where C_ID='001') a,(select s_id,scorefrom SC where C_ID='002') b where a.score>b.score and a.s_id=b.s_id; |
查询平均成绩大于 60 分的同学的学号和平均成绩;
1 | select S_ID,avg(score) from sc group by S_ID having avg(score) >60; |
查询所有同学的学号、姓名、选课数、总成绩;
1 | select Student.S_ID,Student.Sname,count(SC.C_ID),sum(score) from Student left Outer join SC on Student.S_ID=SC.S_ID group by Student.S_ID,Sname |
查询姓 “李” 的老师的个数;
1 | select count(distinct(Tname)) from Teacher where Tname like '李%'; |
查询所有课程成绩小于 60 分的同学的学号、姓名;
1 | select S_ID,Sname from Student where S_ID not in (select S.S_ID from Student AS S,SC where S.S_ID=SC.S_ID and score>60); |
查询至少有一门课与学号为 “1001” 的同学所学相同的同学的学号和姓名;
1 | select distinct S_ID,Sname from Student,SC where Student.S_ID=SC.S_ID and SC.C_ID in (select C_ID from SC where S_ID='1001'); |
JavaScript 语言和 jQuery 技术
JS 中如何将页面重定向到另一个页面?
- 使用 location.href:window.location.href =”https://www.baidu.com/”
- 使用 location.replace:window.location.replace (“https://www.baidu.com/;”);
undefined,null 和 undeclared 有什么区别?
- undefined 表示” 缺少值”,就是此处应该有一个值,但是还没有定义,转为数值时为 NaN。典型用法是:变量被声明了,但没有赋值时,就等于 undefined。调用函数时,应该提供的参数没有提供,该参数等于 undefined。对象没有赋值的属性,该属性的值为 undefined。函数没有返回值时,默认返回 undefined。
- null 表示” 没有对象”,即该处不应该有值,转为数值时为 0。典型用法是:作为函数的参数,表示该函数的参数不是对象。作为对象原型链的终点。
- undeclared:js 语法错误,没有申明直接使用,js 无法找到对应的上下文。
如何在 JavaScript 中每 x 秒调用一个函数?
1 | setInterval(function (){ alert("Hello"); }, 3000); |
JS 中 == 和 === 区别是什么?
- 对于 string,number 等基础类型,== 和 === 有区别:不同类型间比较,== 之比较 “转化成同一类型后的值” 看 “值” 是否相等,=== 如 果类型不同,其结果就是不等。同类型比较,直接进行 “值” 比较,两者结果一样。
- 对于 Array,Object 等高级类型,== 和 === 没有区别,进行 “指针地址” 比较。
JavaScript 内置可用类型
string,number,boolean,null 和 undefined,object,symbol(ES6 新语法)
jQuery 库中的 $() 是什么?
$() 函数是 jQuery () 函数的别称。$() 函数用于将任何对象包裹成 jQuery 对象,接着你就被允许调用定义在 jQuery 对象上的多个不同方法。你可以将一个选择器字符串传入 $() 函数,它会返回一个包含所有匹配的 DOM 元素数组的 jQuery 对象。
jQuery 有几种选择器?
- 基本选择器:
#id
,class
,element
,*
; - 层次选择器:
parent > child
,prev + next
,prev ~ siblings
- 基本过滤器选择器:
:first
,:last
,:not
,:even
- 表单选择器:
:input
,:text
,:password
,:radio
,:checkbox
,:submit
等; - 表单过滤器选择器:
:enabled
,:disabled
,:checked
,:selected
jQuery 中 $.get () 提交和 $.post () 提交有区别吗?
相同点:都是异步请求的方式来获取服务端的数据;
异同点:
- 请求方式不同:$.get () 方法使用 GET 方法来进行异步请求的。$.post () 方法使用 POST 方法来进行异步请求的。
- 参数传递方式不同:get 请求会将参数跟在 URL 后进行传递,而 POST 请求则是作为 HTTP 消息的实体内容发送给 Web 服务器的,这种传递是对用户不可见的。
- 数据传输大小不同:get 方式传输的数据大小不能超过 2KB 而 POST 要大的多
- 安全问题: GET 方式请求的数据会被浏览器缓存起来,因此有安全问题。
window.onload () 函数和 jQuery 中的 document.ready () 有什么区别?
- 执行时间:window.onload 必须等到页面内包括图片的所有元素加载完毕后才能执行。$(document).ready () 是 DOM 结构绘制完毕后就执行,不必等到加载完毕。$(document).ready () 在 window.onload 之前执行。
- 简化写法:window.onload 没有简化写法。$(document).ready (function (){}) 可以简写成 $(function (){});
- 出现地方不同:window.onload 是 js 标准,可出现在任何 js 脚本中。$(document).ready 只有在 jq 库中出现。
什么是 CDN?哪些是流行的 jQuery CDN?使用 CDN 有什么好处?
内容传送网络或内容分发网络(CDN)是部署在因特网上的多个数据中心的大型分布式服务器系统。CDN 的目标是为具有高可用性和高性能的最终用户提供内容。
有 3 个流行的 jQuery CDN:谷歌,微软 jQuery。
使用 CDN 的优势:它减少了服务器的负载。它节省了带宽。jQuery 框架将从这些 CDN 加载更快。最重要的好处是,如果用户访
问过使用任何这些 CDN 的 jQuery 框架的任何站点,它将被缓存
如何从 CDN 加载 jQuery?
下面是从 3 个 CDN 加载 jQuery 的代码。
- 从 Google CDN 加载 jQuery Framework 的代码
1 | <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> |
- 从 Microsoft CDN 加载 jQuery Framework 的代码
1 | <script type="text/javascript" src="http://ajax.microsoft.com/ajax/jquery/jquery-1.9.1.min.js"> </script> |
- 从 jQuery 站点加载 jQuery Framework 的代码(EdgeCast CDN)
1 | <script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.min.js"></script> |
JSP 技术
说一说 Servlet 的生命周期?
- Servlet 有良好的生存期的定义,包括加载和实例化、初始化、处理请求以及服务结束。这个生存期由 javax.servlet.Servlet 接口的 init (),service () 和 destroy 方法表达。
- Servlet 被服务器实例化后,容器运行其 init 方法,请求到达时运行其 service 方法,service 方法自动派遣运行与请求对应的 do***() 方法(doGet,doPost)等,当服务器决定将实例销毁的时候调用其 destroy 方法。
- web 容器加载 servlet,生命周期开始。通过调用 servlet 的 init () 方法进行 servlet 的初始化。通过调用 service () 方法实现,根据请求的不同调用不同的 do***() 方法。结束服务,web 容器调用 servlet 的 destroy () 方法。
JSP 和 Servlet 的区别、共同点、各自应用的范围?
JSP 是 Servlet 技术的扩展,本质上就是 Servlet 的简易方式。JSP 编译后是 “类 servlet”。Servlet 和 JSP 最主要的不同点在于:Servlet 的应用逻辑是在 Java 文件中,并且完全从表示层中的 HTML 里分离开来。而 JSP 的情况是 Java 和 HTML 可以组合成一个扩展名为.jsp 的文件。JSP 侧重于视图,Servlet 主要用于控制逻辑。在 struts 框架中,JSP 位于 MVC 设计模式的视图层,而 Servlet 位于 控制层.
Servlet API 中 forward () 与 redirect () 的区别?
- 从地址栏显示来说
forward 是服务器请求资源,服务器直接访问目标地址的 URL, 把那个 URL 的响应内容读取过来,然后把这些内容再发给浏览器。浏览器根本不知道服务器发送的内容从哪里来的,所以它的地址栏还是原来的地址. redirect 是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址。所以地址栏显示的是新的 URL. 所以 redirect 等于客 户端向服务器端发出两次 request,同时也接受两次 response。
- 从数据共享来说
forward: 转发页面和转发到的页面可以共享 request 里面的数据.redirect: 不能共享数据.redirect 不仅可以重定向到当前应用程序的其他资源,还可以重定向到同一个站点上的其他应用程序中的资源,甚至是使用绝对 URL 重定向到其他站点的资源.forward 方法 只能在同一个 Web 应用程序内的资源之间转发请求.forward 是服务器内部的一种操作.redirect 是服务器通知客户端,让客户端重新发起请求。所以,你可以说 redirect 是一种间接的请求,但是你不能说” 一个请求是属于 forward 还是 redirect “。
- 从运用地方来说
forward: 一般用于用户登陆的时候,根据角色转发到相应的模块. redirect: 一般用于用户注销登陆时返回主页面和跳转到其它的网站等。
- 从效率来说
forward: 高。
redirect: 低。
request.getAttribute () 和 request.getParameter () 有何区别?
- request.getParameter () 取得是通过容器的实现来取得通过类似 post,get 等方式传入的数据。
- getAttribute 是返回对象,getParameter 返回字符串。
- getAttribute () 一向是和 setAttribute () 一起使用的,只有先用 setAttribute () 设置之后,才能够通过 getAttribute () 来获得值,它们传递的是 Object 类型的数据。而且必须在同一个 request 对象中使用才有效。, 而 getParameter () 是接收表单的 get 或者 post 提交过来的参数。
MVC 的各个部分都有那些技术来实现?如何实现?
MVC 是 Model-View-Controller 的简写。Model 代表的是应用的业务逻辑(通过 JavaBean,EJB 组件实现),View 是应用的表示面(由 JSP 页面产生),Controller 是提供应用的处理过程控制(一般是一个 Servlet),通过这种设计模型把应用逻辑,处理过程和显示逻辑分成不同的组件实现。这些组件可以进行交互和重用。
JSP 有哪些内置对象?作用分别是什么?
- request 用户端请求,此请求会包含来自 GET/POST 请求的参数;
- response 网页传回用户端的回应;
- pageContext 网页的属性是在这里管理;
- session 与请求有关的会话期;
- application 封装服务器运行环境的对象;
- out 输出服务器响应的输出流对象;
- config Web 应用的配置对象;
- page JSP 网页本身;
- exception 封装页面抛出异常的对象。
说一下 JSP 的 4 种作用域?
- page:代表与一个页面相关的对象和属性。
- request:代表与客户端发出的一个请求相关的对象和属性。一个请求可能跨越多个页面,涉及多个 Web 组件;需要在页面显示的临时数据可以置于此作用域。
- session:代表与某个用户与服务器建立的一次会话相关的对象和属性。跟某个用户相关的数据应该放在用户自己的 Session 中。
- application:代表与整个 Web 应用程序相关的对象和属性,它实质上是跨越整个 Web 应用程序,包括多个页面、请求和会话的一个全局作用域。
Session 和 Cookie 有什么区别?
- 存储位置不同:Session 存储在服务器端;Cookie 存储在浏览器端。
- 安全性不同:Cookie 安全性一般,在浏览器存储,可以被伪造和修改。
- 容量和个数限制:Cookie 有容量限制,每个站点下的 Cookie 也有个数限制。
- 存储的多样性:Session 可以存储在 Redis 中、数据库中、应用程序中;而 Cookie 只能存储在浏览器中。
说一下 Session 的工作原理?
Session 的工作原理是客户端登录完成之后,服务器会创建对应的 Session,Session 创建完之后,会把 Session 的 ID 发送给客户端,客户端再存储到浏览器中。这样客户端每次访问服务器时,都会带着 Session ID,服务器拿到 Session ID 之后,在内存找到与之对应的 Session 这样就可以正常工作了。
JSP 三大指令是什么?
- Page :指令是针对当前页面的指令;
- Include :用于指定如何包含另一个页面;
- Taglib :用于定义和指定自定义标签。
http 的响应码 200,404,302,500 表示的含义分别是?
- 200 – 确定。客户端请求已成功
- 404 – 未找到文件或目录
- 302 – 临时移动转移,请求的内容已临时移动新的位置
- 500 – 服务器内部错误
如何解决表单提交的中文乱码问题?
- 设置页面编码,若是 jsp 页面,需编写代码:
1 | <%@page language="java" pageEncoding="UTF-8" contentType="text/html;charset=UTF-8" %> |
若是 html 页面,在网页头部(
< /head> )中添加下面这段代码:1 | < meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> |
- 将 form 表单提交方式变为 post 方式,即添加
method="post"
;在 Servlet 类中编写代码request.setCharacterEncoding("UTF-8")
,而且必须写在第一行。 - 如果是 get 请求,在 Servlet 类中编写代码
byte [] bytes = str.getBytes("iso-8859-1")
;String cstr = newString(bytes,"utf-8")
; 或者直接修改 Tomcat 服务器配置文件 server.xml 增加内容:URIEncoding="utf-8"
你的项目中使用过哪些 JSTL 标签?
项目中主要使用了 JSTL 的核心标签库,包括 < c:if>
、< c:choose>
、< c: when>
、< c: otherwise>
、< c:forEach>
等,主要用于构造循环和分支结构以控制显示逻辑。虽然 JSTL 标签库提供了 core
、sql
、fmt
、xml
等标签库,但是实际开发中建议只使 用核心标签库(core),而且最好只使用分支和循环标签并辅以表达式语言(EL),这样才能真正做到数据显示和业务逻辑的分离,这才是最佳实践。
怎么防止重复提交?
- 禁掉提交按钮。表单提交后使用 Javascript 使提交按钮 disable。这种方法防止心急的用户多次点击按钮。但有个问题,如果客户端把 Javascript 给禁止掉,这种方法就无效了。
- Post/Redirect/Get 模式。在提交后执行页面重定向,这就是所谓的 Post-Redirect-Get (PRG) 模式。简言之,当用户提交了表单后,你去执行一个客户端的重定向,转到提交成功信息页面。这能避免用户按 F5 导致的重复提交,而其也不会出现浏览器表单重复提交的警告,也能消除按浏览器前进和后退按导致的同样问题。
- 在 session 中存放一个特殊标志。当表单页面被请求时,生成一个特殊的字符标志串,存在 session 中,同时放在表单的隐藏域里。接受处理表单数据时,检查标识字串是否存在,并立即从 session 中删除它,然后正常处理数据。如果发现表单提交里没有有效的标志串,这说明表单已经被提交过了,忽略这次提交。
- 在数据库里添加约束。在数据库里添加唯一约束或创建唯一索引,防止出现重复数据。这是最有效的防止重复提交数据的方法。
Request 对象的主要方法有哪些?
方法 | 解释 |
setAttribute(String name,Object) | 设置名字为 name 的 request 的参数值 |
getAttribute(String name) | 返回由 name 指定的属性值 |
getAttributeNames() | 返回 request 对象所有属性的名字集合,结果是一个枚举的实例 |
getCookies() | 返回客户端的所有 |
getCharacterEncoding() | 返回请求中的字符编码方式 = getContentLength () :返回请求的 Body 的长度 |
getParameter(String name) | 获得客户端传送给服务器端的有 name 指定的参数值 |
getRequestURI() | 获取发出请求字符串的客户端地址 |
getRemoteAddr() | 获取客户端的 IP 地址 |
getRemoteHost() | 获取客户端的名字 |
getServletPath() | 获取客户端所请求的脚本文件的路径 |
getServerPort() | 获取服务器的端口号 |
removeAttribute(String name) | 删除请求中的一个属性 |
JSP 中动态 include 和静态 include 的区别?
- 静态 include:语法:
1 | <%@ include file="文件名" %> |
相当于复制,编辑时将对应的文件包含进来,当内容变化时,不会再一次对其编译,不易维护。
- 动态 include:语法:
能够自动检查被包含文件,当客户端对 JSP 文件进行请求时,会重新将对应的文件包含进来,进行实时的更新。
什么情况下调用 doGet () 和 doPost ()?
默认情况是调用 doGet () 方法,JSP 页面中的 Form 表单的 method 属性设置为 post 的时候,调用的为 doPost () 方法;为 get 的时候,调用 deGet () 方法。
get 和 post 的区别?
- get 是用来从服务器上获取数据,而 post 是用来向服务器传递数据;
- get 将表单中数据按照 variable=value 的形式,添加到 action 所指向的 URL 后面,并且两者用”?” 连接,变量之间用”&” 连接而 post 是将表单中的数据放在 form 的数据体中,按照变量与值对应的方式,传递到 action 所指定的 URL;
- get 是不安全的,因为在传输过程中,数据是被放在请求的 URL 中;而 post 的所有操作对用户来说都是不可见的;
- get 传输的数据量小,这主要应为受 url 长度限制;而 post 可以传输大量的数据,所有上传文件只能用 post 提交;
- get 限制 form 表单的数据集必须为 ASCII 字符;而 post 支持整个 IS01 0646 字符集;
- get 是 form 表单的默认方法。
Spring 框架
什么是 Spring 框架?
Spring 是一个开放源代码的设计层面框架,它解决的是业务逻辑层和其他各层的松耦合问题,是一个分层的 JavaEE 一站式轻量级开源框架。
Spring 的作用?
方便解耦,简化开发,AOP 编程支持,声明式事务支持,集成 Junit 更加方便的进行分层测试,方便集成各种优秀框架。
什么是 IOC?
控制反转,把创建对象的权利交给 Spring。
什么是 DI?
属性的依赖注入,Spring 在通过 IOC 创建对象的时候,如果对象还有属性,就一并给赋值进去 DI 是在 IOC 的基础上进行对象的属性注入。
依赖注入的三种实现方式?
- 构造器注入
- Setter 方法注入
- 接口注入
解释 Spring 支持的几种 bean 的作用域?
-
singleton
: bean 在每个 Spring IOC 容器中只有一个实例。 -
prototype
:一个 bean 的定义可以有多个实例。 -
request
:每次 http 请求都会创建一个 bean,该作用域仅在基于 web 的 Spring ApplicationContext 情形下有效。 -
session
:在一个 HTTP Session 中,一个 bean 定义对应一个实例。该作用域仅在基于 web 的 Spring ApplicationContext 情形下有效。 -
global-session
:在一个全局的 HTTP Session 中,一个 bean 定义对应一个实例。该作用域仅在基于 web 的 SpringApplicationContext 情形下有效。缺省的 Spring bean 的作用域是 Singleton。
Spring 支持的事务管理类型?
- 编程式事务管理:这意味你通过编程的方式管理事务,给你带来极大的灵活性,但是难维护。
- 声明式事务管理:这意味着你可以将业务代码和事务管理分离,你只需用注解和 XML 配置来管理事务。
你更倾向用那种事务管理类型?
大多数 Spring 框架的用户选择声明式事务管理,因为它对应用代码的影响最小,因此更符合一个无侵入的轻量级容器的思想。声明式事务管理要优于编程式事务管理,虽然比编程式事务管理(这种方式允许你通过代码控制事务)少了一点灵活性。
解释 AOP
在软件业,AOP 为 Aspect Oriented Programming 的缩写,意味:面向切面编程。通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,AOP 是 OOP 的延续。将一些共性的内容进行抽取,在需要用到的地方,以动态代理的方式进行插入。在不修 改源码的基础上,还能对源码进行前后增强。
什么是通知?有哪五种类型的通知?
通知是个在方法执行前或执行后要做的动作,实际上是程序执行时要通过 Spring AOP 框架触发的代码段。
Spring 切面可以应用五种类型的通知:
- before:前置通知,在一个方法执行前被调用。
- after-returning: 仅当方法成功完成后执行的通知。
- after-throwing: 在方法抛出异常退出时执行的通知。
- after: 在方法执行之后调用的通知,无论方法执行是否成功。
- around: 在方法执行之前和之后调用的通知。
Spring MVC 框架
什么是 Spring MVC?
Spring MVC 是 Spring 的一个模块,基于 MVC 的一个框架,无需中间整合层来整合。
Spring MVC 工作原理?
- 客户端发送请求到 DispatcherServlet;
- DispatcherServlet 查询 handlerMapping 找到处理请求的 Controller、Controller 调用业务逻辑后,返回 ModelAndView;
- DispatcherServlet 查询 ModelAndView,找到指定视图、视图将结果返回到客户端。
Spring MVC 流程?
- 用户发送请求至前端控制器 DispatcherServlet。
- DispatcherServlet 收到请求调用 HandlerMapping 处理器映射器。
- 处理器映射器找到具体的处理器 (可以根据 XML 配置、注解进行查找),生成处理器对象及处理器拦截器 (如果有则生成) 一并返回给 DispatcherServlet。
- DispatcherServlet 调用 HandlerAdapter 处理器适配器。
- HandlerAdapter 经过适配调用具体的处理器 (Controller,也叫后端控制器)。
- Controller 执行完成返回 ModelAndView。
- HandlerAdapter 将 controller 执行结果 ModelAndView 返回给 DispatcherServlet。h、DispatcherServlet 将 ModelAndView 传给 ViewReslover 视图解析器。
- ViewReslover 解析后返回具体 View。
- DispatcherServlet 根据 View 进行渲染视图(即将模型数据填充至视图中)。
- DispatcherServlet 响应用户。
如果你也用过 Struts2. 简单介绍下 Spring MVC 和 Struts2 的区别有哪些?
- Spring MVC 的入口是一个 Servlet 即前端控制器,而 Struts2 入口是一个 Filter 过虑器。
- Spring MVC 是基于方法开发 (一个 URL 对应一个方法),请求参数传递到方法的形参,可以设计为单例或多例 (建议单例),Struts2 是基于类开发,传递参数是通过类的属性,只能设计为多例。
- Struts2 采用值栈存储请求和响应的数据,通过 OGNL 存取数据,Spring MVC 通过参数解析器是将 request 请求内容解析,并给方法形参赋值,将数据和视图封装成 ModelAndView 对象,最后又将 ModelAndView 中的模型数据通过 reques 域传输到页面。JSP 视图解析器默认使用 JSTL。
@RequestMapping 注解用在类上面有什么作用?
是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
Spring MVC 的控制器是不是单例模式,如果是,有什么问题,怎么解决?
是单例模式,所以在多线程访问的时候有线程安全问题,不要用同步,会影响性能的,解决方案是在控制器里面不能写字段。
怎么样在方法里面得到 Request, 或者 Session?
直接在方法的形参中声明 Request,Spring MVC 就自动把 Request 对象传入。
如果前台有很多个参数传入,并且这些参数都是一个对象的,那么怎么样快速得到这个对象?
直接在方法中声明这个对象,Spring MVC 就自动会把属性赋值到这个对象里面。
Spring MVC 中函数的返回值是什么?
返回值可以有很多类型,有 String, ModelAndView, 当一般用 String 比较好。
Spring MVC 怎么样设定重定向和转发的?
在返回值前面加”forward:
“就可以让结果转发,譬如”forward:user.do?name=method4
” 在返回值前面加”redirect:
“就可以让返回值重定向,譬如”redirect:http://www.baidu.com
“。
Spring MVC 怎么和 AJAX 相互调用的?
通过 Jackson 框架就可以把 Java 里面的对象直接转化成 Js 可以识别的 Json 对象。
具体步骤如下 :
- 加入 Jackson.jar
- 在配置文件中配置 Json 的映射
- 在接受 AJAX 方法里面可以直接返回 Object,List 等,但方法前面要加上 @ResponseBody
如何解决 POST 请求中文乱码问题,GET 的又如何处理呢?
解决 post 请求乱码问题:在 web.xml 中配置一个 CharacterEncodingFilter 过滤器,设置成 utf-8;
1 | <filter> |
get 请求中文参数出现乱码解决方法有两个:
- 修改 tomcat 配置文件添加编码与工程编码一致,如下:
1 | <ConnectorURIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/> |
- 另外一种方法对参数进行重新编码:
1 | String userName = new String(request.getParamter("userName").getBytes("ISO8859- 1"),"utf-8") |
ISO8859-1 是 tomcat 默认编码,需要将 tomcat 编码后的内容按 utf-8 编码。
MyBatis 框架
什么是 MyBatis?
MyBatis 是一个可以自定义 SQL、存储过程和高级映射的持久层框架。
MyBatis 动态 SQL 是做什么的?都有哪些动态 SQL?能简述一下动态 SQL 的执行原理不?
MyBatis 动态 SQL 是为了解决在 SQL 语句中出现条件判断、分页、动态排序等情况,使得 SQL 语句能够更加灵活地生成。MyBatis 的动态 SQL 包括以下几种:
- if 元素:根据条件判断动态生成 SQL 语句;
- choose(when、otherwise)元素:类似于 Java 中的 switch 语句,根据条件选择生成 SQL 语句;
- where 元素:用于构建动态查询条件,自动去除查询语句中不必要的 where 或 and 关键字;
- set 元素:用于构建动态更新条件;
- foreach 元素:用于遍历一个集合,动态生成 SQL 语句。
动态 SQL 的执行原理是:MyBatis 在解析 Mapper.xml 文件时,会将动态 SQL 语句解析成一个完整的 SQL 语句,然后再将这个完整的 SQL 语句传递给 JDBC 执行。在解析 Mapper.xml 文件时,MyBatis 使用 OGNL 表达式来进行条件判断和数据遍历等操作,然后根据条件生成相应的 SQL 语句,最终生成一条完整的 SQL 语句,传递给 JDBC 执行。
#{} 和 ${} 的区别是什么?
- #{} 是预编译处理,${} 是字符串替换。
- MyBatis 在处理 #{} 时,会将 SQL 中的 #{} 替换为?号,调用 PreparedStatement 的 set 方法来赋值。
- MyBatis 在处理 ${} 时,就是把 ${} 替换成变量的值。
- 使用 #{} 可以有效的防止 SQL 注入,提高系统安全性。
为什么说 MyBatis 是半自动 ORM 映射工具?它与全自动的区别在哪里?
Hibernate 属于全自动 ORM (Object-Relational Mapping) 映射工具,使用 Hibernate 查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。而 MyBatis 在查询关联对象或关联集合对象时,需要手动编写 SQL 来完成,所以,称之为半自动 ORM 映射工具。
MyBatis 与 Hibernate 有哪些不同
- MyBatis 和 Hibernate 不同,它不完全是一个 ORM 框架,因为 MyBatis 需要程序员自己编写 SQL 语句,不过 MyBatis 可以通过 XML 或注解方式灵活配置要运行的 SQL 语句,并将 Java 对象和 SQL 语句映射生成最终执行的 SQL,最后将 SQL 执行的结果再映射生成 Java 对象。
- MyBatis 学习门槛低,简单易学,程序员直接编写原生态 SQL,可严格控制 SQL 执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,例如互联网软件、企业运营类软件等,因为这类软件需求变化频繁,一但需求变化要求成果输出迅速。但是灵活的前提是 MyBatis 无法做到数据库无关性,如果需要实现支持多种数据库的软件则需要自定义多套 SQL 映射文件,工作量大。
- Hibernate 对象 / 关系映射能力强,数据库无关性好,对于关系模型要求高的软件(例如需求固定的定制化软件)如果用 Hibernate 开发可以节省很多代码,提高效率。但是 Hibernate 的缺点是学习门槛高,要精通门槛更高,而且怎么设计 O/R 映射,在性能和对象模型之间如何权衡,以及怎样用好 Hibernate 需要具有很强的经验和能力才行。总之,按照用户的需求在有限的资源环境下只要能做出维护性、扩展性良好的软件架构都是好架构,所以框架只有适合才是最好。
MyBatis 的好处是什么
- MyBatis 把 SQL 语句从 Java 源程序中独立出来,放在单独的 XML 文件中编写,给程序的维护带来了很大便利。
- MyBatis 封装了底层 JDBC API 的调用细节,并能自动将结果集转换成 Java Bean 对象, 大大简化了 Java 数据库编程的重复工作。
- 因为 MyBatis 需要程序员自己去编写 SQL 语句,程序员可以结合数据库自身的特点灵活控制 SQL 语句,因此能够实现比 Hibernate 等全自动 ORM 框架更高的查询效率,能够完成复杂查询。
什么是 MyBatis 的接口绑定,有什么好处?
接口映射就是在 MyBatis 中任意定义接口,然后把接口里面的方法和 SQL 语句绑定,我们直接调用接口方法就可以,这样比起原来了 SqlSession 提供的方法我们可以有更加灵活的选择和设置。
接口绑定有几种实现方式,分别是怎么实现的?
接口绑定有两种实现方式:
- 通过注解绑定,就是在接口的方法上面加上 @Select@Update 等注解里面包含 SQL 语句来绑定。
- 通过 XML 里面写 SQL 来绑定,在这种情况下,要指定 XML 映射文件里面的 namespace 必须为接口的全路径名。
当实体类中的属性名和表中的字段名不一样,如果将查询的结果封装到指定 POJO?
- 通过在查询的 SQL 语句中定义字段名的别名。
- 通过来映射字段名和实体类属性名的一一对应的关系。
Mapper DAO 层开发规范
- 接口的全路径要和映射文件的 namespace 保持一致。
- 接口的方法名要和映射文件中的 statementId 保持一致。
- 接口方法的参数类型,返回类型要和映射文件中的 parameterType,resultType 保持一致。
- 接口和映射文件的名字最好保持一致 例如:UserMapper.java/UserMapper.xml。
- 接口和映射文件最好放到同一个目录。
Shiro 安全框架
Shiro 可以完成哪些工作?
Shiro 可以帮助我们完成:认证、授权、加密、会话管理、与 Web 集成、缓存等。
Apache Shiro 的三大核心组件
- Subject :当前用户的操作。
- SecurityManager:用于管理所有的 Subject。
- Realms:用于进行权限信息的验证。
Shiro 有哪些组件?
- Authentication:身份认证 / 登录,验证用户是不是拥有相应的身份。
- Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限。
- Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通 Java SE 环境的,也可以是如 Web 环境的。
- Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储。
- Web Support:Web 支持,可以非常容易的集成到 Web 环境。
- Caching:缓存,比如用户登录后,其用户信息、拥有的角色 / 权限不必每次去查,这样可以提高效率。
- Concurrency:shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去。
- Testing:提供测试支持。
- Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问。
- Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。
比较 Spring Security 和 Shiro
相比 Spring Security, Shiro 在保持强大功能的同时,使用简单性和灵活性。 SpringSecurity: 即使是一个一个简单的请求,最少得经过它的 8 个 Filter。Spring Security 必须在 Spring 的环境下使用。初学 Spring Security,曲线还是较大,需要深入学习其源码和框架, 配置起来也较费力。
Shiro 的优点
- 简单的身份认证,支持多种数据源。
- 对角色的简单的授权,支持细粒度的授权 (方法级)。
- 支持一级缓存,以提升应用程序的性能。
- 内置的基于 POJO 企业会话管理,适用于 Web 以及非 Web 的环境。
- 非常简单的加密 API。
- 不跟任何的框架或者容器捆绑,可以独立运行。
Maven
什么是 Maven?
- Maven 使用项目对象模型 (POM) 的概念,可以通过一小段描述信息来管理项目的构建,报告和文档的软件项目管理工具。
- Maven 除了以程序构建能力为特色之外,还提供高级项目管理工具。由于 Maven 的缺省构建规则有较高的可重用性,所以常常用两三行 Maven 构建脚本就可以构建简单的项目。由于 Maven 的面向项目的方法,许多 Apache Jakarta 项目发布时使用 Maven,而且公司项目采用 Maven 的比例在持续增长。
- Maven 的出现,解决了开发过程中的 jar 包升级及依赖的难题。它可以对项目依赖的 jar 包进行管理,可以让你的项目保持基本的依赖,排除冗余 jar 包,并且可以让你非常轻松的对依赖的 jar 包进行版本升级。而这些仅仅是 Maven 最基本的功能,它可以在这基础上对项目进行清理、编译、测试、打包、发布等等构建项目的工作。
- 可以说,Maven 是现在 Java 社区中最强大的项目管理和项目构建工具,而更加值得庆幸的是,这样一个强大的工具,它的使用也是非常简单的。现在,JavaEE 项目使用的开源软件都可以通过 Maven 来获取,并且,越来越多的公司也开始使用 Maven 来管理构建项目了。
Maven 仓库是什么?
Maven 仓库是基于简单文件系统存储的,集中化管理 Java API 资源(构件)的一个服务。仓库中的任何一个构件都有其唯一的坐标,根据这个坐标可以定义其在仓库中的唯一存储路径。得益于 Maven 的坐标机制,任何 Maven 项目使用任何一个构件的方式都是完全相同的,Maven 可以在某个位置统一存储所有的 Maven 项目共享的构件,这个统一的位置就是仓库,项目构建完毕后生成的构件也可以安装或者部署到仓库中,供其它项目使用。
对于 Maven 来说,仓库分为两类:
- 本地仓库
- 远程仓库
Maven 的工程类型有哪些?
- POM 工程:POM 工程是逻辑工程。用在父级工程或聚合工程中。用来做 jar 包的版本控制。
- JAR 工程:将会打包成 jar 用作 jar 包使用。即常见的本地工程 – Java Project。
- WAR 工程:将会打包成 war,发布在服务器上的工程。如网站或服务。即常见的网络工程 – Dynamic Web Project。WAR 工程默认没有 WEB-INF 目录及 web.xml 配置文件,IDE 通常会显示工程错误,提供完整工程结构可以解决。
Maven 常用命令有哪些?
- install
本地安装, 包含编译,打包,安装到本地仓库。
编译 – javac。
打包 – jar, 将 java 代码打包为 jar 文件。
安装到本地仓库 – 将打包的 jar 文件,保存到本地仓库目录中。
- clean
清除已编译信息。
删除工程中的 target 目录
- compile
只编译。 javac 命令。
- deploy
部署。 常见于结合私服使用的命令。相当于是 install + 上传 jar 到私服。包含编译,打包,安装到本地仓库,上传到私服仓库。
- package
打包。 包含编译,打包两个功能。
Linux 系统
绝对路径用什么符号表示?当前目录、上层目录用什么表示?主目录用什么表示?切换目录用什么命令?
- 绝对路径:如
/etc/init.d
- 当前目录和上层目录:
./
../
- 主目录:
~/
- 切换目录:
cd
怎么查看当前进程?怎么执行退出?怎么查看当前路径?
- 查看当前进程:
ps
- 执行退出:
exit
- 查看当前路径:
pwd
怎么清屏?怎么退出当前命令?
- 清屏:输入命令
clear
或者使用按键CTRL
+L
- 退出当前命令:
CTRL+C
彻底退出
查看文件内容有哪些命令可以使用?
1 | vi 文件名 # 编辑方式查看,可修 |
1 | cat 文件名 # 显示全部文件内 |
1 | more 文件名 # 分页显示文件内容 |
1 | less 文件名 # 与more相似,更好的是可以往前翻页 |
1 | tail 文件名 # 仅查看尾部,还可以指定 |
1 | head 文件名 # 仅查看头部,还可以指定 |
复制文件包括其子文件到自定目录
1 | cp -r sourceFolder targetFolder # 将名为"sourceFolder"的文件夹及其中的内容复制到名为"targetFolder"的文件夹中 |
- “cp” 是”copy” 的缩写,表示复制命令。
- “-r” 是”recursive” 的缩写,表示递归复制,即复制源文件夹及其中的所有子文件夹和文件。
- “sourceFolder” 是要复制的源文件夹的名称。
- “targetFolder” 是目标文件夹的名称,即将源文件夹复制到该目录下。
如果目标文件夹不存在,该命令会自动创建一个新的目标文件夹。
创建目录
1 | mkdir newFloder # 创建一个名为"newFloder"的新文件夹(或者叫做目录) |
- “mkdir” 是”make directory” 的缩写,表示创建目录的命令。
- “newFloder” 是要创建的新文件夹的名称。
如果同名的文件夹已经存在,则该命令会返回一个错误。
删除目录(此目录是空目录)
1 | rmdir deleteEmptyFolder # 删除一个空的名为"deleteEmptyFolder"的文件夹(或者叫做目录) |
- “rmdir” 是”remove directory” 的缩写,表示删除目录的命令。
- “deleteEmptyFolder” 是要删除的空文件夹的名称。
如果这个文件夹不是空的,或者同名的文件或文件夹不存在,则该命令会返回一个错误。需要注意的是,这个命令不能用于删除非空的文件夹,否则也会返回一个错误。
删除文件包括其子文件
1 | rm -rf deleteFile # 删除名为"deleteFile"的文件或文件夹,包括其中的所有内容,而不进行任何提示或警告。 |
- “rm” 是”remove” 的缩写,表示删除命令。
- “-rf” 是两个选项的组合,其中”-r” 表示递归删除,即删除目录及其子目录和文件;”-f” 表示强制删除,即无需确认直接删除,不会询问是否确认操作。
- “deleteFile” 是要删除的文件或文件夹的名称。
慎用
这个命令将会删除当前工作目录下的一个名为”deleteFile” 的文件或文件夹,包括其中的所有内容,而不进行任何提示或警告。需要非常小心使用该命令,因为它非常强大,一旦误用可能会造成不可逆的损失。
解压文件
1 | tar -zxvf test.tar.gz # 解压缩名为"test.tar.gz"的压缩文件。 |
- “tar” 是”tape archive” 的缩写,是一个常用的打包和压缩命令。
- “-z” 选项表示要对压缩文件使用 gzip 算法进行解压缩。
- “-x” 选项表示要解包(解压缩)文件。
- “-v” 选项表示输出详细的信息,即在屏幕上显示所有被解包的文件名。
- “-f” 选项后面跟着的是要解包的文件名,即”test.tar.gz”。
这个命令将会解压缩当前目录下的”test.tar.gz” 压缩文件,并将其中的文件提取到当前目录下。其中,”.tar” 表示文件经过 tar 命令打包过,”.gz” 表示文件经过 gzip 命令压缩过。需要注意的是,该命令只能用于解压缩 tar 和 gzip 命令打包和压缩的文件。
解压 zip 文件
1 | unzip -oq file.zip # 解压缩名为"file.zip"的zip压缩文件,并且指定不进行任何交互操作和输出过程信息。 |
- “unzip” 是解压缩命令。
- “-o” 选项表示在解压缩时不进行任何交互操作,即覆盖所有现有文件,不会询问用户是否要覆盖。
- “-q” 选项表示不输出解压缩过程的任何信息,即 “quiet” 模式。
- “file.zip” 是要解压缩的 zip 文件的名称。
这个命令将会解压缩当前目录下的”file.zip” 压缩文件,并将其中的文件提取到当前目录下。其中,”.zip” 表示文件经过 zip 命令压缩过。需要注意的是,该命令只能用于解压缩 zip 命令压缩的文件。
Redis 存储系统
什么是 Redis?
Remote Dictionary Server (Redis) 是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key- Value 数据库,并提供多种语言的 API。它通常被称为数据结构服务器,因为值(value)可以是 字符串 (String), 哈希 (Map), 列表 (list), 集合 (sets) 和 有序集合 (sorted sets) 等类型。
Redis 的特点什么是?
- 支持多种数据结构,如 String (字符串)、 List (列表)、Hash (哈希表)、Set (集合)、Sorted Set (有序集合)、HyperLogLog (基数估算)。
- 支持持久化操作,可以进行 aof 及 rdb 数据持久化到磁盘,从而进行数据备份或数据恢复等操作,较好的防止数据丢失的手段。
- 支持通过 Replication 进行数据复制,通过 master-slave 机制,可以实时进行数据的同步复制,支持多级复制和增量复制,master-slave 机制是 Redis 进行 HA 的重要手段。
- 单进程请求,所有命令串行执行,并发情况下不需要考虑数据一致性问题。
Redis 数据类型有哪些?
- String (字符串)
- Hash (hash 表)
- List (链表)
- Set (集合)
- Sorted Set (有序集合 zset)
Redis 的配置以及持久化方案有几种?
- RDB 方式
- AOF 方式
Redis 中的常用命令哪些?
- hset 存储一个哈希键值对的集合
- hget 获取一个哈希键的值
- hdel 删除一个或多个字段
- hgetall 获取一个哈希是键值对的集合
- lpush key value 向链表左侧添加
- rpush key value 向链表右侧添加
- lpop key 从左边移出一个元素
- rpop key 从右边移出一个元素
- keys * 返回所有的 key 可以加 * 通配
- exists key 判断 string 类型一个 key 是否存在 如果存在返回 1 否则返回 0
Redis 主要消耗什么物理资源?
内存
Redis 有哪几种数据淘汰策略?
- noeviction: 返回错误当内存限制达到,并且客户端尝试执行会让更多内存被使用的命令。
- allkeys-lru: 尝试回收最少使用的键(LRU),使得新添加的数据有空间存放。
- volatile-lru: 尝试回收最少使用的键(LRU),但仅限于在过期集合的键,使得新添加的数据有空间存放。
- allkeys-random: 回收随机的键使得新添加的数据有空间存放。
- volatile-random: 回收随机的键使得新添加的数据有空间存放,但仅限于在过期集合的键。
- volatile-ttl: 回收在过期集合的键,并且优先回收存活时间(TTL)较短的键,使得新添加的数据有空间存放。
Redis 官方为什么不提供 Windows 版本?
因为目前 Linux 版本已经相当稳定,而且用户量很大,无需开发 windows 版本,反而会带来兼容性等问题。
为什么 Redis 需要把所有数据放到内存中?
Redis 为了达到最快的读写速度将数据都读到内存中,并通过异步的方式将数据写入磁盘。所以 Redis 具有快速和数据持久化的特征,如果不将数据放在内存中,磁盘 I/O 速度为严重影响 Redis 的性能。在内存越来越便宜的今天,Redis 将会越来越受欢迎, 如果设置了最大使用的内存,则数据已有记录数达到内存限值后不能继续插入新值。
Redis 有哪些适合的场景?
- 会话缓存(Session Cache):最常用的一种使用 Redis 的情景是会话缓存(Session Cache),用 Redis 缓存会话比其他存储(如 Memcached)的优势在于:Redis 提供持久化。当维护一个不是严格要求一致性的缓存时,如果用户的购物车信息全部丢失,大部分人都会不高兴的,现在,他们还会这样吗?幸运的是,随着 Redis 这些年的改进,很容易找到怎么恰当的使用 Redis 来缓存会话的文档。甚至广为人知的商业平台 Magento 也提供 Redis 的插件。
- 全页缓存(FPC):除基本的会话 token 之外,Redis 还提供很简便的 FPC 平台。回到一致性问题,即使重启了 Redis 实例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降,这是一个极大改进,类似 PHP 本地 FPC。再次以 Magento 为例,Magento 提供一个插件来使用 Redis 作为全页缓存后端。此外,对 WordPress 的用户来说,Pantheon 有一个非常好的插件 wp-redis,这个插件能帮助你以最快速度加载你曾浏览过的页面。
- 队列:Reids 在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得 Redis 能作为一个很好的消息队列平台来使用。Redis 作为队列使用的操作,就类似于本地程序语言(如 Python)对 list 的 push/pop 操作。如果你快速的在 Google 中搜索 “Redis queues”,你马上就能找到大量的开源项目,这些项目的目的就是利用 Redis 创建非常好的后端工具,以满足各种队列需求。例如,Celery 有一个后台就是使用 Redis 作为 broker,你可以从这里去查看。
- 排行榜 / 计数器:Redis 在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(SortedSet)也使得我们在执行这些操作的时候变的非常简单,Redis 只是正好提供了这两种数据结构。
- 发布 / 订阅:最后(但肯定不是最不重要的)是 Redis 的发布 / 订阅功能。发布 / 订阅的使用场景确实非常多。我已看见人们在社交网络连接中使用,还可作为基于发布 / 订阅的脚本触发器,甚至用
Spring Boot 框架
什么是 Spring Boot?
多年来,随着新功能的增加,Spring 变得越来越复杂。只需访问 https://spring.io/projects 页面,我们就会看到可以在我们的应用程序中使用的所有 Spring 项目的不同功能。如果必须启动一个新的 Spring 项目,我们必须添加构建路径或添加 Maven 依赖关系,配置应用程序服务器,添加 Spring 配置。因此,开始一个新的 Spring 项目需要很多努力,因为我们现在必须从头开始做所有事情。Spring Boot 是解决这个问题的方法。Spring Boot 已经建立在现有 Spring 框架之上。使用 Spring 启动,我们避免了之前我们必须做的所有样板代码和配置。因此,Spring Boot 可以帮助我们以最少的工作量,更加健壮地使用现有的 Spring 功能。
Spring Boot 有哪些特点?
- 为 Spring 开发提供一个更快、更广泛的入门体验。
- 开箱即用,远离繁琐的配置。
- 提供了一系列大型项目通用的非业务性功能,例如:内嵌服务器、安全管理、运行数据监控、运行状况检查和外部化配置等。
- 绝对没有代码生成,也不需要 XML 配置。
Spring Boot 有哪些优点?
- 减少开发、测试时间和努力。
- 使用 JavaConfig 有助于避免使用 XML。
- 避免大量的 Maven 导入和各种版本冲突。
- 通过提供默认值快速开始开发。没有单独的 Web 服务器需要。这意味着你不再需要启动 Tomcat,Glassfish 或其他任何东西。
- 需要更少的配置 因为没有 web.xml 文件。只需添加用
@ Configuration
注释的类,然后添加用@Bean
注释的方法,Spring 将自动加载对象并像以前一样对其进行管理。您甚至可以将@Autowired
添加到 bean 方法中,以使 Spring 自动装入需要的依赖关系中。基于环境的配置 使用这些属性,您可以将您正在使用的环境传递到应用程序:-Dspring.profiles.active ={enviornment}。在加载主应用程序属性文件后,Spring 将在(application {environment} .properties)中加载后续的应用程序属性文件。
Spring Boot、Spring MVC 和 Spring 有什么区别?
- Spring 最重要的特征是依赖注入。所有 SpringModules 不是依赖注入就是 IOC 控制反转。当我们恰当的使用 DI 或者是 IOC 的时候,我们可以开发松耦合应用。松耦合应用的单元测试可以很容易的进行。
- Spring MVC 提供了一种分离式的方法来开发 Web 应用。通过运用像 DispatcherServelet , MoudlAndView 和 ViewResolver 等一些简单的概念,开发 Web 应用将会变得非常简单。
- Spring 和 SpringMVC 的问题在于需要配置大量的参数。Spring Boot 通过一个自动配置和启动的项来目解决这个问题。为了更快的构建产品就绪应用程序,Spring Boot 提供了一些非功能性特征。
Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?
启动类上面的注解是 @SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 4 个注解:
- @SpringBootConfiguration: 组 合 了 @Configuration 注 解 , 实 现 配 置 文 件 的 功 能 。
- @EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能:
- @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。
- @ComponentScan:Spring 组件扫描。
Spring Boot 需要独立的容器运行吗?
可以不需要,内置了 Tomcat/Jetty 等容器。
你如何理解 Spring Boot 中的 Starters?
Starters 可以理解为启动器,它包含了一系列可以集成到应用里面的依赖包,你可以一站式集成 Spring 及其他技术,而不需要到处找示例代码和依赖包。如你想使用 Spring JPA 访问数据库,只要加入 spring-boot-starter-data-jpa 启动器依赖就能使用了。
Spring Boot 是否可以使用 XML 配置?
Spring Boot 推荐使用 Java 配置而非 XML 配置,但是 Spring Boot 中也可以使用 XML 配置,通过 @ImportResource
注解可以引入一个 XML 配置。
bootstrap.properties 和 application.properties 有何区别?
- 单纯做 Spring Boot 开发,可能不太容易遇到 bootstrap.properties 配置文件,但是在结合 Spring Cloud 时,这个配置就会经常遇到了,特别是在需要加载一些远程配置文件的时侯。bootstrap.properties 在 application.properties 之前加载,配置在应用程序上下文的引导阶段生效。
- 一般来说我们在 Spring Cloud Config 或者 Nacos 中会用到它。bootstrap.properties 被 Spring ApplicationContext 的父类加载,这个类先于加载 application.properties 的 ApplicatonContext 启动。当然,前面叙述中的 properties 也可以修改为 yaml 。
自动装配的原理
@EnableAutoConfiguration 的实现关键在于引入了 AutoConfigurationImportSelector,该类的核心逻辑是 selectImports 方法。在该方法中,首先会从 spring-boot-autoconfigure 包下的 META-INF/spring.factories 文件中加载所有可能用到的自动配置类;然后进行去重操作,并排除 exclude 和 excludeName 属性携带的类;最后进行过滤操作,只将满足条件(@Conditional)的自动配置类返回。这样,通过使用 @EnableAutoConfiguration 注解,就可以实现自动配置的功能。
如何重新加载 Spring Boot 上的更改,而无需重新启动服务器?
这可以使用 DEV 工具来实现。通过这种依赖关系,您可以节省任何更改,嵌入式 tomcat 将重新启动。Spring Boot 有一个开发工具(DevTools)模块,它有助于提高开发人员的生产力。Java 开发人员面临的一个主要挑战是将文件更改自动部署到服务器并自动重启服务器。开发人员可以重新加载 Spring Boot 上的更改,而无需重新启动服务器。这将消除每次手动部署更改的需要。Spring Boot 在发布它的第一个版本时没有这个功能。这是开发人员最需要的功能。DevTools 模块完全满足开发人员的需求。该模块将在生产环境中被禁用。它还提供 H2 数据库控制台以更好地测试应用程序。
1 | <dependency> |
如何在自定义端口上运行 Spring Boot 应用程序?
为了在自定义端口上运行 Spring Boot 应用程序,您可以在 application.properties 中指定端口。
SpringCloud
什么是 Spring Cloud?
- Spring Cloud 是一个微服务框架,相比 Dubbo 等 RPC 框架,Spring Cloud 提供的全套的分布式系统解决方案。
- Spring Cloud 对微服务基础框架 Netflix 的多个开源组件进行了封装,同时又实现了和云端平台以及和 Spring Boot 开发框架的集成。
- Spring Cloud 为微服务架构开发涉及的配置管理,服务治理,熔断机制,智能路由,微代理,控制总线,一次性 token,全局一致性锁,leader 选举,分布式 session,集群状态管理等操作提供了一种简单的开发方式。
- Spring Cloud 为开发者提供了快速构建分布式系统的工具,开发者可以快速的启动服务或构建应用、同时能够快速和云平台资源进行对接。
Spring Cloud 与 Dubbo 的区别是什么?
对比项 | Dubbo | Spring Cloud | |
出生背景 | 阿里系。核心框架是服务化治理 | Spring 社区。核心框架是 Netflix 开源微服务架构群体 | |
活跃度 | 社区活跃度 | 相差不多但是 Spring Cloud 相对更活跃一些 | |
百度指数 | 2570 | 999 | |
招聘岗位 | 636 | 160 | |
文档质量 | 集中,健全 | 较多,内容大部分是英文版 | |
性能 | 1:3 | ||
功能 | 服务注册中心 | ZooKeeper | Spring Cloud Netflix Eureka |
服务调用方式 | PRC | REST API | |
服务网关 | 无 | Spring Cloud Netflix Zuul | |
断路由 | 集群容错 | Spring Cloud Netflix Hystrix | |
分布式配置 | 无 | Spring Cloud Config | |
服务跟踪 | 无 | Spring Cloud Sleuth | |
消息总线 | 无 | Spring Cloud Bus | |
数据流 | 无 | Spring Cloud Stream | |
批量任务 | 无 | Spring Cloud Task |
Spring Cloud 断路器的作用是什么?
在分布式架构中,断路器模式的作用也是类似的,当某个服务单元发生故障(类似用电器发生短路)之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个错误响应,而不是长时间的等待。这样就不会使得线程因调用故障服务被长时间占用不释放,避免了故障在分布式系统中的蔓延。
Spring Cloud 的核心组件有哪些?
- Eureka:服务注册于发现。
- Feign:基于动态代理机制,根据注解和选择的机器,拼接请求 url 地址,发起请求。
- Ribbon:实现负载均衡,从一个服务的多台机器中选择一台。
- Hystrix:提供线程池,不同的服务走不同的线程池,实现了不同服务调用的隔离,避免了服务雪崩的问题。
- Zuul:网关管理,由 Zuul 网关转发请求给对应的服务。
Spring Cloud 如何实现服务的注册?
在服务发布时,需要指定对应的服务名,并将该服务注册到注册中心(例如 Eureka、Zookeeper 等)。可以使用 @EnableEurekaServer 注解开启注册中心,使用 @EnableDiscoveryClient 注解将服务注册到注册中心,然后使用 Ribbon 或 Feign 等工具进行服务之间的调用和发现。这样可以实现服务之间的相互调用,提高应用程序的可扩展性和弹性。
Eureka 自我保护机制是什么?
当 Eureka Server 节点在短时间内丢失了过多实例的连接时(比如网络故障或频繁启动关闭客户端)节点会进入自我保护模式, 保护注册信息,不再删除注册数据,故障恢复时,自动退出自我保护模式。
什么是 Ribbon?
Ribbon 是一个负载均衡客户端,可以很好地控制 HTTP 和 TCP 的一些行为。Feign 默认集成了 Ribbon。
什么是 Spring Cloud Bus?
Spring Cloud Bus 将分布式的节点用轻量的消息代理连接起来,它可以用于广播配置文件的更改或者服务直接的通讯,也可用于监控。如果修改了配置文件,发送一次请求,所有的客户端便会重新读取配置文件。
什么是 Eureka 注册中心?
Eureka 是 Netflix 开发的服务发现组件,本身是一个基于 REST 的服务。Spring Cloud 将它集成在其子项目 spring-cloud-netflix 中, 以实现 Spring Cloud 的服务注册于发现,同时还提供了负载均衡、故障转移等能力。
负载平衡的意义什么?
在计算中,负载平衡可以改善跨计算机,计算机集群,网络链接,中央处理单元或磁盘驱动器等多种计算资源的工作负载分布。负载平衡旨在优化资源使用,最大化吞吐量,最小化响应时间并避免任何单一资源的过载。使用多个组件进行负载平衡而不是单个组件可能会通过冗余来提高可靠性和可用性。负载平衡通常涉及专用软件或硬件,例如多层交换机或域名系统服务器进程。
Dubbo
Dubbo 是什么?
Dubbo 是阿里巴巴开源的基于 Java 的高性能 RPC 分布式服务框架,现已成为 Apache 基金会孵化项目。
Dubbo 默认使用什么注册中心,还有别的选择吗?
推荐使用 ZooKeeper 作为注册中心,还有 Redis、Multicast、Simple 注册中心,但不推荐。
在 Provider 上可以配置的 Consumer 端的属性有哪些?
- timeout:方法调用超时
- retries:失败重试次数,默认重试 2 次
- loadbalance:负载均衡算法,默认随机
- actives 消费者端,最大并发调用限制
Dubbo 推荐使用什么序列化框架,你知道的还有哪些?
推荐使用 Hessian 序列化,还有 Duddo、FastJson、Java 自带序列化。
你还了解别的分布式框架吗?
别的还有 Spring cloud、Facebook 的 Thrift、Twitter 的 Finagle 等。
Dubbo 内置了哪几种服务容器?
Spring Container,Jetty Container,Log4j Container
Dubbo 核心的配置有哪些?
配置 | 配置说明 |
dubbo:service | 服务配置 |
dubbo:reference | 引用配置 |
dubbo:protocol | 协议配置 |
dubbo:application | 应用配置 |
dubbo:module | 模块配置 |
dubbo:registry | 注册中心配置 |
dubbo:monitor | 监控中心配置 |
dubbo:provider | 提供方配置 |
dubbo:consumer | 消费方配置 |
dubbo:method | 方法配置 |
dubbo:argument | 参数配置 |
Dubbo 有哪几种集群容错方案,默认是哪种?
集群容错方案 | 说明 |
Failover Cluster | 失败自动切换,自动重试其他服务器(默认) |
Failfast Cluster | 快速失败,立即报错,只发起一次调用 |
Failsafe Cluster | 失败安全,出现异常时,直接忽略 |
Failback Cluster | 失败自动恢复,记录失败请求,定时重发 |
Forking Cluster | 并行调用多个服务器,只要一个成功即返回 |
Broadcast Cluster | 广播逐个调用所有提供者,任意一个报错则报错 |
Dubbo 有哪几种负载均衡策略,默认是哪种?
负载均衡策略 | 说明 |
Random LoadBalance | 随机,按权重设置随机概率(默认) |
RoundRobin LoadBalance | 轮询,按公约后的权重设置轮询比率 |
LeastActive LoadBalance | 最少活跃调用数,相同活跃数的随机 |
ConsistentHash LoadBalance | 一致性 Hash,相同参数的请求总是发到同一提供者 |
Dubbo 的优势
- 单一应用架构,当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增 删改查工作量的 数据访问框架(ORM)是关键。
- 垂直应用架构,当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效 率。此时,用于加速前端页面开发的 Web 框架(MVC)是关键。
- 分布式服务架构,当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的 服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的 分布式服务框架(RPC)是关键。
- 流动计算架构当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的 资源调度和治理中心(SOA)是关键。
ZooKeeper
ZooKeeper 是什么?
ZooKeeper 是一个开放源码的分布式协调服务,它是集群的管理者,监视着集群中各个节点的状态根据节点提交的反馈进行下一步合理操作。最终,将简单易用的接口和性能高效、功能稳定的系统提供给用户。分布式应用程序可以基于 Zookeeper 实现诸如数据发布 / 订阅、负载均衡、命名服务、分布式协调 / 通知、集群管理、Master 选举、分布式锁和分布式队列等功能。
ZooKeeper 提供了什么?
- 文件系统
- 通知机制
Zookeeper 文件系统
Zookeeper 提供一个多层级的节点命名空间(节点称为 znode)。与文件系统不同的是,这些节点都可以设置关联的数据,而文件系统中只有文件节点可以存放数据而目录节点不行。Zookeeper 为了保证高吞吐和低延迟,在内存中维护了这个树状的目录结构,这种特性使得 Zookeeper 不能用于 存放大量的数据,每个节点的存放数据上限为 1M。
四种类型的数据节点 Znode
- PERSISTENT - 持久化目录节点客户端与 zookeeper 断开连接后,该节点依旧存在。
- PERSISTENT_SEQUENTIAL - 持久化顺序编号目录节点客户端与 zookeeper 断开连接后,该节点依旧存在,只是 Zookeeper 给该节点名称进行序编号。
- EPHEMERAL - 临时目录节点客户端与 zookeeper 断开连接后,该节点被删。
- EPHEMERAL_SEQUENTIAL - 临时顺序编号目录节点客户端与 zookeeper 断开连接后,该节点被删除,只是 Zookeeper 给该节点名称进行顺序号。
Zookeeper 通知机制
Client 端会对某个 ZNode 建立一个 Watcher 事件,当该 ZNode 发生变化时,这些 Client 会收到 ZooKeeper 的通知,然后 Client 可以根据 ZNode 变化来做出业务上的改变等。
ZooKeeper 负载均衡和 Nginx 负载均衡区别
ZooKeeper 的负载均衡是可以调控,Nginx 只是能调权重,其他需要可控的都需要自己写插件;但是 Nginx 的吞吐量比 ZooKeeper 大很多,应该说按业务选择用哪种方式。
ZooKeeper 工作原理
ZooKeeper 的核心是原子广播,这个机制保证了各个 Server 之间的同步。实现这个机制的协议叫做 Zab 协议。Zab 协议有两种模式,它们分别是恢复模式(选主)和广播模式(同步)。当服务启动或者在领导者崩溃后,Zab 就进入了恢复模式,当领导者被选举出来,且大多数 Server 完成了和 leader 的状态同步以后,恢复模式就结束了。状态同步保证了 leader 和 Server 具有相同的系统状态。
ZooKeeper 是如何保证事务的顺序一致性的?
ZooKeeper 采用了递增的事务 ID 来标识,所有的 Proposal(提议)都在被提出的时候加上了 ZXID,ZXID 实际上是一个 64 位的数字,高 32 位是 Epoch(时期;纪元;世;新时代)用来标识 Leader 是否发生改变,如果有新的 Leader 产生出来,Epoch 会自增,低 32 位用来递 增计数。当新产生 Proposal 的时候,会依据数据库的两阶段过程,首先会向其他的 Server 发出事务执行请求,如果超过半数的机器都 能执行并且能够成功,那么就会开始执行。
JVM 面试专题
内存模型以及分区,需要详细到每个区放什么?
JVM 分为堆区和栈区,还有方法区,初始化的对象放在堆里面,引用放在栈里面,class 类信息常量池(static 常量和 static 变量)等放在方法区 a、方法区:主要是存储类信息,常量池(static 常量和 static 变量),编译后的代码(字节码)等数
- 堆:初始化的对象,成员变量 (那种非 static 的变量),所有的对象实例和数组都要在堆上分配。
- 栈:栈的结构是栈帧组成的,调用一个方法就压入一帧,帧上面存储局部变量表,操作数栈,方法出口等信息,局部变量表存放的是 8 大基础类型加上一个应用类 型,所以还是一个指向地址的指针。
- 本地方法栈:主要为 Native 方服务。
- 程序计数器:记录当前线程执行的行号。
堆里面的分区:Eden,survival (from+ to),老年代,各自的特点。
- 堆里面分为新生代和老生代(java8 取消了永久代,采用了 Metaspace),新生代包含 Eden+Survivor 区,survivor 区里面分为 from 和 to 区, 内存回收时,如果用的是复制算法,从 from 复制到 to,当经过一次或者多次 GC 之后,存活下来的对象会被移动到老年区,当 JVM 内存不够用的时候,会触发 Full GC,清理 JVM 老年区当新生区满了之后会触发 YGC, 先把存活的对象放到其中一个 Survice 区,然后进行垃圾清理。
- 因为如果仅仅清理需要删除的对象,这样会导致内存碎片,因此一般会把 Eden 进行完全的清理,然后整理内存。那么下次 GC 的时候,就会使用下一个 Survive,这样循环使用。如果有特别大的对象,新生代放不下,就会使用老年代的担保,直接放到老年代里面。因为 JVM 认为,一般大对象的存活时间一般比较久远。
GC 的两种判定方法:
- 引用计数法:指的是如果某个地方引用了这个对象就 + 1,如果失效了就 - 1,当为 0 就会回收但是 JVM 没有用这种方式,因为无法判定相互循环引用(A 引用 B,B 引用 A)的情况。
- 引用链法: 通过一种 GC ROOT 的对象(方法区中静态变量引用的对象等 - static 变量)来判断,如果有一条链能够到达 GCROOT 就说明,不能到达 GC ROOT 就说明可以回收。
GC 的三种收集方法:标记清除、标记整理、复制算法的原理与特点,分别用在什么地方,如果让你优化收集方法,有什么思路?
先标记,标记完毕之后再清除,效率不高,会产生碎片复制算法:分为 8:1 的 Eden 区和 survivor 区,就是上面谈到的 YGC 标记整理:标记完毕之后,让所有存活的对象向一端移动。
GC 收集器有哪些?CMS 收集器与 G1 收集器的特点。
- 并行收集器:串行收集器使用单个线程进行垃圾收集,会在 GC 时停止服务。而并行收集器使用多个线程同时进行垃圾收集,可以更快地完成垃圾回收。
- 并发收集器:在 GC 时服务不会停止,使用多线程来执行垃圾回收,因此不会出现长时间停顿的情况。CMS 收集器就是一种并发收集器,它是基于 “标记 — 清除” 算法实现的,会经过多次标记才会进行垃圾回收。
- CMS 收集器的特点:是一种并发收集器,能够在 GC 时服务不停顿,但因为采用标记清除算法,容易出现碎片化问题,可能导致频繁进行 Full GC,从而影响系统的性能。
- G1 收集器的特点:从整体上看是基于 “标记 — 整理” 算法实现的收集器,会对堆内存进行划分,然后使用多个线程并发执行垃圾回收。从局部(两个 Region 之间)上看是基于 “复制” 算法实现的,可以避免碎片化问题,因此能够更好地保证系统的性能。同时,G1 收集器还能够根据垃圾分布情况来优化垃圾回收过程,进一步提高系统的性能。
Minor GC 与 Full GC 分别在什么时候发生?
新生代内存不够用时候发生 MGC 也叫 YGC,JVM 内存不够的时候发生 FGC。
简述 Java 垃圾回收机制?
在 Java 中,程序员是不需要显示的去释放一个对象的内存的,而是由虚拟机自行执行。在 JVM 中,有一个垃圾回收线程,它是低优先级的,在正常情况下是不会执行的,只有在虚拟机空闲或者当前堆内存不足时,才会触发执行,扫面那些没有被任何引用的对象,并将它们添加到要回收的集合 中,进行回收。
Java 中垃圾收集的方法有哪些?
- 标记 - 清除:这是垃圾收集算法中最基础的,根据名字就可以知道,它的思想就是标记哪些要被回收的对象,然后统一回收。这种方法很简单,但是会有两个主要问题:1.1. 效率不高,标记和清除的效率都很低;1.2. 会产生大量不连续的内存碎片,导致以后程序在分配较大的对象时,由于没有充足的连续内存而提前触发一次 GC 动作。
- 复制算法:为了解决效率问题,复制算法将可用内存按容量划分为相等的两部分,然后每次只使用其中的一块,当一块内存用完时,就将还存活的对象复制到第二块内存上,然后一次性清楚完第一块内存,再将第二块上的对象复制到第一块。但是这种方式,内存的代价太高,每次基本上都要浪费一般的内存。于是将该算法进行了改进,内存区域不再是按照 1:1 去划分,而是将内存划分为 8:1:1 三部分,较大那份内存交 Eden 区,其余是两块较小的内存区叫 Survior 区。每次都会优先使用 Eden 区,若 Eden 区满,就将对象复制到第二块内存区上,然后清除 Eden 区,如果此时存活的对象太多,以至于 Survivor 不够时,会将这些对象通过分配担保机制复制到老年代中。(Java 堆又分为新生代和老年代)。
- 标记 - 整理:该算法主要是为了解决标记 - 清除,产生大量内存碎片的问题;当对象存活率较高时,也解决了复制算法的效率问题。它的不同之处就是在清除对象的时候现将可回收对象移动到一端,然后清除掉端边界以外的对象,这样就不会产生内存碎片了。
- 分代收集:现在的虚拟机垃圾收集大多采用这种方式,它根据对象的生存周期,将堆分为新生代和老年代。在新生代中,由于对象生存期短,每次回收都会有大量对象死去,那么这时就采用复制算法。老年代里的对象存活率较高,没有额外的空间进行分配担保,所以可以使用标记 - 整理 或者 标记 - 清除。
简述 Java 类加载机制?
虚拟机把描述类的数据从 Class 文件加载到内存,并对数据进行校验,解析和初始化,最终形成可以被虚拟机直接使用的 Java 类型。
什么是类加载器,类加载器有哪些?
实现通过类的权限定名获取该类的二进制字节流的代码块叫做类加载器。主要有一下四种类加载器:
- 启动类加载器 (Bootstrap ClassLoader) 用来加载 java 核心类库,无法被 java 程序直接引用。
- 扩展类加载器 (extensions class loader): 它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
- 系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader () 来获取它。
- 用户自定义类加载器,通过继承 java.lang.ClassLoader 类的方式实现。
说一下 JVM 调优的工具?
DK 自带了很多监控工具,都位于 JDK 的 bin 目录下,其中最常用的是 jconsole 和 jvisualvm 这两款视图监控工具。jconsole:用于对 JVM 中的内存、线程和类等进行监控;jvisualvm:JDK 自带的全能分析工具,可以分析:内存快照、线程快照、程序死锁、监控内存的变化、gc 变化等。
常用的 JVM 调优的参数都有哪些?
-
-Xms2g
:初始化推大小为 2g; -
-Xmx2g
:堆最大内存为 2g; -
-XX:NewRatio=4
:设置年轻的和老年代的内存比例为 1:4; -
-XX:SurvivorRatio=8
:设置新生代 Eden 和 Survivor 比例为 8:2; -
–XX:+UseParNewGC
:指定使用 ParNew + Serial Old 垃圾回收器组合; -
-XX:+UseParallelOldGC
:指定使用 ParNew + ParNew Old 垃圾回收器组合; -
-XX:+UseConcMarkSweepGC
:指定使用 CMS + Serial Old 垃圾回收器组合; -
-XX:+PrintGC
:开启打印 gc 信息; -
-XX:+PrintGCDetails
:打印 gc 详细