jdk1.8和之前的版本有较大的改动,HashMap新增红黑树的实现,ConcurrentHashMap使用cas算法重新实现。并且新增了几个新特性。
1. 二进制变量的表示,支持将整数用二进制来表示,用0b开头。
- 所有整数int,short,long,byte都可以用二进制表示。
byte b= (byte)0b10000101;
int i = 0b10001010;
long l = 0b10010101;
short s = 0b10000001;
2. switch语法支持String类型。
String str = null;
switch (str) {
case "1": {
break;
}
case "2": {
break;
}
case "3": {
break;
}
default: {
System.out.println("没了");
}
}
3. 新增Try-with-resource语句。
try-with-resource
语句是一种声明了一种或多种资源的try语句,所谓资源:是指在使用完成之后需要关闭的对象(例如io流)。try-with-resource
语句保证了每个资源在语句结束时都将被关闭。任何实现了java.lang.AutoCloseable
接口和实现了java.lang.closeable
接口的对象,都可以当做资源来使用。
//之前使用
BufferedWriter bufferedWriter = null;
try {
bufferedWriter = new BufferedWriter(new FileWriter(new File("D://123.txt")));
bufferedWriter.write("我是测试");
} catch (IOException e) {
e.printStackTrace();
} finally {
//需要在finally代码块中关闭
try {
//而且自己捕获的话还会多出一层异常
bufferedWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//Try-with-resources语句(Try括号中可以声明多个资源对象)
try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(new File("D://123.txt")));
BufferedReader bufferedReader = new BufferedReader((new FileReader(new File("D://456.txt"))))
) {
bufferedWriter.write("我是测试");
bufferedReader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
try-with-resource语句,我们可以看到将对象声明写在了try()中的括号中。当代码执行完毕之后java会确保外部资源的close方法被调用。cath只是捕获在读写时的异常,并没有捕获关闭时的异常。这种行为叫做异常抑制,我们可以通过e.getSuppressed()方法来展示关闭时的异常。
4. catch多个异常
public static void main(String[] args) throws Exception {
try {
testthrows();
} catch (IOException | SQLException ex) {
throw ex;
}
}
public static void testthrows() throws IOException, SQLException {
}
5. 数字类型的变量可以添加下划线,更友好的显示方式。下划线只能在数字中间在开始或结束一定不要使用下划线。
int i = 1_000_000;
double d=1_000_00.75;
float f=1_000.12F;
long l=1_000_000_000L;
System.out.println("i=:"+i);
System.out.println("d=:"+d);
System.out.println("f=:"+f);
System.out.println("l=:"+l);
//输出结果
i=:1000000
d=:100000.75
f=:1000.12
l=:1000000000
6. 泛型实例的创建可以通过类型推断来简化,也就是说在new的时候可以不用写new后面<>中的类型。只用<>就可以了。
//之前
List<HashMap> list = new ArrayList<HashMap>();
//之后,省略<>中的
List<HashMap> list = new ArrayList<>();
7. default关键字
jdk1.8添加了default关键字,用作在接口中。被该关键字修饰的方法不是必须被实现的
编写一个interface
/**
* @author yandong@techwis.cn
* @date 2019/5/20.
*/
public interface Test {
/**
* 正常方法
*/
void test();
/**
* default修饰的
*/
default void test1() {
System.out.println("默认不需要实现的方法");
}
}
实现类
/**
*可以看到不需要实现test1方法了
*/
class testImpl implements Test{
@Override
public void test() {
}
public static void main(String[] args) {
Test test = new Test.testImpl();
test.test1();
}
}
我们可以看到test1方法不需要被实现也可以,test1就作为接口中的默认类,如果需要重写在进行重写。在开发中我们可以根据自己的业余来编写这样公共的默认方法。大大减少的代码量。
8. Lambda表达式
Lambda表达式是jdk1.8一个重要的里程碑,代表java也开启了函数时编程。
函数式编程:函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。
函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!
最直观的应该就是集合的排序了
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
list.add(12);
list.add(98102);
list.add(928);
list.add(120);
/**
* 常规写法
*/
list.sort(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
/**
* lambda表达式写法
*/
list.sort((o1, o2) -> o2 - o1);
}
基本语法
(formal parameter list) -> { expression or statements }
参数列表
- 参数列表是一个逗号分隔的形式参数列表,这些参数与功能接口中单一方法的形式参数相对应。
- 参数列表中的参数类型是可选项,如果未指定参数类型,将从上下文推断。
- 参数列表必须用小括号括起来,但如果只有一个参数且不带参数类型时小括号可以省略。
- 参数列表如果为空(即:功能接口方法没有形式参数),则必须指定空括号。
Lambda 主体根据以下选项之一返回结果:
- 如果 lambda 主体是单一表达式,则返回表达式的值(如果有的话)。
- 如果功能接口方法的结果是 void,可以提供一个 return 语句,但这不是必需的。
- 如果功能接口方法具有返回类型,且 lambda 主体不是单一表达式,则 lambda 主体必须使用 return 语句返回匹配的值。
语句块必须包含在大括号内,除非语句块是一个方法调用语句,且功能接口方法的返回结果是void。
Lambda 表达式实际上是一种匿名方法实现,指定形式参数,并使用 return 语句返回值。匿名方法必须按照以下规则所规定的与其实现的功能接口方法兼容。
- Lambda 表达式返回的结果必须与功能接口方法的结果兼容。返回值的类型可以是功能接口方法声明中返回类型的子类型。
- Lambda 表达式签名必须与功能接口方法的签名相同。
- Lambda 表达式只能抛出那些在功能接口方法的 throws 子句中声明了异常类型或异常超类型的异常。
lambda用一行代码就解决排序问题。但是并不是所有的方法都可以使用lambda表达式,只有函数式接口才行。
9. 函数式接口
函数式接口是指:仅仅只包含一个抽象方法的接口。java8提供了一个@FunctionalInterface
注解来定义函数式接口,不符合规范会报错。
/**
* @author yandong@techwis.cn
* @date 2019/5/20.
*/
@FunctionalInterface
public interface FirstLambda {
/**
* 典型的,就一个抽象方法
*/
Integer test(Integer s, Integer s1);
default void test1(){
System.out.println("test1");
}
class Test {
public static void main(String[] args) {
test((s, s1) -> {
return s + s1;
});
}
/**
* 定义一个符合lambda使用的方法
*/
public static void test(FirstLambda firstLambda) {
firstLambda.test1();
System.out.println("把接口作为参数");
}
}
}
10. 方法与构造器参数引用
java8提供了一种调用方法对象::属性
使用::
符号。
最简单的使用,用的最多的还是在stream
流中。这里只做一个写法的演示
/**
* 定义一个内部类
*/
class User {
String age;
String name;
public User(String age, String name) {
}
public User() {
}
public User(Integer integer, Integer integer1) {
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"age='" + age + '\'' +
", name='" + name + '\'' +
'}';
}
}
class Test {
public static void main(String[] args) {
List<User> list = new ArrayList<User>();
User user = new User();
user.setAge("1");
list.add(user);
//print方法用这种方式调用
list.forEach(System.out::print);
}
}
11. Stream流(重要)
Stream的加入可以说是对集合一大加强。大大减少了我们对集合的操作,并且又添加了很多方法。而且和lambda可以一起使用。贴代码:
/**
* 定义一个内部类
*/
class User {
String age;
String name;
public User(String age, String name) {
}
public User() {
}
public User(Integer integer, Integer integer1) {
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"age='" + age + '\'' +
", name='" + name + '\'' +
'}';
}
}
class Test {
public static void main(String[] args) {
List<User> list = new ArrayList<User>();
User user = new User();
user.setAge("1");
list.add(user);
/**
* 遍历
*/
list.stream().forEach(us -> {
//在这里可以进行操作
System.out.println(us.getAge());
});
/**
*寻找第一条
*/
Optional<User> first = list.stream().findFirst();
//得到User对象
User user1 = first.get();
/**
* 转换为set
*/
Set<User> collect = list.stream().collect(Collectors.toSet());
/**
* 默认排序
*/
list.stream().sorted();
/**
* filter 根据条件过滤,可以多个条件
* collect 最后返回过滤好的对象
*/list.stream()
.filter(user2 -> user2.getAge().equals("1"))
.filter(user2 -> user2.getName().equals("张三"))
.collect(Collectors.toList());
/**
* 读取0-10 条数据
*/
list.stream().limit(10).collect(Collectors.toList());
/**
* map方法用于映射每个元素到对应的结果, 可以对age进行操作,改变类型或者进行操作等等。
* 我们通过foreach可以看到 list对象已经转换为age对应类型的集合,也就是说map是吧原集合转换为对应类型的集合。
*/
list.stream().map(user2 -> user2.getAge() + 1).forEach(s -> System.out.println(s));
}
/**
* 划重点,groupingby分组 直接根据属性分组
* 返回结果 key=age value=List<User></>
*/
Map<String, List<User>> collect1 = list.stream().collect(Collectors.groupingBy(User::getAge));
}
12. Optional 优雅的判断空指针。
我们都知道在java中调用空对象去进行一些操作就会抛出空指针异常。
String age = user.getCode().getAddress().getAge()
一直调用的话如果address为空那么就会出现空指针异常,那么我们通常这样子去调整代码
if(null!=user){
Code code = user.getCode();
if(null!=code){
Address address=code.getAddress();
if(null!=address){
String age =address.getAge();
}
}
}
}
可以看出来我们为了得到一个age属性进行了一系列的非空判断,代码很冗余,难以维护。为了简化代码并且让代码可读性更强,我们引入Optional类
首先我们先这样子写
//声明一个user对象
User user = new User();
//如果user不为空那么调用map方法获取Code对象
Optional<User.Code> code = Optional.ofNullable(user).map(u -> u.getCode());
//获取Address对象
Optional<User.Code.Address> address = code.map(c -> c.getAddress());
//获取Age属性
Optional<String> s = address.map(a -> a.getAge());
通过上述代码我们可以看到代码量并没有减少,但是可以清楚的看到每一步的返回值,所以我们可以这样优化代码
Optional<String> s1 = Optional.ofNullable(user)//user不为空返回Optional<User>
.map(u -> u.getCode())//返回Optional<Code>
.map(c -> c.getAddress())//返回Optional<Address>
.map(a -> a.getAge())
.orElse("我是空的");//如果为空怎么办,我们可以自己默认一个值
这样子代码简洁了很多,可读性和可塑性更强了。
简单介绍一下Optional提供的几个方法
方法 | 简单描述 |
---|---|
of | 把指定的值封装为Optional对象,如果指定的值为null,则抛出NullPointerException |
empty | 创建一个空的Optional对象 |
ofNullable | 把指定的值封装为Optional对象,如果指定的值为null,则创建一个空的Optional对象 |
get | 如果创建的Optional中有值存在,则返回此值,否则抛出NoSuchElementException |
orElse | 如果创建的Optional中有值存在,则返回此值,否则返回一个默认值 |
orElseGet | 如果创建的Optional中有值存在,则返回此值,否则返回一个由Supplier接口生成的值 |
orElseThrow | 如果创建的Optional中有值存在,则返回此值,否则抛出一个由指定的Supplier接口生成的异常 |
filter | 如果创建的Optional中的值满足filter中的条件,则返回包含该值的Optional对象,否则返回一个空的Optional对象 |
map | 如果创建的Optional中的值存在,对该值执行提供的Function函数调用 |
flagMap | 如果创建的Optional中的值存在,就对该值执行提供的Function函数调用,返回一个Optional类型的值,否则就返回一个空的Optional对象 |
isPresent | 如果创建的Optional中的值存在,返回true,否则返回false |
ifPresent | 如果创建的Optional中的值存在,则执行该方法的调用,否则什么也不做 |
13. Date Time日期,时间处理
- 我们一直使用的是java.util.Date来处理时间,但是它是线程不安全的。在多线程下会出现问题
- java.sql.Date,java.util.Date 两个包很相似。从设计上来说很糟糕了。
- 新加入的API在java.time包下。重点说两个Local(本地),Zoned(时区)
LocalDate基本用法
public static void main(String[] args) {
//获取当前日期
LocalDate now = LocalDate.now();
System.out.println(now);
//获取年,月,日
int year = now.getYear();
int monthValue = now.getMonthValue();
int dayOfMonth = now.getDayOfMonth();
System.out.println("年"+year+"月"+monthValue+"日"+dayOfMonth);
//通过指定日期生成localDate对象
LocalDate localDate = LocalDate.of(2019, 05, 01);
System.out.println("生成的日期"+localDate);
//判断两个日期是否相等,equals方法被重写了。所以直接可以判断
boolean equals = localDate.equals(now);
System.out.println("两个日期是否相等"+equals);
}
相关的一些API
方法 | 描述 |
---|---|
now | 静态方法,返回今天的日期 |
of | 从指定年份,月份和日期创建LocalDate的静态方法 |
getDayOfMonth, getMonthValue, getYear | 以int形式返回此LocalDate的日,月或年 |
getMonth | 以Month枚举常量返回此LocalDate的月份 |
plusDays, minusDays | 给LocalDate添加或减去指定的天数 |
plusWeeks, minusWeeks | 给LocalDate添加或减去指定的星期数 |
plusMonths, minusMonths | 给LocalDate添加或减去指定的月份数 |
plusYears, minusYears | 给LocalDate添加或减去指定的年数 |
isLeapYear | 检查LocalDate指定的年份是否为闰年 |
isAfter, isBefore | 检查此LocalDate是在给定日期之后还是之前 |
lengthOfMonth | 返回此LocalDate中月份的天数 |
withDayOfMonth | 返回此LocalDate的拷贝,将月份中的某天设置为给定值 |
withMonth | 返回此LocalDate的拷贝,其月份设置为给定值 |
withYear | 返回此LocalDate的拷贝,并将年份设置为给定值 |
LocalDateTime
LocalDateTime相关API
方法 | 描述 |
---|---|
now | 返回当前日期和时间的静态方法。 |
of | 从指定年份,月份,日期,小时,分钟,秒和毫秒创建LocalDateTime的静态方法。 |
getYear, getMonthValue, getDayOfMonth, getHour, getMinute, getSecond | 以int形式返回此LocalDateTime的年,月,日,小时,分钟或秒部分。 |
plusDays, minusDays | 给当前LocalDateTime添加或减去指定的天数。 |
plusWeeks, minusWeeks | 给当前LocalDateTime添加或减去指定的周数。 |
plusMonths, minusMonths | 给当前LocalDateTime添加或减去指定的月数。 |
plusYears, minusYears | 给当前LocalDateTime添加或减去指定的年数。 |
plusHours, minusHours | 给当前LocalDateTime添加或减去指定的小时数 |
plusMinutes, minusMinutes | 给当前LocalDateTime添加或减去指定的分钟数 |
plusSeconds, minusSeconds | 给当前LocalDateTime添加或减去指定的秒数 |
IsAfter, isBefore | 检查此LocalDateTime是否在指定的日期时间之后或之前 |
withDayOfMonth | 返回此LocalDateTime的拷贝,并将月份中的某天设置为指定值 |
withMonth, withYear | 返回此LocalDateTime的拷贝,其月或年设置为指定值 |
withHour, withMinute, withSecond | 返回此LocalDateTime的拷贝,其小时/分钟/秒设置为指定值 |
ZonedDateTime和LocalDateTime用法差不多,只不过ZonedDateTime多了一个时区的概念。
网友评论