java 面试整理

1 篇文章 0 订阅

目录

1  多线程

 线程与进程的区别

创建线程的方式: 

线程池

2 数据库优化

3 mybatis 防止sql注入

4 springboot 单元测试

5 如何设计一个高并发的系统

6  反射

7 session与cookie的区别

8 springMVC

9 java中存储金钱使用什么数据类型

10 static关键字的作用

11 util.date 与sql.date的区别

12 String,StringBuffer,StringBuilder的区别

13 如何将gb2312编码的字符串转换成 ISO-8859-1

14 设计模式及常见设计模式

常见设计模式

15 数据库的事务隔离级别有哪些,各自的定义是什么,  mysql默认的隔离级别是什么

16 集合排序的方式

17 http中get与post的区别

18 mybatis #与$ 的区别

19 mybatis 实现动态sql的元素主要有哪些

20 el表达式的隐式对象 (相当于jsp的内置对象)

21 web项目中如何获取spring的IOC容器

22 Spring注解@Resource和@Autowired区别对比

23 Spring AOP动态代理的方式 

24 ==与equals的区别

25 String,StringBuffer,StringBuilder的区别

26 集合框架(容器)介绍

27 MVC的各部分的实现技术

28 关系型数据库的三大范式

29 数据库事务的基本特征(ACDI)

30 常用linux指令

31 struts2 流程

32 springMvc与Struts2的区别

33 spring的两大核心 IOC AOP

34 mybatis与hibernate的区别

35 hibernate 映射对象的状态

36 hibernate 的缓存

37 查找和定位慢查询

38 mysql中 myisam与innodb的区别

39 索引的优缺点及使用场景

40 数据库分表

41spring优点 

42 序列化与反序列化

为什么需要序列化与反序列化

JDK类库中序列化和反序列化API

43 介绍下java io

44 springboot 常用注解

45 web.xml 文件的作用

46Runnable和Callable的区别

47 springboot 启动过程

48 @Configuration 和 @Component 区别

49 spring如何解决循环依赖问题

50 抽象类和接口的区别

51遍历map

52 过滤器和拦截器的区别

两者的区别

53 final关键字的作用

54 sql语句优化

55 缓存的使用

56 set如何保证不重复

57 数据库主键生成策略

58 消息队列重复消费



1  多线程

 线程与进程的区别

进程:是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态概念,竞争计算机系统资源的基本单位。

线程:是进程的一个执行单元,是进程内科调度实体。比进程更小的独立运行的基本单位。线程也被称为轻量级进程。

一个程序至少一个进程,一个进程至少一个线程。

创建线程的方式: 

继承Thread类,实现Runnable接口

public class MyThread extends Thread {
    public void run() {
        System.out.println("MyThread run");
    }; 
}
public class MyRunnable implements Runnable {

    public void run() {
        System.out.println("MyRunnable run");
    }

}

启动线程

Thread t1= new Thread(new MyRunnable());

t1.setName("线程1"); //为了区分线程,可以在创建线程之后为线程设置名称
        MyThread m1=new MyThread();
        t1.start();  //启动线程使用start()
        m1.start();


线程池获取当前线程的名称 Thread.currentThread().getName()

  线程池的作用 

1 限定线程的个数,不会由于线程过多导致程序运行缓慢或者崩溃

2 线程池不需要每次去创建或者销毁,节约空间

3 相应时间更快

从jdk5之后,内置线程池,顶级接口为 Executor

Executors 类中提供了多种静态方式生成线程池

new FixedThreadPool   创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

new CachedThreadPool 创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程, 当任务数增加时,此线程池又可以智能的添加新线程来处理任务。 此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

new ScheduledThreadPool 创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

new SingleThreadExecutor:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务.  如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

但是阿里代码规约里面推荐使用ThreadPoolExecutor 来创建线程池

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {

 corePoolSize:核心线程数量,会一直存在,除非allowCoreThreadTimeOut设置为true
maximumPoolSize:线程池允许的最大线程数量
keepAliveTime:线程数量超过corePoolSize,空闲线程的最大超时时间
unit:超时时间的单位
workQueue:工作队列,保存未执行的Runnable 任务
threadFactory:创建线程的工厂类
handler:当线程已满,工作队列也满了的时候,会被调用。被用来实现各种拒绝策略。

一个简单的示例

 ExecutorService executorService = new ThreadPoolExecutor(10, 10, 2L,
                                         TimeUnit.SECONDS,
                                         new ArrayBlockingQueue<>(10));
        executorService.execute(() -> System.out.println("线程池"));

2 数据库优化

(mysql)

1 选用最适合的数据类型及其长度

2  在可能的情况下,把数据设置为非空,查询的时候,不用再做NULL 比较

3 使用join代替子查询

连接(JOIN)..之所以更有效率一些,是因为MySQL不需要在内存中创建临时表来完成这个逻辑上的需要两个步骤的查询工作。

4 使用外键

5 使用索引 InnoDB建议大部分表使用默认的自增的主键作为索引

6 优化查询语句 

a 同类型的字段进行比较 

b  尽量不要在使用索引的字段上进行函数操作 (会使索引无效)

7 分表 当一张表的数据比较多,或者字段值比较多的时候

8 读写分离: 

9 缓存,使用redis

3 mybatis 防止sql注入

使用#{} 的格式  例如.like 写的时候 使用 like #{name}   其中 name的形式 类似于  %a% 

4 springboot 单元测试

Spring Boot干货系列:(十二)Spring Boot使用单元测试 | 嘟嘟独立技术

如何设计一个高并发的系统

1) 数据库的优化,包括合理的事务隔离级别、SQL语句优化、索引优化

2) 使用缓存、尽量减少数据库IO

3) 分布式数据库、分布式缓存

4) 服务器的负载均衡

6  反射

反射指程序在运行期间可以访问、检测和修改它本身状态或行为的一种能力

反射机制: 在运行状态中,对于任意一个类,都能够知道这个类的属性和方法;对任意一个对象都能够通过反射机制调用一个类的任意方法,这种动态获取类的信息及动态调用类方法的功能叫做反射机制

7 session与cookie的区别

相同点 : 都是回话跟踪技术,coolie通过在客户端记录的信息确定用户身份,session通过服务端的记录信息确定用户身份,但是session的实现依赖于cookie,sessionId的唯一标识需要存放到客户端

不同点: 

 1 session在服务端,cookie在客户端

2 cookie存在本地,不是很安全,可以通过分析本地的cookie,进行cookie欺骗 ,考虑到安全,使用session

3 session保存在服务器上,访问量增多时,会增加服务器的压力,考虑到服务器性能使用cookie 因此可以把安全性较低的数据存在cookie中,安全性较高的数据存在session中

2 单个cookie保存的数据不能超过4K,很多浏览器都限制一个session最多保存20个cookie

3 session 的运行依赖 session id,而 session id 是存在 cookie 中的,也就是说,如果浏览器禁用了 cookie ,同时 session 也会失效(但是可以通过其它方式实现,比如在 url 中传递 session_id)

使用场景

登录信息及其他重要信息存到session中

其他信息存到cookie中

购物车最好使用cookie,如果本地禁用cookie,可以使用cookie+数据库来实现,不能从cookie中获取数据时,就从数据库获取

8 springMVC

1 用户发送请求,被spring的 DispatcherServlet捕获

2 DispatcherServlet对请求的URL进行解析,得到请求的标识符(URI),然以后根据URI,调用HandlerMapping获得该Handler配置的所有的相关对象

3 DispatcherServlet 根据Handler,选择一个合适的HandlerAdapter,提取到Request中的模型数据,填充Handler入参,开始执行Handler(Controller),执行完成后,想DispatcherServlet返回一个ModelandView对象

4 DispatcherServlet根据返回的ModelAndView,选择一个合适的ViewResolver

5 通过ViewResolver结合Model和View来渲染视图,DispatcherServlet将渲染后的视图返回给客户端

捕获请求=>查找handler=>执行handler=>选择合适的viewResolver=>渲染视图并返回

9 java中存储金钱使用什么数据类型

effectjava中给出建议,java中 如果数值范围没有超过9位10进制,可以使用int; 如果不超过18位数字,可以使用long,如果数值可能超过18位数字,就必须使用BigDecimal

int,long的需要以分为单位,而不是元

因为BigDecimal 与基本数据类型相比,操作不方便,执行慢, 优点是可以完全控制舍入

10 static关键字的作用

  static 静态修饰符

系统在编译之后会为静态变量,创建一个固定位置,固定大小的区域存放,可以很方便的找到

static修饰的变量

 static int num=5;

静态变量在类加载时就会分分配内存,在实例化之前进行, 所以可以直接通过 类名访问

static 修饰方法

public static void main(){ ...}

静态方法亦可以直接通过类名调用,任何实例都可以访问,静态方法中不能使用this,super,不能直接访问所属类的非静态变量和非静态方法

static 代码块

static { ...

}

静态代码块的位置可以放到类中的合适位置,在构造方法之间加载,多个静态块按照先后顺序加载,且只会加载一次

11 util.date 与sql.date的区别

java.sql.Date ,是针对sql语句使用的,只包含日期没有时间,

java.util.Date是java.sql.Date的父类,用在除sql语句外的情况

都有getTime() 得到毫秒数

java.util.Date d = new java.util.Date(sqlDate.getTime());

12 String,StringBuffer,StringBuilder的区别

String是不可变的字符串,

StringBuilder: 可变的字符串,速度快,线程不安全

StringBuffer : 可变字符串,线程安全

13 如何将gb2312编码的字符串转换成 ISO-8859-1

new String( gbStr.getBytes("GB2312")   ," ISO-8859-1")

14 设计模式及常见设计模式

什么是设计模式 :  经过前人在实践中总结的,设计过程中可以反复使用的,解决特定问题的设计方法

设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。(百度百科)

使用设计模式的好处: 为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化

常见设计模式

单例模式: 

public class Singtone {

    private Singtone() {    
    }
    private static Singtone singtone =new Singtone();
    
    public static  Singtone get() {
        return singtone;
    } 
}
public class Single {
    private Single() {    
    }
    private static Single singtone =null;
    
    public synchronized static  Single get() {
        if(singtone==null) {
            singtone =new Single();
        }
        return singtone;
    } 
}

 
适配器模式 : 接口中有多个方法,一个适配器进行空实现, 子类只需要继承适配器,只需要对需要用到的方法进行修改工厂模式: 对象的创建交个一个工厂,

代理模式 :spring的AOP

15 数据库的事务隔离级别有哪些,各自的定义是什么,  mysql默认的隔离级别是什么

  ① Serializable (串行化):可避免脏读、不可重复读、幻读的发生。

  ② Repeatable read (可重复读):可避免脏读、不可重复读的发生。

  ③ Read committed (读已提交):可避免脏读的发生。

  ④ Read uncommitted (读未提交):最低级别,任何情况都无法保证。

以上四种隔离级别最高的是Serializable级别,最低的是Read uncommitted级别,当然级别越高,执行效率就越低。像Serializable这样的级别,就是以锁表的方式(类似于Java多线程中的锁)使得其他的线程只能在锁外等待,所以平时选用何种隔离级别应该根据实际情况。在MySQL数据库中默认的隔离级别为Repeatable read (可重复读)。

16 集合排序的方式

  1. Collections.sort(List list)  参与排序的对象需实现comparable接口,重写其compareTo()方法,方法体中实现对象的比较大小规则
  2. Collections.sort(List list,Comparator c) 自定义排序,需编写匿名内部类,先new一个Comparator接口的比较器对象c,同时实现compare()其方法; 然后将比较器对象c传给Collections.sort()方法的参数列表中,实现排序功能

17 http中get与post的区别

相同点 : get与post都是http的请求方式,用户通过不同的http请求方式完成对资源的操作. GET,POST,PUT,DELETE 对应对资源的查,改,增,删四个操作,即get一般用于获取/查询资源信息, post一般用于更新资源信息

区别 :

1 get请求提交的数据会在地址栏显示,多个参数件使用&连接,post提交地址栏不变 ,post是讲数据方到http的包体中(Request body)

2 get受浏览器地址的长度限制导致传输的数据较少,而post不会有与地址栏长度的限制

3 post比get更安全

4 参数的数据类型,get只接受ASCII字符,post没有限制

5 GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。

6 GET在浏览器回退时是无害的,而POST会再次提交请求。

7 GET请求会被浏览器主动cache,而POST不会,除非手动设置

8 GET产生一个TCP数据包;POST产生两个TCP数据包。 

get请求时,浏览器会把http header和data一起发送出去,服务器返回200

post 请求时,浏览器先发送header,服务器相应100,浏览器再继续发送data,服务器的返回200

. 在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点。

并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。

18 mybatis #与$ 的区别

   #{}: 解析为一个 JDBC 预编译语句(prepared statement)的参数标记符,一个 #{ } 被解析为一个参数占位符 。

   ${}: 仅仅为一个纯碎的 string 替换,在动态 SQL 解析阶段将会进行变量替换。不会当做字符串处理

id=50

$ ,将传入的变量写入id= ${id} => id =50

#,将变量的值传入 id= #{id} => id='50' 

19 mybatis 实现动态sql的元素主要有哪些

if

 <if test="stuName !=null and stuName != ''">
            and stu_name like '%' ||#{stuName}||'%'
        </if>


choose(when,otherwise)

choose元素相当于java语句的if … else if …else语句

<choose>
  <when test="stuName != null and stuName != ''">
         and stu_name=#{stuName}
  </when>
  <when test="stuBirthdate != null">
            and stu_birthdate=#{stuBirthdate}
 </when>
 <otherwise>
           and stu_phone=#{stuPhone}
 </otherwise>
 </choose>


trim

trim元素的主要功能是可以在自己包含的内容前加上某些前缀,也可以在其后加上某些后缀,与之对应的属性是prefix和suffix;可以把包含内容的首部某些内容覆盖,即忽略,也可以把尾部的某些内容覆盖,对应的属性是prefixOverrides和suffixOverrides 

select * from student
 <trim prefix="where" prefixOverrides="and|or">
  <if test="stuName != null and stuName != ''">
      and stu_name=#{stuName}
  </if>
  <if test="stuBirthdate != null">
      and stu_birthdate=#{stuBirthdate}
 </if>
 <if test="stuPhone != null and stuPhone != ''">
     or stu_phone=#{stuPhone}
 </if>
 </trim>


where

使用where元素会自动根据条件的个数增删where语句and运算符,所以不需要写where 1=1之类的语句

select * from student
  <where>
  <if test="stuName != null and stuName != ''">
     and stu_name=#{stuName}
  </if>
  <if test="stuBirthdate != null">
     and stu_birthdate=#{stuBirthdate}
 </if>
 <if test="stuPhone != null and stuPhone != ''">
     and stu_phone=#{stuPhone}
 </if>
 </where>
 </select>

set

set元素主要是用在更新操作的时候,它的主要功能和where元素其实是差不多的,主要是在包含的语句前输出一个set,然后如果包含的语句是以逗号结束的话将会把该逗号忽略,如果set包含的内容为空的话则会出错。有了set元素我们就可以动态的更新那些修改了的字段。

   <!-- 动态SQL:set更新 -->
 <update id="updateByCondition" parameterType="student">
     update student
      <set>
          <if test="stuName!=null and stuName!=''">
              stu_name=#{stuName},
          </if>
          <if test="stuBirthdate!=null">
              stu_birthdate=#{stuBirthdate},
         </if>
         <if test="stuPhone!=null and stuPhone!=''">
             stu_phone=#{stuPhone}
         </if>
     </set>
     where stu_id=#{stuId}
 </update>

foreach

foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合。foreach元素的属性主要有item,index,collection,open,separator,close。item表示集合中每一个元素进行迭代时的别名,index指定一个名字,用于表示在迭代过程中,每次迭代到的位置,open表示该语句以什么开始,separator表示在每次进行迭代之间以什么符号作为分隔符,close表示以什么结束,在使用foreach的时候最关键的也是最容易出错的就是collection属性,该属性是必须指定的,但是在不同情况下,该属性的值是不一样的,主要有以下3种情况。

  1. 如果传入的是单参数且参数类型是一个List的时候,collection属性值为list
  2. 如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array
  3. 如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以封装成map,实际上如果你在传入参数的时候,在MyBatis里面也是会把它封装成一个Map的,map的key就是参数名,所以这个时候collection属性值就是传入的List或array对象在自己封装的map里面的key

<!-- 动态SQL:传入Array数组 -->

<select id="queryByInArray" resultMap="BaseResultMap">
          select * from student 
       <if test="array.length>0">
           where stu_id in 
           <foreach collection="array" index="i" item="stuId" open="(" close=")" separator=",">
               #{stuId}
           </foreach>
       </if>
       </select>
    !-- 动态SQL:传入List集合 -->
       <select id="queryByInList" resultMap="BaseResultMap">
           select * from student
           <if test="list.size()>0">
               where stu_id in
               <foreach collection="list" index="i" item="stuId" open="("
                   close=")" separator=",">
                   #{stuId}
               </foreach>
           </if>
       </select>
    !-- 动态SQL:传入Map集合包含List集合 -->
       <select id="queryByInMap" resultMap="BaseResultMap">
           select * from student
           <if test="ids.size()>0">
               where stu_id in
               <foreach collection="ids" index="i" item="stuId" open="("
                   close=")" separator=",">
                   #{stuId}
               </foreach>
           </if>
       </select>

<SQL>和<include>

可以编写一些语句片段<SQL>标签,然后在其他语句标签汇中用<include>引用,这样可以是SQL语句片段得到重用

<!-- SQL片段 -->
<SQL id="SQL_select">
         select *
 </SQL>
<SQL id="SQL_count">
         select count(*)
</SQL>

<!-- 包含SQL片段 -->
<select id="query6" resultMap="BaseResultMap">
         <include refid="SQL_select"/>
         from student
</select>

<select id="query7" resultType="java.lang.Integer">
         <include refid="SQL_count"/>
         from student
</select>

20 el表达式的隐式对象 (相当于jsp的内置对象)

page pageContext,request response

session application out config exception

21 web项目中如何获取spring的IOC容器

new ClassPathXmlApplicationContext("bean.xml")

基于注解

new AnnotationConfigApplicationContext(MainConfig.class)

22 Spring注解@Resource和@Autowired区别对比

@Autowired为Spring提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired;只按照byType注入。是按照类型查找,如果找到多个,则去匹配ID, 也可以使用@Qualifier指定要装配的ID

@Resource默认按照ByName自动注入,由J2EE提供,需要导入包javax.annotation.Resource。@Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不制定name也不制定type属性,这时将通过反射机制使用byName自动注入策略。

@Resource装配顺序:

①如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。

②如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。

③如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。

④如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。

@Resource的作用相当于@Autowired,只不过@Autowired按照byType自动注入。

23 Spring AOP动态代理的方式 

JDK动态代理 ,需要有接口,运行时生产代理类

cglib 在运行时生成子类,然后进行增强

24 ==与equals的区别

==用来判断两个变量的值是否相同,基本数据类型比较值,引用类型比较对应内存的首地址

equals 用来比较两个对象的内存的首地址是否相同

25 String,StringBuffer,StringBuilder的区别

都是操作字符串的

String s="a"+"b";

StringBuffer sb=new StringBuffer("ab");

String是不可变字符串 private final char value[];

StringBuffer和StringBuilder是可变的字符串    char[] value; 

字符串追加

StringBuilder效率高,线程不安全

StringBuffer线程安全,效率较低

26 集合框架(容器)介绍

主要分为两大类  存储值的 Collection, 存储键值对的Map

Collection 分为两大类 List,Set

List与Set的区别

LIst是有序的.可重复的

Set是无序的,不可重复的,根据equals和hashCode进行判断

ArrayList ,LinkedList,Vector的区别

ArrayList  底层使用数组实现  transient Object[] elementData;

Vector 底层使用数组 protected Object[] elementData;

LinkedList 底层使用链表实现  transient Node<E> first;   transient Node<E> last;

ArrayList   拥有索引,查询方便,增删比较慢  (数组在内存中是一块连续的内存,增删需要移动内存)

LinkedList  增删比较快,查询比较慢 (链表不需要内存是连续的,只存放上一个地址与下一个地址增删时只需改变对应位置的引用指向 ; 查询时需要从头开始查找,效率较低)

Vector 是线程安全的ArrayList   

hashMap与hashTable,  ConcurrentHashMap 区别 

hashMap 允许key/value为null ,线程不安全

hashTable 不允许key/value为null ,线程安全

TreeMap key是可比较的(Comparable)

EnumMap, key是枚举

ConcurrentHashMap , 将集合分为几段,每段相当于一个hashTable,每段有一个锁,(首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁。这里“按顺序”是很重要的,否则极有可能出现死锁,在ConcurrentHashMap内部,段数组是final的,并且其成员变量实际上也是final的,但是,仅仅是将数组声明为final的并不保证数组成员也是final的,这需要实现上的保证。这可以确保不会出现死锁,因为获得锁的顺序是固定的。)

27 MVC的各部分的实现技术

M(model) 模型 javaBean

V(view) 视图 jsp,hrml

C(controller) 控制器 Servlet,Action

最典型的MVC就是jsp+servlet+javabean模式。

控制器收到请求之后 调用javabean完成业务, 通过页面跳转将处理后的结果反馈, jsp将返回结果进行渲染

MVC的优点:

1.耦合性

视图层和业务层分离,这样就允许更改视图层代码而不用重新编译模型和控制器代码,同样,一个应用的业务流程或者业务规则的改变只需要改动MVC的模型层即可。因为模型与控制器和视图相分离,所以很容易改变应用程序的数据层和业务规则。

2.重用性高

MVC模式允许使用各种不同样式的视图来访问同一个服务器端的代码,因为多个视图能共享一个模型,它包括任何WEB(HTTP)浏览器或者无线浏览器(wap),比如,用户可以通过电脑也可通过手机来订购某样产品,虽然订购的方式不一样,但处理订购产品的方式是一样的。由于模型返回的数据没有进行格式化,所以同样的构件能被不同的界面使用。

3.部署快,生命周期成本低

MVC使开发和维护用户接口的技术含量降低。使用MVC模式使开发时间得到相当大的缩减,它使程序员(Java开发人员)集中精力于业务逻辑,界面程序员(HTML和JSP开发人员)集中精力于表现形式上。

4.可维护性高

分离视图层和业务逻辑层也使得WEB应用更易于维护和修改。

MVC的缺点:

1.调试困难。

因为模型和视图要严格的分离,这样也给调试应用程序带来了一定的困难,每个构件在使用之前都需要经过彻底的测试。

3.不适合小型,中等规模的应用程序

在一个中小型的应用程序中,强制性的使用MVC进行开发,往往会花费大量时间,并且不能体现MVC的优势,同时会使开发变得繁琐。

4.增加系统结构和实现的复杂性

对于简单的界面,严格遵循MVC,使模型、视图与控制器分离,会增加结构的复杂性,并可能产生过多的更新操作,降低运行效率。

5.视图与控制器间的过于紧密的连接并且降低了视图对模型数据的访问

视图与控制器是相互分离,但却是联系紧密的部件,视图没有控制器的存在,其应用是很有限的,反之亦然,这样就妨碍了他们的独立重用。

依据模型操作接口的不同,视图可能需要多次调用才能获得足够的显示数据。对未变化数据的不必要的频繁访问,也将损害操作性能。

28 关系型数据库的三大范式

第一范式(1NF):确保关系型数据库中的每个列都是不可再分的原子值,即每个列不能再分解出更小的数据项。保证数据的原子性。

第二范式(2NF):在满足第一范式的基础上,消除非主键列对于主键的部分依赖。也就是说,每个非主键列都必须完全依赖于主键。通过将非主键列移动到新的表中,使每个表中只包含一个主键。

第三范式(3NF):在满足第二范式的基础上,消除非主键列之间的传递依赖。也就是说,如果非主键列依赖于其他非主键列,则必须将其移动到新的表中。这样可以避免数据冗余和数据更新异常。

 三大范式只是一般设计数据库的基本理念,可以建立冗余较小、结构合理的数据库。如果有特殊情况,当然要特殊对待,数据库设计最重要的是看需求跟性能,需求>性能>表结构。所以不能一味的去追求范式建立数据库。

29 数据库事务的基本特征(ACDI)

原子性,事务内部不可分分割

一致性: ,只能同时成功或者同时失败,失败则回滚 ,一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态

隔离性,一个事务开始后,不受其它事务的影响

,持久性: 持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的

30 常用linux指令

pwd 获取当前路径

su  切换到管理员

ll   ls

文件操作命令

tail 

rm -rf  删除

vi/vim 编辑

top 

netstat

grep   查看端口

less 查看

touch 生成文件

文件夹

mkdir 创建文件夹

31 struts2 流程

1 浏览器发送请求之后,经过一系列的过滤器,到达核心过滤器(strutsprepareAndExecuteFilter)

2 核心过滤器通过ActionMapper判断是否需要对请求进行处理,如果不需要,则放行,如果需要,则把请求交给ActionProxy处理

3 ActionProxy通过configuration Manager找到配置文件struts.xml,找到需要调用的Action

4 创建一个ActionInvoction实例,来调用Action的对应方法来获取结果集的name,在调用前后会执行相关拦截器

5 通过结果集的name知道对应的结果集来对浏览器进行响应

32 springMvc与Struts2的区别

1 核心控制器不同: 核心控制器是用来处理所有的请求,springmvc是Servlet,struts2是filter

2 控制器实例不同: 理论上springmvc 比Struts2要快,因为springmvc是基于方法设计的,Struts2是基于对象设计的,每次请求,都会实例一个Action,每个Action被注入属性,springmvc是基于servlet,只有一个实例,每次请求调用方法即可

3 管理方式: 大部分的项目中都会使用spring,而sprinmvc是spring的一个模块.spring对springmvc的控制管理更加方便,而Struts2使用的是xml配置的方式

4 参数传递: Struts2提供多种参数接受的方式,实际上都是值栈(valuestack)传值,而springmvc是通过方法参数传值

5 拦截器的实现机制:Struts2有自己的拦截器机制,springmvc是独立的aop方式

6 springMvc处理ajax请求时,直接返回数据,直接添加@responseBody

33 spring的两大核心 IOC AOP

IOC,控制反转

spring发现A需要使用到B的时候,自动注入

核心原理就是  容器+反射+配置文件

AOP,面向切面编程

核心原理: 使用动态代理的方式在执行方法前后或者异常 后做相关的逻辑

 主要使用场景: 

1 事务处理 执行前,开启事务,执行后关闭事务,出现异常回滚

2 权限判断 执行前.判断是否有权限

3 日志

34 mybatis与hibernate的区别

相同点: 都是orm框架,屏蔽了jdbc api的底层访问细节,可以快速完成对数据库的持久化操作

不同点:

1  hibernate是全自动的orm映射工具,能够自动生成sql语句

2 mybatis需要在xml中写sql语句,hibernate无法直接控制sql语句,对于一些简单的sql查询,hibernate更高效,对于一些特别复杂的查询.,mybatis是更好的选择,因为可以自己写sql语句

3 mybatis是面向sql的,不用考虑对象间的映射关系

35 hibernate 映射对象的状态

瞬时状态: 刚刚new出来,没有持久化,不处于sesion中

持久化对象: 已经被持久化的对象,加入到session缓存中,sesion是没有关闭的状态

游离状态; 已被持久化,但是不处于session中

删除状态: 对象有关联的ID,并且在session管理下,但是已被计划(事务提交的时候.commit)删除

36 hibernate 的缓存

为了提高访问速度,把磁盘或者数据库访问变成内存访问

一级缓存: session级别缓存

session缓存内置不能被卸载,session缓存是事务范围内的缓存,一级缓存中,持久化类的每个实例都具有唯一idoid

二级缓存 sessionFactory级别缓存,是可选的,从应用启动到应用结束有效,(使用ehcache开启)

适合放到二级缓存中的数据

1 很少被修改的数据

2 经常被查询的数据

37 查找和定位慢查询

在项目自验转测试之前,在启动mysql数据库时,开启慢查询,把执行慢的语句写到日志中,在运行一段时间后,查看日志,找到慢查询语句

使用explain慢查询语句,详细分析语句的问题

38 mysql中 myisam与innodb的区别

1 事务安全 myisam不支持事务,innodb支持事务

2 查询与增加销量, myisam查询与添加的速度快(不支持事务)

3 myisam只是全文索引,innodb不支持

4 mysiam支持表锁,innodb支持行锁(事务)

5mysim不支持外键,innodb支持外键, 

39 索引的优缺点及使用场景

优点

提高查询效率

缺点: 

占用磁盘空间

需要维护

因此,索引的使用场景应该是

 1 字段内容不是频繁变化的

2 字段的内容不是唯一的几个值

3 在查询时经常使用到的,(where条件经常使用)

使用技巧

1 创建多列索引(复合索引),不是使用第一部分就不会使用索引

例如: 一个索引  myindex(name,add)

如果查询的条件中 where name='zs' 会使用到索引

where add='sh' 不会使用到索引

2 使用like查询时,如果 %a 不会使用到索引,使用 a% 则会使用到索引

 3 如果条件中有or ,如果有条件没有索引,则其它条件是否有索引都不会使用

4 如果列类型是字符串,必须把条件中的数据使用引号引起来,否则不使用索引

5 如果mysql估计使用全表扫描要比使用索引快,则不使用索引(表中只有一条记录)

40 数据库分表

分为水平(按照行)分表和垂直(按照列)分表

mysql表中数据达到百万,查询效率会下降,可以考虑水平分表

 水平分表策略

  1 按时间分表

 具有一定的局限,当数据有较强的时效性,例如微信记录消息只有很少的用户会查询几个月的数据,可以进行按月分表

2 按区间范围分表

一般在严格的自增id上

分表1  1-100W 

分表2  100W-200W

...

3 hash分表

通过一个原始目标的id或者名称通过一定的hash算法计算出数据存储表的表名,然后访问相应的表

垂直分表

如果一张表中某个字段的值非常多(长文本,二进制等),而且只有在很少的情况下才会查询,这时候可以把字段单独放到一个 表中,,听过外键关联起来

41spring优点 


1 方便解耦,简化开发
spring就是一个工厂(容器),可以将所有对象创造和依赖关系维护,交给spring管理
spring工厂用于生产bean
2 aop编程支持
 面向切面编程,可以方便的实现对程序进行权限拦截,运行监控等
3 声明式事务的支持
 只需要通过配置就可以完成对事务的管理,无需手动编程\
4 方便集成各种优秀的框架
5降低javaEE API的使用难度
jdbc  

42 序列化与反序列化

序列化就是将java对象转为字节对象的过程,反序列化就是将字节对恢复为java对象的过程

序列化:对象序列化的最主要的用处就是在传递和保存对象的时候,保证对象的完整性和可传递性。序列化是把对象转换成有序字节流,以便在网络上传输或者保存在本地文件中。序列化后的字节流保存了Java对象的状态以及相关的描述信息。序列化机制的核心作用就是对象状态的保存与重建。

反序列化:客户端从文件中或网络上获得序列化后的对象字节流后,根据字节流中所保存的对象状态及描述信息,通过反序列化重建对象。

为什么需要序列化与反序列化

我们知道,当两个进程进行远程通信时,可以相互发送各种类型的数据,包括文本、图片、音频、视频等, 而这些数据都会以二进制序列的形式在网络上传送。

那么当两个Java进程进行通信时,实现进程间的对象传送,就需要Java序列化与反序列化了!

换句话说,一方面,发送方需要把这个Java对象转换为字节序列,然后在网络上传送;另一方面,接收方需要从字节序列中恢复出Java对象。

当我们明晰了为什么需要Java序列化和反序列化后,我们很自然地会想Java序列化的好处。其好处一是实现了数据的持久化,通过序列化可以把数据永久地保存到硬盘上(通常存放在文件里),二是,利用序列化实现远程通信,即在网络上传送对象的字节序列。

总的来说可以归结为以下几点:

(1)永久性保存对象,保存对象的字节序列到本地文件或者数据库中; 
(2)通过序列化以字节流的形式使对象在网络中进行传递和接收; 
(3)通过序列化在进程间传递对象;

JDK类库中序列化和反序列化API

(1)java.io.ObjectOutputStream:表示对象输出流;

它的writeObject(Object obj)方法可以对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中;

(2)java.io.ObjectInputStream:表示对象输入流;

它的readObject()方法源输入流中读取字节序列,再把它们反序列化成为一个对象,并将其返回;
注意: 实现了Serializable接口的类的对象才能被序列化,否则抛出异常!

public class SerialDemo {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //序列化
        FileOutputStream fos = new FileOutputStream("object.out");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        User user1 = new User("shj", "123456", "male");
        oos.writeObject(user1);
        oos.flush();
        oos.close();
        //反序列化
        FileInputStream fis = new FileInputStream("object.out");
        ObjectInputStream ois = new ObjectInputStream(fis);
        User user2 = (User) ois.readObject();
        System.out.println(user2.getUserName()+ " " + 
            user2.getPassword() + " " + user2.getSex());
        //反序列化的输出结果为:shj 123456 male
    }
}

public class User implements Serializable {
    private String userName;
    private String password;
    private String sex;
    //全参构造方法、get和set方法省略
}

43 介绍下java io

字节输入流 InputStream 
字节输出流 OutputStream
字符输入流 Reader
字符输出流 Writer

InputStream read返回int (0-255) -1表示读到文件末尾

BufferedReader readLine   一次度一行, null表示读到文件末尾

字节流转为字符流 

New OutputStreamWriter(new FileOutputStream(File file))

package com.example.demo.io;

import java.io.*;

public class MyInputStream {
    private static String FILE_NAME = "D:/新的资源.txt";
    private static String NEW_FILE = "D:/new的资源.txt";
    private static String OUT_FILE = "D:/writer的资源.txt";

    public static void main(String[] args) {
        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream = null;
        OutputStreamWriter outputStreamWriter = null;
        InputStreamReader inputStreamReader = null;
        try {
                fileInputStream = new FileInputStream(FILE_NAME);
                fileOutputStream = new FileOutputStream(NEW_FILE);
                outputStreamWriter = new OutputStreamWriter(new FileOutputStream(OUT_FILE));
                inputStreamReader = new InputStreamReader(fileInputStream);
                int bytes;
//                while( (bytes = fileInputStream.read()) != -1) {
//                    fileOutputStream.write(bytes);
//                }
                while(  (bytes = inputStreamReader.read()) != -1) {
                    outputStreamWriter.write(bytes);
                }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                fileInputStream.close();
                fileOutputStream.close();
                outputStreamWriter.close();
                inputStreamReader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
package com.example.demo.io;

import java.io.*;

public class MyPrint {
    private static String FILE_NAME = "D:/新的资源.txt";
    private static String NEW_FILE = "D:/printwriter.txt";

    public static void main(String[] args) {
        BufferedReader bufferedReader = null;
        PrintWriter printWriter = null;

        char [] chars = new char[1024];
        try {
            bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(FILE_NAME)));
            printWriter = new PrintWriter(new OutputStreamWriter(new FileOutputStream(NEW_FILE)));
            String  str = null;
            while( (str = bufferedReader.readLine()) != null) {
                printWriter.write(str + "\n");
            }


        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                bufferedReader.close();
                printWriter.close();

            } catch (IOException e) {
                e.printStackTrace();
            }
        }


    }
}

44 springboot 常用注解

@SpringBootApplication

@Controller 

@RestController 

@ResponseBody

@RequestMapping

@Autowired:

@Configuration
@Component

@Service

@Resource

45 web.xml 文件的作用

1指定欢迎页面,例如: 
<welcome-file-list> 
  <welcome-file-list> 
    <welcome-file>index.jsp</welcome-file> 
    <welcome-file>index1.jsp</welcome-file> 
  </welcome-file-list> 

2 定制初始化参数(init-param):可以定制servlet、JSP、Context的初始化参数,然后可以再servlet、JSP、Context中获取这些参数值。 

<servlet> 
    <servlet-name>servlet1</servlet-name> 
    <servlet-class>org.whatisjava.TestServlet</servlet-class> 
    <init-param> 
          <param-name>userName</param-name> 
          <param-value>Daniel</param-value> 
    </init-param> 
    <init-param> 
          <param-name>E-mail</param-name> 
          <param-value>125485762@qq.com</param-value> 
    </init-param> 
</servlet> 

3 指定错误处理页面,可以通过“异常类型”或“错误码”来指定错误处理页面

<error-page>
    <error-code>404</error-code>
    <location>/error404.jsp</location>
</error-page>

4 设置过滤器

<filter> 
    <filter-name>XXXCharaSetFilter</filter-name> 
    <filter-class>net.test.CharSetFilter</filter-class> 
</filter> 
<filter-mapping> 
    <filter-name>XXXCharaSetFilter</filter-name> 
    <url-pattern>/*</url-pattern> 
</filter-mapping> 

5 设置监听器

<listener> 
<listener-class>net.test.XXXLisenet</listener-class> 
</listener> 

6设置会话(Session)过期时间,其中时间以分钟为单位,假如设置60分钟超时: 
<session-config> 
<session-timeout>60</session-timeout> 
</session-config>

46Runnable和Callable的区别

(1)Runnable是自从java1.1就有了,而Callable是1.5之后才加上去的
(2)Callable规定的方法是call(),Runnable规定的方法是run()
(3)Callable的任务执行后可返回值,而Runnable的任务是不能返回值(是void)
(4)call方法可以抛出异常,run方法不可以
(5)运行Callable任务可以拿到一个Future对象,表示异步计算的结果。
它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。
通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。
(6)加入线程池运行,Runnable使用ExecutorService的execute方法,Callable使用submit方法。

47 springboot 启动过程

48 @Configuration 和 @Component 区别

@Configuration 标记的类必须符合下面的要求:

  • 配置类必须以类的形式提供(不能是工厂方法返回的实例),允许通过生成子类在运行时增强(cglib 动态代理)。
  • 配置类不能是 final 类(没法动态代理)。
  • 配置注解通常为了通过 @Bean 注解生成 Spring 容器管理的类,
  • 配置类必须是非本地的(即不能在方法中声明,不能是 private)。
  • 任何嵌套配置类都必须声明为static
  • @Bean 方法可能不会反过来创建进一步的配置类(也就是返回的 bean 如果带有 @Configuration,也不会被特殊处理,只会作为普通的 bean)。

Spring 容器在启动时,会加载默认的一些 PostPRocessor,其中就有 ConfigurationClassPostProcessor,这个后置处理程序专门处理带有 @Configuration 注解的类,这个程序会在 bean 定义加载完成后,在 bean 初始化前进行处理。主要处理的过程就是使用 cglib 动态代理增强类,而且是对其中带有 @Bean 注解的方法进行处理。

@Component 注解并没有通过 cglib 来代理@Bean 方法的调用

有些特殊情况下,我们不希望 MyBeanConfig 被代理,可以使用Component 

public class Cars {
}
public class Drivers {
    private Cars cars;
    public Drivers(Cars cars) {
        this.cars = cars;
    }
    public Cars getCars() {
        return cars;
    }
}

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

//@Configuration
@Component
public class MyTest {

        @Bean
        public Drivers driver(){
            Drivers driver = new Drivers(car());
            return driver;
        }

        @Bean
        public Cars car(){
            Cars car = new Cars();
            return car;
        }
    }

 测试

    @Autowired
    Drivers drivers;

    @Autowired
    Cars cars;
    @Test
    void getUserInfo(){
        System.out.println(drivers.getCars() == cars);
    }

 false

将MyTest的注解改为@Configuration 

结果为true

继续修改MyTest

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

//@Configuration
@Component
public class MyTest {

    @Autowired
    private Cars cars;
        @Bean
        public Drivers driver(){
            Drivers driver = new Drivers(cars);
            return driver;
        }

        @Bean
        public Cars car(){
            Cars car = new Cars();
            return car;
        }
    }

测试结果为true 

49 spring如何解决循环依赖问题

循环依赖问题就是A->B->A,spring在创建A的时候,发现需要依赖B,因为去创建B实例,发现B又依赖于A,又去创建A,因为形成一个闭环,无法停止下来就可能会导致cpu计算飙升

如何解决这个问题呢?spring解决这个问题主要靠巧妙的三层缓存,所谓的缓存主要是指这三个map,singletonObjects主要存放的是单例对象,属于第一级缓存;singletonFactories属于单例工厂对象,属于第三级缓存;earlySingletonObjects属于第二级缓存,如何理解early这个标识呢?它表示只是经过了实例化尚未初始化的对象。Spring首先从singletonObjects(一级缓存)中尝试获取,如果获取不到并且对象在创建中,则尝试从earlySingletonObjects(二级缓存)中获取,如果还是获取不到并且允许从singletonFactories通过getObject获取,则通过singletonFactory.getObject()(三级缓存)获取。如果获取到了则移除对应的singletonFactory,将singletonObject放入到earlySingletonObjects,其实就是将三级缓存提升到二级缓存,这个就是缓存升级。spring在进行对象创建的时候,会依次从一级、二级、三级缓存中寻找对象,如果找到直接返回。由于是初次创建,只能从第三级缓存中找到(实例化阶段放入进去的),创建完实例,然后将缓存放到第一级缓存中。下次循环依赖的再直接从一级缓存中就可以拿到实例对象了。

50 抽象类和接口的区别

抽象类不能被实例化, 依然可以在类中定义成员变量,成员方法,构造方法等

抽象类使用abstract 修饰, 抽象类可以被继承extends

抽象类中可以有非抽象方法

抽象方法可以有public、protected和default这些修饰符 

接口使用interface修饰

接口可以被实现 implements, 接口可以多实现

接口可以多继承

public interface ObserveFace extends Observer, Quackable {
}

接口中的方法都是抽象方法, 必须是public修饰

接口没有构造方法

51遍历map

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

// 遍历map
public class MapIterator  {
    public static void main(String[] args) {
        Map<Integer, String> map = getMaps();
        System.out.println(map);
        Set<Integer>  keys = map.keySet();
        Iterator iterator = keys.iterator();
        while (iterator.hasNext()) {
            Integer key = (Integer) iterator.next();
            System.out.println(key + ":" + map.get(key));
        }
       Set<Map.Entry<Integer, String>> entries =  map.entrySet();
        iterator = entries.iterator();
        while (iterator.hasNext()) {
            Map.Entry<Integer, String> entry = (Map.Entry<Integer, String> )iterator.next();
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }

    }
    private static Map<Integer, String>  getMaps() {
        Map<Integer, String> hashMap = new HashMap<>();
        for (int i = 0; i <10 ; i++) {
            hashMap.put(i, "value" + i);
        }
        return hashMap;
    }

}

52 过滤器和拦截器的区别

过滤器(filter):

  • 过滤器处于客户端与Web资源(Servlet、JSP、HTML)之间,客户端与Web资源之间的请求和响应都要通过过滤器进行过滤。举例:在过滤器中定义了禁止访问192.10.10.1这个地址,那么当客户端发出访问192.10.10.1的请求时,经过过滤器后,客户端得到的响应是出现该IP禁止访问的提示。
  • 在java web中,你传入的request,response提前过滤掉一些信息,或者提前设置一些参数,然后再传入servlet或者struts的 action进行业务逻辑,比如过滤掉非法url(不是login.do的地址请求,如果用户没有登陆都过滤掉),或者在传入servlet或者 struts的action前统一设置字符集,或者去除掉一些非法字符

拦截器(interceptor):

  • 拦截器是一种面向方面/切面编程(AOP Aspect-Oriented Programming).
  • 面向切面就是将多个模块的的通用服务进行分离,如权限管理、日志服务,他们在多个模块中都会用到,就可以将其各自封装为一个可重用模块。而这些通用服务的具体实现是通过拦截器来完成,比如用户客户端访问一些保密模块都应先通过权限审查的拦截器来进行权限审查,确定用户是否具有该项操作的权限后方能向下执行。
  • 在面向切面编程的就是在你的service或者一个方法,前调用一个方法,或者在方法后调用一个方法比如动态代理就是拦截器的简单实现,在你调用方法前打印出字符串(或者做其它业务逻辑的操作),也可以在你调用方法后打印出字符串,甚至在你抛出异常的时候做业务逻辑的操作。

两者的区别

  • 拦截器是基于java反射机制的,而过滤器是基于函数回调。
  • 拦截器不依赖于Servlet容器,而过滤器依赖于servlet容器。
  • 拦截器只能对action请求起作用,而过滤器可以对几乎所以的请求起作用。
  • 拦截器可以访问action上下文,值栈里的对象,而过滤器不能。
  • 在Action的生命周期周,拦截器可以被多次调用,而过滤器只能在容器初始化的时候被调用一次。

执行顺序 :过滤前 - 拦截前 - Action处理 - 拦截后 - 过滤后。个人认为过滤是一个横向的过程,首先把客户端提交的内容进行过滤(例如未登录用户不能访问内部页面的处理);过滤通过后,拦截器将检查用户提交数据的验证,做一些前期的数据处理,接着把处理后的数据发给对应的Action;Action处理完成返回后,拦截器还可以做其他过程(还没想到要做啥),再向上返回到过滤器的后续操作。

拦截器 :是在面向切面编程的就是在你的service或者一个方法前调用一个方法,或者在方法后调用一个方法比如动态代理就是拦截器的简单实现,在你调用方法前打印出字符串(或者做其它业务逻辑的操作),也可以在你调用方法后打印出字符串,甚至在你抛出异常的时候做业务逻辑的操作。

过滤器:是在java web中,你传入的request,response提前过滤掉一些信息,或者提前设置一些参数,然后再传入servlet或者struts的 action进行业务逻辑,比如过滤掉非法url(不是login.do的地址请求,如果用户没有登陆都过滤掉),或者在传入servlet或者 struts的action前统一设置字符集,或者去除掉一些非法字符.

53 final关键字的作用

修饰类 类不能被继承,类中的方法

修饰方法, 不能被重写,

成员变量:  表示常量,只能被赋值一次, 声明时赋值,构造方法中赋值

局部变量:  参数列表中和调用方法中不能修改

线程中参数使用final是为了安全发布

对于final,当你创建一个对象时,使用final关键字能够使得另一个线程不会访问到处于“部分创建”的对象

因为:当构造函数退出时,final字段的值保证对访问构造对象的其他线程可见

54 sql语句优化

mysql版本(5.7)

一个test表,id自增主键, name有索引,age没有索引

数据如下

 表结构

1 避免全表扫描, 如<>, !=, like "%"

 可以看到 name为null,不会被查到, 所以在数据库设计中,尽量将字段设置为not null

2 在where和order by 字段上建立索引

3 where中判断null,如果字段没有索引, 会导致全表扫描 

 

4  使用or时,如果字段有索引,则会使用索引,如果一个有索引,一个没有索引,则会导致全表扫描

更改数据 

 

 5 主键使用or, 会导致索引失效, 可以使用union all来优化

 

 

 6 如果字段和判断的数据数据类型不一致,也会导致全表扫描

7 主键使用in和not in 会导致全表扫描, 索引列不会 

 8 

55 缓存的使用

提高访问速度

56 set如何保证不重复

以hashset为例, hashset是由hashMap实现的, add的时候,

public HashSet() {
        map = new HashMap<>();
    }

 public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

map在put时

 public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

首先根据key的hashCode()返回值决定该Entry的存储位置,如果两个key的hash值相同,那么它们的存储位置相同。如果这个两个key的equals比较返回true,那么新添加的Entry的value会覆盖原来的Entry的value

map在put时,如果key已存在,则返回旧的值,将新的值赋上去

如果已有值,则set 在add的之后,返回false 会导致添加失败

结论: map key重复时,覆盖旧的数据, 

set key重复时,不会覆盖

对 hashset来说, 只要hashcode一样,就算是一个重复的元素,新增时,就会失败

57 数据库主键生成策略

UUID

UUID是128位的全局唯一标识符,通常由32字节的字符串表示

它通过MAC地址、时间戳、命名空间、随机数、伪随机数来保证生成ID的唯一性。

优点:
简单,代码方便。
全球唯一,在遇见数据迁移,系统数据合并,或者数据库变更等情况下,可以从容应对。
  
缺点:
没有排序,无法保证趋势递增。
UUID往往是使用字符串存储,查询的效率比较低。
存储空间比较大,如果是海量数据库,就需要考虑存储量的问题。
传输数据量大
不可读。

Sequence ID 

数据库自增长序列或字段,最常见的方式。由数据库维护,数据库唯一

优点

简单,代码方便,性能可以接受。
数字ID天然排序,对分页或者需要排序的结果很有帮助。
缺点

不同数据库语法和实现不同,数据库迁移的时候或多数据库版本支持的时候需要处理。
在单个数据库或读写分离或一主多从的情况下,只有一个主库可以生成。有单点故障的风险。
在性能达不到要求的情况下,比较难于扩展。
如果遇见多个系统需要合并或者涉及到数据迁移会相当痛苦。
分表分库的时候会有麻烦。

58 消息队列重复消费

首先,比如 RabbitMQ、RocketMQ、Kafka,都有可能会出现消息重复消费的问题。因为这问题通常不是 MQ 自己保证的,是由我们开发来保证的

例如

Kafka 实际上有个 offset 的概念,就是每个消息写进去,都有一个 offset,代表消息的序号,然后 consumer 消费了数据之后,每隔一段时间(定时定期),会把自己消费过的消息的 offset 提交一下,表示“我已经消费过了,下次我要是重启啥的,你就让我继续从上次消费到的 offset 来继续消费吧”。

但是凡事总有意外,比如我们之前生产经常遇到的,就是你有时候重启系统,看你怎么重启了,如果碰到点着急的,直接 kill 进程了,再重启。这会导致 consumer 有些消息处理了,但是没来得及提交 offset,尴尬了。重启之后,少数消息会再次消费一次。

其实重复消费不可怕,可怕的是你没考虑到重复消费之后,怎么保证幂等性(幂等性,通俗点说,就一个数据,或者一个请求,给你重复来多次,你得确保对应的数据是不会改变的,不能出错)。

假设你有个系统,消费一条消息就往数据库里插入一条数据,要是你一个消息重复两次,你不就插入了两条,这数据不就错了?但是你要是消费到第二次的时候,自己判断一下是否已经消费过了,若是就直接扔了,这样不就保留了一条数据,从而保证了数据的正确性。

一条数据重复出现两次,数据库里就只有一条数据,这就保证了系统的幂等性。

59 数组转list

1 Arrays.asList(arr)

Integer [] arr = new Integer[6];
        for (int i = 0; i < 6 ; i++) {
            arr[i] = i;
        }
        List<Integer> list = Arrays.asList(arr);

注意: 1不支持基本数据类型的数组

         2 不能增删,只能查询

        3 默认返回类型为List

增加元素时报错: UnsupportedOperationException

原因: 返回的是Arrays工具类中的一个内部类 java.util.Arrays.ArrayList, 不具备add()和remove()

 public static <T> List<T> asList(T... a) {
        return new ArrayList<>(a);
    }
private static class ArrayList<E> extends AbstractList<E>
        implements RandomAccess, java.io.Serializable
    {
        private static final long serialVersionUID = -2764017481108945198L;
        private final E[] a;

        ArrayList(E[] array) {
            a = Objects.requireNonNull(array);
        }

        @Override
        public int size() {
            return a.length;
        }

        @Override
        public Object[] toArray() {
            return a.clone();
        }

        @Override
        @SuppressWarnings("unchecked")
        public <T> T[] toArray(T[] a) {
            int size = size();
            if (a.length < size)
                return Arrays.copyOf(this.a, size,
                                     (Class<? extends T[]>) a.getClass());
            System.arraycopy(this.a, 0, a, 0, size);
            if (a.length > size)
                a[size] = null;
            return a;
        }

        @Override
        public E get(int index) {
            return a[index];
        }

        @Override
        public E set(int index, E element) {
            E oldValue = a[index];
            a[index] = element;
            return oldValue;
        }

        @Override
        public int indexOf(Object o) {
            E[] a = this.a;
            if (o == null) {
                for (int i = 0; i < a.length; i++)
                    if (a[i] == null)
                        return i;
            } else {
                for (int i = 0; i < a.length; i++)
                    if (o.equals(a[i]))
                        return i;
            }
            return -1;
        }

        @Override
        public boolean contains(Object o) {
            return indexOf(o) != -1;
        }

        @Override
        public Spliterator<E> spliterator() {
            return Spliterators.spliterator(a, Spliterator.ORDERED);
        }

        @Override
        public void forEach(Consumer<? super E> action) {
            Objects.requireNonNull(action);
            for (E e : a) {
                action.accept(e);
            }
        }

        @Override
        public void replaceAll(UnaryOperator<E> operator) {
            Objects.requireNonNull(operator);
            E[] a = this.a;
            for (int i = 0; i < a.length; i++) {
                a[i] = operator.apply(a[i]);
            }
        }

        @Override
        public void sort(Comparator<? super E> c) {
            Arrays.sort(a, c);
        }
    }

2 使用ArrayList

优点: 能够实现增删查改的功能

Integer [] arr = new Integer[6];
        for (int i = 0; i < 6 ; i++) {
            arr[i] = i;
        }
        List<Integer> list = Arrays.asList(arr);
        ArrayList<Integer> arrayList = new ArrayList<>(list);
        arrayList.add(1);
        System.out.println(arrayList);

3 使用工具类Collections

优点: 效率快

ArrayList<Integer> arrayList1 = new ArrayList<>(arr.length);
        Collections.addAll(arrayList1, arr);
        arrayList1.add(11);
        System.out.println(arrayList1);

60 springcloud 核心组件及作用

Eureka 注册中心 , 各个服务启动时, eureka client 会注册到 eureka server中, eureka client 也可以从eureka server中拉取注册表, 从而知道其它服务器在哪里

ribbon 服务间做负载均衡时,给予ribbon,找到对应的服务

feign 基于动态代理, 根据接口上的注解,动态构造要请求的地址, 根据地址,发送请求

hystrix 提供线程池,不同的服务走不同的线程池,实现服务调用的隔离,如果某个依赖出现延时过高的情况,也只会对该依赖服务的调用有影响,不会影响其它依赖服务

zuul 网关管理,请求统一从zuul进入,然后转发个对应的服务

61 beanFactory和 FactoryBean的区别

BeanFactory定义了IOC容器的最基本形式,并提供了IOC容器应遵守的的最基本的接口,也就是Spring IOC所遵守的最底层和最基本的编程规范。在Spring代码中,BeanFactory只是个接口,并不是IOC容器的具体实现,但是Spring容器给出了很多种实现,如 DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等,都是附加了某种功能的实现。
 

package org.springframework.beans.factory;  
import org.springframework.beans.BeansException;  
public interface BeanFactory {  
    String FACTORY_BEAN_PREFIX = "&";  
    Object getBean(String name) throws BeansException;  
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;  
    <T> T getBean(Class<T> requiredType) throws BeansException;  
    Object getBean(String name, Object... args) throws BeansException;  
    boolean containsBean(String name);  
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;  
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;  
    boolean isTypeMatch(String name, Class<?> targetType) throws NoSuchBeanDefinitionException;  
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;  
    String[] getAliases(String name);  
}  

一般情况下,Spring通过反射机制利用<bean>的class属性指定实现类实例化Bean,在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在<bean>中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。
FactoryBean接口对于Spring框架来说占用重要的地位,Spring自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂Bean的细节,给上层应用带来了便利。从Spring3.0开始,FactoryBean开始支持泛型,即接口声明改为FactoryBean<T>的形式
 

package org.springframework.beans.factory;  
public interface FactoryBean<T> {  
    T getObject() throws Exception;  
    Class<?> getObjectType();  
    boolean isSingleton();  
}   

区别

BeanFactory是个Factory,也就是IOC容器或对象工厂,FactoryBean是个Bean。
在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。
但对FactoryBean而言,这个Bean不是简单的Bean,
而是一个能生产或者修饰对象生成的工厂Bean,
它的实现与设计模式中的工厂模式和修饰器模式类似。

62 幂等

多次执行的结果与一次执行的结果相同

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hero_孙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值