2018面试题,个人总结

本文详细解析了Java面试中常见的技术问题,涵盖了OOP、集合框架、多线程、异常处理、数据库操作等方面的知识点,提供了Spring框架的核心原理介绍。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

常见面试题个人总结-私人总结

转载请表明jam__zheng
OOP:
    Object Oriented Programming 记不住这三个单词,简历上就要不出现OOP简称。
口述用到的循环和流程控制语句

String和StringBuffer的区别:
    String
    1,String是不可变字符串,一旦创建对象以后对象的值不能改变,
    2,String 是final类,即不能被继承。
    3,Sting为空字符初始容量为0。
    StringBuffer
    1,StringBuffer是可变字符串,可以通过apend添加字符串。
    2,StringBuffer是final类,不能被继承。
    4,StringBuffer初始容量为16
String创建对象new和不new的区别
     1,String直接赋值,在java中叫直接量,它是在常量池中。当以这种形式声明一个字符串的时候,
     JVM会首先在常量池查找有这个值的对象,如果有,就会把它赋给当前引用.即原来那个引用和现
     在这个引用指点向了同一对象,如果没有,则在常量池中新创建一个。
     2,new创建对象,不管有没有这个对象都会创建一个新的对象。以String a = new String(“abc”)为例。他会在堆内存中保存new出来的对象abc,同时在栈中有一个引用a指向堆内存中的对象abc。
     这种方式会创建两个对象abc和new String(”abc”).因为abc本身就是一个字符串对象,
     new String(“abc”)又返回一个字符串对象。
抽象类和接口的区别:
    抽象和接口都不能直接实例化。
    抽象类
    1,它可以有默认的方法实现
    2,子类使用extends关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明的方法的实现。
    3,抽象类可以有构造器
    4,抽象方法可以有public、protected和default这些修饰符
    5,抽象方法可以继承一个类和实现多个接口
    6,它比接口速度要快
    7,如果你往抽象类中添加新的方法,你可以给它提供默认的实现。因此你不需要改变你现在的代码。
    接口
    1,接口完全是抽象的。它根本不存在方法的实现
    2,子类使用关键字implements来实现接口。它需要提供接口中所有声明的方法的实现
    3,接口不能有构造器
    4,接口方法默认修饰符是public。你不可以使用其它修饰符。
    5,接口只可以继承一个或多个其它接口
    6,接口是稍微有点慢的,因为它需要时间去寻找在类中实现的方法。
    7,如果你往接口中添加方法,那么你必须改变实现该接口的类。
什么时候使用抽象类和接口
    1,如果你拥有一些方法并且想让它们中的一些有默认实现,那么使用抽象类吧。
    2,如果你想实现多重继承,那么你必须使用接口。由于Java不支持多继承,子类不能够继承多个类,
       但可以实现多个接口。因此你就可以使用接口来解决它。
    3,如果基本功能在不断改变,那么就需要使用抽象类。如果不断改变基本功能并且使用接口,
       那么就需要改变所有实现了该接口的类
throw和throws的区别
    1,throw是语句抛出一个异常,一般是在代码块的内部,当程序出现某种逻辑错误时由程序员主动抛出某种特定类型的异常
       throw则是抛出了异常,执行throw则一定抛出了某种异常对象。
    2,throws是在类上抛出异常,throws表示出现异常的一种可能性,并不一定会发生这些异常
final,finally和finaize的区别
    final可以用来修饰类,方法和变量,
    final修饰类该类不能被继承,final修饰方法该方法不能被覆盖,final修饰变量,如果是基本数据类型的变量,
    则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
    finally在异常处理时提供 finally 块来执行任何清除操作。用于关闭资源,或做最后的通知等。在try…catch块中,
    无论是执行了try语句体,或进入了catch块,finally块的内容都会被执行,即使return也会执行。
java中支持多继承吗
     java中类不支持多继承,因为一个子类继承父类,说明该子类,就是父类代表的事物中的某个更集体的类别。单继承
     更易读,易维护,逻辑清晰。而不是一个子类即是这又是那。所以Java类不支持多继承
     接口与接口之间可以多继承,接口中只定义一些公共方法,类对于接口是实现而不是继承,接口之间的多继承,可理解为一种扩展
ajax的同步和异步
    同步请求:是发送请求以后页面不可再进行操作,需要等待页面返回用户以后才可以进行操作
    异步请求:发送请求以后用户可以继续再页面进行其他操作,由后台进行数据的交互。
    ajax中async 类型为 Boolean 值, 设置 true 时代码请求使用异步, 设置 false 时代码该请求使用同步。
    一般情况下 async 都是配置为true(异步), 且当未进行配置时会默认为true; 
    ajax本身就是比较适合用于异步请求,同步请求可以通过其他方式实现,aiax的同步请求比较少见
重写和重载的区别
    重写常发生在父子类中,重写返回类型,方法签名相同,方法体不同,子类函数的访问修饰权限不能少于父类的。当父类的方法被重写以后,再次调用时,实际是在调用父类引用指向子类对象的子类的重写后的方法
    重载发生在一个类中,方法名相同,但是参数类型和个数不同,返回值类型可以相同也可以不相同。无法以返回型别作为重载函数的区分标准
spring的两大核心
    IOC和AOP
    IOC控制反转:原先对象的创建和对象关系需要我们管理。控制反转是将这些交予spring容器。IOC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。
    IOC和DI
    DI即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
  理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:
  ●谁依赖于谁:当然是应用程序依赖于IoC容器;
  ●为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;
  ●谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;
  ●注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。
    IOC和DI有什么关系呢?其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IOC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”
    AOP:Aspect Oriented Programming,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
    通过AOP技术,旨在处理编程中涉及到的日志记录,性能统计,安全控制,事务处理,异常处理等需求时,将这些代码从业务逻辑代码中划分出来,进而将它们独立开来到非指导业务逻辑的方法中,以便处理这些需求时不影响或者减少影响业务逻辑的代码。
    Spring框架的AOP技术底层是动态代理技术,提供了两种方式:1,基于jdk的动态代理,必须要面向接口,只有创建实现了具体接口的类,才能生成代理对象;2,基于CGLIB的动态代理,对于没有实现接口的类,也可以产生代理,方式是创建这个类的子类。
    简单解释下代理,就是代替某某处理或办理本来由某某办的事情。比如,烟酒代理,本来卖烟酒是工厂的事情,如果厂商直接销售给顾客商品,当厂商的一批商品出厂后,所有的销售方案都已经定下来,它根据这一套方案去销售,但是,在销售过程中,突然发现,需要根据不同区域顾客的差异性,采取相应的销售方案,但是方案已经初始化,如果更改,将需要很大的工程,这就暴露了厂商与顾客高耦合的缺点。那么,如果使用代理,酒厂把销售的业务委托给代理来做。尽管,酒厂的方案已经初始化,但是代理既可以在酒厂初始化销售方案之前就有一套因地制宜的方案,这可以理解为代码实现中的静态代理,也可以在销售的过程中,根据新的情况,设定新的方案,这可以理解为代码实现中的动态代理,那么,这个动态代理就类似于spring中aop动态代理的实现方式。
数据库中表的关系有几种
    一对一
        什么是一对一:有两张表A和B,A表中有一条数据对应B表中的一条数据称为一对一关系
        应用场景: 用户表和用户信息扩展表,商品表和商品信息扩展
        如何建立关系:在从表中添加一个字段记录主表的id,用户表和用户信息扩展表中,用户表为主表,信息扩展表为从表,用户表中的id 称为主键, 从表中记录主表id的字段称为外键,主键用来表示数据的唯一性,外键用来和其它表建立关系.
    一对多
        什么是一对多:AB两张表中A表中一条数据对应B表中多条数据,并且B表中一条数据对应A表中一条数据,两张表的关系称为一对多.
        应用场景:部门和员工,商品和分类等
        如何建立关系: 一对多的两张表,在多的表中添加一个字段记录另外一张表的id.
    多对多
        什么是多对多:AB两张表,A表中的一条数据对应B表的多条数据,同时B表的一条数据对应A表的多条数据,这种关系称为多对多
        应用场景: 老师表和学生表,用户表和角色表
        如何建立关系:需要创建一个关系表,在关系表中记录两个表的id.
常见集合:
    集合:collection是一个接口定义了集合相关的操作方法
    list:有序,可重复集
        ArrayList:动态数组实现,适合查询,随机访问
        LinkedList:链表实现,适合插入删除操作
    set:不可重复集
        HashSet:调用对象的hashCode()方法,获得哈希码,然后再集合中计算存放对象的位置。通过比较哈希码与equals()方法来判别是        否重复。所以,重载了equals()方法同时也要重载hashCode()方法。
        TreeSet:继承ShortedSet接口,能够对集合中对象排序。默认排序方式是自然排序,但该方式只能对实现了Comparable接口的对象排序
            java中对Integer、Byte、Double、Character、String等数值型和字符型对象都实现了该接口。
    map:Map对值没有唯一性要求,对健要求唯一,如果加入已有的健,原有的值对象将被覆盖。键值对存储结构的集合,无序
        HashMap:HashMap类按照哈希算法来存取键对象,可以重载equals()、hashCode()方法来比较键,但是两者必须一致
        TreeMap:TreeMap,可自然排序,也可通过传递Comparator的实现类构造TreeMap。
常用的SQL优化
    1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
    2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描。
    3.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。    
    4.应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描。
    5.in 和 not in 也要慎用,否则会导致全表扫描。
    6,模糊查询中的‘%xxx%’也将进行权标扫描。而‘%’不会
    7.应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。
    8.应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。
    9.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引.
    10.在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,    
        否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。
    11.不要写一些没有意义的查询,如需要生成一个空表结构:
        select col1,col2 into #t from t where 1=0    
        这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:    
        create table #t(…)    
    12.很多时候用 exists 代替 in 是一个好的选择。
    13.并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,    
        如一表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。    
    14.索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,    
        因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。    
        一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。    
    15.尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。    
        这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。        
    16.尽可能的使用 varchar 代替 char ,因为首先变长字段存储空间小,可以节省存储空间,    
        其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。    
    17.任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。    
    18.避免频繁创建和删除临时表,以减少系统表资源的消耗。
    19.临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于        一次性事件,最好使用导出表。    
    20.在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,    
        以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。
    21.如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table                                             然后drop table这样可以避免系统表的较长时间锁定。    
    22.尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。    
    23.使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。
    24.与临时表一样,游标并不是不可使用。对小型数据集使用FAST_FORWARD游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才
        能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。
    25.尽量避免大事务操作,提高系统并发能力。26.尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。
 springMVC的工作方式
     浏览器向Spring发出请求,请求交给前端控制器DispatcherServlet
     控制器通过HandlerMapping找到相应的Controller组件处理请求
     执行Controller组件约定方法处理请求,在约定方法调用模型组件完成业务处理,约定方法可以返回一个ModelAndView对象,封装了处理结果数据和视图名称信息
     控制器接收ModelAndView之后,调用ViewResolver组件,定位View(jsp)并传递信息数据,生成响应界面结果
 数据库使用索引的优缺点:
     优点:1,建立索引的列可以保证行的唯一性,生成唯一的rowId
          2,建立索引可以有效缩短数据的检索时间
          3,建立索引可以加快表与表之间的连接
          4,为用来排序或者是分组的字段添加索引可以加快分组和排序顺序
    缺点:1,创建索引和维护索引需要时间成本,这个成本随着数据量的增加而加大
          2,创建索引和维护索引需要空间成本,每一条索引都要占据数据库的物理存储空间,数据量越大,占用空间也越大(数据表占据的是数据库的数据空间)
          3,会降低表的增删改的效率,因为每次增删改索引需要进行动态维护,导致时间变长
JQuery的选择器有哪些:
    基本选择器:id选择器,类选择器,元素选择器,选择器组
    层次选择器:
    过滤选择器:基本过滤选择器,内容过滤选择器,可见性过滤选择器,属性过滤选择器,状态过滤选择器
    表单选择器:text,password,radio,submit,checkbox,reset,button,file,hidden
页面间对象传递的方法?
    1,通过request对象的setAttribute()方法设置对象参数
        然后通过getAttribute()方法获取对象进行对象传递
    2,通过session对象的setAttribute()和getAttribute()传递
    3,通过application对象的setAttribute()和getAttribute()传递
    4,使用url传递,直接把要传递的对象拼接在url后面,不过,这种方式不安全,在客户端的地址栏能看到传递的信息。
    5,也可使用表单的方式传递
Request 对象的主要方法
    setAttribute(String name,Obj);参数绑定
    getAttribute(String name);获取指定名字的对象的指
    removeAttribute(String name); 删除指定绑定名的对象值
    getCookies();得到客户端的所有的cookies,返回值是一个数组
    getContextLength();得到请求的body的长度
    getHeader(String name);获取http协议的定义的消息头信息
    getCharacterEncoding();获取请求中的字符编码集,返回请求中的字符编码方式
get 和post 的区别?
    Get请求会把请求信息拼接在url后面在用户浏览器地址栏可见,这样做不安全,可能会被其他中间转发站获取,如有隐私很容易就泄露了。而且其提交的数据有限制,大概2K。Get请求在浏览器回退是无害的
    Post请求会把请求信息数据放在消息正文里,浏览器地址栏不可见,相对安全,而且他的请求数据理论上是没有限制的。
    但是这两个提交方式如不进行加密处理,如果被别人抓包获取,若有隐私信息在里面一样会造成信息泄露。可使用md5加密成密文进行传输
如何从form 表单中得取checkbox 的值?
    可以使用jq的选择器
    $(“:checkbox:checked”)得到选中的复选框的值
    如果是多个可以循环遍历,拿到每一个里面的值
javascript的内置对象
    内置对象:String Number Boolean Array RegExp Function Math Date
    外部对象:window对象:document history location screen navigator
             Dom对象:所有的标签都可以看dom对象
JSP的内置(隐含)对象
    输入输出对象:out,request,response
    作用域对象:session,pageContext。application
    servlet对象:page,config
    异常对象:exception
JSP中写java代码的方法:
    JSP表达式:<%=…%>,用于输出表达式到浏览器
    JSP小脚本:<%…%>,书写逻辑代码
    JSP声明:<%!…%>,用于在jsp页面定义变量和方法
使用Mybatis向数据库批量插入数据
    把数据封装到List集合中,再在mybatis的xml配置文件中使用foreach标签,循环,foreach标签的四个属性,
    collection:参数名称,根据Mapper接口的参数名确定,
    item:参数调用名称,通过此属性来获取集合单项的值,
    index:索引、下标,
    separator:分隔符,每次循环完成后添加此分隔符
线程的常用方法:
    1,sleep();方法,属于Thread类,主要的作用是让当前线程停止执行,把cpu让给其他线程执行,但不会释放对象锁和监控的状态,到了指定时间后线程又会自动恢复运行状态
    2,wait()方法属于Object类,与sleep()的区别是当前线程会释放锁,进入等待此对象的等待锁定池。比方说,线程A调用Obj.wait(),线程A就会停止运行,而转为等待状态。至于等待多长时间? 那就看其他线程是否调用Obj.notify().其优势显而易见,成为多个线程之间进行通讯的有手段!
    3,join()方法在某些情况下,子线程需要进行大量的耗时运算,主线程可能会在子线程执行结束之前结束,但是如果主线程又需要用到子线程的结果,换句话说,就是主线程需要在子线程执行之后再结束。这就需要用到join()方法
    4,yield()方法一个线程调用yield()意味着告诉虚拟机自己非常乐于助人,可以把自己的位置让给其他线程(这只是暗示,并不表绝对)。但得注意,让出cpu并不代表当前线程不执行了。当前线程让出cpu后,还会进行cpu资源的争夺,但是能不能再次分配到,就不一定了
    5,isAlive():测试线程是否处于活动状态
    6,setPriority(intnewPriority):更改线程的优先级。
    7,getName():返回该线程的名称。
    8,currentThread():返回对当前正在执行的线程对象的引用。
java中线程的状态分为几种,分别是什么:
    1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
    2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的成为“运行”。
    线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得cpu 时间片后变为运行中状态(running)。
    3.阻塞(BLOCKED):表线程阻塞于锁。
    4.等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
    5.超时等待(TIME_WAITING):该状态不同于WAITING,它可以在指定的时间内自行返回。
    6. 终止(TERMINATED):表示该线程已经执行完毕。
有一千个有序数字,怎么实现快速找到给定的数:
    使用二分查找法。
    package sort;
 
public class BinarySearchDemo {
 
    /**
     * @param args
     */
    public static void main(String[] args) {
        Integer [] a ={1 ,4 ,5 ,8 ,15 ,33 ,48 ,77 ,96};
        int [] b ={1 ,4 ,5 ,8 ,15 ,33 ,48 ,77 ,96};
        
        int result = binarySearch(a,5);
        System.out.println(“binarySearch result:”+result);
        
        int result1 = searchRecursively(b,0,8,5);
        System.out.println(“searchRecursively result:”+result1);
    }
 
       /* 
     * 非递归二分查找算法 
     * 参数:整型数组,需要比较的数. 
     * 返回值:要查找的数所对应的下标
     */  
    public static int binarySearch(Integer[]srcArray,int des){  
        //第一个位置.  
        int low=0;  
        //最高位置.数组长度-1,因为下标是从0开始的.  
        int high=srcArray.length-1;  
        //当low”指针”和high不重复的时候.  
        while(low<=high){  
            //中间位置计算,low+ 最高位置减去最低位置,右移一位,相当于除2.也可以用(high+low)/2  
            int middle=low+((high-low)>>1);  
            //与最中间的数字进行判断,是否相等,相等的话就返回对应的数组下标.  
            if(des==srcArray[middle]){  
                return middle;  
            //如果小于的话则移动最高层的”指针”  
            }else if(des<srcArray[middle]){  
                high=middle-1;  
            //移动最低的”指针”   
            }else{  
                low=middle+1;  
                }  
        }  
        return-1;  
     }  
      
    /** 
     * 递归方法实现二分查找法. 
     * @param Array数组 
     * @param low 数组第一位置 
     * @param high 最高 
     * @param key 要查找的值. 
     * @return 返回值. 
     */  
   public static int searchRecursively(int Array[],int low,int high,int key)  
    {  
        if (low<=high)  
        {  
            int mid = (low+high)/2;  
            if(key == Array[mid])  
                return mid;  
            else if(key<Array[mid])  
                //移动low和high  
                return searchRecursively(Array,low,mid-1,key);  
            else if(key>Array[mid])  
                return searchRecursively(Array,mid+1,high,key);  
        }  
        else  
            return -1;
        return -1;  
    }  
}
现有一部分无序字母,使用map记录每个字母出现的次数,并对其进行排序:

            </div>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值