0%

java8新特性学习

java9已经发布一段时间了,AndroidStudio才开始支持Java 8,Java 8是Java的一个重大版本,虽然这些新特性令Java开发人员十分期待,但同时也需要花不少精力去学习,对于android开发者而言了解Java8的新特性是必要的,虽然现在Google力推kotlin,但是Java仍然是排名第一的使用最广泛的语言,社区依旧很活跃,也是处于不断进步和发展中,而且在新特性中我们能看到一些kotlin的影子。

lambda表达式的支持

格式为:(入参) -> {函数体}

基本形式:

入参和返回值的类型可被编译器推测,入参一个参数不需要括号,无参直接写为(),函数体多行可加上括号

1
2
3
(int a, int b) -> a + b;
(a, b) -> a - b;
(int a, int b) -> { return a * b; };

用法:

for循环

以前的写法:

1
2
3
for (String player : players) {  
System.out.print(player + "; ");
}

lambda写法:

1
players.forEach((player) -> System.out.print(player + "; "));

ui回调

以前的写法:

1
2
3
4
5
6
check_all.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {

}
});

lambda写法:

1
check_all.setOnClickListener(v->{});

创建线程

以前的写法:

1
2
3
4
5
6
new Thread(new Runnable() {  
@Override
public void run() {
System.out.println("Hello world !");
}
}).start();

lambda写法:

1
new Thread(() -> System.out.println("Hello world !")).start();

排序

以前的写法:

1
2
3
4
5
6
Arrays.sort(players, new Comparator<String>() {  
@Override
public int compare(String s1, String s2) {
return (s1.length() - s2.length());
}
});

lambda写法:

1
Arrays.sort(players, (String s1, String s2) -> (s1.length() - s2.length()));

结合Stream

Stream是对集合的包装,通常和lambda一起使用。 使用lambdas可以支持许多操作,如 map, filter, limit, sorted, count, min, max, sum, collect 等等。
定义一个类,医生

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class Doctor {
private String name;
private int age;
private String degree;
private int salary;

public Doctor(String name, int age, String degree, int salary) {
this.name = name;
this.age = age;
this.degree = degree;
this.salary = salary;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}


public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String getDegree() {
return degree;
}

public void setDegree(String degree) {
this.degree = degree;
}

public int getSalary() {
return salary;
}

public void setSalary(int salary) {
this.salary = salary;
}
}

先初始化医生列表,foreach循环输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
List<Doctor> doctors =new ArrayList<>();
doctors.add(new Doctor("joe",20,"bachelor",2000));
doctors.add(new Doctor("jack",23,"bachelor",2300));
doctors.add(new Doctor("kris",27,"Master",4200));
doctors.add(new Doctor("bob",28,"doctor",5000));
doctors.add(new Doctor("tom",26,"Master",4400));
doctors.add(new Doctor("jay",29,"doctor",5500));

doctors.forEach(
(doctor)->{
System.out.println(doctor.getName());

}
);

过滤器filter(),过滤出年龄大于25岁且学历为博士的医生

1
2
3
doctors.stream().filter( doctor -> doctor.getAge()>25)
.filter(doctor -> "doctor".equals(doctor.getDegree()))
.forEach(doctor -> System.out.println(doctor.getName()));

limit方法,限制结果个数,年龄大于25的前三个医生

1
2
3
doctors.stream().filter( doctor -> doctor.getAge()>25)
.limit(3)
.forEach(doctor -> System.out.println(doctor.getName()));

sorted方法,按薪水由多到少排序

1
2
3
4
5
6
doctors.stream().sorted((d1,d2)->d2.getSalary()-d1.getSalary())
.forEach(
(doctor)->{
System.out.println(doctor.getName());
}
);

min和max方法,找出最低和最高的薪水

1
2
3
4
5
Doctor doctor1=doctors.stream().min((d1,d2)->d1.getSalary()-d2.getSalary()).get();
System.out.print(doctor1.getName());

Doctor doctor2=doctors.stream().min((d1,d2)->d1.getSalary()-d2.getSalary()).get();
System.out.print(doctor2.getName());

结合 map 方法,使用 collect 方法来将我们的结果集放到一个字符串,一个 Set 中
将所有医生的名字拼接在一个string中,用逗号隔开

1
2
String names=doctors.stream().map(Doctor::getName).collect(joining(","));
System.out.print(names);

将所有医生的名字存放到一个set中

1
2
Set<String> doctorSet=doctors.stream().map(Doctor::getName).collect(Collectors.toSet());
octorSet.forEach((name)->System.out.println(name));

summaryStatistics方法获得stream中元素的汇总数据,求和,平均值,最大最小值,个数

1
2
3
4
5
6
7
8
IntSummaryStatistics statistics=doctors.stream()
.mapToInt(doctor->doctor.getSalary())
.summaryStatistics();
System.out.println(statistics.getSum());
System.out.println(statistics.getAverage());
System.out.println(statistics.getMax());
System.out.println(statistics.getMin());
System.out.println(statistics.getCount());

接口的默认方法和静态方法

默认方法和抽象方法之间的区别在于抽象方法需要实现,而默认方法不需要。接口提供的默认方法可以被接口的实现类继承或者覆写。

1
2
3
4
5
6
7
8
9
public interface MyInterface{
default String defaultMethod(){
return "this is default method";
}
}

public class MyClass implements MyInterface{

}

在接口中可以定义静态方法

1
2
3
4
5
6
7
8
public interface MyInterface{
default String defaultMethod(){
return "this is default method";
}
static String staticMethod(){
return "this is static method";
}
}

方法的引用方式

初始化数组

1
2
3
4
5
6
Function<Integer, String[]> fun = x -> new String[x];
String[] strs = fun.apply(10);
System.out.println(strs.length);
Function<Integer,String[]> fun1 = String[]::new;
strs = fun1.apply(20);
System.out.println(strs.length);

构造方法的引用 Class::new只针对无参构造,若该类只有有参构造,需要添加一个无参构造方法:

1
2
3
4
Supplier<Student> supplier=Student::new;
Student student=supplier.get();
student.setName("小明");
System.out.println(student.getName());

有参构造调用

1
2
3
Supplier<Student> supplier=()->new Student("小张","man",12);
Student student=supplier.get();
System.out.println(student.getName());

静态方法调用

1
2
Supplier<String> supplier=Student::saying;
System.out.println(supplier.get());

成员方法调用

1
2
3
4
5
6
7
Supplier<Student> supplier=Student::new;
Student student=supplier.get();
Consumer<String> consumer1=student::setName;
consumer1.accept("小明");
Consumer<Integer> consumer2=student::setAge;
consumer2.accept(18);
System.out.println(student.getName()+":"+student.getAge());

注解的变化

支持重复注解

1
2
3
4
@Filter( "filter1" )
@Filter( "filter2" )
public interface Filterable {
}

调用

1
Filterable.class.getAnnoation(Filters.class)

控制台输出:
filter1
filter2

注解使用范围扩大

java8可以使用在任何元素上:局部变量、方法、接口、类和泛型,甚至可以用在函数的异常定义上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Retention( RetentionPolicy.RUNTIME )
@Target( { ElementType.TYPE_USE, ElementType.TYPE_PARAMETER } )
public @interface NonEmpty {
}

public static class Holder< @NonEmpty T > extends @NonEmpty Object {
public void method() throws @NonEmpty Exception {
}
}

@SuppressWarnings( "unused" )
public static void main(String[] args) {
final Holder< String > holder = new @NonEmpty Holder< String >();
@NonEmpty Collection< @NonEmpty String > strings = new ArrayList<>();
}

Map新增方法

初始化一个map

1
2
3
4
Map<Integer, String> map = new HashMap<>();
map.put(1, "Tom");
map.put(2, "Bob");
map.put(3, "Jack");

forEach 遍历

1
map.forEach((key,value)->System.out.println(key+":"+value));

getOrDefault

如果指定的key存在,则返回该key对应的value,如果不存在,则返回指定的值。

1
System.out.println(map.getOrDefault(4, "name"));

replaceAll修改所有的value值

1
2
3
//所有value前面加上一个编号
map.replaceAll((key,value)->"20000"+key+value);
map.forEach((key,value)->System.out.println(key+":"+value));

putIfAbsent返回key对应的value,如果value为空则填入一个值

1
2
3
4
5
6
map.putIfAbsent(3, "xiaoming");
map.putIfAbsent(4, "xiaoming");
//Jack
System.out.println(map.get(3));
//xiaoming
System.out.println(map.get(4));

remove指定元素删除

如果key获取的value值与给的value值相等,则删除这个元素

1
2
3
4
5
6
7
map.remove(1, "Bob");
// 未删除成功, 输出 Tom
System.out.println(map.get(1));

map.remove(2, "Bob");
// 删除成功,输出 null
System.out.println(map.get(2))

replace替换

如果key获取的value值与给的value值相等,则把这个value替换成一个新的value

1
2
3
4
5
6
map.replace(3, "Tom", "Joe");
// 未替换成功,输出 Jack
System.out.println(map.get(3));
map.replace(1, "Tom", "Joe");
// 替换成功, 输出 Joe
System.out.println(map.get(1));

如果map中存在key,则替换成value值,替换成功返回旧值,否则返回null

1
2
3
4
5
6
7
8
//输出Tom
System.out.println( map.replace(1, "Joe"));
//输出Joe
System.out.println( map.get(1));
//输出null
System.out.println( map.replace(4, "Joe"));
//输出null
System.out.println( map.get(4));

computeIfAbsent

如果指定的key不存在,则可通过key计算value为新的值

1
2
3
4
5
6
map.computeIfAbsent(1,key->key+"newName");
//输出Tom
System.out.println(map.get(1));
map.computeIfAbsent(4,key->key+"newName");
//输出4newName
System.out.println(map.get(4));

computeIfPresent

如果指定的key存在,则根据key和value计算一个新的newValue, 如果这个newValue不为null,则设置key新的值为这个newValue, 如果newValue为null, 则删除该key的值

1
2
3
4
5
6
7
map.computeIfPresent(1, (key, value) -> key + "新的值:" + value);
// 输出1新的值:Tom
System.out.println(map.get(1));

map.computeIfPresent(2, (key, value) -> null);
// 输出 null
System.out.println(map.get(2));

compute

如果指定的key不存在,则设置指定的value值,否则根据key和value计算一个新的newValue, 如果这个newValue不为null,则设置key新的值为这个newValue, 如果newValue为null, 则删除该key的值。
这个解释有点长,容易绕,实际上用if-else可以很简单的表达

1
2
3
4
5
6
if(!key.exist()){
map.computeIfAbsent(key,key->key+"newValue");
}
else{
map.computeIfPresent(key, (key, value) -> key + "新的值:" + value);
}

所以就是if key不存在,调computeIfAbsent,否则调computeIfPresent

1
2
3
4
5
6
map.compute(4,(key,value)->key+":"+value+"新的值");
//输出 4:null新的值
System.out.println(map.get(4));
map.compute(1,(key,value)->key+":"+"新的值");
//输出 1:新的值
System.out.println(map.get(1));

merge

如果指定的key不存在,直接设置指定值,如果存在根据旧的value,指定值计算出新的值newValue, 如果这个newValue不为空,设置key的新值newValue,如果newValue为null, 则删除该key。最后返回当前最新的值

1
2
3
4
5
6
  //输出 指定的名字
System.out.println(map.merge(4,"指定的名字",(oldValue,assignValue)->oldValue+","+assignValue));
//输出 Tom,指定的名字
System.out.println(map.merge(1,"指定的名字",(oldValue,assignValue)->oldValue+","+assignValue));
//输出 null
System.out.println(map.merge(1,"指定的名字",(oldValue,assignValue)->null));

空指针判断类Optional

1
2
3
4
5
6
7
8
9
10
11
12
13
14
String str1=null;
String str2="abc";
//创建初始化Optional对象,可为空的
Optional<String> optionalStr1=Optional.ofNullable(str1);
//创建初始化Optional对象,不可为空,会抛异常
Optional<String> optionalStr2=Optional.of(str2);
System.out.println("第一个参数是否存在"+optionalStr1.isPresent());
System.out.println("第二个参数是否存在"+optionalStr2.isPresent());
//不存在时返回默认值,存在时返回该值
String getStr1=optionalStr1.orElse("这个值不存在的默认值");
//返回该值,该值必须存在
String getStr2=optionalStr2.get();
System.out.println(getStr1);
System.out.println(getStr2);

日期和时间Joda-Time的支持

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 获取当前的日期时间
LocalDateTime currentTime = LocalDateTime.now();
System.out.println("当前时间: " + currentTime);
// 获取当前日期
LocalDate currentDate = currentTime.toLocalDate();
System.out.println("当前日期: " + currentDate);
System.out.println("年"+currentDate.getYear()+"月"+currentDate.getMonthOfYear()+"日"+currentDate.getDayOfMonth());
// 修改当前时间的月份和年份
currentDate.withYear(2013).withMonthOfYear(3);
System.out.println("修改后的日期: " + currentDate);
// 日期格式化
System.out.println(currentDate.toString("yyyy-MM-dd"));
// 修改时区
DateTimeZone zoneEurope = DateTimeZone.forID("Europe/London");
DateTime dtLondon = new DateTime().withZone(zoneEurope);
System.out.println("修改时区: " + dtLondon);
DateTimeZone currentZone = DateTimeZone.getDefault();
DateTime dtCurrent = new DateTime().withZone(currentZone);
System.out.println("当期时区: " + dtCurrent);

base64支持

基本编解码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
try {
// 使用基本编码
String base64encodedString = Base64.getEncoder().encodeToString("这是待编码的字符串".getBytes("utf-8"));
System.out.println("Base64编码后" + base64encodedString);
// 解码
byte[] base64decodedBytes = Base64.getDecoder().decode(base64encodedString);
System.out.println("原始字符串: " + new String(base64decodedBytes, "utf-8"));
//使用url编码
String base64encodedUrl = Base64.getUrlEncoder().encodeToString("这是待编码的字符串".getBytes("utf-8"));
System.out.println("Base64 URL编码:" + base64encodedUrl);
//生成随机uuid
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < 1; ++i) {
stringBuilder.append(UUID.randomUUID().toString());
}
//使用mime编码
byte[] mimeBytes = stringBuilder.toString().getBytes("utf-8");
String mimeEncodedString = Base64.getMimeEncoder().encodeToString(mimeBytes);
System.out.println("Base64 MIME编码:" + mimeEncodedString);
}catch(UnsupportedEncodingException e){
System.out.println("Error :" + e.getMessage());
}