首页 > 程序开发 > 软件开发 > Java >

分享最新的java笔试面试问题汇总

2018-07-19

分享最新的java笔试面试问题汇总。1,java基本数据类型已经拆装箱:java基本数据类型:int(4),float(4),short(2),long(8),double(8),char(2),boolean(1),byte(1)

1,java基本数据类型已经拆装箱:

java基本数据类型:int(4),float(4),short(2),long(8),double(8),char(2),boolean(1),byte(1)

包装类型:Integer,Float,Short,Long,Double,Char,Boolean,Byte

基本数据类型与包装类型之间的转化为拆装箱

基本数据类型在传递参数时是按照值传递,其实例变量的默认值与它们的类型有关

包装类型在传递参数时按照引用传递,其实例变量的默认值为null;

2,按值传递和按引用传递的区别:

值传递,在方法调用中,实参把它的值传给形参,形参只是用实参的值初始化一个临时的存储区,实参和形参有相同的值,确实不同的存储单元,因此对形 参的改变不影响实参的值。

引用传递,在方法调用中,传递的是对象的地址,这是形参和实参指向同一块存储单元,因此对形参的修改会影响实参。

3,java中String ,Stringbuilder,Stringbuffer区别:

String是不可变类,一旦被创建,其值不可改变,当字符量较小时,常用String.

Stringbuffer是可变类,可以动态添加修改字符串,

String可以用构造函数实例化,也可以用赋值的方式实例化,但StringBuffer只能用构造函数实例化,

String字符串修改时,实际等价于创建一个StringBuffer,调用StringBuffer.append()方法,再将StringBuffer().tostring;

因此对字符串的修改不及StringBuffer方便。

Stringbuilder也可修改字符串,都是字符串缓冲区,与StringBuffer不同的是,StringBuilder是线程不安全的,因此效率较高,如果需要在单线程环境下操 作大量字符串,可以选择StringBuilder.

String提供了length()方法计算字符串的长度,length属性可以获取数组的长度。

4,"==","equals","hashcode"的区别和用法:

“==”用来比较基本数据类型的数据或者两个引用变量是否相等,即比较变量对象在内存中所存储的数值是否相等。

如果一个变量指向的数据是对象(引用类型),则变量指向了两块内存区域,用来存放对象的堆内存,用来存储对象在堆内存中首地址的变量,该变量一般在栈中,如果要比较两个变量是否相等,即要看两个变量是否存的是同一块存储空间,可以用“==”,否则需要用equals,

equals是Object基类提供的方法之一,用来比较引用中的数据内容,在equals未被重写之前,每一个类的equals方法都是直接使用“==”运算符比较两个对象。

String类重写了equals方法,用来比较两个对立对象的内容是否相同,即堆中的内容是否相同。如

String s1 = new String("abc");

String s2 = new String("abc");

s1和s2是指向两个对象,这两个对象的首地址不同,但内容相同,‘’==‘’时false,equals时true,

hashcode也是Object类的方法之一,object类中的hashcode()返回的是内存地址转换成int类型后的值,因此,没有重写hashcode()方法前,hashcode()比较两个两个变量是否相等永远返回false.

一般覆盖equals()方法是也会覆盖hashcode()方法,如果两个变量equals后返回true,那么hashcode()返回也为true,如果equals返回为false,hashcode()可能想等也可能不等,而当hashcde()返回为fasle时,用equals比较也为false,但当hashcode()返回为true时,equals可能相等也可能不等。

5,jvm内存数据模型

1)程序计数器,是一个数据结构,用来保存当前正在执行的程序的内存地址,java虚拟机的多线程就是通过线程轮流切换并分配处理器时间来实现的,该区域为线程私有,为了使线程切换后能恢复到正常的位置,每个线程有一个独立的计数器,互不影响。

2)java虚拟栈,用来存放基本数据类型对象的引用等局部变量表,操作栈,方法返回值等,是线程私有的,与线程的生命周期相同。

3)本地方法栈,线程私有,为java虚拟栈用到的native方法服务。

4)java堆,程序运行中为对象分配内存区域,线程共享的,用过new关键字创建,

5)运行时方法区,各个线程共享的区域,存储虚拟机加载的类信息,常量,静态变量,编译后的代码。

6)运行时常量区,代表运行时每个class文件的常量表,包括几种常量:编译时的数字常量,方法或者域的引用。

6,jvm中栈和堆的区别

栈主要用来存放基本数据类型和引用变量,栈内存的管理是通过压栈和弹栈的方式进行的,以栈帧为基本单位来管理程序的调用关系,当一个线程生命周期结束后,栈中的数据通过弹栈方式销毁

堆主要是引用类型的变量,是在程序运行过程中动态分配内存,需要通过new关键字创建

从功能来看,栈用来执行程序,堆用来存放对象,栈的大小和生存周期是确定的,因此缺乏一定的灵活性,但存取速度快,堆在运行时动态的分配内存,生存期不必提前告诉编译器,这也导致了存取速度慢,堆内存需要垃圾回收器处理。

在堆中产生了一个数组或者对象后,可以在栈中定义一个变量,变量的取值等于数组或者对象在堆内存中的首地址,栈中这个变量就成了数组或者变量的引用变量,引用变量相当于数组或者对象的别名,以后就可以通过栈中的引用变量访问队中的数组或者对象,如String s = new String("abc").

7,垃圾回收器机制

常用的垃圾回收算法

1)引用计数器法,在堆中每个对象都有一个引用计数器,当对象被引用时,引用计数器加一,当引用被置为空或者离开作用域时,引用计数器减1,但无法解决相互引用的问题,一次不常用

2)追踪回收算法,利用jvm维护的对象引用图,从根节点开始遍历对象的应用图,同时标记遍历到的对象,当遍历结束后,未被标记的对象就是目前已不被使用的对象,就被回收了

3)压缩回收算法,把堆中活动的对象移到堆的另一端,这样就会在堆中另外一端留出很大一块空间区域,相当于对堆中的碎片整理,简化消除堆碎片的工作,但影响性能。

4)复制回收算法,将堆分成大小相同的两块区域,在任何时候只在一个区域中被使用,直到这个区域被消耗完,此时垃圾回收器中断程序的执行,通过遍历地方法把堆中所有活动的方法移动到另一块区域中,在复制过程中,它们紧挨着布局,从而可以消除碎片,缺点,对指点大小的堆来说,需要两倍的内存空间,同时由于移动过程中需要中断所有程序执行,影响了效率。

5)按代回收算法,由于程序创建的大部分对象的生命周期都很短,一小部分有较长的生命周期,因此,按代回收算法的基本思想是把堆分成两个或多个子堆,每个堆视为一代,算法在进行的时候优先收集那些“年幼的”子堆,如果一个对象经过多次收集后仍然存活,将其移入老年代,减少对其扫描次数。基本做法新生代有一个eden区和两个survivor区,首先将对象放在eden区,如果空间不足,就放在其中一个survivor区,如果仍然放不下就会引发一次发生在新生代的minor GC,将存活的对象放在另一个survivor区,然后清空eden区和之前那个survivor区的内存,在多次GC过程中,仍有放不下的对象,就将这些对象放入老年代内存里去,大对象已经长期存活的对象直接放入老年代,当每次执行minor GC时应该要对晋升到老年代的对象进行分析,如果这些马上要进入到老年代的对象超过了老年区的剩余大小,则引发一次fullGC以尽可能获得老年区的空间,新生代主要负责复制清理工作,老年代用标记-清除和标记-压缩算法,而永久代主要存放java中的类和加载类的类加载器本身。

8,线程同步实现方式:

synchronized 和lock方法:

synchronized方法或者synchronized代码块,可以作用于静态方法,类或者某个实例,是悲观锁机制,独占锁,是java关键字,在jvm层面上,已获取锁的线程执行完同步代码,会释放锁,当线程执行时发生异常,jvm也会让线程释放锁,但不能判断锁的状态,当线程A获取锁并阻塞时,同步执行的线程B会一直等待,

lack方法已非阻塞的方法获取锁,是一个类,需要指定起始和终止位置,执行完成后需要在finally中释放。trylock以非阻塞的方式获取锁,尝试性地获取锁,若获取返回true,否则返回false,

reentrantlock具有公平锁功能,每个到来的线程都排队等候。

9,sleep()方法和wait()方法

1)原理不同。sleep()方法使线程暂停执行一段时间,等时间截至,自动苏醒,是thread类的静态方法,是线程用来控制自身流程的,而wait()是Object类的方法之一,是线程间的通信,该方法是当前拥有该对象锁的进程等待,用notify()或者notifyAll()方法唤醒

2)对锁的处理机制不同,调用sleep()方法不会释放锁,而wait()方法被调用后,线程会释放当前占用的锁,从而使线程所在的对象中的其他synchronized数据可被其他县城使用,sleep()方法不会释放锁,所以导致死锁发生。

3)作用域不同,sleep()方法可在代码的任何地方,而wait()方法需用在synchronized方法或代码块中。

10,notify()和notifyAll()方法:notify()方法只唤醒一个进程,notifyAll()方法唤醒所有进程,多个锁之间存在竞争关系。

11,ArrayList,LinkedList,vector

ArrayList与Vector最大的区别是synchronized的使用,ArrayList是非线程安全的,而vector的绝大多数方法都是线程同步的

ArrayList和Vector是基于存储元素的Objectp[] array实现的,在内存中是一块连续的存储空间,因此对插入操作执行的慢,且都有一个初始化的容量的大写,vector扩充为原来的两倍,arrayList为原来的1.5倍。

linkedList采用双向链表丝线,对数据的索引需要从列表头开始便利,因此用于随机访问效率比较低,但是插入和删除效率较高。

12,HashMap,HashTable

HashMap是HashTable 的轻量级实现,都完成了Map接口,HashMap是非线程安全的,允许键值(仅一条记录)为null,HashTable不允许,HashMap改HashTable的contains方法为ContainsKey和ContainsValue方法,HashTable是线程安全的,因此效率较低,两个都使用hash/rehash算法,因此性能不会有很大差距。

13,Collection是一个集合接口,Collections是针对集合类的包装类,该类不能实例化,是一个工具类,服务于Collection框架。

14,Spring ioc

IOC的思想是:Spring容器来实现这些相互依赖对象的创建、协调工作。对象只需要关系业务逻辑本身就可以了。从这方面来说,对象如何得到他的协作对象的责任被反转了(IOC、DI)。

Spring所倡导的开发方式就是如此:所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转

IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。其实依赖注入的思想也很简单,它是通过反射机制实现的,在实例化一个类时,它通过反射调用类中set方法将事先保存在HashMap中的类属性注入到类中。

15,Sring aop

Spring中的AOP代理还是离不开Spring的IOC容器,代理的生成,管理及其依赖关系都是由IOC容器负责,Spring默认使用JDK动态代理,在需要代理类而不是代理接口的时候,Spring会自动切换为使用CGLIB代理,不过现在的项目都是面向接口编程,所以JDK动态代理相对来说用的还是多一些。

静态代理的缺点很明显:一个代理类只能对一个业务接口的实现类进行包装,如果有多个业务接口的话就要定义很多实现类和代理类才行。而且,如果代理类对业务方法的预处理、调用后操作都是一样的(比如:调用前输出提示、调用后自动关闭连接),则多个代理类就会有很多重复代码。这时我们可以定义这样一个代理类,它能代理所有实现类的方法调用:根据传进来的业务实现类和方法名进行具体调用。——那就是动态代理。

JDK动态代理所用到的代理类在程序调用到代理类对象时才由JVM真正创建,JVM根据传进来的 业务实现类对象 以及 方法名 ,动态地创建了一个代理类的class文件并被字节码引擎执行,然后通过该代理类对象进行方法调用。我们需要做的,只需指定代理类的预处理、调用后操作即可。

JDK动态代理的代理对象在创建时,需要使用业务实现类所实现的接口作为参数(因为在后面代理方法时需要根据接口内的方法名进行调用)。如果业务实现类是没有实现接口而是直接定义业务方法的话,就无法使用JDK动态代理了。并且,如果业务实现类中新增了接口中没有的方法,这些方法是无法被代理的(因为无法被调用)。

cglib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理。因为采用的是继承,所以不能对final修饰的类进行代理。

静态代理是通过在代码中显式定义一个业务实现类一个代理,在代理类中对同名的业务方法进行包装,用户通过代理类调用被包装过的业务方法;

JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;

CGlib动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理;

16,hibernate

一级缓存,二级缓存,分页机制,

hibernate的API没有侵入性,但只适用于针对单一对象的增删改查,不适用批量修改场合,当需要使用数据库的特定优化机制时,不适用。两个配置文件,hibernate.cfg.xml配置数据库使用的驱动类,数据库链接的url和用户名密码等, *.hbm.xml配置java对象和关系数据库记录的映射关系

saveorUpdate()方法,get()和load()

17,mybatis和hibernate区别

相同点:Hibernate与MyBatis都可以是通过SessionFactoryBuider由XML配置文件生成SessionFactory,然后由SessionFactory生成Session,最后由Session来开启执行事务和SQL语句。其中SessionFactoryBuider,SessionFactory,Session的生命周期都是差不多的。

Hibernate和MyBatis都支持JDBC和JTA事务处理。

Mybatis优势

MyBatis可以进行更为细致的SQL优化,可以减少查询字段。

MyBatis容易掌握,而Hibernate门槛较高。

Hibernate优势

Hibernate的DAO层开发比MyBatis简单,Mybatis需要维护SQL和结果映射。

Hibernate对对象的维护和缓存要比MyBatis好,对增删改查的对象的维护要方便。

Hibernate数据库移植性很好,MyBatis的数据库移植性不好,不同的数据库需要写不同SQL。

Hibernate有更好的二级缓存机制,可以使用第三方缓存。MyBatis本身提供的缓存机制不佳。

他人总结

Hibernate功能强大,数据库无关性好,O/R映射能力强,如果你对Hibernate相当精通,而且对Hibernate进行了适当的封装,那么你的项目整个持久层代码会相当简单,需要写的代码很少,开发速度很快,非常爽。

Hibernate的缺点就是学习门槛不低,要精通门槛更高,而且怎么设计O/R映射,在性能和对象模型之间如何权衡取得平衡,以及怎样用好Hibernate方面需要你的经验和能力都很强才行。

iBATIS入门简单,即学即用,提供了数据库查询的自动对象绑定功能,而且延续了很好的SQL使用经验,对于没有那么高的对象模型要求的项目来说,相当完美。

iBATIS的缺点就是框架还是比较简陋,功能尚有缺失,虽然简化了数据绑定代码,但是整个底层数据库查询实际还是要自己写的,工作量也比较大,而且不太容易适应快速数据库修改。

18,springboot

Spring Boot就是一些库的集合,它能够被任意项目的构建系统所使用。简便起见,该框架也提供了命令行界面,它可以用来运行和测试Boot应用。框架的发布版本,包括集成的CLI(命令行界面),可以在Spring仓库中手动下载和安装。一种更为简便的方式是使用Groovy环境管理器(Groovy enVironment Manager,GVM),它会处理Boot版本的安装和管理。Boot及其CLI可以通过GVM的命令行gvm install springboot进行安装。

19,Gradle

Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化构建开源工具。它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置,抛弃了基于XML的各种繁琐配置。

面向Java应用为主。当前其支持的语言限于Java、Groovy、Kotlin和Scala

20,消息队列

主要原因是由于在高并发环境下,由于来不及同步处理,请求往往会发生堵塞,比如说,大量的insert,update之类的请求同时到达MySQL,直接导致无数的行锁表锁,甚至最后请求会堆积过多,从而触发too many connections错误。通过使用消息队列,我们可以异步处理请求,从而缓解系统的压力。

21,基本数据结构:栈,队列,链表(双向链表,单链表)

22,redis

redis使用了两种文件格式:全量数据和增量请求。

全量数据格式是把内存中的数据写入磁盘,便于下次读取文件进行加载;

增量请求文件则是把内存中的数据序列化为操作请求,用于读取文件进行replay得到数据,序列化的操作包括SET、RPUSH、SADD、ZADD。

redis的存储分为内存存储、磁盘存储和log文件三部分,配置文件中有三个参数对其进行配置。

save seconds updates,save配置,指出在多长时间内,有多少次更新操作,就将数据同步到数据文件。这个可以多个条件配合,比如默认配置文件中的设置,就设置了三个条件。

appendonly yes/no ,appendonly配置,指出是否在每次更新操作后进行日志记录,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为redis本身同步数据文件是按上面的save条件来同步的,所以有的数据会在一段时间内只存在于内存中。

appendfsync no/always/everysec ,appendfsync配置,no表示等操作系统进行数据缓存同步到磁盘,always表示每次更新操作后手动调用fsync()将数据写到磁盘,everysec表示每秒同步一次。

23,spring MVC

24,web service

25,cookies 和session

cookie是在http上,服务器或脚本可以维护客户工作站上信息的一种方式,是web服务器保留在用户浏览器上的小文件,session是指用来在客户端与服务端之间保持状态的解决方案以及存储结构。

cookie存在浏览器上,但安全性不高,但是性能较好,单个cookie保存的数据不能超过4kb,而session不存在此问题。session存在服务器上,安全性好一些,但性能略差。

26,工厂模式:Spring中BeanFactory即是工程模式

适配器模式:字节流向字符流转化

观察者模式:点击事件

27,java反射机制

28,枚举类与普通类的区别

使用enum定义的枚举类默认继承了java.lang.Enum类

枚举类的构造器只能使用private

枚举类的每个实例必须在枚举类中显示的列出(,分隔;结尾) 列出的实例系统会自动添加public static final修饰

所有的枚举类都定义了一个values方法,该方法可以很方便的遍历所有的枚举值

可以在switch表达式使用枚举类对象作为表达式,case子句可以直接使用枚举的名字,无需添加枚举类作为限定

枚举类对象的属性不能更改,所以要用private final修饰

枚举类对象要在构造器中被赋值

29,中间件

30,jvm虚指令

31,java多态实现

override

overloading:一个类中有多个同名方法,但方法有不同的参数

32,jdk1.8的新功能

33,xml检查工具主要想法

34,类加载器工作机制

加载:类加载过程的一个阶段:通过一个类的完全限定查找此类字节码文件,并利用字节码文件创建一个Class对象

验证:目的在于确保Class文件的字节流中包含信息符合当前虚拟机要求,不会危害虚拟机自身安全。主要包括四种验证,文件格式验证,元数据验证,字节码验证,符号引用验证。

准备:为类变量(即static修饰的字段变量)分配内存并且设置该类变量的初始值即0(如static int i=5;这里只将i初始化为0,至于5的值将在初始化时赋值),这里不包含用final修饰的static,因为final在编译的时候就会分配了,注意这里不会为实例变量分配初始化,类变量会分配在方法区中,而实例变量是会随着对象一起分配到Java堆中。

解析:主要将常量池中的符号引用替换为直接引用的过程。符号引用就是一组符号来描述目标,可以是任何字面量,而直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。有类或接口的解析,字段解析,类方法解析,接口方法解析(这里涉及到字节码变量的引用,如需更详细了解,可参考《深入Java虚拟机》)。

初始化:类加载最后阶段,若该类具有超类,则对其进行初始化,执行静态初始化器和静态初始化成员变量(如前面只初始化了默认值的static变量将会在这个阶段赋值,成员变量也将被初始化)。

这便是类加载的5个过程,而类加载器的任务是根据一个类的全限定名来读取此类的二进制字节流到JVM中,然后转换为一个与目标类对应的java.lang.Class对象实例,在虚拟机提供了3种类加载器,引导(Bootstrap)类加载器、扩展(Extension)类加载器、系统(System)类加载器(也称应用类加载器),下面分别介绍

启动(Bootstrap)类加载器

启动类加载器主要加载的是JVM自身需要的类,这个类加载使用C++语言实现的,是虚拟机自身的一部分,它负责将 /lib路径下的核心类库或-Xbootclasspath参数指定的路径下的jar包加载到内存中,注意必由于虚拟机是按照文件名识别加载jar包的,如rt.jar,如果文件名不被虚拟机识别,即使把jar包丢到lib目录下也是没有作用的(出于安全考虑,Bootstrap启动类加载器只加载包名为java、javax、sun等开头的类)。

扩展(Extension)类加载器

扩展类加载器是指Sun公司(已被Oracle收购)实现的sun.misc.Launcher$ExtClassLoader类,由Java语言实现的,是Launcher的静态内部类,它负责加载/lib/ext目录下或者由系统变量-Djava.ext.dir指定位路径中的类库,开发者可以直接使用标准扩展类加载器。

系统(System)类加载器

也称应用程序加载器是指 Sun公司实现的sun.misc.Launcher$AppClassLoader。它负责加载系统类路径java -classpath或-D java.class.path 指定路径下的类库,也就是我们经常用到的classpath路径,开发者可以直接使用系统类加载器,一般情况下该类加载是程序中默认的类加载器,通过ClassLoader#getSystemClassLoader()方法可以获取到该类加载器。

  在Java的日常应用程序开发中,类的加载几乎是由上述3种类加载器相互配合执行的,在必要时,我们还可以自定义类加载器,需要注意的是,Java虚拟机对class文件采用的是按需加载的方式,也就是说当需要使用该类时才会将它的class文件加载到内存生成class对象,而且加载某个类的class文件时,Java虚拟机采用的是双亲委派模式即把请求交由父类处理,它一种任务委派模式,下面我们进一步了解它。

35,抽象类和接口

36, 对于Web Service与RESTful而言,

1,java基本数据类型已经拆装箱:

java基本数据类型:int(4),float(4),short(2),long(8),double(8),char(2),boolean(1),byte(1)

包装类型:Integer,Float,Short,Long,Double,Char,Boolean,Byte

基本数据类型与包装类型之间的转化为拆装箱

基本数据类型在传递参数时是按照值传递,其实例变量的默认值与它们的类型有关

包装类型在传递参数时按照引用传递,其实例变量的默认值为null;

2,按值传递和按引用传递的区别:

值传递,在方法调用中,实参把它的值传给形参,形参只是用实参的值初始化一个临时的存储区,实参和形参有相同的值,确实不同的存储单元,因此对形 参的改变不影响实参的值。

引用传递,在方法调用中,传递的是对象的地址,这是形参和实参指向同一块存储单元,因此对形参的修改会影响实参。

3,java中String ,Stringbuilder,Stringbuffer区别:

String是不可变类,一旦被创建,其值不可改变,当字符量较小时,常用String.

Stringbuffer是可变类,可以动态添加修改字符串,

String可以用构造函数实例化,也可以用赋值的方式实例化,但StringBuffer只能用构造函数实例化,

String字符串修改时,实际等价于创建一个StringBuffer,调用StringBuffer.append()方法,再将StringBuffer().tostring;

因此对字符串的修改不及StringBuffer方便。

Stringbuilder也可修改字符串,都是字符串缓冲区,与StringBuffer不同的是,StringBuilder是线程不安全的,因此效率较高,如果需要在单线程环境下操 作大量字符串,可以选择StringBuilder.

String提供了length()方法计算字符串的长度,length属性可以获取数组的长度。

4,"==","equals","hashcode"的区别和用法:

“==”用来比较基本数据类型的数据或者两个引用变量是否相等,即比较变量对象在内存中所存储的数值是否相等。

如果一个变量指向的数据是对象(引用类型),则变量指向了两块内存区域,用来存放对象的堆内存,用来存储对象在堆内存中首地址的变量,该变量一般在栈中,如果要比较两个变量是否相等,即要看两个变量是否存的是同一块存储空间,可以用“==”,否则需要用equals,

equals是Object基类提供的方法之一,用来比较引用中的数据内容,在equals未被重写之前,每一个类的equals方法都是直接使用“==”运算符比较两个对象。

String类重写了equals方法,用来比较两个对立对象的内容是否相同,即堆中的内容是否相同。如

String s1 = new String("abc");

String s2 = new String("abc");

s1和s2是指向两个对象,这两个对象的首地址不同,但内容相同,‘’==‘’时false,equals时true,

hashcode也是Object类的方法之一,object类中的hashcode()返回的是内存地址转换成int类型后的值,因此,没有重写hashcode()方法前,hashcode()比较两个两个变量是否相等永远返回false.

一般覆盖equals()方法是也会覆盖hashcode()方法,如果两个变量equals后返回true,那么hashcode()返回也为true,如果equals返回为false,hashcode()可能想等也可能不等,而当hashcde()返回为fasle时,用equals比较也为false,但当hashcode()返回为true时,equals可能相等也可能不等。

5,jvm内存数据模型

1)程序计数器,是一个数据结构,用来保存当前正在执行的程序的内存地址,java虚拟机的多线程就是通过线程轮流切换并分配处理器时间来实现的,该区域为线程私有,为了使线程切换后能恢复到正常的位置,每个线程有一个独立的计数器,互不影响。

2)java虚拟栈,用来存放基本数据类型对象的引用等局部变量表,操作栈,方法返回值等,是线程私有的,与线程的生命周期相同。

3)本地方法栈,线程私有,为java虚拟栈用到的native方法服务。

4)java堆,程序运行中为对象分配内存区域,线程共享的,用过new关键字创建,

5)运行时方法区,各个线程共享的区域,存储虚拟机加载的类信息,常量,静态变量,编译后的代码。

6)运行时常量区,代表运行时每个class文件的常量表,包括几种常量:编译时的数字常量,方法或者域的引用。

6,jvm中栈和堆的区别

栈主要用来存放基本数据类型和引用变量,栈内存的管理是通过压栈和弹栈的方式进行的,以栈帧为基本单位来管理程序的调用关系,当一个线程生命周期结束后,栈中的数据通过弹栈方式销毁

堆主要是引用类型的变量,是在程序运行过程中动态分配内存,需要通过new关键字创建

从功能来看,栈用来执行程序,堆用来存放对象,栈的大小和生存周期是确定的,因此缺乏一定的灵活性,但存取速度快,堆在运行时动态的分配内存,生存期不必提前告诉编译器,这也导致了存取速度慢,堆内存需要垃圾回收器处理。

在堆中产生了一个数组或者对象后,可以在栈中定义一个变量,变量的取值等于数组或者对象在堆内存中的首地址,栈中这个变量就成了数组或者变量的引用变量,引用变量相当于数组或者对象的别名,以后就可以通过栈中的引用变量访问队中的数组或者对象,如String s = new String("abc").

7,垃圾回收器机制

常用的垃圾回收算法

1)引用计数器法,在堆中每个对象都有一个引用计数器,当对象被引用时,引用计数器加一,当引用被置为空或者离开作用域时,引用计数器减1,但无法解决相互引用的问题,一次不常用

2)追踪回收算法,利用jvm维护的对象引用图,从根节点开始遍历对象的应用图,同时标记遍历到的对象,当遍历结束后,未被标记的对象就是目前已不被使用的对象,就被回收了

3)压缩回收算法,把堆中活动的对象移到堆的另一端,这样就会在堆中另外一端留出很大一块空间区域,相当于对堆中的碎片整理,简化消除堆碎片的工作,但影响性能。

4)复制回收算法,将堆分成大小相同的两块区域,在任何时候只在一个区域中被使用,直到这个区域被消耗完,此时垃圾回收器中断程序的执行,通过遍历地方法把堆中所有活动的方法移动到另一块区域中,在复制过程中,它们紧挨着布局,从而可以消除碎片,缺点,对指点大小的堆来说,需要两倍的内存空间,同时由于移动过程中需要中断所有程序执行,影响了效率。

5)按代回收算法,由于程序创建的大部分对象的生命周期都很短,一小部分有较长的生命周期,因此,按代回收算法的基本思想是把堆分成两个或多个子堆,每个堆视为一代,算法在进行的时候优先收集那些“年幼的”子堆,如果一个对象经过多次收集后仍然存活,将其移入老年代,减少对其扫描次数。基本做法新生代有一个eden区和两个survivor区,首先将对象放在eden区,如果空间不足,就放在其中一个survivor区,如果仍然放不下就会引发一次发生在新生代的minor GC,将存活的对象放在另一个survivor区,然后清空eden区和之前那个survivor区的内存,在多次GC过程中,仍有放不下的对象,就将这些对象放入老年代内存里去,大对象已经长期存活的对象直接放入老年代,当每次执行minor GC时应该要对晋升到老年代的对象进行分析,如果这些马上要进入到老年代的对象超过了老年区的剩余大小,则引发一次fullGC以尽可能获得老年区的空间,新生代主要负责复制清理工作,老年代用标记-清除和标记-压缩算法,而永久代主要存放java中的类和加载类的类加载器本身。

8,线程同步实现方式:

synchronized 和lock方法:

synchronized方法或者synchronized代码块,可以作用于静态方法,类或者某个实例,是悲观锁机制,独占锁,是java关键字,在jvm层面上,已获取锁的线程执行完同步代码,会释放锁,当线程执行时发生异常,jvm也会让线程释放锁,但不能判断锁的状态,当线程A获取锁并阻塞时,同步执行的线程B会一直等待,

lack方法已非阻塞的方法获取锁,是一个类,需要指定起始和终止位置,执行完成后需要在finally中释放。trylock以非阻塞的方式获取锁,尝试性地获取锁,若获取返回true,否则返回false,

reentrantlock具有公平锁功能,每个到来的线程都排队等候。

9,sleep()方法和wait()方法

1)原理不同。sleep()方法使线程暂停执行一段时间,等时间截至,自动苏醒,是thread类的静态方法,是线程用来控制自身流程的,而wait()是Object类的方法之一,是线程间的通信,该方法是当前拥有该对象锁的进程等待,用notify()或者notifyAll()方法唤醒

2)对锁的处理机制不同,调用sleep()方法不会释放锁,而wait()方法被调用后,线程会释放当前占用的锁,从而使线程所在的对象中的其他synchronized数据可被其他县城使用,sleep()方法不会释放锁,所以导致死锁发生。

3)作用域不同,sleep()方法可在代码的任何地方,而wait()方法需用在synchronized方法或代码块中。

10,notify()和notifyAll()方法:notify()方法只唤醒一个进程,notifyAll()方法唤醒所有进程,多个锁之间存在竞争关系。

11,ArrayList,LinkedList,vector

ArrayList与Vector最大的区别是synchronized的使用,ArrayList是非线程安全的,而vector的绝大多数方法都是线程同步的

ArrayList和Vector是基于存储元素的Objectp[] array实现的,在内存中是一块连续的存储空间,因此对插入操作执行的慢,且都有一个初始化的容量的大写,vector扩充为原来的两倍,arrayList为原来的1.5倍。

linkedList采用双向链表丝线,对数据的索引需要从列表头开始便利,因此用于随机访问效率比较低,但是插入和删除效率较高。

12,HashMap,HashTable

HashMap是HashTable 的轻量级实现,都完成了Map接口,HashMap是非线程安全的,允许键值(仅一条记录)为null,HashTable不允许,HashMap改HashTable的contains方法为ContainsKey和ContainsValue方法,HashTable是线程安全的,因此效率较低,两个都使用hash/rehash算法,因此性能不会有很大差距。

13,Collection是一个集合接口,Collections是针对集合类的包装类,该类不能实例化,是一个工具类,服务于Collection框架。

14,Spring ioc

IOC的思想是:Spring容器来实现这些相互依赖对象的创建、协调工作。对象只需要关系业务逻辑本身就可以了。从这方面来说,对象如何得到他的协作对象的责任被反转了(IOC、DI)。

Spring所倡导的开发方式就是如此:所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转

IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。其实依赖注入的思想也很简单,它是通过反射机制实现的,在实例化一个类时,它通过反射调用类中set方法将事先保存在HashMap中的类属性注入到类中。

15,Sring aop

Spring中的AOP代理还是离不开Spring的IOC容器,代理的生成,管理及其依赖关系都是由IOC容器负责,Spring默认使用JDK动态代理,在需要代理类而不是代理接口的时候,Spring会自动切换为使用CGLIB代理,不过现在的项目都是面向接口编程,所以JDK动态代理相对来说用的还是多一些。

静态代理的缺点很明显:一个代理类只能对一个业务接口的实现类进行包装,如果有多个业务接口的话就要定义很多实现类和代理类才行。而且,如果代理类对业务方法的预处理、调用后操作都是一样的(比如:调用前输出提示、调用后自动关闭连接),则多个代理类就会有很多重复代码。这时我们可以定义这样一个代理类,它能代理所有实现类的方法调用:根据传进来的业务实现类和方法名进行具体调用。——那就是动态代理。

JDK动态代理所用到的代理类在程序调用到代理类对象时才由JVM真正创建,JVM根据传进来的 业务实现类对象 以及 方法名 ,动态地创建了一个代理类的class文件并被字节码引擎执行,然后通过该代理类对象进行方法调用。我们需要做的,只需指定代理类的预处理、调用后操作即可。

JDK动态代理的代理对象在创建时,需要使用业务实现类所实现的接口作为参数(因为在后面代理方法时需要根据接口内的方法名进行调用)。如果业务实现类是没有实现接口而是直接定义业务方法的话,就无法使用JDK动态代理了。并且,如果业务实现类中新增了接口中没有的方法,这些方法是无法被代理的(因为无法被调用)。

cglib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理。因为采用的是继承,所以不能对final修饰的类进行代理。

静态代理是通过在代码中显式定义一个业务实现类一个代理,在代理类中对同名的业务方法进行包装,用户通过代理类调用被包装过的业务方法;

JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;

CGlib动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理;

16,hibernate

一级缓存,二级缓存,分页机制,

hibernate的API没有侵入性,但只适用于针对单一对象的增删改查,不适用批量修改场合,当需要使用数据库的特定优化机制时,不适用。两个配置文件,hibernate.cfg.xml配置数据库使用的驱动类,数据库链接的url和用户名密码等, *.hbm.xml配置java对象和关系数据库记录的映射关系

saveorUpdate()方法,get()和load()

17,mybatis和hibernate区别

相同点:Hibernate与MyBatis都可以是通过SessionFactoryBuider由XML配置文件生成SessionFactory,然后由SessionFactory生成Session,最后由Session来开启执行事务和SQL语句。其中SessionFactoryBuider,SessionFactory,Session的生命周期都是差不多的。

Hibernate和MyBatis都支持JDBC和JTA事务处理。

Mybatis优势

MyBatis可以进行更为细致的SQL优化,可以减少查询字段。

MyBatis容易掌握,而Hibernate门槛较高。

Hibernate优势

Hibernate的DAO层开发比MyBatis简单,Mybatis需要维护SQL和结果映射。

Hibernate对对象的维护和缓存要比MyBatis好,对增删改查的对象的维护要方便。

Hibernate数据库移植性很好,MyBatis的数据库移植性不好,不同的数据库需要写不同SQL。

Hibernate有更好的二级缓存机制,可以使用第三方缓存。MyBatis本身提供的缓存机制不佳。

他人总结

Hibernate功能强大,数据库无关性好,O/R映射能力强,如果你对Hibernate相当精通,而且对Hibernate进行了适当的封装,那么你的项目整个持久层代码会相当简单,需要写的代码很少,开发速度很快,非常爽。

Hibernate的缺点就是学习门槛不低,要精通门槛更高,而且怎么设计O/R映射,在性能和对象模型之间如何权衡取得平衡,以及怎样用好Hibernate方面需要你的经验和能力都很强才行。

iBATIS入门简单,即学即用,提供了数据库查询的自动对象绑定功能,而且延续了很好的SQL使用经验,对于没有那么高的对象模型要求的项目来说,相当完美。

iBATIS的缺点就是框架还是比较简陋,功能尚有缺失,虽然简化了数据绑定代码,但是整个底层数据库查询实际还是要自己写的,工作量也比较大,而且不太容易适应快速数据库修改。

18,springboot

Spring Boot就是一些库的集合,它能够被任意项目的构建系统所使用。简便起见,该框架也提供了命令行界面,它可以用来运行和测试Boot应用。框架的发布版本,包括集成的CLI(命令行界面),可以在Spring仓库中手动下载和安装。一种更为简便的方式是使用Groovy环境管理器(Groovy enVironment Manager,GVM),它会处理Boot版本的安装和管理。Boot及其CLI可以通过GVM的命令行gvm install springboot进行安装。

19,Gradle

Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化构建开源工具。它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置,抛弃了基于XML的各种繁琐配置。

面向Java应用为主。当前其支持的语言限于Java、Groovy、Kotlin和Scala

20,消息队列

主要原因是由于在高并发环境下,由于来不及同步处理,请求往往会发生堵塞,比如说,大量的insert,update之类的请求同时到达MySQL,直接导致无数的行锁表锁,甚至最后请求会堆积过多,从而触发too many connections错误。通过使用消息队列,我们可以异步处理请求,从而缓解系统的压力。

21,基本数据结构:栈,队列,链表(双向链表,单链表)

22,redis

redis使用了两种文件格式:全量数据和增量请求。

全量数据格式是把内存中的数据写入磁盘,便于下次读取文件进行加载;

增量请求文件则是把内存中的数据序列化为操作请求,用于读取文件进行replay得到数据,序列化的操作包括SET、RPUSH、SADD、ZADD。

redis的存储分为内存存储、磁盘存储和log文件三部分,配置文件中有三个参数对其进行配置。

save seconds updates,save配置,指出在多长时间内,有多少次更新操作,就将数据同步到数据文件。这个可以多个条件配合,比如默认配置文件中的设置,就设置了三个条件。

appendonly yes/no ,appendonly配置,指出是否在每次更新操作后进行日志记录,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为redis本身同步数据文件是按上面的save条件来同步的,所以有的数据会在一段时间内只存在于内存中。

appendfsync no/always/everysec ,appendfsync配置,no表示等操作系统进行数据缓存同步到磁盘,always表示每次更新操作后手动调用fsync()将数据写到磁盘,everysec表示每秒同步一次。

23,spring MVC

24,web service

25,cookies 和session

cookie是在http上,服务器或脚本可以维护客户工作站上信息的一种方式,是web服务器保留在用户浏览器上的小文件,session是指用来在客户端与服务端之间保持状态的解决方案以及存储结构。

cookie存在浏览器上,但安全性不高,但是性能较好,单个cookie保存的数据不能超过4kb,而session不存在此问题。session存在服务器上,安全性好一些,但性能略差。

26,工厂模式:Spring中BeanFactory即是工程模式

适配器模式:字节流向字符流转化

观察者模式:点击事件

27,java反射机制

28,枚举类与普通类的区别

使用enum定义的枚举类默认继承了java.lang.Enum类

枚举类的构造器只能使用private

枚举类的每个实例必须在枚举类中显示的列出(,分隔;结尾) 列出的实例系统会自动添加public static final修饰

所有的枚举类都定义了一个values方法,该方法可以很方便的遍历所有的枚举值

可以在switch表达式使用枚举类对象作为表达式,case子句可以直接使用枚举的名字,无需添加枚举类作为限定

枚举类对象的属性不能更改,所以要用private final修饰

枚举类对象要在构造器中被赋值

29,中间件

30,jvm虚指令

31,java多态实现

override:子类覆盖父类的方法,同样的方法在子类和父类中有不同的表现形式,是一种运行时多态

overloading:一个类中有多个同名方法,但方法有不同的参数,是一种编译时多态

32,jdk1.8的新功能

34,类加载器工作机制

加载:类加载过程的一个阶段:通过一个类的完全限定查找此类字节码文件,并利用字节码文件创建一个Class对象

验证:目的在于确保Class文件的字节流中包含信息符合当前虚拟机要求,不会危害虚拟机自身安全。主要包括四种验证,文件格式验证,元数据验证,字节码验证,符号引用验证。

准备:为类变量(即static修饰的字段变量)分配内存并且设置该类变量的初始值即0(如static int i=5;这里只将i初始化为0,至于5的值将在初始化时赋值),这里不包含用final修饰的static,因为final在编译的时候就会分配了,注意这里不会为实例变量分配初始化,类变量会分配在方法区中,而实例变量是会随着对象一起分配到Java堆中。

解析:主要将常量池中的符号引用替换为直接引用的过程。符号引用就是一组符号来描述目标,可以是任何字面量,而直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。有类或接口的解析,字段解析,类方法解析,接口方法解析(这里涉及到字节码变量的引用,如需更详细了解,可参考《深入Java虚拟机》)。

初始化:类加载最后阶段,若该类具有超类,则对其进行初始化,执行静态初始化器和静态初始化成员变量(如前面只初始化了默认值的static变量将会在这个阶段赋值,成员变量也将被初始化)。

这便是类加载的5个过程,而类加载器的任务是根据一个类的全限定名来读取此类的二进制字节流到JVM中,然后转换为一个与目标类对应的java.lang.Class对象实例,在虚拟机提供了3种类加载器,引导(Bootstrap)类加载器、扩展(Extension)类加载器、系统(System)类加载器(也称应用类加载器),下面分别介绍

启动(Bootstrap)类加载器

启动类加载器主要加载的是JVM自身需要的类,这个类加载使用C++语言实现的,是虚拟机自身的一部分,它负责将 /lib路径下的核心类库或-Xbootclasspath参数指定的路径下的jar包加载到内存中,注意必由于虚拟机是按照文件名识别加载jar包的,如rt.jar,如果文件名不被虚拟机识别,即使把jar包丢到lib目录下也是没有作用的(出于安全考虑,Bootstrap启动类加载器只加载包名为java、javax、sun等开头的类)。

扩展(Extension)类加载器

扩展类加载器是指Sun公司(已被Oracle收购)实现的sun.misc.Launcher$ExtClassLoader类,由Java语言实现的,是Launcher的静态内部类,它负责加载/lib/ext目录下或者由系统变量-Djava.ext.dir指定位路径中的类库,开发者可以直接使用标准扩展类加载器。

系统(System)类加载器

也称应用程序加载器是指 Sun公司实现的sun.misc.Launcher$AppClassLoader。它负责加载系统类路径java -classpath或-D java.class.path 指定路径下的类库,也就是我们经常用到的classpath路径,开发者可以直接使用系统类加载器,一般情况下该类加载是程序中默认的类加载器,通过ClassLoader#getSystemClassLoader()方法可以获取到该类加载器。

  在Java的日常应用程序开发中,类的加载几乎是由上述3种类加载器相互配合执行的,在必要时,我们还可以自定义类加载器,需要注意的是,Java虚拟机对class文件采用的是按需加载的方式,也就是说当需要使用该类时才会将它的class文件加载到内存生成class对象,而且加载某个类的class文件时,Java虚拟机采用的是双亲委派模式即把请求交由父类处理,它一种任务委派模式,下面我们进一步了解它。

相关文章
最新文章
热点推荐