<repo_name>acc4mei/acc4j<file_sep>/README.md
# acc4j

## java-gist

- Java
  - JDK8
    - **函数式接口**，Lambda表达式，方法引用
    - Stream API
    - LocalDateTime API
    - Optional
    - 注解，可重复注解，类型注解

- Jasypt

  无侵入式数据加密，通常用于Spring配置文件的关键信息加密使用。

- Fel

  轻量级表达式计算引擎，兼容Java语法。
  
- EasyExcel

  基于Java的简单、节约内存的Excel读写工具。<file_sep>/java-gist/src/main/java/com/acc4j/jdk8/methodInterface/LambdaExpressionSample.java
package com.acc4j.jdk8.methodInterface;

import java.util.concurrent.Callable;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * Lambda表达式：以匿名函数的形式“实现”函数式接口
 * Lambda简化了匿名内部类形式，但实现原理并不同；匿名内部类在编译过程种会产生类；Lambda则是调用JVM的 invokedynamic 指令实现，不会产生类
 */
public class LambdaExpressionSample {

    /**
     * Lambda结构：(参数列表) -> {方法体}
     * 语法：
     * 1. 参数类型可省略，通常可以从上下文信息推断，只有一个参数时可省略小括号；当显式声明类型时，小括号不能省略
     * 2. 方法体只有一行时可省略花括号；一行为返回值时可省略return；没有内容或者多行内容时花括号不能省略
     * 3. 支持函数式接口调用对应方法实现
     */
    public void sample() throws Exception {
        System.out.println("--------------- LambdaExpression sample --------------");

        // 不能省略{}
        Runnable l0 = () -> {
        };
        Runnable l1 = () -> System.out.println("无参数，无返回值");
        // 可以省略return
        Callable<String> l2 = () -> "无参数，有返回值";
        // 显式声明了类型，只有一个参数也不能省略小括号
        Consumer<String> l3 = (String a) -> System.out.println("有参数，无返回值");
        // 参数不声明类型，通过上下文信息推断，只有一个参数时可以省略小括号
        Function<String, String> l4 = a -> "有参数，有返回值";
        // 自定义函数式接口，多个参数，无返回值；用lambda来达成实现并调用
        IFunctionSample<String, String> l5 = (String a, String b) -> System.out.println("自定义函数式接口，多个参数，无返回值");

        l0.run();
        l1.run();
        System.out.println(l2.call());
        l3.accept("l3");
        System.out.println(l4.apply("l4"));
        l5.sample("l", "5");
        l5.desc();
        System.out.println();
    }
}
<file_sep>/java-gist/src/main/java/com/acc4j/jdk8/annotation/Sample.java
package com.acc4j.jdk8.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/*
    @Documented 元注解，表示注释信息纳入JavaDoc

    @Target 元注解，表示注解的目标类型，值类型 ElementType[]；没有声明时支持所有类型
        JDK8新增TYPE_USE，TYPE_PARAMETER的类型注解

    @Retention 元注解，表示注解保留周期，值类型 RetentionPolicy；没有声明时，默认CLASS
        SOURCE  只保留在源码中，纯标记
        CLASS  保留到Class文件中，但是【JVM不会持有该类别信息，所以反射也无法获取】
        RUNTIME  保留到Class文件，JVM会持有该类别信息，支持反射获取

    @Inherited 元注解，表示注解信息可顺延着被标注元素的父子关系进行获取；注意，注解之间不存在继承关系

    @Repeatable JDK8新增的元注解，表示注解可重复标注，实质上必须提供一个容器注解进行容纳。
        容器注解注意事项：
        ① 必须含有目标注解类型数组的成员
        ② Target范围为目标注解的子集
        ③ Retention范围大于目标注解
        ④ 目标注解标注@Documented，@Inherited时，容器也需要对应标注
        ⑤ 被标注元素上的目标注解的值和@Inherited获取的值【不会被视为同组的值】，只有复数个目标注解才会纳入容器

    参考资料：https://blog.csdn.net/javazejian/article/details/71860633
*/
@Documented
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Repeatable(value = SampleContainer.class)
public @interface Sample {
    /*
        注解成员，可以定义多个，合法的类型有基本类型、String、Class、枚举、注解、以及这些类型的数组
        在使用含有成员的注解时必须保证成员有值，显式赋值或默认值，默认值必须为常量内容

        语法糖：成员名取为value并且只赋值该成员时，可直接 @Simple("xxx") 赋值
     */
    String value() default "";

    String other() default "";
}
<file_sep>/java-gist/src/test/java/com/acc4j/jdk8/optional/OptionalSampleTest.java
package com.acc4j.jdk8.optional;

import org.junit.Test;

public class OptionalSampleTest {

    @Test
    public void optionalSample() {
        OptionalSample optionalSample = new OptionalSample();

        optionalSample.create();
        optionalSample.getOps();
        optionalSample.valueOps();
    }
}<file_sep>/java-gist/src/test/java/com/acc4j/easyExcel/EasyExcelSampleTest.java
package com.acc4j.easyExcel;

import com.acc4j.po.User;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class EasyExcelSampleTest {

    @Test
    public void easyExcelSample() {
        EasyExcelSample.simpleWrite(getUserList(), "simple.xlsx", "simple");
        EasyExcelSample.sheetWriter(getUserListList(), "sheet.xlsx");

        List<User> r1 = EasyExcelSample.simpleRead("simple.xlsx", "simple");
        List<User> r2 = EasyExcelSample.sheetRead("sheet.xlsx");
        System.out.println(r1);
        System.out.println(r2);
    }

    private List<User> getUserList() {
        List<User> dataList = new ArrayList<>();
        Random random = new Random();
        int size = random.nextInt(10);
        for (int i = 0; i < size; i++) {
            User user = User.builder()
                    .name("tu" + i)
                    .age(i)
                    .sex(i > (size / 2) ? "男" : "女").build();
            dataList.add(user);
        }
        return dataList;
    }

    private List<List<User>> getUserListList() {
        List<List<User>> list = new ArrayList<>();
        Random random = new Random();
        int size = random.nextInt(10);
        for (int i = 0; i < size; i++) {
            list.add(getUserList());
        }
        return list;
    }
}<file_sep>/java-gist/src/test/java/com/acc4j/jasypt/JasyptSampleTest.java
package com.acc4j.jasypt;

import org.junit.Test;

public class JasyptSampleTest {
    @Test
    public void jasyptSample() {
        String e1 = JasyptSample.encrypt("simple message", "secret-password", "<PASSWORD>");
        String e2 = JasyptSample.encrypt("simple message", "secret-password", "<PASSWORD>");
        String e3 = JasyptSample.encrypt("simple message", "secret-password", "<PASSWORD>");

        System.out.println("相同的原文多次加密后获取的密文并不一样：");
        System.out.println("第1次加密：" + e1);
        System.out.println("第2次加密：" + e2);
        System.out.println("第3次加密：" + e3);
        System.out.println();

        String d1 = JasyptSample.decrypt(e1, "secret-password", "<PASSWORD>");
        String d2 = JasyptSample.decrypt(e2, "secret-password", "<PASSWORD>");
        String d3 = JasyptSample.decrypt(e3, "secret-password", "<PASSWORD>");

        System.out.println("相同密钥和算法的情况下，即使原文多次加密产生的密文不一样也能解密恢复成原文：");
        System.out.println("第1次解密：" + d1);
        System.out.println("第2次解密：" + d2);
        System.out.println("第3次解密：" + d3);
    }

    @Test
    public void decryptSomething() {
        System.out.println(JasyptSample.decrypt("LQrPL758gMeJ7Q89/zryriCLt6hJVBhC", "EWRREWRERWECCCXC", "PBEWithMD5AndDES"));
    }
}<file_sep>/java-gist/src/main/java/com/acc4j/jdk8/methodInterface/MethodReferenceSample.java
package com.acc4j.jdk8.methodInterface;

import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.Callable;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * 方法引用：以具名函数的形式“实现”函数式接口
 * 可以访问到的方法（包括构造方法）都能化作方法引用，从而作为函数式接口使用
 */
public class MethodReferenceSample {

    /**
     * 方法引用结构：类，接口，对象::方法名
     * 1. 方法引用的参数是隐含的，与对应的函数式接口匹配
     * 2. 支持函数式接口调用对应方法实现
     */
    public void sample() throws Exception {
        System.out.println("--------------- MethodReference sample --------------");

        Consumer<String> m1 = System.out::println;
        BiFunction<Collection<String>, Object, Boolean> m2 = Collection<String>::contains;
        Callable<String> m3 = "a"::toUpperCase;
        Function<String, String> m4 = String::new;

        m1.accept("sout!");
        Boolean apply = m2.apply(Arrays.asList("a", "b", "c"), "a");
        System.out.println("判断元素是否存在：" + apply);
        System.out.println("变换大写：" + m3.call());
        System.out.println("创建字符串：" + m4.apply("new str"));
        System.out.println();
    }
}
<file_sep>/java-gist/src/main/java/com/acc4j/jdk8/methodInterface/IFunctionSample.java
package com.acc4j.jdk8.methodInterface;

/**
 * 函数式接口：标注了@FunctionalInterface，有且仅有一个抽象方法的接口
 */
@FunctionalInterface
public interface IFunctionSample<A, B> {
    void sample(A a, B b);

    default void desc() {
        System.out.println("函数式接口可以有default方法；default方法具备方法体，不会导致匿名实现产生歧义");
    }
}
<file_sep>/java-gist/src/test/java/com/acc4j/jdk8/methodInterface/IFunctionSampleTest.java
package com.acc4j.jdk8.methodInterface;

import org.junit.Test;

public class IFunctionSampleTest {

    @Test
    public void functionInterface() throws Exception {
        LambdaExpressionSample lambdaExpressionSample = new LambdaExpressionSample();
        lambdaExpressionSample.sample();

        MethodReferenceSample methodReferenceSample = new MethodReferenceSample();
        methodReferenceSample.sample();
    }
}<file_sep>/java-gist/src/main/java/com/acc4j/po/Parent.java
package com.acc4j.po;

import com.acc4j.jdk8.annotation.Sample;
import lombok.Data;

@Sample("on Parent")
@Data
public class Parent {

    public void repeat(@Sample("on Parent Method Parameter") String msg) {
        System.out.println(msg);
    }
}
<file_sep>/java-gist/src/test/java/com/acc4j/jdk8/annotation/AnnotationSampleTest.java
package com.acc4j.jdk8.annotation;

import com.acc4j.po.Child;
import com.acc4j.po.Parent;
import org.junit.Test;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class AnnotationSampleTest {

    @Test
    public void annotationSample() throws NoSuchMethodException {
        String v1 = Parent.class.getAnnotation(Sample.class).value();
        String v2 = Child.class.getAnnotation(Sample.class).value();
        Sample[] samples = Child.class.getAnnotation(SampleContainer.class).value();
        List<String> v3 = Arrays.stream(samples).map(Sample::value).collect(Collectors.toList());
        // 获取类型注解，更加细粒度的进行代码元数据分析
        Method repeat = Parent.class.getMethod("repeat", String.class);

        System.out.println("获取Parent标注的@Sample：" + v1);
        System.out.println("获取Child标注的@Inherited继承Parent获取的@Sample：" + v2);
        System.out.println("获取Child重复标注的多个@Sample后自动装入容器注解@SampleContainer：" + v3);
        System.out.println("获取Parent的方法上标注的@Sample：" + Arrays.deepToString(repeat.getParameterAnnotations()));
    }
}
