未完待续
Lambda表达式 函数式接口 在Java8中,引入了函数式接口的概念,函数式接口是一个只有一个抽象方法的接口,通常用于Lambda表达式和方法引用,函数式接口可以有多个默认方法 或静态方法 ,但是必须只有一个抽象方法
1.语法
@FunctionalInterface
public interface MyPredicate < T > {
boolean fun ( T obj) ;
default void other ( ) {
System . out. println ( "hello world" ) ;
}
static void staticMethod ( ) {
System . out. println ( "static method" ) ;
}
}
@FunctionalInterface
注解:这是一个可选的注解,它可以帮助编译器在编译时检查接口是否符合函数式接口的要求,即是否只有一个抽象方法,如不符合还加这个注解,会导致编译器报错。
2.函数式接口的使用
编写一个实体类Employee
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Employee {
private String name;
private Double salary;
private Integer age;
public Employee ( Integer age) {
this . age = age;
}
public Employee ( Integer age, String name) {
this . age = age;
this . name = name;
}
}
新增一个按条件过滤的方法filter
,将List<Employee>
作为第一个参数,函数式接口MyPredicate<Employee>
作为第二个参数传进filter()
方法,方法体内循环将每个Employee对象一一作为参数传入接口的抽象方法fun()
中,并调用,根据抽象方法运行后得到的布尔值判断是否过滤掉。
private List < Employee > filter ( List < Employee > employees, MyPredicate < Employee > predicate) {
List < Employee > list = new ArrayList < > ( ) ;
for ( Employee e : employees) {
if ( predicate. fun ( e) ) {
list. add ( e) ;
}
}
return list;
}
声明一个员工集合employees
,插入5个对象,然后调用filter()
方法,将employees
作为第一个参数传入,然后直接new一个实现MyPredicate
接口抽象方法的匿名内部类作为第二个参数传入,这样一来,调用时既告诉了目标方法filter()
要处理的数据是employees
,也一并将数据的具体处理规则obj.getAge() > 16
告诉了目标方法,调用同一个方法可以有无数种处理数据的策略,这个实际上就是一种典型的策略模式 ,实际上Java8已经为我们写好了一种策略模式的函数式接口。
private List < Employee > employees = Arrays . asList (
new Employee ( "soo" , 8547.322 , 17 ) ,
new Employee ( "lili" , 1000D , 15 ) ,
new Employee ( "王萌" , 2154D , 16 ) ,
new Employee ( "张帆" , 8547.322 , 22 ) ,
new Employee ( "goog" , 353D , 12 )
) ;
@Test
public void test3 ( ) {
List < Employee > list = filter ( employees, new MyPredicate < Employee > ( ) {
@Override
public boolean fun ( Employee obj) {
return obj. getAge ( ) > 16 ;
}
} ) ;
System . out. println ( list) ;
}
Java8中,通过将策略接口实现简写为Lambda
表达式的方式,可以使得语法显得更加简洁
List < Employee > list2 = filter ( employees, ( e) -> e. getAge ( ) < 16 ) ;
3.常用的内置函数式接口
Java8提供了一些预定义的函数式接口,位于java.util.function
包中
java.util.function.Consumer
消费
java.util.function.Supplier
供给
java.util.function.Function
函数
java.util.function.Predicate
断言
java.util.function.BinaryOperator
不常用
java.util.function.UnaryOperator
不常用
编写4个将函数式接口作为参数的方法
private void testConsumer ( String str, Consumer < String > consumer) {
consumer. accept ( str) ;
}
private String testSupplier ( Supplier < String > supplier) {
return supplier. get ( ) ;
}
private Integer testFunction ( String str, Function < String , Integer > function) {
return function. apply ( str) ;
}
private boolean testPredicate ( String str, Predicate < String > predicate) {
return predicate. test ( str) ;
}
分别调用这些方法,按照业务逻辑通过匿名内部类的lambda表达式写法实现函数式接口的抽象方法,作为参数传入
@Test
public void test4 ( ) {
testConsumer ( "hello lambda" , ( x) -> System . out. println ( x) ) ;
String str = testSupplier ( ( ) -> { return "hello world" ; } ) ;
System . out. println ( str) ;
Integer integer = testFunction ( "66" , ( x) -> Integer . valueOf ( x) ) ;
System . out. println ( integer) ;
boolean b = testPredicate ( "hello" , ( e) -> e. equals ( "hello" ) ) ;
System . out. println ( b) ;
}
得到运行结果
hello lambda
hello world
66
true
还可以通过lambda表达式的方法引用和构造器引用将调用修改的更简洁一些
@Test
public void test2 ( ) {
testConsumer ( "hello lambda" , System . out:: println ) ;
Integer integer = testFunction ( "66" , Integer :: valueOf ) ;
}
Stream API 接口的默认方法 Java8前的接口,只能有两个成员,全局静态常量和抽象方法,Java8引入了接口的默认方法和静态方法作为新特性,它们的引入是为了增强接口的功能,特别是在接口的扩展性和灵活性方面。
接口中的默认方法,使用default
修饰符修饰,可以带有实现,实现类可以直接继承使用,实现类可以选择重写默认方法,也可以直接使用。
接口中的静态方法只能通过接口名调用,不能通过接口的实现类或实例调用,为接口提供相关的工具性功能,而不需要依赖具体的实现类,静态方法不会被实现类继承,也不能被实现类重写。
案例1:接口的默认方法和静态方法
编写一个接口test.testinterface.MyInterface
,拥有两个默认方法test()
,hello()
和一个静态方法helloworld()
package test. testinterface ;
public interface MyInterface {
default String test ( ) {
System . out. println ( "default" ) ;
return "default" ;
}
default void hello ( ) {
System . out. println ( "my interface" ) ;
}
static void helloworld ( ) {
System . out. println ( "hello java8!!!" ) ;
}
}
编写一个类test.testinterface.SubClass
,实现接口MyInterface
package test. testinterface ;
public class SubClass implements MyInterface {
public static void main ( String [ ] args) {
SubClass subClass = new SubClass ( ) ;
subClass. hello ( ) ;
MyInterface . helloworld ( ) ;
}
}
不实现接口里面的hello()
方法也可以直接调用默认方法hello()
,而且可以通过接口名直接调用接口的静态方法helloworld()
,程序输出:
my interface
hello java8!!!
案例2:默认方法冲突
编写另一个接口test.testinterface.OtherInterface
,并实现一个默认方法hello
package test. testinterface ;
public interface OtherInterface {
default void hello ( ) {
System . out. println ( "other interface" ) ;
}
}
令类test.testinterface.SubClass
再实现一个接口OtherInterface
,该接口含有和接口MyInterface
一样定义的default方法hello()
,就产生了接口冲突,当实现的多个接口中有相同签名的默认方法时,子类必须显式重写冲突的方法hello()
,最终程序输出结果:”sub hello!”
package test. testinterface ;
public class SubClass implements MyInterface , OtherInterface {
@Override
public void hello ( ) {
System . out. println ( "sub hello!" ) ;
}
public static void main ( String [ ] args) {
SubClass subClass = new SubClass ( ) ;
subClass. hello ( ) ;
}
}
案例3:类优先
编写一个类test.testinterface.MyClass
,里面有一个方法String test()
,并让SubClass
类继承它,并执行subClass.test();
,得到输出结果:”class”,但是SubClass
实现的接口MyInterface
里面也有个方法String test()
,却没有被执行,而是执行了类里面的方法,说明类优先,如果类或其父类中已经提供了方法实现,则优先使用类的实现,而不是接口的默认方法。
package test. testinterface ;
public class MyClass {
public String test ( ) {
System . out. println ( "class" ) ;
return "class" ;
}
}
package test. testinterface ;
public class SubClass extends MyClass implements MyInterface , OtherInterface {
@Override
public void hello ( ) {
System . out. println ( "sub hello!" ) ;
}
public static void main ( String [ ] args) {
SubClass subClass = new SubClass ( ) ;
subClass. test ( ) ;
}
}
新的日期和时间API (java.time) 旧API的线程安全问题 旧的日期时间工具类java.text.SimpleDateFormat
存在线程安全问题,例如SimpleDateFormat线程不安全,内部依赖一个Calendar实例来解析和格式化日期,而Calendar是线程不安全的,多线程格式化会并发更新Calendar状态会导致出现异常。
以下代码使用100个线程并发调用一个format对象进行日期解析操作,会导致出现错误。
package test. time ;
import java. text. SimpleDateFormat ;
public class Test1 {
public static void main ( String [ ] args) {
SimpleDateFormat format = new SimpleDateFormat ( "yyyyMMdd" ) ;
Runnable r = new Runnable ( ) {
@Override
public void run ( ) {
try {
System . out. println ( format. parse ( "20191231" ) ) ;
} catch ( Exception e) {
throw new RuntimeException ( e) ;
}
}
} ;
for ( int i= 0 ; i< 100 ; i++ ) {
new Thread ( r, "t" + i) . start ( ) ;
}
}
}
可以采取同步块,线程单独持有format对象,以及线程池内使用ThreadLocal的办法解决。采用同步代码块时,只能有一个线程执行parse方法,可以避免线程安全问题。
package test. time ;
import java. text. SimpleDateFormat ;
public class Test1 {
public static void main ( String [ ] args) {
SimpleDateFormat format = new SimpleDateFormat ( "yyyyMMdd" ) ;
Runnable r = new Runnable ( ) {
@Override
public void run ( ) {
synchronized ( format) {
try {
System . out. println ( format. parse ( "20191231" ) ) ;
} catch ( Exception e) {
throw new RuntimeException ( e) ;
}
}
}
} ;
for ( int i= 0 ; i< 100 ; i++ ) {
new Thread ( r, "t" + i) . start ( ) ;
}
}
}
采用线程独自持有format对象的方法解决,每个线程执行时创建一个format对象,每个线程单独持有,防止线程安全问题。
package test. time ;
import java. text. SimpleDateFormat ;
public class Test1 {
public static void main ( String [ ] args) {
Runnable r = new Runnable ( ) {
@Override
public void run ( ) {
try {
System . out. println ( new SimpleDateFormat ( "yyyyMMdd" ) . parse ( "20191231" ) ) ;
} catch ( Exception e) {
throw new RuntimeException ( e) ;
}
}
} ;
for ( int i= 0 ; i< 100 ; i++ ) {
new Thread ( r, "t" + i) . start ( ) ;
}
}
}
线程池+ThreadLocal,10个线程同时派发100个格式化任务,可以为每个线程绑定一个format对象,各自使用,也可以避免线程安全问题。
package test. time ;
import java. text. SimpleDateFormat ;
import java. util. concurrent. ExecutorService ;
import java. util. concurrent. Executors ;
public class Test1 {
public static void main ( String [ ] args) {
ExecutorService executorService = Executors . newFixedThreadPool ( 10 ) ;
ThreadLocal < SimpleDateFormat > threadLocal = ThreadLocal . withInitial ( ( ) -> new SimpleDateFormat ( "yyyyMMdd" ) ) ;
Runnable runnable = new Runnable ( ) {
@Override
public void run ( ) {
try {
System . out. println ( threadLocal. get ( ) . parse ( "20191231" ) ) ;
}
catch ( Exception e) {
throw new RuntimeException ( e) ;
}
}
} ;
for ( int i= 0 ; i< 100 ; i++ ) {
executorService. submit ( runnable, "t" + i) ;
}
executorService. shutdown ( ) ;
}
}
新的日期时间API Java 8通过发布新的Date-TimeAPI(JSR310)进一步加强了对日期与时间的处理。
首先,在旧版的Java中,日期时间API存在诸多问题,首先java.util.Date
是非线程安全的,所有的日期类都是可变的。
其次,Java的日期/时间类的定义并不一致,在java.util
和java.sql
的包中都有日期类,负责格式化和解析的类又在java.text包中定义。java.util.Date
同时包含日期和时间,而java.sql.Date
仅包含日期,而且放进sql包下并不合理。
而且,无法更好的处理时区,日期类不能国际化,没有时区支持,因此Java引入了java.util.Calendar
和java.util.TimeZone
,但是它们仍然存在一样的问题。
于是Java8引入了新的日期时间API,位于java.time
包下,该包下有几个重要的类:
java.time.Instant
时间戳
java.time.Duration
时间差
java.time.LocalDate
只包含日期,例如2011-07-11
java.time.LocalTime
只包含时间,例如09:00:01
java.time.LocalDateTime
同时包含日期和时间,例如2024-11-30 04:09:45
java.time.Period
时间段
java.time.OffsetDateTime
带有时区偏移量的日期和时间,是LocalDateTime和ZoneOffset的结合体,更适用于需要精确到时间和偏移量的场景,尤其当你关心的只是某个时间点相对于 UTC 的偏移。例如,在处理需要表示时间差(例如时间戳、系统日志等)时,OffsetDateTime 比较合适。
java.time.ZoneOffset
时区偏移量,比如+8:00
java.time.ZonedDateTime
带有时区的日期和时间,是LocalDateTime
和ZoneId
的组合,ZonedDateTime更适用于需要考虑时区历史和夏令时等复杂问题的场景。例如,如果你需要表示某个特定时区(如America/New_York)的时间,并且要处理夏令时,ZonedDateTime会更加准确
java.time.Clock
时钟
package test. time ;
import org. junit. Test ;
import java. time. * ;
import java. time. format. DateTimeFormatter ;
import java. time. format. FormatStyle ;
import java. time. temporal. * ;
import java. util. Date ;
public class Test4 {
@Test
public void current ( ) {
Instant instant = Instant . now ( ) ;
LocalDate localDate = LocalDate . now ( ) ;
LocalTime localTime = LocalTime . now ( ) ;
LocalDateTime localDateTime = LocalDateTime . now ( ) ;
ZonedDateTime zonedDateTime = ZonedDateTime . now ( ) ;
System . out. println ( instant) ;
System . out. println ( localDate) ;
System . out. println ( localTime) ;
System . out. println ( localDateTime) ;
System . out. println ( zonedDateTime) ;
}
@Test
public void testInstant ( ) {
Instant now = Instant . now ( ) ;
System . out. println ( now) ;
OffsetDateTime us = now. atOffset ( ZoneOffset . ofHours ( - 4 ) ) ;
System . out. println ( us) ;
OffsetDateTime offsetDateTime = now. atOffset ( ZoneOffset . ofHours ( + 8 ) ) ;
System . out. println ( offsetDateTime) ;
System . out. println ( now. atOffset ( ZoneOffset . ofHours ( + 9 ) ) ) ;
System . out. println ( now. atOffset ( ZoneOffset . ofHours ( + 10 ) ) ) ;
Instant instant = Instant . ofEpochSecond ( 1 ) ;
System . out. println ( instant) ;
ZonedDateTime zonedDateTime = now. atZone ( ZoneId . of ( "GMT+9" ) ) ;
LocalDateTime localDateTime = zonedDateTime. toLocalDateTime ( ) ;
System . out. println ( localDateTime) ;
}
@Test
public void testLocalDateTime ( ) {
LocalDateTime now = LocalDateTime . now ( ) ;
System . out. println ( now) ;
LocalDateTime localDateTime = LocalDateTime . of ( 2019 , 8 , 8 , 12 , 23 , 50 ) ;
System . out. println ( localDateTime) ;
System . out. println ( LocalDateTime . of ( LocalDate . now ( ) , LocalTime . now ( ) ) ) ;
System . out. println ( localDateTime. getYear ( ) ) ;
System . out. println ( localDateTime. getDayOfYear ( ) ) ;
System . out. println ( localDateTime. getDayOfMonth ( ) ) ;
DayOfWeek dayOfWeek = localDateTime. getDayOfWeek ( ) ;
System . out. println ( dayOfWeek) ;
System . out. println ( localDateTime. getNano ( ) ) ;
System . out. println ( LocalDateTime . now ( ) . plusMonths ( 2 ) ) ;
System . out. println ( LocalDateTime . now ( ) . minusYears ( 2 ) ) ;
System . out. println ( LocalDateTime . now ( ) . plusHours ( 24 ) ) ;
System . out. println ( LocalDateTime . now ( ) . plusNanos ( 500 ) ) ;
System . out. println ( LocalDateTime . now ( ) . plusYears ( 2 ) . plusMonths ( 8 ) . plusDays ( 9 ) ) ;
System . out. println ( LocalDateTime . now ( ) . plus ( Period . of ( 3 , 5 , 20 ) ) ) ; ;
System . out. println ( LocalDateTime . now ( ) . plus ( 3 , ChronoUnit . DECADES ) ) ;
System . out. println ( LocalDateTime . now ( ) . withMonth ( 2 ) ) ;
System . out. println ( LocalDateTime . now ( ) . withDayOfMonth ( 25 ) ) ;
System . out. println ( LocalDateTime . now ( ) . withSecond ( 22 ) ) ;
System . out. println ( LocalDateTime . now ( ) . with ( ChronoField . DAY_OF_MONTH , 2 ) ) ;
System . out. println ( LocalDateTime . now ( ) . with ( ChronoField . MONTH_OF_YEAR , 8 ) ) ;
System . out. println ( LocalDate . of ( 2020 , 1 , 19 ) ) ;
System . out. println ( LocalDate . of ( 2020 , Month . AUGUST , 19 ) ) ;
System . out. println ( LocalDate . of ( 2020 , Month . of ( 12 ) , 19 ) ) ;
System . out. println ( LocalTime . of ( 20 , 0 ) ) ;
System . out. println ( LocalDate . now ( ) . withMonth ( 8 ) ) ;
System . out. println ( LocalDate . of ( 2020 , Month . AUGUST , 19 ) . plusDays ( 5 ) ) ;
System . out. println ( LocalDate . of ( 2020 , Month . of ( 12 ) , 19 ) ) ;
System . out. println ( LocalTime . of ( 20 , 0 ) . plusHours ( 8 ) ) ;
System . out. println ( LocalDate . now ( ) . isLeapYear ( ) ) ;
}
@Test
public void testTemporalAdjusters ( ) {
LocalDateTime dateTime = LocalDateTime . now ( ) ;
dateTime. with ( TemporalAdjusters . next ( DayOfWeek . THURSDAY ) ) ;
System . out. println ( dateTime) ;
dateTime. with ( TemporalAdjusters . previous ( DayOfWeek . THURSDAY ) ) ;
System . out. println ( dateTime) ;
dateTime. with ( TemporalAdjusters . nextOrSame ( DayOfWeek . THURSDAY ) ) ;
System . out. println ( dateTime) ;
dateTime. with ( TemporalAdjusters . previousOrSame ( DayOfWeek . THURSDAY ) ) ;
System . out. println ( dateTime) ;
System . out. println ( LocalDate . now ( ) . with ( TemporalAdjusters . nextOrSame ( DayOfWeek . SATURDAY ) ) ) ;
System . out. println ( LocalDate . now ( ) . with ( TemporalAdjusters . firstDayOfMonth ( ) ) ) ;
System . out. println ( LocalDate . now ( ) . with ( TemporalAdjusters . firstDayOfNextMonth ( ) ) ) ;
LocalDateTime nextWorkDay = LocalDateTime . now ( ) . with ( ( e) -> {
LocalDateTime temp = LocalDateTime . from ( e) ;
DayOfWeek dayOfWeek = temp. getDayOfWeek ( ) ;
if ( dayOfWeek. equals ( DayOfWeek . FRIDAY ) ) {
return temp. plusDays ( 3 ) ;
} else if ( dayOfWeek. equals ( DayOfWeek . SATURDAY ) ) {
return temp. plusDays ( 2 ) ;
} else {
return temp. plusDays ( 1 ) ;
}
} ) ;
System . out. println ( nextWorkDay) ;
}
public void test ( ) {
System . out. println ( Year . now ( ) ) ;
System . out. println ( YearMonth . now ( ) ) ;
System . out. println ( MonthDay . now ( ) ) ;
}
@Test
public void testChronoUnit ( ) {
LocalDateTime from = LocalDateTime . of ( 2020 , Month . JANUARY , 23 , 10 , 0 , 0 ) ;
LocalDateTime to = LocalDateTime . of ( 2020 , Month . APRIL , 8 , 0 , 0 , 0 ) ;
long days = ChronoUnit . DAYS . between ( from, to ) ;
long hours = ChronoUnit . HOURS . between ( from, to ) ;
System . out. println ( days ) ;
System . out. println ( hours ) ;
}
@Test
public void testTemporalQuery ( ) {
long l = LocalDateTime . now ( ) . query ( new TemporalQuery < Long > ( ) {
@Override
public Long queryFrom ( TemporalAccessor temporal) {
LocalDateTime now = LocalDateTime . from ( temporal) ;
LocalDateTime from = LocalDateTime . of ( 2020 , Month . JANUARY , 19 , 10 , 0 , 0 ) ;
return ChronoUnit . HOURS . between ( from, now) ;
}
} ) ;
System . out. println ( l) ;
}
@Test
public void testDurationPeriod ( ) {
LocalTime start = LocalTime . of ( 20 , 0 ) ;
LocalTime end = LocalTime . of ( 21 , 30 ) ;
Duration between = Duration . between ( start, end) ;
System . out. println ( between. toHours ( ) ) ;
System . out. println ( between. toMinutes ( ) ) ;
}
@Test
public void testDateTimeFormatter ( ) {
DateTimeFormatter formatter = DateTimeFormatter . ISO_DATE_TIME ;
System . out. println ( LocalDateTime . now ( ) . format ( formatter) ) ;
LocalDate localDate = LocalDate . parse ( "2009-12-31" , DateTimeFormatter . ofPattern ( "yyyy-MM-dd" ) ) ;
System . out. println ( localDate) ;
LocalDateTime localDateTime = LocalDateTime . parse ( "2009-12-31 01:01:02" , DateTimeFormatter . ofPattern ( "yyyy-MM-dd HH:mm:ss" ) ) ;
System . out. println ( localDateTime) ;
System . out. println ( LocalDateTime . now ( ) . format ( DateTimeFormatter . ofLocalizedDate ( FormatStyle . FULL ) ) ) ;
System . out. println ( LocalDateTime . now ( ) . format ( DateTimeFormatter . ofLocalizedDate ( FormatStyle . LONG ) ) ) ;
System . out. println ( LocalDateTime . now ( ) . format ( DateTimeFormatter . ofLocalizedDate ( FormatStyle . SHORT ) ) ) ;
System . out. println ( LocalDateTime . now ( ) . format ( DateTimeFormatter . ofLocalizedDate ( FormatStyle . MEDIUM ) ) ) ;
}
@Test
public void getAvailableZoneIds ( ) {
System . out. println ( ZoneId . systemDefault ( ) ) ;
ZoneId . getAvailableZoneIds ( ) . forEach ( System . out:: println ) ;
}
@Test
public void testOffsetDateTime ( ) {
OffsetDateTime offsetDateTime = new Date ( ) . toInstant ( ) . atOffset ( ZoneOffset . of ( "-4" ) ) ;
System . out. println ( offsetDateTime) ;
System . out. println ( offsetDateTime. toLocalDateTime ( ) ) ;
OffsetDateTime of = OffsetDateTime . of ( LocalDateTime . now ( ) , ZoneOffset . of ( "-4" ) ) ;
System . out. println ( of) ;
}
@Test
public void testZonedDateTime ( ) {
ZonedDateTime zonedDateTime = ZonedDateTime . now ( ZoneId . of ( "Asia/Tokyo" ) ) ;
System . out. println ( zonedDateTime) ;
System . out. println ( zonedDateTime. toLocalDateTime ( ) ) ;
ZonedDateTime of = ZonedDateTime . of ( LocalDateTime . now ( ) , ZoneId . of ( "Asia/Tokyo" ) ) ;
System . out. println ( of) ;
ZonedDateTime tokyo = LocalDateTime . now ( ) . atZone ( ZoneId . of ( "Asia/Tokyo" ) ) ;
System . out. println ( tokyo) ;
System . out. println ( tokyo. toLocalDateTime ( ) ) ;
ZonedDateTime beijing = tokyo. withZoneSameInstant ( ZoneId . of ( "GMT+8" ) ) ;
System . out. println ( beijing) ;
ZonedDateTime usa = LocalDateTime . now ( )
. atZone ( ZoneId . systemDefault ( ) )
. withZoneSameInstant ( ZoneId . of ( "GMT-4" ) ) ;
System . out. println ( usa) ;
}
}
新API和旧的Date之前的互转
package test. time ;
import org. junit. Test ;
import java. sql. Timestamp ;
import java. time. * ;
import java. util. Calendar ;
import java. util. Date ;
public class Test5 {
@Test
public void toDate ( ) {
LocalDateTime localDateTime = LocalDateTime . now ( ) ;
Instant instant = localDateTime. atZone ( ZoneId . systemDefault ( ) ) . toInstant ( ) ;
Date date = Date . from ( instant) ;
System . out. println ( date) ;
}
@Test
public void toLocalDateTime ( ) {
Date date = new Date ( ) ;
LocalDateTime dateTime = LocalDateTime . ofInstant ( date. toInstant ( ) , ZoneId . systemDefault ( ) ) ;
System . out. println ( dateTime) ;
}
@Test
public void sqlDate ( ) {
java. sql. Date date = new java. sql. Date( System . currentTimeMillis ( ) ) ;
LocalDate localDate = date. toLocalDate ( ) ;
System . out. println ( localDate) ;
Timestamp timestamp = new Timestamp ( System . currentTimeMillis ( ) ) ;
LocalDateTime localDateTime = timestamp. toLocalDateTime ( ) ;
System . out. println ( localDateTime) ;
}
@Test
public void calendarToLocalDateTime ( ) {
Calendar calendar = Calendar . getInstance ( ) ;
ZonedDateTime zonedDateTime = ZonedDateTime . ofInstant ( calendar. toInstant ( ) , calendar. getTimeZone ( ) . toZoneId ( ) ) ;
System . out. println ( zonedDateTime. toLocalDateTime ( ) ) ;
}
}
Optional java.util.Optional
是一个容器类,用来表示可能包含或者不包含值的对象。它提供了一种优雅的方式来避免出现空指针,从而帮助开发者更安全、更清晰地处理可能为NULL的值。
1.创建一个Optional
对象
包装一个非空的值,如果传入的变量为null
会直接抛出空指针异常,如果直接写死null
进去,IDEA可能直接编译出错
Optional < String > optional = Optional . of ( "Hello, World!" ) ;
ofNullable
方法允许填充一个可能为空的值进去
Optional < String > optional = Optional . ofNullable ( null ) ;
空的Optional
对象
Optional < String > optional = Optional . empty ( ) ;
2.检查值是否存在
可以使用isPresent()
方法判断
Optional < String > optional = Optional . empty ( ) ;
if ( optional. isPresent ( ) ) {
System . out. println ( "Value: " + optional. get ( ) ) ;
} else {
System . out. println ( "No value present" ) ;
}
还可以采用ifPresent()
避免if
显式调用
optional. ifPresent ( value -> System . out. println ( "Value: " + value) ) ;
3.默认值
如果为空,提供一个默认值
Optional < String > optional = Optional . empty ( ) ;
String value = optional. orElse ( "Default Value" ) ;
还可以通过提供的Supplier
函数式接口生成默认值
Optional < String > optional = Optional . empty ( ) ;
String value = optional. orElseGet ( ( ) -> "Generated Default Value" ) ;
如果值不存在,可以抛出自定义异常
Optional < String > optional = Optional . empty ( ) ;
String value = optional. orElseThrow ( ( ) -> new RuntimeException ( "Value is missing!" ) ) ;
4.转换
map()
如果有值进行处理,并返回处理后的Optional
对象,否则返回Optional.empty()
空值,不执行输出
Optional < String > optional = Optional . empty ( ) ;
Optional < String > upperCase = optional. map ( String :: toUpperCase ) ;
upperCase. ifPresent ( System . out:: println ) ;
非空,处理后返回新的Optional
,输出:HELLO WORLD
Optional < String > optional = Optional . ofNullable ( "hello world" ) ;
Optional < String > upperCase = optional. map ( String :: toUpperCase ) ;
upperCase. ifPresent ( System . out:: println ) ;
使用flatMap()
进一步防止空指针异常,如果optional中的值为null,flatMap()
直接返回Optional.empty()
,否则,它返回一个包含e.getName()
的Optional对象
Employee employee = new Employee ( ) ;
employee. setName ( "XXX" ) ;
Optional < Employee > optional = Optional . ofNullable ( employee) ;
Optional < String > s = optional. flatMap ( ( e) -> Optional . of ( e. getName ( ) ) ) ;
s. ifPresent ( System . out:: println ) ;
重复注解 (Repeating Annotations) 1.首先创建一个容器注解,这个注解类型包含一个注解数组,存储多个相同类型的注解
package test. anno ;
import java. lang. annotation. Retention ;
import java. lang. annotation. RetentionPolicy ;
import java. lang. annotation. Target ;
import static java. lang. annotation. ElementType . CONSTRUCTOR ;
import static java. lang. annotation. ElementType . FIELD ;
import static java. lang. annotation. ElementType . LOCAL_VARIABLE ;
import static java. lang. annotation. ElementType . METHOD ;
import static java. lang. annotation. ElementType . PARAMETER ;
import static java. lang. annotation. ElementType . TYPE ;
@Target ( { TYPE , FIELD , METHOD , PARAMETER , CONSTRUCTOR , LOCAL_VARIABLE } )
@Retention ( RetentionPolicy . RUNTIME )
public @interface MyAnnotations {
MyAnnotation [ ] value ( ) ;
}
2.定义一个重复注解,并使用@Repeatable
标记
package test. anno ;
import java. lang. annotation. Repeatable ;
import java. lang. annotation. Retention ;
import java. lang. annotation. RetentionPolicy ;
import java. lang. annotation. Target ;
import static java. lang. annotation. ElementType . CONSTRUCTOR ;
import static java. lang. annotation. ElementType . FIELD ;
import static java. lang. annotation. ElementType . LOCAL_VARIABLE ;
import static java. lang. annotation. ElementType . METHOD ;
import static java. lang. annotation. ElementType . PARAMETER ;
import static java. lang. annotation. ElementType . TYPE ;
import static java. lang. annotation. ElementType . TYPE_PARAMETER ;
@Target ( { TYPE , FIELD , METHOD , PARAMETER , CONSTRUCTOR , LOCAL_VARIABLE , TYPE_PARAMETER } )
@Retention ( RetentionPolicy . RUNTIME )
@Repeatable ( MyAnnotations . class )
public @interface MyAnnotation {
String value ( ) default "hello world" ;
}
3.测试,通过反射访问方法上的注解,由于MyAnnotation是重复注解,所以一个方法加上多个也不会语法报错,然后提取其中的多个MyAnnotation注解。
package test. anno ;
import java. lang. reflect. Method ;
import java. util. Arrays ;
public class TestAnnotation {
@MyAnnotation ( "hello" )
@MyAnnotation ( "world" )
public void test ( String s) {
}
public static void main ( String [ ] args) {
Class < TestAnnotation > clazz = TestAnnotation . class ;
try {
Method method = clazz. getMethod ( "test" , String . class ) ;
MyAnnotation [ ] annotations = method. getAnnotationsByType ( MyAnnotation . class ) ;
Arrays . stream ( annotations) . map ( MyAnnotation :: value ) . forEach ( System . out:: println ) ;
}
catch ( Exception e) {
e. printStackTrace ( ) ;
}
}
}
Nashorn JavaScript引擎