java实战面试题

java实战面试题,第1张

实战面试题

国际劳动节又称“国际示威游行日”是世界上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 视图 来查询哪些索引从未被使用。
3. 查看执行计划

(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 等的查询。这种范围扫描索引比全表扫描要好,因为它只需要开始于索引树的某一点,而结束于另一点,不用扫描全部索引。            
indexFull Index Scan,全索引扫描,indexALL的区别为 index 类型只遍历索引树。      也就是说虽然 ALLindex 都是读全表,但是 index 是从索引中读的,ALL 是从磁盘中读取的。            
ALLFull 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用于本地缓存。

三、Mybatis中#{}和¥{}的区别?
  • 是 P r o p e r t i e s 文 件 中 的 变 量 占 位 符 , 它 可 以 用 于 标 签 属 性 值 和 s q l 内 部 , 属 于 静 态 文 本 替 换 , 比 如 {}是 Properties 文件中的变量占位符,它可以用于标签属性值和 sql 内部,属于静态文本替换,比如 Propertiessql{driver}会被静态替换为 com.mysql.jdbc. Driver。

  • #{}是 sql 的参数占位符,MyBatis 会将 sql 中的#{}替换为? 号,在 sql 执行前会使用 PreparedStatement 的参数设置方法,按序给 sql 的? 号占位符设置参数值,比如 ps.setInt(0, parameterValue),#{item.name} 的取值方式为使用反射从参数对象中获取 item 对象的 name 属性值,相当于 param.getItem().getName()。

四、多线程的创建方式? 方式一:继承Thread类
  • 定义子类继承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

六、主线程执行完,子线程会用什么方式阻塞?
  • 主线程执行完,子线程也就执行完了,不存在阻塞了
七、SpringBoot的核心注解是什么?
  • 核心注解是@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项目上看到提交了。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/794546.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-06
下一篇 2022-05-06

发表评论

登录后才能评论

评论列表(0条)

保存