java8 new features curated (full)

Foreword

More and more projects have used Java 8 , and there is no doubt, Java 8 is the most important after the Java since Java 5 (released in 2004) version. This version contains more than a dozen new features of the language, compiler, libraries, tools, and other aspects of the JVM. In this article we will learn these new features, and with practical examples illustrate suitable for use in any scenario.

Quote: This reference to these two articles to be their own understanding, organized into Java8 a new feature article easiest to understand, there are a small number of chapters may consistent content, but definitely not plagiarism, just for integrity of the article, most commonly place plus my own understanding and examples.

https://blog.csdn.net/yczz/article/details/50896975

https://blog.csdn.net/maosijunzi/article/details/38658095

For readers and objectives

Target population

  • Suitable have used lambda expressions the students, want to completely understand clearly
  • The new study of specific Java8

aims

  • Learn java8 function interface and Lambda expressions
  • Use method references
  • Static methods of the interface and the default method
  • Use Date / Time Api's
  • Use of the Stream API

1. The new Java language features

Using a lambda Java8 indeed a lot of convenience, but also for the first time to understand people feel difficult to read, in fact, is the reason you are not accustomed to. Many languages ​​from the outset to support the Lambda expressions, like Groovy, Scala and so on.

1.1 Lambda expressions and functional interfaces

In Java8 before, we want to make a way to interact with the user, such as the use of local variables within the method; this time can only use the interface as a parameter, allowing users to implement this interface or in the form of an anonymous inner class, the local variables passed to the user via the interface method.

Traditional anonymous inner classes Disadvantages: Code bloated, difficult to read

Lambda expressions

Lambda expressions as a function parameters are passed to a method, or data processing as the code itself;

Syntax:

  • Use a comma-separated list of arguments
  • -> symbol
  • And statement blocks
Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) );

Equivalent to

List<String> list = Arrays.asList( "a", "b", "d" );
for(String e:list){
    System.out.println(e);
}

If the statement block is complex, the use of {}wrap

Arrays.asList( "a", "b", "d" ).forEach( e -> {
    String m = "9420 "+e;
    System.out.print( m );
});

Retrofitted on anonymous inner classes Lambda essence, it uses the variables are implicitly converted to finalthe

String separator = ",";
Arrays.asList( "a", "b", "d" ).forEach( 
    e -> System.out.print( e + separator ) );

Equivalent to

final String separator = ",";
Arrays.asList( "a", "b", "d" ).forEach( 
    e -> System.out.print( e + separator ) );

Lambda parameter and return value type inference drawn by the compiler, the definition does not need to show, if only one line of code can not write return statements

Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> e1.compareTo( e2 ) );

Equivalent to

List<String> list = Arrays.asList("a", "b", "c");
Collections.sort(list, new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
        return o1.compareTo(o2);
    }
});

Functional Interface

  • There is only one interface method interface
  • You can have static methods and the default method
  • Use @FunctionalInterfacemarkers
  • The default method can be overridden
@FunctionalInterface
public interface FunctionalDefaultMethods {
    void method();
 
    default void defaultMethod() {            
    }
    
    static void staticMethod(){
    }
}
private interface Defaulable {
    // Interfaces now allow default methods, the implementer may or 
    // may not implement (override) them.
    default String notRequired() { 
        return "Default implementation"; 
    }        
}
 
private static class DefaultableImpl implements Defaulable {
}
 
private static class OverridableImpl implements Defaulable {
    @Override
    public String notRequired() {
        return "Overridden implementation";
    }
}

// 也可以由接口覆盖 
public interface OverridableInterface extends Defaulable{
    @Override
    public String notRequired() {
        return "interface Overridden implementation";
    }
}

As the realization of the default method provided support in the JVM bytecode level, so the efficiency is very high. The default method allows to improve the interface on the basis of inheritance does not break the existing system on. Application of this feature in the official repository is: to java.util.Collection interfaces to add new methods, such as Stream () , parallelStream () , forEach () and removeIf () and so on.

Function already defined interface Java8

We basically do not need to define your own functions interface, Java8 has provided us with a lot of default function interface, basic enough, the rt.jarpackage java.util.functioncan see all the default directory function interface, divided into several categories

  • Function<T,R> T as input and returns the R as output
  • Predicate<T> T as input and returns boolean output value
  • Consumer<T> T as an input, no output
  • Supplier<R>No input, R & lt an output
  • BinaryOperator<T> Two T as an input, T is also output
  • UnaryOperator<T>Is Functiona variant of those input and output is T

Various other extensions are several of the above, only more convenient to use, the following examples demonstrate, you can use it as a normal interface, the user Lambda passed.

// hello world 示例
Function<String,String> function = (x) -> {return x+"Function";};
System.out.println(function.apply("hello world"));  // hello world Function

UnaryOperator<String> unaryOperator = x -> x + 2;
System.out.println(unaryOperator.apply("9420-"));   // 9420-2

// 判断输入值是否为偶数示例
Predicate<Integer> predicate = (x) ->{return x % 2 == 0 ;};
System.out.println(predicate.test(1));              // false

// 这个没有返回值
Consumer<String> consumer = (x) -> {System.out.println(x);};
consumer.accept("hello world ");                    // hello world

// 这个没有输入 
Supplier<String> supplier = () -> {return "Supplier";};
System.out.println(supplier.get());                 // Supplier

// 找出大数
BinaryOperator<Integer> bina = (x, y) ->{return x > y ? x : y;};
bina.apply(1,2);                                    // 2 

1.2 reference method

Method references so that developers can directly reference the existing method, constructor, or Java class instance object. Lambda expressions and the reference method with the use of such java class constructor looks compact and simple, without many complex template code.

public static 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 follow( final Car another ) {
        System.out.println( "Following the " + another.toString() );
    }
 
    public void repair() {   
        System.out.println( "Repaired " + this.toString() );
    }
}

The first type is the constructor method cited reference, the syntax is Class :: new new , or more general form: Class ::new . Note: This constructor has no parameters.

final Car car = Car.create( Car::new );

Equivalent to

Car car = Car.create(() -> new Car());

The second method is a reference type static method reference syntax is Class :: static_method . Note: This method takes a parameter of type Car.

cars.forEach( Car::collide );

forEachPrototype for forEach(Consumer<? super T> action)using Consumer only the parameters, no return value; this parameter T is the car type, because it is cars.forEachincorrect, it is equivalent to the above cited method of

cars.forEach(car -> Car.collide(car));

The third type is a reference method cited member method of a class, the syntax is Class :: Method, , note that this method does not define the parameters:

cars.forEach( Car::repair );

It is equivalent to

cars.forEach(car -> car.repair());

1.3 Repeat comment

Since the introduction of Java 5 annotations since this feature became very popular, and is widely used in various frameworks and projects. However, notes that there is a big limitation: You can not use the same comment multiple times in the same place. Java 8 eliminates this restriction, we introduced the concept of repeated notes, allowing the use of the same comment multiple times in the same place.

In Java 8 used @Repeatable annotation defines repeat notes, in fact, this is not the language level of improvement, but the compiler to do a trick, the underlying technology remains the same. Can be described using the following code:

@Target( ElementType.TYPE )
@Retention( RetentionPolicy.RUNTIME )
@Repeatable( Filters.class )
public @interface Filter {
    String value();
};

@Filter( "filter1" )
@Filter( "filter2" )
public interface Filterable {        
}

public static void main(String[] args) {
    for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class ) ) {
        System.out.println( filter.value() );
    }
}

As we have seen, here Filter class uses the @Repeatable(Filters.class)annotation modification, and Filters are stored Filter annotated container, the compiler try to shield these details to developers. In this way, the Filterable interface with two Filter annotated comments (here and did not mention any information about the Filters).

Further, the reflection API provides a new method: getAnnotationsByType () , can return a type of annotation is repeated, for example, Filterable.class.getAnnoation(Filters.class)the two return Filter instances.

1.4 Better type inference

Java 8 compiler has greatly improved in terms of type inference, in many scenes the compiler can deduce the data type of a parameter, thus making the code more concise. Examples code is as follows:

public class Value< T > {
    public static< T > T defaultValue() { 
        return null; 
    }
 
    public T getOrDefault( T value, T defaultValue ) {
        return ( value != null ) ? value : defaultValue;
    }
}
public class TypeInference {
    public static void main(String[] args) {
        final Value< String > value = new Value<>();
        value.getOrDefault( "22", Value.defaultValue() );
    }
}

Parameter Value.defaultValue () is deduced by the compiler type stars, you need not be explicitly specified. In Java 7 in this code will compile error, unlessValue.<String>defaultValue()

1.5 broaden Notes application scenarios

Java 8 broadens the application scenarios annotations. Now, annotations can be used on almost any element: local variables, interface type, interface class and superclass, even can be used in an abnormal function definition. Here are some examples:

package com.javacodegeeks.java8.annotations;
 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Collection;
 
public class Annotations {
    @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<>();        
    }
}

ElementType.TYPE_USER and ElementType.TYPE_PARAMETER are two Java 8 new notes for notes describing usage scenarios. Java language also made corresponding changes to identify these new notes.

2. New features of Java compiler

Java 8 officially supported parameter names, and finally do not need to read class byte code to get the name of the parameter, which is particularly useful for people who regularly use reflection.

In Java8 This feature is off by default, you need to open in order to obtain parameters Parameter name:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.1</version>
    <configuration>
        <compilerArgument>-parameters</compilerArgument>
        <source>1.8</source>
        <target>1.8</target>
    </configuration>
</plugin>

3. JVM's new features

Use Metaspace ( the JEP 122 ) instead of the permanent generation ( the PermGen Space). In the JVM parameters, using -XX: MetaSpaceSize and -XX: MaxMetaspaceSize replace the original -XX: PermSize and -XX: MaxPermSize .

4. New features of Java libraries official

Java 8 adds many new tools (date / time-based), and extends the existing tools, to support modern concurrent programming, programming and other functions, the present description reference section, and extracts the common functions.

4.1 Streams

Streams operating late into the middle of the operation and operation, intermediate operation returns a new Stream, just do it operational records only, and will not really perform late operation is really going through the list and perform all operations

Another value of Stream is to support parallel processing parallelmethod.

Stream API set of operations is simplified, and the extended set of packets, summation, mapReduce, flatMap, sorting and other functions, the following listed items frequently used functions will use frequency sort.

  1. Prepare a following example of a test object
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Vehicle {
    //车架号
    private String vin;
    // 车主手机号
    private String phone;
    // 车主姓名
    private String name;
    // 所属车租车公司
    private Integer companyId;
    // 个人评分
    private Double score;
    //安装的设备列表imei,使用逗号分隔
    private String deviceNos;
}
  1. Prepare some vehicle data
static List<Vehicle> vehicles = new ArrayList<>();

@Before
public void init(){
    List<String> imeis = new ArrayList<>();
    for (int i = 0; i <5 ; i++) {
        List<String> singleVehicleDevices = new ArrayList<>();
        for (int j = 0; j < 3; j++) {
            String imei = RandomStringUtils.randomAlphanumeric(15);
            singleVehicleDevices.add(imei);
        }
        imeis.add(StringUtils.join(singleVehicleDevices,','));
    }
    vehicles.add(new Vehicle("KPTSOA1K67P081452","17620411498","9420",1,4.5,imeis.get(0)));
    vehicles.add(new Vehicle("KPTCOB1K18P057071","15073030945","张玲",2,1.4,imeis.get(1)));
    vehicles.add(new Vehicle("KPTS0A1K87P080237","19645871598","sanri1993",1,3.0,imeis.get(2)));
    vehicles.add(new Vehicle("KNAJC526975740490","15879146974","李种",1,3.9,imeis.get(3)));
    vehicles.add(new Vehicle("KNAJC521395884849","13520184976","袁绍",2,4.9,imeis.get(4)));
}

4.1.1 forEach data traversing Collection

vehicles.forEach(vehicle -> System.out.println(vehicle));

//这样就可以遍历打印
vehicles.forEach(System.out::println);

4.1.2 forEach iterate Map Data

Map<String,Integer> map = new HashMap<>();
map.put("a",1);map.put("b",2);map.put("c",3);

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

4.1.3 filter data filtering

// 去掉评分为 3 分以下的车
List<Vehicle> collect = vehicles.stream().filter(vehicle -> vehicle.getScore() >= 3).collect(Collectors.toList());

4.1.4 map object map

One List<Object>in most cases, we only need a column list, or the need to convert the inside of each object into another object, this time you can use the map mapping example:

// 取出所有的车架号列表
 List<String> vins = vehicles.stream().map(Vehicle::getVin).collect(Collectors.toList());

4.1.5 groupBy be grouped by an attribute

// 按照公司 Id 进行分组
Map<Integer, List<Vehicle>> companyVehicles = vehicles.stream().collect(Collectors.groupingBy(Vehicle::getCompanyId));

// 按照公司分组求司机打分和
Map<Integer, Double> collect = vehicles.stream().collect(Collectors.groupingBy(Vehicle::getCompanyId, Collectors.summingDouble(Vehicle::getScore)));

4.1.6 sort Sort according to a property, and multi-column sorting

// 单列排序 
vehicles.sort((v1,v2) -> v2.getScore().compareTo(v1.getScore()));

// 或使用 Comparator 类来构建比较器,流处理不会改变原列表,需要接收返回值才能得到预期结果
 List<Vehicle> collect = vehicles.stream().sorted(Comparator.comparing(Vehicle::getScore).reversed()).collect(Collectors.toList());

// 多列排序,score 降序,companyId 升序排列
List<Vehicle> collect = vehicles.stream().sorted(Comparator.comparing(Vehicle::getScore).reversed()
                .thenComparing(Comparator.comparing(Vehicle::getCompanyId)))
                .collect(Collectors.toList());

4.1.7 flatMap flattening data processing

// 查出所有车绑定的所有设备
List<String> collect = vehicles.stream().map(vehicle -> {
    String deviceNos = vehicle.getDeviceNos();
    return StringUtils.split(deviceNos,',');
}).flatMap(Arrays::stream).collect(Collectors.toList());

flatMap is suitable List<List>or List<object []>this construction may be processed as a list; a list of the device as above, is stored in the database structure is a comma-separated data, and the list is a list of vehicle data.

4.1.8 mapReduce data processing

// 对所有司机的总分求和
Double reduce = vehicles.stream().parallel().map(Vehicle::getScore).reduce(0d, Double::sum);

4.1.9 Integrated Processing Example

// 总的分值
Double totalScore = vehicles.stream().parallel().map(Vehicle::getScore).reduce(0d, Double::sum);

// 查看每一个司机占的分值比重
List<String> collect = vehicles.stream()
    .mapToDouble(vehicle -> vehicle.getScore() / totalScore)
    .mapToLong(weight -> (long) (weight * 100))
    .mapToObj(percentage -> percentage + "%")
    .collect(Collectors.toList());

Original boxed not know what that means, there is great hope that God can help answer next, not boxed are possible

4.2 Optional

Optional Java to solve that often appear NullPointerException , in order to avoid being the source of various air pollution inspection, the source code more concise and easier to read

// 假设有一个对象 obj ,你不知道它是不是为空的,但是你想用它的方法,可以这么玩
Optional<T> canUseObj = Optional.ofNullable(obj);
canUseObj.ifPresent(System.out::println);       //如果 obj 不为空,则可以使用 obj 的方法,这里做个简单输出 

4.3 Date/Time API(JSR 310)

The new date and time are all tools in java.timeits subpackages.

4.3.1 New Date / Time API design principles

Java 8 Date / Time API is JSR-310 implementation specifications, and its goal is to overcome the legacy of the date / time API implementation of all defects, a new date / time API some of the design principles are as follows:

  • Invariance: the new date / time API, all classes are immutable, this design facilitates concurrent programming.
  • Separation of Concerns: New API to human-readable date and time and machine time (unix timestamp) a clear separation, which is the date (Date), time (Time), the date and time (DateTime), timestamp (unix timestamp) and the time zone It defines the different classes.
  • Clear: In all classes, methods are clearly defined in order to accomplish the same behavior. For example, to get the current instance, we can use now () method, in all the classes are defined in the format () and parse () method, rather than have a separate special class as before. In order to better deal with the problem, all classes use the factory pattern and strategy pattern, once you use a method in a class, and other types of work is not difficult.
  • Practical Operation: All new date / time API class implements a number of methods to perform common tasks, such as: addition, subtraction, formatting, resolution, and other operations to extract a separate part from a date / time.
  • Scalability: The new date / time API works in ISO-8601日历系统上的, but we can also be used in non-IOS calendar.

4.3.2 Common Its Usage

Time can be roughly divided into three parts: the date, time, time zone

Which date broken down into year, month, day; when the time is subdivided into hours, minutes, seconds

General machine time used from 1970-01-01T00: 00 to represent the current number of seconds to time; here to correct a misconception most of the prisoners, the time stamp refers to the number of seconds, not milliseconds.

Almost all of the time objects are implemented Temporalinterface, interface parameters are generallyTemporal

  • Instant: represents a point on the timeline, the reference point is the standard Java epoch (epoch), namely 1970-01-01T00: 00: 00Z (1970 Nian 1 Yue 1 Ri 00:00 GMT)

  • LocalDate: date value objects such 2019-09-22

  • LocalTime: time value objects such as 21:25:36

  • LocalDateTime: date + time value of the object

  • ZoneId: Time zone

  • ZonedDateTime: date + time zone when the value of the object +

  • DateTimeFormatter: a date and time format

  • Period: for calculating the date interval

  • Duration: for calculating the time interval

4.3.2.1 Instantdenotes a point of time line (instantaneous)
// 测试执行一个 new 操作使用的时间(纳秒值)
Instant begin = Instant.now();
StreamMain streamMain = new StreamMain();
Instant end = Instant.now();
System.out.println(Duration.between(begin,end).toNanos());
4.3.2.2 LocalDate, LocalTime, LocalDateTime, ZonedDateTimeit can be regulated as a set, for indicating time
// 可以使用 of 方法构建它们的实例,如下面创建了一个 2019-9-22 21:42:59 东八区 的时间对象 
LocalDate localDate = LocalDate.of(2019, Month.SEPTEMBER, 22);
LocalTime localTime = LocalTime.of(21, 42, 59);
LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);
ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, ZoneId.systemDefault());

// 获取现在的时间,这是一个静态方法
LocalDate now = LocalDate.now();

// 每个实例可以获取它们的 part 信息,如获取年 
int year = localDate.getYear();

// 可以修改 part 信息,这将返回一个新对象,如增加一年
LocalDate localDatePlus = localDate.plusYears(1);

// 设置 part 信息,也会返回新的对象,如设置为 2017 年 
LocalDate localDateWithYear = localDate.withYear(2017);

// 比较两个日期 isAfter,isBefore
boolean after = localDate.isAfter(LocalDate.now());

// 格式化日期时间
// yyyy-MM-dd
System.out.println(now.format(DateTimeFormatter.ISO_DATE));
// yyyy-MM-ddTHH:mm:ss
System.out.println(now.format(DateTimeFormatter.ISO_DATE_TIME));
// yyyy-MM-dd HH:mm:ss
System.out.println(now.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)));

// 日期解析 
System.out.println(LocalDate.parse("2019-09-22"));
System.out.println(LocalDateTime.parse("2019-09-22T21:05:22"));
System.out.println(LocalDateTime.parse("2019-09-22 21:05:22",DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)));
4.3.2.3 ZoneIdfor operating time zone, it provides a method for obtaining all the local time zone and time zone
ZoneId zoneId = ZoneId.systemDefault();
Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
4.3.2.4 Period, Durationcan be regarded as a set, for calculating the time interval
// 创建一个两周的间隔
Period periodWeeks = Period.ofWeeks(2);

// 一年三个月零二天的间隔
Period custom = Period.of(1, 3, 2);

// 一天的时长
Duration duration = Duration.ofDays(1);

// 计算2015/6/16 号到现在 2019/09/22 过了多久,它这个把间隔分到每个 part 了
LocalDate now = LocalDate.now();
LocalDate customDate = LocalDate.of(2015, 6, 16);
Period between = Period.between(customDate, now);
// 结果为 4:3:6 即过去了 4年3个月6天了
System.out.println(between.getYears()+":"+between.getMonths()+":"+between.getDays());

// 比较两个瞬时的时间间隔 
Instant begin = Instant.now();
Instant end = Instant.now();
Duration.between(begin,end);

// 同样可以修改 part 信息和设置 part 信息,都是返回新的对象来表示设置过的值,原来的对象不变
Period plusDays = between.plusDays(1);
Period withDays = between.withDays(4);

4.4 Base64

For Base64 finally do not referenced third-party packages, and can be completed using a java library

// 编码
final String encoded = Base64.getEncoder().encodeToString( text.getBytes( StandardCharsets.UTF_8 ) );
// 解码
final String decoded = new String( Base64.getDecoder().decode( encoded ),StandardCharsets.UTF_8 );

4.5 JUC expansion kit

Lambda expressions and based on the new steam properties as Java 8 for java.util.concurrent.ConcurrentHashMap added a new class to support the focusing operation method; and is, for java.util.concurrentForkJoinPool added to a new class of methods support common thread pool operations (more can refer to our concurrent programming course ).

Java 8 also adds new java.util.concurrent.locks.StampedLock class, based on the capacity to support the lock - the lock has three models to support read and write operations (as is the lock can java.util.concurrent .locks.ReadWriteLock replacement).

In java.util.concurrent.atomic package also added a number of tools, are listed below:

  • DoubleAccumulator
  • DoubleAdder
  • LongAccumulator
  • Long Adder

5. The new tools

Java 8 provides several new command line tool, this chapter explains some of the most useful tools for developers.

5.1 class dependent parser: jdeps

deps is a really nice command-line tool that can show package level and class hierarchy Java class dependencies, it .class file, directory or Jar file as input, then output will depend on the relationship to the console.

We can use jedps Analysis Spring Framework library , in order to make the results a little less, only a JAR file analysis: org.springframework.core-3.0.5.RELEASE.jar .

jdeps org.springframework.core-3.0.5.RELEASE.jar

This command will output a lot of results, we only look at some of them: dependency according to package grouping, if no reliance on the classpath, "not found" is displayed.

org.springframework.core-3.0.5.RELEASE.jar -> C:\Program Files\Java\jdk1.8.0\jre\lib\rt.jar
   org.springframework.core (org.springframework.core-3.0.5.RELEASE.jar)
      -> java.io                                            
      -> java.lang                                          
      -> java.lang.annotation                               
      -> java.lang.ref                                      
      -> java.lang.reflect                                  
      -> java.util                                          
      -> java.util.concurrent                               
      -> org.apache.commons.logging                         not found
      -> org.springframework.asm                            not found
      -> org.springframework.asm.commons                    not found
   org.springframework.core.annotation (org.springframework.core-3.0.5.RELEASE.jar)
      -> java.lang                                          
      -> java.lang.annotation                               
      -> java.lang.reflect                                  
      -> java.util

Little promotion

Writing is not easy, I hope the support of open source software, and my gadgets, welcome to gitee point star, fork, put bug.

Excel common import and export, support Excel formulas
blog address: https://blog.csdn.net/sanri1993/article/details/100601578
gitee: https://gitee.com/sanri/sanri-excel-poi

Use the template code, the gadget generate code from the database, and some projects can often be used in the
blog address: https://blog.csdn.net/sanri1993/article/details/98664034
gitee: https://gitee.com/ sanri / sanri-tools-maven

Guess you like

Origin www.cnblogs.com/sanri1993/p/11569863.html