1、jdk1.7与jdk1.8的新特性
1.7
1、switch语法支持String
2、可以直接使用0b开头表示二进制变量
3、try-with-resource语句用于生命资源,保证资源在程序结束后关闭
4、catch多个异常
5、数字类型的下划线表示方式
1.8
1、允许给接口添加default的方法的实现
2、lambda表达式
3、函数式接口
4、关于日期处理在java.time包下有了新的api
2、基本类型与包装类、基本类型的运算
类型 | 长度(位) |
---|---|
boolean | 1 |
byte | 8 |
char | 16 |
short | 16 |
int | 32 |
float | 32 |
long | 64 |
double | 64 |
Integer是一个不可变对象,自动装箱和拆箱是由编译器来完成的。
1、newInteger(i)与Integer.valueof(i)方法的区别:
构造方法产生一个新的Integer对象,valueOf()方法有可能通过缓存经常请求的值而显著提高空间和时间性能。
1 | public static Integer valueOf(int i) { |
其中IntegerCache.high值默认为127,可以通过虚拟机参数AutoBoxCacheMax自定义
1 | Integerc = 1; //这是自动装箱,编译器调用valueOf(1)方法 |
2、自动包装机制的好处:
节省了常用数值的内存开销和创建对象的开销,提高了效率。
(1)Integer和int之间可以进行各种比较:Integer对象将自动拆箱后与int值比较。
(2)两个Integer对象之间也可以用\>、\<符号比较大小:两个Integer对象都拆箱后,在比较大小,但是两个Integer对象最好不要用==比较,因为-128~127范围内是取缓存内对象,所以相等,该范围外是两个不同对象引用比较,所以不等。
3、其他基本类型的包装机制
Byte、Short、Long对应的是-128~127
Character对应的是0~127
Float和Double没有自动装箱池
4、基本类型的运算
(1)在将float、double类型转换为int类型的时候总是截尾,如果想象四舍五入可以+0.5后强转,或者使用Math.round()方法。
Math.floor()取下整,Math.ceil()取上整。
(2)char、byte、short在运算之前会自动转换为int类型,结果自然也就是int类型
1 | int a = 2;int b = 3; |
包装类的equals()方法会取值进行比较
3、switch是否能作用在byte、long、String上?
在Java5以前,switch(expr)中,expr只能是byte、short、char、int。从Java5开始,Java中引入了枚举类型,expr也可以是enum类型,从Java7开始,expr还可以是字符串(String),但是长整型(long)在目前所有的版本中都是不可以的。
4、String、StringBuffer、StringBuilder
StringBuffer是线程安全的
String对象是不可变的,当需要改变它的内容的时候会返回一个新的对象
StringBuffer、StringBuilder是变量
三者在执行速度上:StringBuilder\>StringBuffer\>String
String对+运算符的重载实际上是使用StringBuilder.append()创建了一个新的String对象
String为什么设计成不可变对象
1 | public final class String implements java.io.Serializable, Comparable<String>, CharSequence { |
包含两个成员变量:value[]:String的字符序列,hash:该String对象的hash值的缓存。我们通过普通代码对一个String的改变都是通过返回一个新的String对象来完成的。但是String也不是绝对不可变的,我们可以通过反射拿到value对象,然后改变它。(final域是不能修改的,但是如果final域指向的对象内的域是可变的话,我们就可以修改final域指向对象的内容)
设计成不可变的好处
1、因为它是不可变的,所以其hashCode()永远保持一致,也就是每次在使用一个String对象的hashCode的时候都不用重新计算,直接返回hash值即可,效率更高
2、不可变对象天生线程安全,可以无需同步的在多个线程间共享
3、安全性,常用于数据库连接、网络连接、打开文件等,避免了参数修改
5、常见String比较,intern()
1、new出来的对象引用永远指向堆内存,String.intern()返回对应pool中的对象
2、Strings1="dsfsadf";这种字面量写法直接返回pool中的对象
3、Strings2="dsafd"+s1;指向堆,只有+左右都是""字面量才返回pool中。
4、String.intern()返回字符串对象的规范化表示形式。一个初始时为空的字符串池,它由类String私有地维护。String.intern()是一个Native方法,它的作用是:如果字符串常量池中已经包含一个等于此String对象的字符串,则返回代表池中这个字符串的String对象;否则,将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用。记录首次出现的实例
6、请描述抽象类和接口的区别
1、抽象类的域没有特殊限制,接口的域自动为publicstaticfinal的,在声明的同时必须初始化
2、接口中定义的方法必须(自动为)是publicabstract的,抽象类中的抽象方法可以是默认类型或者public类型或者protected类型
3、Implements一个接口必须override所有方法,extends一个抽象类则只要override抽象方法。一个类只能继承一个类(抽象类),但是可以实现多个接口
4、从选择上来讲,当所表达的意思是某些事物的共性的时候最好使用接口,而当表达的意思是某一个具体的事物的时候就用抽象类
7、内部类
使用场景: 只考虑为它的外部类提供服务
每个内部类都能够独立的实现接口或者继承类
成员内部类: 可以无条件访问外部类的所有成员属性和成员方法(包括private)。当成员内部类的field或method与外部类相同时默认访问的是内部类。每个实例都包含一个指向外围实例的引用,如果需要访问外围实例可使用outer.this.fild/method。在外部类中访问内部类的成员时需要先创建一个外部类,
1 | Outter outter = new Outter(); |
局部内部类: 定义在一个方法内部或者是一段作用域内的类,仅限于在作用域内访问
匿名内部类:
静态内部类: 不依赖于外部类,不能使用外部类的非static的域或者方法
1、为什么成员内部类可以无条件的访问外部类的成员?
内部类是java的一颗语法糖,编译之后会生成两个class文件,匿名内部类名字为外部类名$x,成员内部类名字为外部类名$内部类名,编译器会默认为成员内部类添加一个参数类型为指向外部类对象的构造方法。
2、为什么局部内部类和匿名内部类只能访问部局部final变量?
比如我们在一个方法内写了一个Thread匿名内部类,当外部的方法执行完毕后,方法内的局部变量生命周期结束,而Thread对象的生命周期还没有结束,所以必须使用final修改外部类的局部变量以使它在编译阶段就被放入常量池当中
3、Object.this与Object.new
当我们需要实例化一个成员内部类的时候,可以通过OuterClass.newInnerClass的方式初始化
如果我们需要在内部类中生成对外部类对象的引用可以使用OuterClass.this
8、java多态的实现原理
多态就是同一个消息根据发送对象的不同而采取多种不同的行为方式。主要体现在Overload和Override上
9、Object类都有什么方法
getClass(),toString(),hashCode(),clone(),wait(),notify(),notifyAll(),equals(),finalize()
要用想使用clone(),目标类必须实现Cloneable接口,然后以public的方式重写clone()方法,Object中原生的native方法clone()执行的是此对象的浅复制
10、hashCode()的作用,域equals()有什么关系
HashCode()返回该对象的hash()码值,Object()中的原生hashCode()是一个native方法,它将对象的内部地址转换成一个整数来实现。
HashCode()有一个常规协定:
1、当一个对象参与hash计算的成员域的值没有改变时,重复调用hashCode()值都相等;(一致性)
2、若两个对象equals()那么他们的hashCode()必须相等;
3、但是hashCode()相等,两个对象不一定equals()。这一点在hashMap的存储上有深刻体现。
Overrideequals()方法必须重写hashCode()。重写equals()要满足对称性(x.equals(y)返回true时,y.equals(x)也必须返回true)、自反性(x.equals(x)必须返回true)、一致性(当x和y引用的对象信息没有被修改时,多次调用x.equals(y)应该得到同样的返回值)、传递性(x.equals(y)和y.equals(z)都返回true时,x.equals(z)也必须返回true)。
11、深复制与浅复制区别
浅复制: 会创建一个新对象,对于源对象的基本类型属性拷贝一份,引用类型属性也只是拷贝引用。实现方式:类要重写实现Cloneable接口,以public的方式直接super重写clone()方法
深复制: 拷贝对象所有的属性,并拷贝引用类型属性所指向的对象。实现方法:重写clone()方法自定义重写过程;
对象序列化
12、说一下java异常体系
Throwable是根接口,有两个子接口Error、Exception
不可查异常:RunTimeException、Error
可查异常:除不可查异常之外的Exception,比如IOException、SQLException
注意事项:
finally语句块,总会被执行,但除以下情况:
1、finally语句块中出现异常
2、前面的代码调用了System.exit()
当try-catch语句块中有return语句时,方法会在返回前执行finally语句中的内容
finally语句块中重新抛出异常会覆盖掉之前的异常,在finally中直接使用return会丢失异常。
13、java中存在内存泄漏吗?请简单描述
从语法层面上来说没有,因为java有垃圾会收机制,然而在实际开发中,可能会存在无用但可达的对象,这些对象不能被GC回收,因此也会导致内存泄露的发生。
1、HashMap、ArrayList这一类型的集合对象,经常会导致内存泄漏,当他们被声明为static时,生命周期是和程序一样长的。所以它们引用的对象在程序的整个生命周期内都存在。
关于集合类还有一种情况是:当已添加到Set/Map中的对象,参与hashcode和equals()计算的变量被修改后,调用remove()无效,也会造成内存泄漏
2、单例模式的使用也可能导致内存泄漏,因为单例对象初始化后将在java虚拟机的整个生命周期中存在,如果他引用了一个对象,这个对象以及这个对象引用的所有对象都无法被垃圾收集器回收。
3、各种连接,如:dataSource、getConnection()、socket、io这些连接,JVM都不会主动回收,需要我们自己显式的在finally语句中close()
4、使用ThreadLocal也可能会导致内存泄漏,ThreadLocal的get()方法是从当前线程对象的ThreadLocalMap属性中获取对应的值的,ThreadLocalMap中维护着以ThreadLocal的弱引用为key的元素,当外部没有一个引用引用ThreadLocal的时候,就会被回收,这时Map中的key就为null了,这个元素也就无法被访问了,所以可能会被回收。
5、在使用Hibernate进行批量更新数据的时候也可能导致,因为更新数据后对象为持久态,会一直在session中存在,所以需要定期session、clear()强制清空缓存、session、flush()
14、简述整理容器体系

Stack是extendsVector的
LinkedHashSet、LinkedHashMap按照插入顺序排序
Queue:
在容量不足时抛异常 | 为空或容量不足时回返回false或null | |
---|---|---|
插入 | add(e) | offer(e) |
移除 | remove(e) | poll(e) |
仅返回 | element(e) | peek(e) |
15、ArrayList、LinkedList源码分析
16、HashMap、HashSet源码分析
Jdk7:位桶+链表
初始值:16最大值:2^30负载因子:0.75
底层数组的长度总是2^n
1 | final Entry <?,?>[] EMPTY_TABLE = {}; |
Entry是一个实现了Map.Entry\<K,V\>的类。
1 | static class Entry<K,V> implements Map.Entry<K,V> { |
核心构造方法:HashMap(intinitialCapacity,floatloadFactor);
其他的构造方法都是调用这个构造方法。
核心方法:
1 | public V put(K key, V value){ |
Entry放到数组的哪一个位置上是通过计算key的hashCode()得到的,当发生hash冲突(碰撞),HashMap解决hash冲突的方式是用链表。这里要注意的是,比如A和B都hash后都映射到下标i中,之前已经有A了,当map.put(B)时,将B放到下标i中,A则为B的next,所以新值存放在数组中,旧值在新值的链表上。
当size大于threshold(极限值)时,会发生扩容。threshold=capacity*loadfactor(容量*负载因子),扩容为原来的22倍。
Jdk8:位桶+链表/红黑树
当某个位桶的链表的长度达到某个阀值(默认为8)的时候,这个链表就将转换成红黑树。因为链表查找的时间复杂度为O(n),而红黑树一直是O(logn),这样会提高效率
HashSet:有一个HashMap成员域,所有方法直接调用HashMap中的方法。
如何保证元素唯一性的?
首先要存入的对象想要保证唯一性必须重写hashcode()和equals()方法,因为HashSet的底层实现是HashMap,Set的add()方法直接调用Map的put()方法,再解释map的put()方法
17、List和Set区别
它们都实现了Collection接口,List允许重复元素,并且维护一定顺序。其中ArrayList底层为数组,随机访问O(1),但是插入和删除速度慢O(n);LinkedList底层为双向链表,插入删除快O(1),查找慢O(n),LinkedList可以用作栈、队列。
Set不保存重复元素,HashSet使用了散列,长与查询;TreeSet将元素存储在红黑树当中,元素必须实现Comparable接口,维护特定顺序;LinkedHashSet也使用了散列,但又使用了链表来维护元素插入顺序。
18、说一下I/O框架
主要包含File类、I/O流、RandomAccessFile类(支持对文件的随机读取和写入,主要结合nio使用)
I/O包含字符流和字节流,各又分为输入输出两部分
1、字节流
InputStream表示从不同数据源产生的输入类:ByteArrayInputStream、StringBufferInuputStream、FileIInputStream、PipedInputStream、SequenceInputStream、FilterInputStream
FilterInputStream做为装饰器的接口,来控制特定的输入流:DataInputStream、BufferedInputStream、LineNumInputStream
OutputStream、FilterOutputStream有与输入流相对应的类PrintStream
2、字符流,供提供Unicode操作
Reader有FileReader、StringReader、CharArrayReader、PipedReader、BufferedReader
Writer有FileWriter、StringWriter、CharArrayWriter、PipedWriter、BufferedWriter
3、对象序列化
应用场景:web服务器中的session对象,当有10万用户访问时,服务器内存可能会吃不消了,这时可以先把session序列化到磁盘上,等到需要用的时候再序列化回来。
ObjectInputStream、ObjectOutputStream:用于序列化和反序列化
默认序列化:
要序列化的类必须实现Serializable接口(默认序列化)或者Externalizable(可以控制序列化的过程)
用于普通序列化(深复制),默认序列化恢复的时候直接从存储的二进制为基础恢复,不调用任何构造器
控制序列化的过程:
(1)序列化一个Externalizable对象会按照writeExternal()方法进行,对于恢复一个Externalizable的对象,所有普通的构造器都会被调用(包括在字段定义时的初始化),然后调用readExternal()方法。
(2)使用transient关键字逐个字段的关闭序列化
HashSet中的map就是transient的
注意:对于static的字段必须手动序列化,因为static的域不属于实例
4、压缩类
压缩输出类:ZipOutputStream、GZipOutputStream
压缩输入类:ZipInputStream、GZipInputStream
BufferedOutputStreamout=newBufferedOutputStream(newGZIPOutputStream(newFileOutputStream("E:/J2EE/新建文件夹/2.gz")));
//压缩多个文件,可是用ZipOutputStream.putNextEntry()方法
19、java nio
1、与I/O的的不同点
(1)I/O使用的是基于流的方式,每次产生或消费一个字节的数据,速度慢、NIO实用的是基于块的传送方式每次产生或消费一个数据块,速度较快
(2)I/O的read()和write()方法都是阻塞的,而NIO有非阻塞模式
(3)NIO的Selector允许一个线程监视多个输入通道
2、NIO概述
NIO是基于Channel和Buffer的io方式,通道是对流的模拟,传送数据必须通过一个通道,向通道发送数据的时候必须先把数据放到缓冲区中,从通道中读取数据的时候也必须读到缓冲区中。
3、通道
FileChannel从文件中读写数据DatagramChannel通过UDP读写网络中数据
SocketChannel通过TCP读写网络中数据
ServerSocketChannel可以监听新进来的TCP连接,对每一个新进来的连接都会创建一个SocketChannel
FileChannel不能切换到非阻塞模式,套接字Channel可以
4、缓冲区
ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer等。
Buffer的三个属性:
capacity:缓冲区的容量,只能往里边写入capacity个Char、Byte等
position、limit
写数据时:position指示当前的位置(初始值为0),最大值为capacity-1
limit表示最大能往缓冲区内写多少数据,写模式下==capacity
读数据时:从写切换到读时(调用flip()方法),position将会被置为0,limit表示最多能够读多少数据,从写切换到读时,limit将会被设置为写时候的position的值
5、使用Buffer读写数据一般步骤:
写入数据到Buffer调用flip()方法从Buffer中读数据调用clear()方法(会清除缓冲区)或者compact()方法(只清除读过的数据)
例:使用NIO读取文件中的汉字
1 | public static void read() throws IOException { |
6、ServerSocketChannel
用于监听新进来的TCP连接
1 | ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); |
默认为阻塞模式,serverSocketChannel.configureBlocking(false);设置为非阻塞模式,serverSocketChannel.accept();会立即返回,所以需要判断返回的socektChannel是否为null
7、SocketChannel
1 | socketChannel = SocketChannel.open(); |
默认为阻塞模式,socketChannel.configureBlocking(false);设置为非阻塞模式,在非阻塞模式下,connect()、read()、write()方法可能在没有完成的情况下就返回了,因此要在循环中调用write()使用socketChannel.finishConnect()判断连接是否建立关注read()方法的返回值
8、Selectors
一个Selector可以检测一到多个NIO通道,这样一个单独的线程就可以管理多个Channel,从而可以管理多个网络链接。因为线程之间的切换存在着很大的开销,所以使用的线程越少越好。
1 | Selector selector = Selector.open(); // 打开选择器 |
20、字符编码
1、iso8859-1
是一种单字节编码与ASCII类似,最多能表示的字符范围是0-255,应用于英文系列。
2、GBK
GB2312只能表示简体字,英文和ISO8859-1一致,GBK能够同时用来表示简体字和繁体字,兼容GB2312。他们两者都兼容ISO8859-1。是是不定长编码。
3、unicode
是一种定长编码,可用来表示所有语言的字符,为定长双字节,不兼容ISO8859-1,java内部使用Unicode来处理。
4、UTF-8
是一种多字节不定长格式,UTF-8将ASCII字符编码成单一的字节形式,将非ASCII字符编码成2-3个字节。字符串的长度存储在UTF-8字符串的前两个字节中。
21、泛型
1、Java允许使用List等原生类型是为了向后兼容。
2、泛型子类型化的规则:
1 | List ll; |
用使用List\<Objetct\>与与用直接使用List的区别:后者逃避了编译器的类型检查,前者明确告诉编译器持有类型。前者不能与其它泛型的引用相互,但List的引用可以指向任意泛型的List,所以使用List\<Object\>更加安全。