国际劳动节又称“国际示威游行日”是世界上80多个国家的全国性节日。定在每年的五月一日。它是全世界劳动人民共同拥有的节日。
所以今天枫哥也没闲着,5月1号嘛,我给大家准备了一些学员面试实战的一些问题。那我们大家一来看一看下面的真实面试问题。
一、sql索引执行计划 1.索引的分类- 单值索引:一个索引只包含单个列,一个表可以有多个单列索引。
- 唯一索引:索引的值必须唯一,但允许有空值(null)。
- 复合索引:一个索引包含多个列。
注:一张表建立的索引最好不要超过5个。
1. 创建索引 [UNIQUE] 可以省略
2. 如果只写一个字段就是单值索引,写多个字段就是复合索引 create [UNIQUE] INDEX indexName ON 表名(字段名(length));
3. 删除索引 DROP INDEX 索引名称 ON 表名;
4. 查看索引 SHOW INDEX FROM 表名 \G; 加上 \G 就可以以列的形式查看,不加 \G 以表的形式查看。
注: 查看执行计划:EXPLAIN * FROM 表名;
查看索引:show index from 数据库名.表名 where column_name like '列名';
建立普通索引:CREATE INDEX index_name(索引名称) ON 表名 (字段名) ; 删除索引:drop index index_name(索引名) on 表名 ;
2.使用索引的注意事项
- 要在经常搜索的列上加 索引,这样可以加快搜索的速度。
- 在经常使用使用 where 字句的列上加索引,加快条件的判断速度。
- 要在经常 排序 的列上加索引,因为索引已经是排好序的,这样查询可以利用索引的排序,加快查询的时间。
- 对于 中到大型表 索引都是非常有效的,但是对于特大型表的话维护开销很大,不适合建立索引。
- 索引加在经常用来连接的列上,这些列主要是一些 外键,可以加快连接的速度。
- 避免where字句中对字段加 函数,这样会造成索引失效,导致无法命中索引。
- 在使用 InnoDB 时使用与1业务无关的自增主键作为主键,即使用逻辑主键,而不要使用业务主键。
- 将打算加索引的列设置为 NOT NULL,否则将导致引擎放弃使用索引而进行全表扫描。
- 删除长期未使用的索引,不用的索引继续存在的话会造成不必要的性能损耗,MySQL5.7可以通过查询 sys库 的 10.chema_unused_indexes 视图 来查询哪些索引从未被使用。
(1).EXPLAIN
SQL的执行计划,使用EXPLAIN关键字可以模拟优化器执行 SQL 查询语句,从而知道MySQL是如何处理SQL语句的。
语法:explain + SQLEXPLAIN SELECT*FROMwork_code_st wcsWHEREwcs.commit_added_lines > 0;
(2).用 EXPLAIN 能够查看的信息
可以查看以下信息:
id:表的读取顺序,id 越大优先级越高,越先被执行。
select_type:数据读取 *** 作的 *** 作类型。
possible_keys:哪些索引可以使用。
key:哪些索引被实际使用。
ref:表之间的引用。
rows:每张表有多少行被优化器查询,根据表统计信息及索引选用情况,大致估算出找到所需的记录需要读取的行数。
type:访问类型排列。
从最好到最差依次是: system > const > eq_ref > ref > range > index > ALL。
除了 ALL 没有用到索引,其他级别都用到索引了。一般来说,得保证查询至少达到 range 级别,最好达到 ref。
system: 表只有一行记录(等于系统表),这是const类型的特例,平时不会出现,这个也可以忽略不计。
const:表示通过索引一次就找到了,const 用于比较 primary key 或者 unique 索引。因为只匹配一行数据,所以很快。如将主键置于where 列表中,MySQL 就能将该查询转化为一个常量。
eq_ref:唯一性索引扫描,读取本表中和关联表表中的每行组合成的一行,查出来只有一条记录。除 了 system 和 const 类型之外, 这是最好的联接类型。 ref:非唯一性索引扫描,返回本表和关联表某个值匹配的所有行,查出来有多条记录;显示索引的哪一列被使用了,如果可能的话,是一个常数。哪些列或常量被用于查找索引列上的值。
range:只检索给定范围的行,一般就是在 WHER E语句中出现了 BETWEEN、< >、in 等的查询。这种范围扫描索引比全表扫描要好,因为它只需要开始于索引树的某一点,而结束于另一点,不用扫描全部索引。
index:Full Index Scan,全索引扫描,index和ALL的区别为 index 类型只遍历索引树。 也就是说虽然 ALL 和 index 都是读全表,但是 index 是从索引中读的,ALL 是从磁盘中读取的。
ALL:Full Table Scan,没有用到索引,全表扫描。
二、集合有哪些?
-
List集合:ArrayList和LinkedList,他们的区别是ArrayList是基于索引的数据接口,底层是动态数组,查询快,增删改的速度较慢
-
LinkedList是以元素列表的形式存储它的数据,底层是列表,查询慢增删快,
LinkedList比ArrayList更占用内存因为LinkedList为每一个节点存储了两个引用,一个指向前一个元素,一个指向下一个元素。
-
Set集合:HashSet、TreeSet、LinkedHashSet。
一般来说,如果我们需要保证集合元素是唯一的,就用set集合
一般我在工作中常用的就是HashSet。
-
Map集合:HashMap、LinkedHashMap、TreeMap、HashTable
HashMap是最常用的Map,他根据键的HashCode值存储数据,根据键可以直接获取他的值,具有很快的访问速度。
遍历时,取到的数据顺序是随机的。
HashMap最多只允许一条记录的键为Null,而可以允许多个记录的值为null。
HashMap不支持线程同步,是非线程安全的。
HashTable与HashMap类似,他不允许记录的键或者值为空,支持线程同步,所以在写入时较慢。
如果考虑线程安全的问题时用ConcurrentHashMap,我们很多时候把ConcurrentHashMap用于本地缓存。
-
是 P r o p e r t i e s 文 件 中 的 变 量 占 位 符 , 它 可 以 用 于 标 签 属 性 值 和 s q l 内 部 , 属 于 静 态 文 本 替 换 , 比 如 {}是 Properties 文件中的变量占位符,它可以用于标签属性值和 sql 内部,属于静态文本替换,比如 是Properties文件中的变量占位符,它可以用于标签属性值和sql内部,属于静态文本替换,比如{driver}会被静态替换为 com.mysql.jdbc. Driver。
-
#{}是 sql 的参数占位符,MyBatis 会将 sql 中的#{}替换为? 号,在 sql 执行前会使用 PreparedStatement 的参数设置方法,按序给 sql 的? 号占位符设置参数值,比如 ps.setInt(0, parameterValue),#{item.name} 的取值方式为使用反射从参数对象中获取 item 对象的 name 属性值,相当于 param.getItem().getName()。
-
定义子类继承Thread类。
-
子类中重写Thread类中的run方法。
-
创建Thread子类对象,即创建了线程对象。
-
调用线程对象start方法:启动线程,调用run方法。
/** * 多线程创建,方式一,继承Thread类 *
1. 创建一个继承与Thread类的子类 *
2. 重写Thread类的run() -->将执行的 *** 作声明在run()中 *
3. 创建Thread类的子类的对象 *
4. 通过子对象调用start() * * example:遍历100以内所有偶数 */
// 1. 创建一个继承与Thread类的子类 class MyThread extends Thread {
// 2. 重写Thread类的run()
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if(i % 2 == 0){
System.out.println(i); } } } }
public class ThreadTest {
public static void main(String[] args) {
// 3. 创建Thread类的子类的对象
MyThread myThread = new MyThread();
// 4. 通过子对象调用start(),只能用start myThread.start();
//如要再启动一个线程,就需要多创建一个线程对象
MyThread myThread1 = new MyThread(); myThread1.start();
for (int i = 0; i < 100; i++) { System.out.println(i + "*********main()***********"); } } }
方式二:实现Runnable接口
-
定义子类,实现Runnable接口。
-
子类中重写Runnable接口中的run方法。
-
通过Thread类含参构造器创建线程对象。
-
将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。
-
调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法
class MThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) { i
f(i % 2 == 0){ System.out.println(Thread.currentThread().getName() + ":" + i); } } } }
public class TreadTest1 {
public static void main(String[] args) {
MThread mThread = new MThread();
Thread t1 = new Thread(mThread);
t1.setName("线程一");
t1.start();
Thread t2 = new Thread(mThread);
t2.setName("线程二");
t2.start(); } }
方式三:实现Callable接口
-
步骤
-
创建Callable的实现类
-
重写call方法,将线程的任务代码封装到call方法中
-
创建Callable接口实现子类的对象;
-
创建FutureTask的对象,将此Callable接口实现类的对象作为构造器的参数进行传递
-
创建Thread对象,并调用start()。将FutureTask的对象作为参数传递到Thread类的构造器中
-
获取Callable中call方法的返回值。get() 返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值
package com.zzq.thread; i
mport java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class MyCallable {
public static void main(String[] args) {
// 3、创建Callable接口实现子类的对象
Demo3 d = new Demo3();
// 4、创建FutureTask的对象,将此Callable接口实现类的对象作为构造器的参数进行传递
FutureTask futureTask = new FutureTask(d);
// 5、创建Thread对象 T
hread thread = new Thread(futureTask); thread.start();
Object sum;
try {
//6、获取Callable中call方法的返回值
sum = futureTask.get();
System.out.println("总和是:" + sum); } catch (InterruptedException e) { e.printStackTrace(); }
catch (ExecutionException e) { e.printStackTrace(); } } }
//1、创建Callable的实现类
class Demo3 implements Callable {
//2、重写call方法,将线程的任务代码封装到call方法中
@Override
public Object call() throws Exception {
// 遍历1-20打印出偶数之和
int sum = 0;
for (int i = 1; i <= 20; i++) {
if ((i & 1) == 0) { // i%2 == 0 System.out.println(i);
sum += i; } }
return sum; } }
方式四:使用线程池
-
步骤
-
提供指定线程数量的线程池
-
执行指定的线程的 *** 作,需要提供实现Runnable接口或Callable接口实现类的对象
-
Runnable
-
Callable
-
关闭连接池
package com.zzq.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class MyExecutors {
public static void main(String[] args) {
//1、提供指定线程数量的线程池
ExecutorService executors = Executors.newFixedThreadPool(10);
//2、执行指定的线程的 *** 作,需要提供实现Runnable接口或Callable接口实现类的对象
Future future = executors.submit(new demo4());
// 适用于Callable
try {
Thread.sleep(5000);// 先延迟5秒 System.out.println("输出结果"+future.get()); } c
atch (InterruptedException e) { e.printStackTrace(); }
catch (ExecutionException e) { e.printStackTrace(); }
finally {
//3. 关闭连接池
executors.shutdown(); } } }
class demo4 implements Callable {
// 2. 重写call方法,将此线程需要执行的 *** 作声明在call中 @Override
public Object call() throws Exception {
// 遍历1-20打印出奇数之和
int sum = 0;
for (int i = 1; i <= 20; i++) {
if ((i & 1) != 0) {
System.out.println(i +" "+ Thread.currentThread().getName());
sum += i; } }
return sum; } }
五、怎么保证线程安全,举了一个场景让判断?
-
参考连接如下:
-
https://blog.csdn.net/weixin_49986365/article/details/111309663
- 主线程执行完,子线程也就执行完了,不存在阻塞了
- 核心注解是@SpringBootApplication,它主要由@SpringBootConfiguration,@EnableAutoConfiguration和@ComponentScan这三个构成
- 代码:
public static void main(String[] args) {
int num1 = 10;
int num2 = 20;
swap(num1, num2);
System.out.println("num1 = " + num1); System.out.println("num2 = " + num2); }
public static void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
System.out.println("a = " + a);
System.out.println("b = " + b); }
- 输出:
a = 20 b = 10 num1 = 10 num2 = 20
通过上面例子,我们已经知道了一个方法不能修改一个基本数据类型的参数,而对象引用作为参数就不一样,请看案例2。
案例2:传递引用类型参数 int[] arr = { 1, 2, 3, 4, 5 }; System.out.println(arr[0]);
change(arr);
System.out.println(arr[0]); }
public static void change(int[] array) {
// 将数组的第一个元素变为0
array[0] = 0; }
输出
10
九、git提交代码的步骤?
-
三部曲:
-
git add .
-
git commit -m ‘message’
-
git push origin XXX:XXX
-
1.首先看自己在哪个分支,使用git status查看:查看后是这个界面:
-
2.确认是在自己的分支后,先将工作区的内容提交到暂存区,可提交多个文件到暂存区,提交的命令是:git add .提交后没有提示就是成功了,
-
3.然后将暂存区的提交到本地生成一个节点:git commit -m ‘message’提交后会提示类似如下: 2 files changed, 278 insertions(+)代表提交到本地成功了,进行下一步
-
4.然后将暂存区的内容提交到远程服务器,使用git push origin XXX:XXXXXX是自己的分支名
-
提交后是这个界面:
- 代表提交到远程服务器成功了,可以在github项目上看到提交了。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)