浓缩精华版java8新特性
一、lambda
1、定义/设计原因
官方解释:允许把函数作为一个方法的参数。使代码变的更加简洁紧凑。表达式免去了使用匿名方法的麻烦。
个人解释:用来创建匿名方法
2、结构
lambda表达式可由逗号分隔的参数列表、->符号和语句块组成
- 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
- 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
- 可选的大括号:如果主体只有一个语句,就不需要使用大括号。
- 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号必须指定return值
3、规则
表达式用到的局部变量不管是不是final,都会被变成final,后面代码不可以改它了。全局变量则不受限。
4、使用
public class java8tester { @functionalinterface interface mathoperation { int operation(int a, int b); } @functionalinterface interface greetingservice { void saymessage(string message); } public static void main(string args[]){ //老版本写法 mathoperation oldadd = new mathoperation() { @override public int operation(int a, int b) { return a+b; } }; // jdk8类型声明 mathoperation addition = (int a, int b) -> a + b; // 不用类型声明 mathoperation subtraction = (a, b) -> a - b; // 大括号中的返回语句 mathoperation multiplication = (int a, int b) -> { return a * b; }; // 没有大括号及返回语句 mathoperation division = (int a, int b) -> a / b; // 不用括号 greetingservice greetservice1 = message -> system.out.println("hello " + message); // 用括号 greetingservice greetservice2 = (message) -> system.out.println("hello " + message); greetservice1.saymessage("runoob"); greetservice2.saymessage("google"); } }
二、函数式接口
1、定义
官方解释:函数式接口(functional interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。函数式接口可以被隐式转换为 lambda 表达式和方法引用。
@functionalinterface,用于编译级错误检查,加上该注解,当你写的接口不符合函数式接口定义的时候,编译器会报错。
2、设计原因
lambda语法只能用函数式接口,为方便检查函数式接口,给了这个@functionalinterface注解
3、使用
@functionalinterfaceinterface greetingservice { void saymessage(string message); }
//使用lambda表达式来表示该接口的一个实现 greetingservice greetservice1 = message -> system.out.println("hello " + message);
三、方法引用
1、定义/设计原因
官方解释:通过方法的名字来指向一个方法。可以使语言的构造更紧凑简洁,减少冗余代码。
个人解释:方法引用是一种更简洁易懂的lambda表达式。直接访问类或者实例的方法或者构造方法。
2、使用
先创建个接口和类
//函数式接口 @functionalinterface public interface supplier<t> { t get(); } class car { public static car create(final supplier<car> supplier) { return supplier.get(); } public static void collide(final car car) { system.out.println("collided " + car.tostring()); } public void repair() { system.out.println("repaired " + this.tostring()); } }
开始使用方法引用:
@test public void methodreferencetest() { // 1、旧方法写法 // final car oldcar = car.create(new supplier<car>() { // @override // public car get() { // return new car(); // } // }); // 2、jdk8的lambda // final car oldcar1 = car.create(() -> new car()); //3、jdk8写法 //构造器引用 class::new final car newcar = car.create(car::new); //该方法返回的 list 与传入数组是映射关系(视图):set/get 操作直接作用于数组;直接修改数组,list 也会改变 final list<car> cars = arrays.aslist(newcar); /*-----------------------------分割线---------------------------------------*/ // 1、旧方法写法 // for (car car : cars) { // car.collide(car); // } // 2、jdk8的foreach // cars.foreach(new consumer<car>() { // @override // public void accept(car car) { // car.collide(car); // } // }); // 3、jdk8的lambda // cars.foreach(car -> car.collide(car)); //4、jdk8方法引用写法 //类名的方法引用 class::static_method 或者 class::method cars.foreach(car::collide); /*-----------------------------分割线----------------------------------------*/ final car police = car.create(car::new); // 1、jdk8的lambda // cars.foreach(car -> police.follow(car)); //2、jdk8方法引用写法 //特定对象的方法引用 instance::method cars.foreach(police::follow); } /*-----------------------------分割线---------------------------------------*/ string[] stringsarray= {"4","5"}; // 1、旧方法写法 // arrays.sort(stringsarray, new comparator<string>() { // @override // public int compare(string s1, string s2) { // return s1.comparetoignorecase(s2); // } // }); // 2、jdk8的lambda // arrays.sort(stringsarray,(s1,s2)->s1.comparetoignorecase(s2)); //3、jdk8的方法引用 arrays.sort(stringsarray, string::comparetoignorecase);
四、接口的默认方法
1、定义
官方解释:默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法。我们只需在方法名前面加个 default 关键字即可实现默认方法。
2、设计原因
问:为什么要有这个特性?
答:首先,之前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是,当需要修改接口时候,需要修改全部实现该接口的类,通常能想到的解决办法是在jdk里给相关的接口添加新的方法及实现。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。所以引进的默认方法。他们的目的是为了解决接口的修改与现有的实现不兼容的问题。
3、使用
(1)接口的默认方法
最基本使用,接口通过default定义默认方法,实现类无需实现该方法
interface vehicle { default void print() { system.out.println("我是一辆车!"); } } class car implements vehicle{ }
如果一个类实现了多个接口,且这些接口有相同的默认方法,则可以通过
重写接口的默认方法,或者使用 super 来调用指定接口的默认方法
public interface vehicle { default void print(){ system.out.println("我是一辆车!"); } }
public interface fourwheeler { default void print(){ system.out.println("我是一辆四轮车!"); } }
//创建自己的默认方法,来覆盖重写接口的默认方法 public class car implements vehicle, fourwheeler { default void print(){ system.out.println("我是一辆四轮汽车!"); } }
//使用 super 来调用指定接口的默认方法 public class car implements vehicle, fourwheeler { public void print(){ vehicle.super.print(); } }
(2)接口的静态默认方法
不实例化接口的时候也可以用,个人感觉失去了接口的本质,但是妥协旧代码是这样的啦
public interface animal { static void putuphands(){ system.out.println("举手"); } } class cat { void catputuphands(){ animal.putuphands(); } }
五、stream
1、定义
官方解释:让你以一种声明的方式处理数据。将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。元素流在管道中经过中间操作的处理,最后由最终操作得到前面处理的结果。stream api可以极大提高java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
个人解释:把数组通过.stream()或者.parallelstream()转换成流,然后通过filter、map、distinct、sorted、limit等方法进行中间处理,最后通过collect、foreach、count得出最终结果。
2、设计原因
简化数组处理
2、使用
public static void main(string[] args) { list<string> strings = arrays.aslist("abc", "", "bc", "efg", "abcd", "", "jkl"); list<integer> numbers = arrays.aslist(3, 2, 2, 3, 7, 3, 5, 1, 9, 10, 4, 1); /*=======================返回stream<t>==========================*/ //1、filter过滤 strings.stream().filter(string -> !string.isempty()); //2、map映射每个元素到对应的结果 numbers.stream().map(i -> i * i); //3、distinct去重 numbers.stream().distinct(); //4、sorted排序 numbers.stream().sorted(); //4、limit限制数量 numbers.stream().limit(5); //5、并行处理,主要适用于大数据量的数组 strings.parallelstream(); /*=======================对流进行转换或处理的==========================*/ //1、collect转换成列表或字符串,collectors 可将流转换成集合和聚合元素 list<string> collect = strings.stream().collect(collectors.tolist()); string collectstr = strings.stream().collect(collectors.joining(", ")); //2、foreach迭代流中的每个数据 strings.stream().foreach(system.out::println); //3、count统计数量 long count = strings.stream().count(); /*=======================拓展intstream,longstream,doublestream==========================*/ //1.1、maptoint将stream转换成intstream, summarystatistics是对intstream数据进行汇总统计的方法,(longstream,doublestream同理) intsummarystatistics summary = numbers.stream().maptoint(x ->x).summarystatistics(); system.out.println(summary.getaverage()); system.out.println(summary.getcount()); system.out.println(summary.getmax()); system.out.println(summary.getmin()); system.out.println(summary.getsum()); //1.2、intstream,longstream创建区间方式是一样的 int[] range1 = intstream.rangeclosed(13, 15).toarray();//生产区间 [a,b] range1=[13,14,15] int[] range2 = intstream.range(13, 15).toarray();//生产区间 [a,b) range2=[13,14] double[] doubles = doublestream.of(5.33, 2.34, 5.32, 2.31, 3.51).toarray(); //doubles=[5.33, 2.34, 5.32, 2.31, 3.51] //1.2、intstream的统计方法(longstream,doublestream同理) double average = intstream.range(13, 15).average().getasdouble();//average=13.5 int max = intstream.range(13, 15).max().getasint(); //max=14 int min = intstream.range(13, 15).min().getasint(); //min=13 int sum = intstream.range(13, 15).sum(); //sum=27 }
六、optional 类
1、定义
官方解释:optional 类是一个可以为null的容器对象。如果值存在则ispresent()方法会返回true,调用get()方法会返回该对象。optional 是个容器:它可以保存类型t的值,或者仅仅保存null。
个人解释:换言之就是把变量转成optional对象,其中null都转成optional.empty()(就是一个空的optional对象),然后就可以对optional对象进行操作。好处就是发现null可选择抛出异常。
2、设计原因
不用显式进行空值检测。解决空指针异常。避免null。
3、使用
public class optionaltest { public static void main(string args[]){ handleparam(null,1);//后面两个参数自己随意写 } //处理参数举例子 static void handleparam(string a,integer b){ //对于处理string参数 optional<string> aopt = optional.ofnullable(a); //允许参数空。如果非空返回一个包含引用optional实例,否则返回optional.empty()。 system.out.println(aopt.ispresent());//输出aopt是否为空 a=aopt.orelse("defaultvalue");//如果为空,就给一个默认值defaultvalue system.out.println(a); //同理对于处理int参数 optional<integer> bopt = optional.of(b);//不允许参数空,不然会抛出异常 b=bopt.get();//get的时候不允许变量为空 system.out.println(b); } }
六、日期时间类
1、定义
加强对日期与时间的处理。
2、设计原因
在旧版的 java 中,日期时间 api 存在诸多问题
- (1)非线程安全 − java.util.date 是非线程安全的,所有的日期类都是可变的,这是java日期类最大的问题之一。
- (2)设计很差 − java的日期/时间类的定义并不一致,在java.util和java.sql的包中都有日期类,此外用于格式化和解析的类在java.text包中定义。
java.util.date同时包含日期和时间,而java.sql.date仅包含日期,将其纳入java.sql包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。 - (3)时区处理麻烦 − 日期类并不提供国际化,没有时区支持,因此java引入了java.util.calendar和java.util.timezone类,但他们同样存在上述所有的问题。
3、使用
public static void testtime() { // 当前详细时间 localdatetime currenttime = localdatetime.now(); //currenttime = 2019-05-30t15:05:46.408 // 当前年月日 localdate date1 = currenttime.tolocaldate(); //date1 = 2019-05-30 //获取详细时间的月日秒 month month = currenttime.getmonth();//month=may int day = currenttime.getdayofmonth();//day=30 int seconds = currenttime.getsecond();//seconds=46 //替换详细时间的年月 localdatetime date2 = currenttime.withdayofmonth(10).withyear(2012);//date2 = 2012-05-10t15:05:46.408 //自定义年月日 localdate date3 = localdate.of(2014, month.december, 12);//date3= 2014-12-12 //自定义时分 localtime date4 = localtime.of(22, 15); //date4 = 22:15 //解析字符串 localtime date5 = localtime.parse("20:15:30"); //date5 = 20:15:30 localdatetime date6 = localdatetime.parse("2019-05-30t15:05:46.408");//date6 = 2019-05-30t15:05:46.408 // 获取当前时间日期 zoneddatetime date7 = zoneddatetime.parse("2015-12-03t10:15:30+05:30[asia/shanghai]");//date6=2015-12-03t10:15:30+08:00[asia/shanghai] // 获取时区id zoneid id = zoneid.of("europe/paris");//id= europe/paris //获取默认时区 zoneid defaultzone = zoneid.systemdefault();//defaultzone=sia/shanghai }
七、base64
1、定义
官方解释:base64工具类提供了一套静态方法获取下面三种base64编码器和解码器:
- 基本:输出被映射到一组字符a-za-z0-9+/,编码不添加任何行标,输出的解码仅支持a-za-z0-9+/。
- url:输出映射到一组字符a-za-z0-9+_,输出是url和文件。
- mime:输出隐射到mime友好格式。输出每行不超过76字符,并且使用'\r'并跟随'\n'作为分割。编码输出最后没有行分割。
2、使用
public static void main(string args[]) { try { // 使用基本编码 string base64encodedstring = base64.getencoder().encodetostring("我是测试字符串".getbytes("utf-8"));//base64encodedstring = "5oir5piv5rwl6k+v5a2x56ym5liy" // 解码 byte[] base64decodedbytes = base64.getdecoder().decode(base64encodedstring); string base64decodedstr= new string(base64decodedbytes, "utf-8");//base64decodedstr = "我是测试字符串" //使用url编码 string urlencodedstring = base64.geturlencoder().encodetostring("testurl?java8".getbytes("utf-8"));//urlencodedstring = "vhv0b3jpywxzug9pbnq_amf2ytg=" //使用mime编码 string mimeencodedstring = base64.getmimeencoder().encodetostring(("qwertyuiop-asdfghjkl-zxcvbnm").getbytes("utf-8"));//mimeencodedstring = "uvdfulrzvulpuc1bu0rgr0hks0wtwlhdvkjotq==" }catch(unsupportedencodingexception e){ system.out.println("error :" + e.getmessage()); } }
上一篇: 新手站长推广容易犯的小错误
下一篇: 10分钟看懂中国30年营销演义