Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

New features of jdk1.8

2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >

Share

Shulou(Shulou.com)06/03 Report--

I. the default method of the interface

Java 8 allows us to add a non-abstract method implementation to the interface by using the default keyword, which is also called an extension method, as shown in the following example:

The code is as follows:

Interface Formula {

Double calculate (int a)

Default double sqrt (int a) {

Return Math.sqrt (a)

}

}

The Formula interface not only has the calculate method, but also defines the sqrt method. The subclass that implements the Formula interface only needs to implement a calculate method, and the default method sqrt can be used directly on the subclass.

The code is as follows:

Formula formula = new Formula () {

@ Override

Public double calculate (int a) {

Return sqrt (a * 100)

}

}

Formula.calculate; / / 100.0

Formula.sqrt (16); / / 4.0

The formula in this article is implemented as an example of an anonymous class, the code is very easy to understand, 6 lines of code implements the computational sqrt (a * 100). In the next section, we will see a simpler approach to implementing a single-method interface.

Translator's note: in Java, there is only single inheritance. If you want a class to give new features, it is usually implemented using interfaces. In C++, multiple inheritance is supported, and a subclass is allowed to have interfaces and functions of multiple parent classes at the same time. In other languages, the method of letting a class have other reusable code at the same time is called mixin. This feature of the new Java 8 is closer to Scala's trait in terms of compiler implementation. There is also a concept called extension method in C #, which allows you to extend methods to existing types, which is semantically different from this one in Java 8.

II. Lambda expression

First take a look at how strings are arranged in older versions of Java:

The code is as follows:

List names = Arrays.asList ("peter", "anna", "mike", "xenia")

Collections.sort (names, new Comparator () {

@ Override

Public int compare (String a, String b) {

Return b.compareTo (a)

}

});

You only need to pass in a List object and a comparator to the static method Collections.sort to arrange them in the specified order. It is common practice to create an anonymous comparator object and pass it to the sort method.

You don't have to use this traditional anonymous object approach in Java 8, which provides a more concise syntax, lambda expressions:

The code is as follows:

Collections.sort (names, (String a, String b)-> {

Return b.compareTo (a)

});

You see, the code becomes more segmented and more readable, but it can actually be written shorter:

The code is as follows:

Collections.sort (names, (String a, String b)-> b.compareTo (a))

For functions with only one line of code, you can remove the curly braces {} and the return keyword, but you can also write it shorter:

The code is as follows:

Collections.sort (names, (a, b)-> b.compareTo (a))

The Java compiler can automatically derive parameter types, so you don't have to write the type again. Let's see what else lambda expressions can do more conveniently:

III. Functional interface

How are Lambda expressions represented in java's type system? Every lambda expression corresponds to a type, usually an interface type. A "functional interface" is an interface that contains only one abstract method, to which every lambda expression of that type is matched. Because default methods are not abstract methods, you can also add default methods to your functional interfaces.

We can treat lambda expressions as any interface type that contains only one abstract method, and make sure that your interface meets this requirement. You only need to add @ FunctionalInterface annotation to your interface, and the compiler will report an error if you find that the interface marked with this annotation has more than one abstract method.

Examples are as follows:

The code is as follows:

@ FunctionalInterface

Interface Converter {

T convert (F from)

}

Converter converter = (from)-> Integer.valueOf (from)

Integer converted = converter.convert ("123")

System.out.println (converted) / / 123

Note that if @ FunctionalInterface is not specified, the above code is also correct.

Translator's note mapping lambda expressions to a single-method interface has been implemented in other languages before Java 8, such as Rhino JavaScript interpreter, if a function parameter receives a single-method interface and you pass an function,Rhino interpreter will automatically make a single-interface instance to the function adapter, a typical application scenario has org.w3c.dom.events.EventTarget 's second parameter EventListener of addEventListener.

Fourth, method and constructor reference

The code in the previous section can also be represented by static method references:

The code is as follows:

Converter converter = Integer::valueOf

Integer converted = converter.convert ("123")

System.out.println (converted) / / 123

Java 8 allows you to pass a method or constructor reference using the:: keyword. The above code shows how to reference a static method, or we can reference an object's method:

The code is as follows:

Converter = something::startsWith

String converted = converter.convert ("Java")

System.out.println (converted); / / "J"

Next, let's look at how the constructor is referenced using the:: keyword. First, let's define a simple class that contains multiple constructors:

The code is as follows:

Class Person {

String firstName

String lastName

Person () {}

Person (String firstName, String lastName) {

This.firstName = firstName

This.lastName = lastName

}

}

Next, we specify an object factory interface for creating Person objects:

The code is as follows:

Interface PersonFactory

{

P create (String firstName, String lastName)

}

Here we use constructor references to associate them instead of implementing a complete factory:

The code is as follows:

PersonFactory personFactory = Person::new

Person person = personFactory.create ("Peter", "Parker")

We just need to use Person::new to get a reference to the constructor of the Person class, and the Java compiler automatically selects the appropriate constructor based on the signature of the PersonFactory.create method.

5. Lambda scope

The way you access the outer scope in lambda expressions is similar to that in older versions of anonymous objects. You can directly access the outer local variables marked with final, or the fields and static variables of the instance.

Access to local variables

We can access the outer local variables directly in the lambda expression:

The code is as follows:

Final int num = 1

Converter stringConverter =

(from)-> String.valueOf (from + num)

StringConverter.convert (2); / / 3

But unlike anonymous objects, the variable num here does not have to be declared as final, and the code is also correct:

The code is as follows:

Int num = 1

Converter stringConverter =

(from)-> String.valueOf (from + num)

StringConverter.convert (2); / / 3

However, the num here must not be modified by the later code (that is, implicitly with the semantics of final), for example, the following cannot be compiled:

The code is as follows:

Int num = 1

Converter stringConverter =

(from)-> String.valueOf (from + num)

Num = 3

Attempts to modify num in lambda expressions are also not allowed.

Access object fields and static variables

Unlike local variables, the fields and static variables of the instance are both readable and writable within lambda. This behavior is consistent with anonymous objects:

The code is as follows:

Class Lambda4 {

Static int outerStaticNum

Int outerNum

Void testScopes () {

Converter stringConverter1 = (from)-> {

OuterNum = 23

Return String.valueOf (from)

}

Converter stringConverter2 = (from)-> {

OuterStaticNum = 72

Return String.valueOf (from)

}

}

}

The default method of accessing the interface

Remember the formula example in the first section, interface Formula defines a default method sqrt that can be accessed directly by instances of formula, including anonymous objects, but not in lambda expressions.

The default method is not accessible in the Lambda expression, and the following code cannot be compiled:

The code is as follows:

Formula formula = (a)-> sqrt (a * 100)

Built-in Functional Interfaces

JDK 1.8 API includes many built-in functional interfaces, such as the Comparator or Runnable interfaces commonly used in old Java, which are annotated with @ FunctionalInterface so that they can be used on lambda.

Java 8 API also provides a number of new functional interfaces to make work easier, some of which are from the Google Guava library, and even if you are familiar with these, it is important to see how they are extended to lambda.

Predicate interface

The Predicate interface takes only one parameter and returns a boolean type. This interface contains several default methods to combine Predicate into other complex logic (such as and, or, not):

The code is as follows:

Predicate predicate = (s)-> s.length () > 0

Predicate.test ("foo"); / / true

Predicate.negate () .test ("foo") / / false

Predicate nonNull = Objects::nonNull

Predicate isNull = Objects::isNull

Predicate isEmpty = String::isEmpty

Predicate isNotEmpty = isEmpty.negate ()

Function interface

The Function interface takes a parameter and returns a result, along with some default methods (compose, andThen) that can be combined with other functions:

The code is as follows:

Function toInteger = Integer::valueOf

Function backToString = toInteger.andThen (String::valueOf)

BackToString.apply ("123"); / /" 123"

Supplier interface

The Supplier interface returns a value of any paradigm, unlike the Function interface, which does not have any parameters

The code is as follows:

Supplier personSupplier = Person::new

PersonSupplier.get (); / / new Person

Consumer interface

The Consumer interface indicates that operations are performed on a single parameter.

The code is as follows:

Consumer greeter = (p)-> System.out.println ("Hello," + p.firstName)

Greeter.accept (new Person ("Luke", "Skywalker"))

Comparator interface

Comparator is a classic interface in old Java, and Java 8 adds several default methods on top of it:

The code is as follows:

Comparator comparator = (p1, p2)-> p1.firstName.compareTo (p2.firstName)

Person p1 = new Person ("John", "Doe")

Person p2 = new Person ("Alice", "Wonderland")

Comparator.compare (p1, p2); / / > 0

Comparator.reversed (). Compare (p1, p2); /

< 0 Optional 接口 Optional 不是函数是接口,这是个用来防止NullPointerException异常的辅助类型,这是下一届中将要用到的重要概念,现在先简单的看看这个接口能干什么: Optional 被定义为一个简单的容器,其值可能是null或者不是null。在Java 8之前一般某个函数应该返回非空对象但是偶尔却可能返回了null,而在Java 8中,不推荐你返回null而是返回Optional。 代码如下: Optional optional = Optional.of("bam"); optional.isPresent(); // true optional.get(); // "bam" optional.orElse("fallback"); // "bam" optional.ifPresent((s) ->

System.out.println (s.charAt (0)); / / "b"

Stream interface

Java.util.Stream represents the last sequence of operations that can be applied to a set of elements. Stream operations are divided into intermediate operations or final operations. The final operation returns a specific type of calculation result, while the intermediate operation returns Stream itself, so you can string multiple operations in turn. To create Stream, you need to specify a data source, such as a subclass of java.util.Collection, List or Set, which is not supported by Map. Stream operations can be performed serially or in parallel.

First, let's take a look at how to use Stream. First, create the data List used in the example code:

The code is as follows:

List stringCollection = new ArrayList ()

StringCollection.add ("ddd2")

StringCollection.add ("aaa2")

StringCollection.add ("bbb1")

StringCollection.add ("aaa1")

StringCollection.add ("bbb3")

StringCollection.add ("ccc")

StringCollection.add ("bbb2")

StringCollection.add ("ddd1")

Java 8 extends the collection class to create a Stream through Collection.stream () or Collection.parallelStream (). The following sections explain common Stream operations in detail:

Filter filtering

Filtering filters through a predicate interface and retains only eligible elements, which is an intermediate operation, so we can apply other Stream operations (such as forEach) to the filtered results. ForEach requires a function to execute the filtered elements in turn. ForEach is a final operation, so we cannot perform other Stream operations after forEach.

The code is as follows:

StringCollection

.stream ()

.filter ((s)-> s.startsWith ("a"))

.forEach (System.out::println)

/ / "aaa2", "aaa1"

Sort sorting

Sorting is an intermediate operation that returns the sorted Stream. If you do not specify a custom Comparator, the default sort will be used.

The code is as follows:

StringCollection

.stream ()

.sorted ()

.filter ((s)-> s.startsWith ("a"))

.forEach (System.out::println)

/ / "aaa1", "aaa2"

It is important to note that sorting only creates an arranged Stream without affecting the original data source, and the original data stringCollection will not be modified after sorting:

The code is as follows:

System.out.println (stringCollection)

/ / ddd2, aaa2, bbb1, aaa1, bbb3, ccc, bbb2, ddd1

Map mapping

The intermediate operation map converts the element into another object in turn according to the specified Function interface. The following example shows how to convert a string to an uppercase string. You can also convert objects to other types through map. The Stream type returned by map is determined by the return value of the function passed in by your map.

The code is as follows:

StringCollection

.stream ()

.map (String::toUpperCase)

Sorted ((a, b)-> b.compareTo (a))

.forEach (System.out::println)

/ / "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"

Match matching

Stream provides a variety of matching operations that allow you to detect whether the specified Predicate matches the entire Stream. All matching operations are final and return a value of type boolean.

The code is as follows:

Boolean anyStartsWithA =

StringCollection

.stream ()

.anyMatch ((s)-> s.startsWith ("a"))

System.out.println (anyStartsWithA); / / true

Boolean allStartsWithA =

StringCollection

.stream ()

.allMatch ((s)-> s.startsWith ("a"))

System.out.println (allStartsWithA); / / false

Boolean noneStartsWithZ =

StringCollection

.stream ()

.noneMatch ((s)-> s.startsWith ("z"))

System.out.println (noneStartsWithZ); / / true

Count count

Counting is a final operation that returns the number of elements in Stream, with a return type of long.

The code is as follows:

Long startsWithB =

StringCollection

.stream ()

.filter ((s)-> s.startsWith ("b"))

.count ()

System.out.println (startsWithB); / / 3

Reduce protocol

This is a final operation, which allows multiple elements in the stream to be reduced to one element through the specified function, and the result of the violation is expressed through the Optional API:

The code is as follows:

Optional reduced =

StringCollection

.stream ()

.sorted ()

.reduce (S1, S2)-> S1 + "#" + S2)

Reduced.ifPresent (System.out::println)

/ / "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2"

Parallel Streams

As mentioned earlier, there are two types of Stream: serial and parallel. Operations on serial Stream are done in turn in one thread, while parallel Stream is executed on multiple threads at the same time.

The following example shows how to improve performance through parallel Stream:

First, let's create a large table without repeating elements:

The code is as follows:

Int max = 1000000

List values = new ArrayList (max)

For (int I = 0; I

< max; i++) { UUID uuid = UUID.randomUUID(); values.add(uuid.toString()); } 然后我们计算一下排序这个Stream要耗时多久, 串行排序: 代码如下: long t0 = System.nanoTime(); long count = values.stream().sorted().count(); System.out.println(count); long t1 = System.nanoTime(); long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0); System.out.println(String.format("sequential sort took: %d ms", millis)); // 串行耗时: 899 ms 并行排序: 代码如下: long t0 = System.nanoTime(); long count = values.parallelStream().sorted().count(); System.out.println(count); long t1 = System.nanoTime(); long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0); System.out.println(String.format("parallel sort took: %d ms", millis)); // 并行排序耗时: 472 ms 上面两个代码几乎是一样的,但是并行版的快了50%之多,唯一需要做的改动就是将stream()改为parallelStream()。 Map 前面提到过,Map类型不支持stream,不过Map提供了一些新的有用的方法来处理一些日常任务。 代码如下: Map map = new HashMap(); for (int i = 0; i < 10; i++) { map.putIfAbsent(i, "val" + i); } map.forEach((id, val) ->

System.out.println (val))

The above code is easy to understand, putIfAbsent does not require us to do additional existence checks, while forEach receives a Consumer interface to operate on each key-value pair in map.

The following example shows other useful functions on map:

The code is as follows:

Map.computeIfPresent (3, (num, val)-> val + num)

Map.get (3); / / val33

Map.computeIfPresent (9, (num, val)-> null)

Map.containsKey (9); / / false

Map.computeIfAbsent (23, num-> "val" + num)

Map.containsKey (23); / / true

Map.computeIfAbsent (3, num-> "bam")

Map.get (3); / / val33

Next, show how to delete an item in Map where all the keys and values match:

The code is as follows:

Map.remove (3, "val3")

Map.get (3); / / val33

Map.remove (3, "val33")

Map.get (3); / / null

Another useful method:

The code is as follows:

Map.getOrDefault (42, "not found"); / / not found

It is also easy to merge elements of Map:

The code is as follows:

Map.merge (9, "val9", (value, newValue)-> value.concat (newValue))

Map.get (9); / / val9

Map.merge (9, "concat", (value, newValue)-> value.concat (newValue))

Map.get (9); / / val9concat

What Merge does is insert if the key name does not exist, otherwise merge the corresponding value of the original key and reinsert it into the map.

IX. Date API

Java 8 contains a new set of time and date API under the package java.time. The new date API is similar to the open source Joda-Time library, but not exactly the same. The following example shows some of the most important parts of this new set of API:

Clock clock

The Clock class provides methods to access the current date and time, and Clock is time zone sensitive and can be used instead of System.currentTimeMillis () to get the current number of microseconds. The Instant class can also be used to represent a particular point in time, and the Instant class can also be used to create old java.util.Date objects.

The code is as follows:

Clock clock = Clock.systemDefaultZone ()

Long millis = clock.millis ()

Instant instant = clock.instant ()

Date legacyDate = Date.from (instant); / / legacy java.util.Date

Timezones time zone

The time zone in the new API is represented by ZoneId. The time zone can be easily obtained using the static method of. The time zone defines the time difference to the UTS time, which is extremely important when converting an Instant point-in-time object to a local date object.

The code is as follows:

System.out.println (ZoneId.getAvailableZoneIds ())

/ / prints all available timezone ids

ZoneId zone1 = ZoneId.of ("Europe/Berlin")

ZoneId zone2 = ZoneId.of ("Brazil/East")

System.out.println (zone1.getRules ())

System.out.println (zone2.getRules ())

/ / ZoneRules [currentStandardOffset=+01:00]

/ / ZoneRules [currentStandardOffset =-03:00]

LocalTime local time

LocalTime defines a time when there is no time zone information, such as 10:00 or 17:30:15 in the evening. The following example creates two local times using the time zone created by the previous code. Then compare the time and calculate the time difference between the two times in hours and minutes:

The code is as follows:

LocalTime now1 = LocalTime.now (zone1)

LocalTime now2 = LocalTime.now (zone2)

System.out.println (now1.isBefore (now2)); / / false

Long hoursBetween = ChronoUnit.HOURS.between (now1, now2)

Long minutesBetween = ChronoUnit.MINUTES.between (now1, now2)

System.out.println (hoursBetween); / /-3

System.out.println (minutesBetween); / /-239

LocalTime provides a variety of factory methods to simplify object creation, including parsing time strings.

The code is as follows:

LocalTime late = LocalTime.of (23,59,59)

System.out.println (late); / / 23:59:59

DateTimeFormatter germanFormatter =

DateTimeFormatter

.ofLocalizedTime (FormatStyle.SHORT)

.withlocale (Locale.GERMAN)

LocalTime leetTime = LocalTime.parse ("13:37", germanFormatter)

System.out.println (leetTime); / / 13:37

LocalDate local date

LocalDate indicates an exact date, such as 2014-03-11. The object value is immutable and is basically the same as LocalTime. The following example shows how to add or subtract days / months / years from a Date object. It is also important to note that these objects are immutable and that the operation always returns a new instance.

The code is as follows:

LocalDate today = LocalDate.now ()

LocalDate tomorrow = today.plus (1, ChronoUnit.DAYS)

LocalDate yesterday = tomorrow.minusDays (2)

LocalDate independenceDay = LocalDate.of (2014, Month.JULY, 4)

DayOfWeek dayOfWeek = independenceDay.getDayOfWeek ()

System.out.println (dayOfWeek); / / FRIDAY

Parsing a LocalDate type from a string is as simple as parsing LocalTime:

The code is as follows:

DateTimeFormatter germanFormatter =

DateTimeFormatter

.ofLocalizedDate (FormatStyle.MEDIUM)

.withlocale (Locale.GERMAN)

LocalDate xmas = LocalDate.parse ("24.12.2014", germanFormatter)

System.out.println (xmas); / / 2014-12-24

LocalDateTime Local date time

LocalDateTime represents both time and date, which is equivalent to merging the first two sections into one object. LocalDateTime, like LocalTime and LocalDate, is immutable. LocalDateTime provides ways to access specific fields.

The code is as follows:

LocalDateTime sylvester = LocalDateTime.of (2014, Month.DECEMBER, 31, 23, 59, 59)

DayOfWeek dayOfWeek = sylvester.getDayOfWeek ()

System.out.println (dayOfWeek); / / WEDNESDAY

Month month = sylvester.getMonth ()

System.out.println (month); / / DECEMBER

Long minuteOfDay = sylvester.getLong (ChronoField.MINUTE_OF_DAY)

System.out.println (minuteOfDay); / / 1439

As long as the time zone information is attached, it can be converted into a point-in-time Instant object, and the Instant point-in-time object can be easily converted into old-fashioned java.util.Date.

The code is as follows:

Instant instant = sylvester

.atZone (ZoneId.systemDefault ())

.toInstant ()

Date legacyDate = Date.from (instant)

System.out.println (legacyDate); / / Wed Dec 31 23:59:59 CET 2014

Formatting LocalDateTime is the same as formatting time and date. In addition to using a predefined format, we can also define the format ourselves:

The code is as follows:

DateTimeFormatter formatter =

DateTimeFormatter

.ofPattern ("MMM dd, yyyy-HH:mm")

LocalDateTime parsed = LocalDateTime.parse ("Nov 03, 2014-07:13", formatter)

String string = formatter.format (parsed)

System.out.println (string); / / Nov 03, 2014-07:13

Unlike java.text.NumberFormat, the new version of DateTimeFormatter is immutable, so it is thread safe.

More information about time and date format: http://download.java.net/jdk8/docs/api/java/time/format/DateTimeFormatter.html

10. Annotation comments

Multiple annotations are supported in Java 8. Let's take a look at an example to understand what it means.

First, define a wrapper class Hints annotation to place a specific set of Hint annotations:

The code is as follows:

@ interface Hints {

Hint [] value ()

}

@ Repeatable (Hints.class)

@ interface Hint {

String value ()

}

Java 8 allows us to use the same type of annotation multiple times, simply by tagging the annotation @ Repeatable.

Example 1: use the wrapper class as a container to store multiple annotations (old method)

The code is as follows:

@ Hints ({@ Hint ("hint1"), @ Hint ("hint2")})

Class Person {}

Example 2: using multiple annotations (new method)

The code is as follows:

@ Hint ("hint1")

@ Hint ("hint2")

Class Person {}

In the second example, the java compiler will implicitly define the @ Hints annotation for you, which will help you use reflection to get this information:

The code is as follows:

Hint hint = Person.class.getAnnotation (Hint.class)

System.out.println (hint); / / null

Hints hints1 = Person.class.getAnnotation (Hints.class)

System.out.println (hints1.value () .length); / / 2

Hint [] hints2 = Person.class.getAnnotationsByType (Hint.class)

System.out.println (hints2.length); / / 2

Even if we don't define @ Hints annotations on the Person class, we can still get @ Hints annotations through getAnnotation (Hints.class), and it's easier to get all @ Hint annotations directly using getAnnotationsByType.

In addition, Java 8 annotations have been added to two new target:

The code is as follows:

@ Target ({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})

@ interface MyAnnotation {}

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Servers

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report