要找工作啦,在这里记录下整理的面试题。Java体系还是很庞大的,我将其分为Java语言部分(JavaSE、JavaEE等官方方面),非Java语言部分(数据库,WEB前端,redis,框架等技术方面)和项目部分。
一、Java基础部分
- 简单介绍下Java的跨平台原理
不同的操作系统的操作指令集不同,所以做程序开发的时候需要根据不同的操作系统开发程序。比如想要程序支持MacOS、Windows和Linux就要开发三套不同的程序代码。这会导致开发成本成倍提升,所以很多程序都只开发了windows版本。这是相对于桌面程序来说的,如果是WEB应用程序就没有这个烦恼,这也是WEB开发这么流行的原因。
Sun公司开发了适用于不同操作系统及位数的JVM虚拟机来屏蔽系统之间的差异并提供了统一的接口,对于Java程序而言,只需要遵循Java语言规范,就可以在所有的操作系统上运行程序。 - Java中int数据占用几个字节?
int数据占用4个字节,这在哪个位数操作系统上都是一样的,主要是为了跟CPU的字长一致,目的当然是提高处理速度。
- Java面向对象有哪些特征?
主要有四个特征:抽象,封装,继承和多态。
抽象的目的是忽略与主题无关的其他信息,使用abstract关键字修饰,规定抽象方法只能为public/protected,被修饰的类不能被实例化,只允许通过继承的方式来实现。
封装的意思就是想让你看到就让你看到不想让你看到就不让你看到,使用private关键字实现,当你需要对属性进行修改的时候就使用类中的get/set方法,使用public关键字。这是出于安全和易用性方面的考虑。
继承的意义在于不需要重复造轮子,子类继承父类就可以得到除了private之外的父类的全部属性和方法(父类的构造方法除外)。需要知道的是Java只支持单继承,这是出于对继承链可能会出现过于复杂的情况的设计方案,如果想实现类似C++语言的多继承,可以使用接口来实现。还需要注意这几点,如果没有指定父类,那么类的直接父类是java.lang.Object;一般继承会导致重写,子类有时候会需要重写父类的方法,在重写时必须保证方法名和形参列表相同,返回值类型和异常类型子类需要小于父类;访问权限,子类需要大于父类。
多态是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用只有在程序运行期间才会确定,在Java中通过继承的方式来实现多态,即父类接口引用变量指向子类或实现类的实例对象,这样程序调用的方法在运行期才会动态绑定引用变量所指向的具体实例对象的方法,也就是内存里正在运行的对象方法。。比如公司规定十点上班工作,但并不会对程序员说你要敲代码,并不会对HR说你要检查邮件这个工作的具体实现。需要注意的是多态是方法的多态而不是属性的多态,多态是在继承的基础上实现的。
- 接口和抽象的区别是什么?
两者都是Java面向对象的重要实现。它们都声明方法而不去实现,本质的区别在于抽象类是一个类,而接口是其他的类型,在OC中都没有接口这一说,而是使用protocol。抽象类除了不能实例化之外跟普通的类没有区别,而接口完全不存在方法的实现,不能有构造器,只能使用public访问修饰符,没有main方法等。接口提供抽象方法,类通过interface关键字对接接口,重写抽象方法实现对应业务逻辑以此对外提供服务。比如Map接扣作为一个容器就会提供put方法来往容器中填充值,get方法获取容器中某一个值,size属性获取容器大小,remove方法来删除容器中的值。接口除了做规范之外还是多继承在Java中的解决方案,另外接口也是各种框架和API的实现,比如RedisTemplate就是封装了jedis,如果在项目中使用redis,直接通过注入RedisTemplate接口就可以完成对redis的操作。
- 已经有基本类型了,为什么还需要包装类型呢?
Java提供了八种基本类型,每一种类型都会有一个对应的包装类型,比如boolean-Boolean,JVM会在两者之间自动转换,这是JDK1.5做的改变。
Integer i = 1; //自动装箱,编译时会调用Integer.valueOf(1)
int j = i; //自动拆箱,编译时调用intValue();
值得注意的是自动装箱时系统会创建缓存,并将当前数据添加到缓存中,有两个作用,第一是为了缓解使用包装类型导致数据存储会略微变慢的问题,第二是解决了包装类型判断是否相等的问题。
Java是一门面向对象的语言,基本数据类型并不具备面向对象的特征,这会导致数据存储的问题,例如在处理空值的时候,使用Integer时需要判断null,使用int的时候需要判断0,而在数据库中通常默认值为0。这会导致在数据库中存储id的时候,id类型为bigint,当控制类方法的接收值为long而不是Long的话会报错。使用Long表示类的ID的时候,在需要判断类是否存在只需要判断为null就可以了,而且使用包装类型就可以通过其提供的方法MAX和MIN获取最大值和最小值等。
- 说一下"=="和equals方法究竟有什么区别?
==用于判断两个变量是否相等。变量可以分为基本数据类型和引用数据类型,如果是引用数据类型需要比较引用的内存首地址,如果是基本数据类型直接比较值。
equals方法是判断两个对象的某些特征是否一致,通过重写对象的equals方法。
如何理解?通过观察源码会发现,equals方法会首先判断两个比较值类型是否一致,然后再比较数值或字符,例如Integer类型会自动拆箱为int类型进行比较,String会将字符串拆分成字符。
- 讲一下String和StringBuilder的区别(final)?StringBuffer和StringBuilder的区别?
java提供了三个类String、StringBuilder和StringBuffer来表示和操作字符串。字符串即是由多个字符(char)的集合。
String是内容不可变的字符串(private final char value[])
StringBuilder和StringBuffer继承自AbstractStringBuilder类(char[] value);
因此拼接字符串的时候:
String:String s = "a"+"b";
StringBuilder sb = new StringBuilder();
sb.apend("a").apend("c")
拼接字符串的时候不能使用String进行拼接,要使用StringBuilder和StringBuffer。前者效率高但线程不安全,后者效率低但线程安全。
-
简单说一下java中的集合
java中的集合主要分为value和key-value两种。 存储值分类list和set,list重复有序,set不可重复无序。并且set根据equals和hashcode判断,如果对象存储在set中,则必须重写这两个方法。 存储键值在java中是称之为map,在OC语言中叫字典。
-
List常用的ArrayList和LinkedList的区别和使用场景?
ArrayList底层使用数组(private transient Object[] elementData;) LinkedList底层使用链表(transient Node<E> first; transient Node<E> last;) 数组是一块连续的内存,因此插入和删除的时候需要移动内存。由于有这个特点所以说数组查询元素快,插入和删除删除慢。 链表的原理是在当前元素中存放上一个和下一个元素的地址,查询时从头部开始一个个找,因此查询效率慢,由于做修改的时候不需要做内存移动操作,只需要改变引用指向即可完成,所以插入和删除操作效率高。 不难看出链表和数组是相反的,所以它们的使用场景就呼之欲出了,而由于数据库操作的特殊性(查询占90),所以一般我们会选择ArrayList做数据保存的容器。
-
讲一下HashMap和 HashTable的区别?HashTable和ConcurrentHashMap的区别?
首先HashTable是不可以使用null作为key或者value的,HashTable在存储数据的时候会返回synchronized。因此可以得出结论HashTable安全但效率不高。 如果想既效率高又安全,可以使用ConcurrentHashMap并发容器的方式,原理是将整个Map分成N个Segment(类似HashTable不加锁),提供相同的线程安全,但是效率提升N倍默认为16倍。
-
实现一个拷贝文件的工具类使用字节流还是字符流?
字节流和字符流都可以实现对文件的拷贝,如果确认不包含例如图片和音频这样特殊的文件,使用字符流即可,否则应该使用字节流。
-
线程的几种实现方式,启动方式?
两种实现方式:继承Thread类和实现Runnable接口。一般采用实现接口的方式,因为java只支持单继承。
Thread thread = new Thread(new MyThread());//MyThread类继承了Thread对象或Runnable接口
thread.setName("xiaoming");//通过设置线程名称来区分线程
thread.start();//通过start()方法来启动线程,但实际执行的方法是run方法。run()方法是Thread类和Runnable接口自带的方法,需要重写和实现run()。
- 线程池的作用
- 线程过多的话会导致系统运行缓慢甚至崩溃,所以需要限制线程的个数
- 线程池如果每次都创建或者销毁会导致资源的浪费
什么是设计模式?常用的设计模式有哪些?
设计模式是前人经验所得的可以反复使用,解决特定问题的设计方法。
- 单例模式是最常见的设计模式之一,它的作用是保证某一实例的唯一性。具体实现就是在类中定义一个实例对象并提供一个公开方法获取该实例,然后将构造器私有化,如果需要使用该实例的话,只能通过提供的公开方法而不能new出这个类,这样每次得到的实例就是同一个实例。单例分为懒加载和立即加载两种模式,懒加载在创建对象的时候就加载实例,立即加载在需要的时候才加载实例。
- 工厂模式,SpringIoC中的BeanFactory,MyBatis中的SqlSessionFactory就都是工厂模式的实现
- 代理模式,SpringAOP是通过动态代理实现
- 观察者模式,Spring中的listener实现ApplicationListener
- 模板模式,Spring集成了JdbcTemplate、RedisTemplate、SolrTemplate、JmsTemplate
- 适配器模式,SpringMVC中的处理器映射器HandlerMapper
二、J2EE部分
- 说一下你对servlet的理解?或者servlet是什么?
Java Servlet是使用J2EE服务端程序,后端代码通过实现Servlet接口并重写HTttpServlet的doGet/doPost方法来进行对数据库和浏览器之间的交流,Servlet程序需要运行在支持Java程序的应用服务器中,比如tomcat,jetty等。
- 简单说一下servlet的生命周期?
Servlet是一个接口规范,所以有比较明确的生存期定义,接口中的init,service和destory方法进行表述。
- Servlet API中forward()与redirect()的区别?
forward就是转发,是服务端行为因此只发送一次请求也不会改变url地址,redirect是重定向,是客户端行为会改变url地址并且发送两次请求
- JSP和Servlet有哪些相同点和不同点?
JSP是Servlet的扩展,所有的jsp文件都会被编译成一个继承自HttpServlet的类,因为servlet输出html比较麻烦,所以设计出jsp。
-
JSP有哪些内置对象?作用分别是什么?
九个内置的对象:
- request 用户端请求,此请求会包含来自GET/POST请求的参数
- response 网页传回用户端的回应
- pageContext 网页的属性是在这里管理
- session 与请求有关的会话期
- application servlet正在执行的内容
- out 用来传送回应的输出
- config servlet的构架部件
- page JSP网页本身
- exception 针对错误网页,未捕捉的例外
四大作用域:
- pageContext 整个page页面
- request(HttpServletRequest) service方法
- session(HttpSession) 第一次调用request.getSession()方法时
- application(ServletContext) 整个WEB应用程序
- 说一下Session和Cookie的区别?你在项目中都有哪些地方使用了?
session和cookie都是HTTP的会话跟踪技术,是为了解决HTTP无状态的特性而设计的技术。cookie在客户端浏览器记录信息认证身份,session在服务端记录信息确认身份。session通过在cookie中存放的sessionId来保证信息的一致性。
session和cookie的区别如下
- 由于cookie数据保存在浏览器中,因此有安全和数据限制的问题,考虑到这种情况应该使用session保存数据
- session数据存在服务器上,当访问比较多的情况下会耗费服务器资源,考虑到这种情况应该使用cookie
购物车是最好的cookie技术实现场景,用户登录信息的存储是最好的session技术实现场景。
我在做用户注册生成短信验证码和solr异步删除索引库数据,静态页面生成的时候使用过jmsTemplate.send()方法,该方法中有一个内部类(new MessageCreator()),此类中接收session参数,然后通过这个session创建消息实例设置消息并返回。
- HTTP中GET和POST请求的区别
用户通过不同的请求方式完成对资源的不同操作,比如PUT请求就是代表对请求资源的增加,DELETE请求就是对请求资源的删除。为了方便开发,一般我们只使用GET和POST方式就可以了。两者本质上都是TCP链接,区别在于:
- get请求时,地址栏会显示请求信息,以?开头,多个参数以&连接,而post请求在request body中
- get请求由于浏览器对地址长度的限制而导致传输的数据有限,post则没有这个限制
- 由于请求信息会暴露所以get请求会有安全隐患
- get产生一个TCP数据包,浏览器会将http header和data数据一起发送,等待服务器响应200并返回数据;post请求产生两个数据包,浏览器会发送header,等待服务器响应100 continue,浏览器才会发送data,最后等待服务器响应200和请求的数据
- 对于get和post的区别,有一个比较有趣的比喻。get传递数据如同写在脸上,post传递数据如同方法肚子里,脸上自然不能放太多东西也不能放隐私的东西,而肚子里就无所谓了。