3.说一说你对java.lang.Object对象中hashCode和equals方法的理解。在什么场景下需要重新实现这两个方法。
当我们需要对值进行比较时,就需要重写这两个方法。例如String就重写了这两个方法,比较的是value值是否相等。
1)对于equals()与hashcode(),比较通用的规则:
①两个obj,如果equals()相等,hashCode()一定相等
②两个obj,如果hashCode()相等,equals()不一定相等
2)在设计一个类的时候往往需要重写equals方法,比如String类,在重写equals方法的同时,必须重写hashCode方法。
如果我们对一个对象重写了euqals,意思是只要对象的成员变量值都相等那么euqals就等于true,但不重写hashcode,那么我们再new一个新的对象,当原对象.equals(新对象)等于true时,两者的hashcode却是不一样的,由此将产生了理解的不一致,如在存储散列集合时(如Set类),将会存储了两个值一样的对象,导致混淆,因此,就也需要重写hashcode()。
通俗一点就是在例如hashMap集合中put一个对象的时候其实就是将一个对象的值(属性、哈希值)放入,如果在根据这个对象get取出来时也会考虑它的哈希值,但是get时如果没有重写hashcode()方法,你的新对象就是一个新的哈希值与原对象不想等,应该get时肯定为空。
1.什么是序列化,怎么序列化,为什么序列化,反序列化会遇到什么问题,如何解决。
1)序列化 (Serialization):
将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。
2) 序列化实现:
1)、Serializable接口:类通过实现 java.io.Serializable 标志接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。 2)、Externalizable接口:除了Serializable 之外,java中还提供了另一个序列化接口Externalizable。Externalizable继承了Serializable,该接口中定义了两个抽象方法:writeExternal()与readExternal()。当使用Externalizable接口来进行序列化与反序列化的时候需要开发人员重写writeExternal()与readExternal()方法。
3)、ObjectOutput和ObjectInput 接口。
4)、ObjectOutputStream类和ObjectInputStream类。
注意:
1)、Transient关键字:作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。
2)、序列化ID:虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化 ID 是否一致(就是 private static final long serialVersionUID)。序列化 ID 在 Eclipse 下提供了两种生成策略,一个是固定的 1L,一个是随机生成一个不重复的 long 类型数据(实际上是使用 JDK 工具生成),在这里有一个建议,如果没有特殊需求,就是用默认的 1L 就可以,这样可以确保代码一致时反序列化成功。那么随机生成的序列化 ID 有什么作用呢,有些时候,通过改变序列化 ID 可以用来限制某些用户的使用。
4) 为什么序列化:
1)、当你想把的内存中的对象保存到一个文件中或者数据库中时候;
2)、当你想用套接字在网络上传送对象的时候;
3)、当你想通过RMI传输对象的时候;
3) 反序列化问题及解决办法:
1)、类里面一定要serialVersionUID,否则旧数据会反序列化会失败。
private static final long serialVersionUID = -6849794470754667710L;
String类中的serialVersionUID的定义,即任何类如果想实现可序列化以及反序列化的话,就必须要实现可序列化接口以及定义serialVersionUID
解决方法:serialVersionUID是根据该类名、方法名等数据生产的一个整数,用来验证版本是否一致。如果不加这个字段,当你的类修改了字段,在反序列化的时候会直接报异常:InvalidCastException,导致无法完成反序列化。
2)、一旦序列化保存到磁盘操作后,就不要修改类名了,否则旧数据会反序列化会失败。
解决方法:所以尽量把对象转换成JSON保存更稳妥。
2.java8的新特性
1)、Lambda 表达式:将函数式编程引入了Java。Lambda允许把函数作为一个方法的参数,或者把代码看成数据。
String[] atp = {"Nadal", "Djokovic", "Wawrinka"};
List<String> players = Arrays.asList(atp);
// 以前的循环方式
for (String player : players) {
System.out.print(player + "; ");
}
// 使用 lambda 表达式以及函数操作(functional operation)
players.forEach((player) -> System.out.print(player + "; "));
2)、接口的默认方法与静态方法:我们可以在接口中定义默认方法,使用default关键字,并提供默认的实现。所有实现这个接口的类都会接受默认方法的实现,除非子类提供的自己的实现。
public interface DefaultFunctionInterface {
default String defaultFunction() {
return "default function";
}
}
//我们还可以在接口中定义静态方法,使用static关键字,也可以提供实现。
public interface StaticFunctionInterface {
static String staticFunction() {
return "static function";
}
}
3)、方法引用:通常与Lambda表达式联合使用,可以直接引用已有Java类或对象的方法。
4)、重复注解:在Java 5中使用注解有一个限制,即相同的注解在同一位置只能声明一次。Java 8引入重复注解,这样相同的注解在同一地方也可以声明多次。重复注解机制本身需要用@Repeatable注解。Java 8在编译器层做了优化,相同注解会以集合的方式保存,因此底层的原理并没有变化。
5)、扩展注解的支持:Java 8扩展了注解的上下文,几乎可以为任何东西添加注解,包括局部变量、泛型类、父类与接口的实现,连方法的异常也能添加注解。
6)、Optional:Java 8引入Optional类来防止空指针异常,Optional类最先是由Google的Guava项目引入的。Optional类实际上是个容器:它可以保存类型T的值,或者保存null。使用Optional类我们就不用显式进行空指针检查了。
Optional<String> str = Optional.of("test");
//1、判断str是否为null,如果不为null,则为true,否则为false
if (str.isPresent()) {
//get用于获取变量的值,当变量不存在的时候会抛出NoSuchElementException,如果不能确定变量一定存在值,则不推荐使用
str.get();
}
!
7)、Stream:Stream API是把真正的函数式编程风格引入到Java中。其实简单来说可以把Stream理解为MapReduce,当然Google的MapReduce的灵感也是来自函数式编程。她其实是一连串支持连续、并行聚集操作的元素。从语法上看,也很像linux的管道、或者链式编程,代码写起来简洁明了,非常酷帅!
8)、Date/Time API (JSR 310):Java 8新的Date-Time API (JSR 310)受Joda-Time的影响,提供了新的java.time包,可以用来替代 java.util.Date和java.util.Calendar。一般会用到Clock、LocaleDate、LocalTime、LocaleDateTime、ZonedDateTime、Duration这些类,对于时间日期的改进还是非常不错的。
9)、JavaScript引擎Nashorn:Nashorn允许在JVM上开发运行JavaScript应用,允许Java与JavaScript相互调用。
10)、Base64:在Java 8中,Base64编码成为了Java类库的标准。Base64类同时还提供了对URL、MIME友好的编码器与解