Optional类

一个元素容器,通过它来封装对象,可以减少判空,就是解决空指针异常问题(NullPointerException)if判空越多会造成过多的代码分支,后续代码维护也就越来越复杂
如:

/**
 *    该学生有只猫的住所
 */
public class Student {
    private String name;
    private  int age;
    private Cat cat;
    private Cat getCat(){
        return cat;
    }
    class Cat{
      private String nick;

      private House house;
      private House getHouse(){
          return house;
      }

    }

    class House{
        private String location;
        private String getLocation(){
            return location;
        }
        public String getLocation1(){
            //不做判断条件容易出现空指针异常
            String location = new Student().getCat().getHouse().getLocation();
            return location;
        }

        //加入判断添加,if嵌套太深
        public String getLocation2(Student student){
            if(student!=null)
            {
                Cat cat =student.getCat();
                if(cat!=null)
                {
                    House house =cat.getHouse();
                    if(house!=null)
                    {
                       String location = house.getLocation();
                       return location;
                    }
                }
            }
            return "无地点";
        }

        //提前加上判空退出,加入了三个退出条件,非常不利于后面代码维护
        public String getLocation3(Student student){
            String location ="无地点";
            if(student==null)
            {
                return location;
            }
            Cat cat = student.getCat();
            if(cat==null)
            {
                return location;
            }

            House house =cat.getHouse();
            if(house==null)
            {
                return location;
            }

            return house.getLocation();
        }
    }
    
}

修改之后:

public String getLocation4(Student student){
            Optional.ofNullable(student)
                    .map(Student::getCat)
                    .map(Cat::getHouse)
                    .map(House::getLocation).orElse("无地点");
      
      
            return location;
        }

java.lang.Object
包名:java.util.Optional ,对象的容器,包含或者没有包含一个非空的值
减少代码中的NullPointerExceptions,虽然不能百分之百的消除,但也是精心设计的
1、public static Optional of(T value)返回具有 Optional的当前非空值的Optional。如果传入参数为null,则抛出NullPointerException
源码:

public static <T> Optional<T> of(T value) {
        return new Optional<>(value);
    }

 public T get() {
        if (value == null) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }
System.out.println(Optional.of("豆豆").get());

2、public static Optional ofNullable(T value)返回一个 Optional指定值的Optional,如果指定为空,则返回一个空的 Optional
get:Optional有值就返回,没有抛出NoSuchElementException
源码:

public static <T> Optional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }
 

  public static<T> Optional<T> empty() {
        @SuppressWarnings("unchecked")
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }

  public static <T> Optional<T> of(T value) {
        return new Optional<>(value);
    }
System.out.println(Optional.ofNullable(null));//Optional.empty
   System.out.println(Optional.ofNullable(null).get());//报错 No value present

3、public boolean isPresent()如果存在值,则返回 true ,否则为 false
源码:

public boolean isPresent() {
        return value != null;
    }
Optional optional = Optional.ofNullable(null);
        System.out.println(optional.isPresent());//false

        Optional optional1 =Optional.of("豆豆");
        System.out.println(optional1.isPresent());//true

4、public void ifPresent(Consumer<? super T> consumer)如果存在值,则使用该值调用指定的消费者,否则不执行任何操作。
参数
源码:

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}


public void ifPresent(Consumer<? super T> consumer) {
        if (value != null)
            consumer.accept(value);
    }
Optional<Object> optional = Optional.ofNullable(null);
        System.out.println(optional.isPresent());//false
        optional.ifPresent(s-> System.out.println(s));//false

5、public T orElse(T other)返回值如果存在,否则返回 other 。
源码:

public T orElse(T other) {
        return value != null ? value : other;
    }
Optional optional = Optional.ofNullable(null);
        System.out.println(optional.orElse("出现了空指针异常"));//出现了空指针异常
        Optional optional1 = Optional.of("皮皮");
        System.out.println(optional1.orElse("不是空,不做处理"));//皮皮

6、public T orElsGet(Supplier<? extends T> other)返回值(如果存在),否则调用 other并返回该调用的结果
源码:

public T orElseGet(Supplier<? extends T> other) {
        return value != null ? value : other.get();
    }
Optional optional = Optional.of("皮皮");
        System.out.println(optional.orElseGet(() -> "ok"));//皮皮

        Optional optional1 = Optional.ofNullable(null);
        System.out.println(optional1.orElseGet(() -> "null--null"));//null--null
public String testString(String location){

        System.out.println("传入的地点:"+location);
        Optional.ofNullable(location).orElseGet(this::generateDefaultString);//为空才返回值
        return location;
    }

    private String generateDefaultString() {
        System.out.println("出现空的时候,就会返回空(null)");
        return "无地点";
    }
public class OptionalDemo {
    public static void main(String[] args) {

        Student student = new Student();
        System.out.println(student.testString("苏州"));
        System.out.println("====================================");
        System.out.println(student.testString(null));

    }
}

传入的地点:苏州
苏州
====================================
传入的地点:null
出现空的时候,就会返回空(null)
null

7、public T orElseThrow(Supplier<? extends X> exceptionSupplier)
throws X extends Throwable返回包含的值(如果存在),否则抛出supplier接口创建创建的异常。
源码:

public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
        if (value != null) {
            return value;
        } else {
            throw exceptionSupplier.get();
        }
    }
Optional optional = Optional.ofNullable(null);
        try{
            optional.orElseThrow(()-> new Exception("空指针异常--异常!"));
        } catch (Throwable e) {
            System.out.println("info:===>"+e.getMessage());
        }

8、public Optional map(Function<? super T,? extends U> mapper)如果存在值,则应用提供的映射函数,如果结果不为空,则返回一个Optional结果的Optional 。 否则返回一个空的Optional

Optional<String> optional = Optional.of("doudou");
        System.out.println(optional.map(s -> s.toUpperCase()).get());//DOUDOU

        Optional<String> optionalO = Optional.ofNullable(null);
        System.out.println(optionalO.map(s -> s.toUpperCase()).orElse("空空空"));//空空空

9、public Optional flatMap(Function<? super T,Optional> mapper)如果一个值存在,应用提供的Optional映射函数给它,返回该结果,否则返回一个空的Optional 。 这种方法类似于map(Function) ,但是提供的映射器是一个结果已经是Optional映射器,如果被调用, flatMap不会用额外的Optional

Optional<String> optional = Optional.of("皮皮");
        System.out.println(optional.flatMap(e -> Optional.of("豆豆")).get());//豆豆

10、public Optional filter(Predicate<? super T> predicate)如果一个值存在,并且该值给定的谓词相匹配时,返回一个 Optional描述的值,否则返回一个空的 Optional 。

List<String> strings = Arrays.asList("无聊", "学起java", "来");
        for(String s:strings){
            System.out.print(Optional.of(s).filter(s1 -> s1.contains("来")).orElse("null")+" ");
        }//null null 来

orElse和orElseGet区别:
当Optional 为空时,orElse和orElseGet 区别不大,但当Optional有值时,orElse仍然会去调用方法创建对象,而orElseGet不会再调用方法

private static User creatInstance(){
        System.out.println("创建对象");
        return new User("豆豆","男");
    }
    public static void main(String[] args) {
        User user = null;
        System.out.println(Optional.ofNullable(null).orElse("对象为空"));//对象为空
        System.out.println(Optional.ofNullable(null).orElseGet(() -> "对象为空"));//对象为空

        System.out.println("--------------------------");

        System.out.println("对象为空的时候--》测试orElse:"+Optional.ofNullable(user).orElse(creatInstance()));
        System.out.println("对象为空的时候---》测试orElseGet"+Optional.ofNullable(user).orElseGet(() -> creatInstance()));

        System.out.println("-------------------");
        System.out.println("但当Optional有值时,orElse仍然会去调用方法创建对象,而orElseGet不会再调用方法");
        User user1 = new User("皮皮","女");
        System.out.println("对象不为空--->"+Optional.ofNullable(user1).orElse(creatInstance()));
        //创建对象
        //对象不为空--->User{name='皮皮', sex='女'}
        System.out.println("--------");
        System.out.println("对象不为空---->"+Optional.ofNullable(user1).orElseGet(() -> creatInstance()));//对象不为空---->User{name='皮皮', sex='女'}
    }

进行判空前,注意:
1、null是一个有效有意义的返回值
举例:查询数据库,数据库字段可以为空,查出来没有对应的值,此时null表达空的概念
2、null是无效有误的
直接就是空指针异常,此时null是个不合理的参数,可以抛出,也可以进行上面的处理(Optional类)
用equal方法,object<不可能为空>.equal(object<可能为空>))

例如使用 :

"苏州".equals(location)

小结:
空值处理:
string.equalsTo(variableName)
Optional.ofNullable()
orElse()

ThreadLocal类

该类位于java.lang.ThreadLocal<T>
这个类提供线程局部变量。 这些变量与其正常的对应方式不同,因为访问一个的每个线程(通过其get或set方法)都有自己独立初始化的变量副本。 ThreadLocal实例通常是希望将状态与线程关联的类中的私有静态字段(例如,用户ID或事务ID)。
作用:
不同线程之间不会被相互打扰,该变量在线程生命周期内起作用,可减少同一个线程内多个函数或者组件之间一些公共变量传递的复杂度(线程并发传递数据(保存每个线程绑定的数据,在需要的地方直接获取,避免参数传递带来的耦合性)、线程隔离(各个线程之间的数据相互隔离又具有并发性,避免同步加锁带来的性能损失。))
public T get()
返回当前线程的此线程局部变量的副本中的值。 如果变量没有当前线程的值,则首先将其初始化为调用initialValue()方法返回的值。
public void set(T value)
将当前线程的此线程局部变量的副本设置为指定的值
public void remove()
删除此线程局部变量的当前线程的值
常规写法:用多个线程去获取字符串:

public class ThreadLocalDemo {
    private String str="";

    public void setStr(String str) {
        this.str = str;
    }

    public String getStr() {
        return str;
    }

    public static void main(String[] args) {
        ThreadLocalDemo threadLocalDemo = new ThreadLocalDemo();
        for(int i=0;i<10;i++){
            new Thread(()->{
             
                try {
                   threadLocalDemo.setStr(Thread.currentThread().getName()+"的数据");
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName()+"====>"+threadLocalDemo.getStr());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                

            },"线程"+i).start();
        }

    }
}

测试结果如下:

线程1====>线程9的数据
线程0====>线程9的数据
线程9====>线程9的数据
线程8====>线程9的数据
线程4====>线程9的数据
线程5====>线程9的数据
线程7====>线程9的数据
线程2====>线程9的数据
线程6====>线程9的数据
线程3====>线程9的数据

我们加入同步代码块试试看:

public class ThreadLocalDemo {
    private String str="";
    public void setStr(String str) {
        this.str = str;
    }
    public String getStr() {
        return str;
    }
    public static void main(String[] args) {
        ThreadLocalDemo threadLocalDemo = new ThreadLocalDemo();
        for(int i=0;i<10;i++){
            new Thread(()->{
                synchronized (ThreadLocalDemo .class) {
                    // 唯一区别就是用了同步方法块
                    try {
                        threadLocalDemo.setStr(Thread.currentThread().getName() + "的数据");
                        Thread.sleep(1000);
                        System.out.println(Thread.currentThread().getName() + "===> " + threadLocalDemo.getStr());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },"线程"+i).start();
        }
    }
}

测试结果如下:

线程0===> 线程0的数据
线程9===> 线程9的数据
线程8===> 线程8的数据
线程7===> 线程7的数据
线程6===> 线程6的数据
线程5===> 线程5的数据
线程4===> 线程4的数据
线程3===> 线程3的数据
线程2===> 线程2的数据
线程1===> 线程1的数据

3、我们使用ThreadLocal

public class ThreadLocalDemo {
    private static ThreadLocal<String> str=new ThreadLocal<>();
    public void setStr(String s) {
        str.set(s);
    }
    public String getStr() {
        return str.get();
    }
    public static void main(String[] args) {
        ThreadLocalDemo threadLocalDemo = new ThreadLocalDemo();
        for(int i=0;i<10;i++){
           Thread thread = new Thread(new Runnable() {
               @Override
               public void run() {
                   try {
                       threadLocalDemo.setStr(Thread.currentThread().getName()+"的数据");
                       Thread.sleep(1000);
                       System.out.println(Thread.currentThread().getName()+"====>"+threadLocalDemo.getStr());
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }
           });
           thread.setName("线程"+i);
           thread.start();
        }
    }
}

测试结果如下:

线程4====>线程4的数据
线程3====>线程3的数据
线程7====>线程7的数据
线程0====>线程0的数据
线程2====>线程2的数据
线程6====>线程6的数据
线程5====>线程5的数据
线程8====>线程8的数据
线程9====>线程9的数据
线程1====>线程1的数据

小结: 多个线程同时对同一个共享变量里对一些属性赋值会产生不同步跟数据混乱,加锁通过现在同步使用可以实现有效性,通过ThreadLocal也可以实现。
synchronized和ThreadLocal原理和侧重点
synchronized原理:以时间换正确性,不同线程排队访问,加了时间延至看出它是一秒过后执行一次,一秒过后执行一次
ThreadLocal原理:一空间换取准确性,为每个线程都提供了一份变量副本,从而实现互不干扰
侧重点:
synchronized:多个线程之间访问资源对同步
ThreadLocal:多线程中让每个线程之间的数据相互隔离
总之,ThreadLocal不是用来解决对象共享访问问题的,而主要是提供了保持对象的方法和避免参数传递的方便的对象访问方式。归纳了两点:
1。每个线程中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象。
2。将一个共用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,然后在线程执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象,避免了将这个对象作为参数传递的麻烦。
当然如果要把本来线程共享的对象通过ThreadLocal.set()放到线程中也可以,可以实现避免参数传递的访问方式,但是要注意get()到的是那同一个共享对象,并发访问问题要靠其他手段来解决。但一般来说线程共享的对象通过设置为某类的静态变量就可以实现方便的访问了,似乎没必要放到线程中。
ThreadLocal的应用场合,按线程多实例(每个线程对应一个实例)的对象的访问,并且这个对象很多地方都要用到。

类的加载

类的加载
1.当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三部来实现对这个类进行初始化
2.加载
就是指将class文件读入内存,并为之创建了一个Class对象
任何类被使用时系统都被建立一个Class对象
3.连接
验证是否有正确的内容结构,并和其他类协调一致
准备负为类的静态成员分配内存,并设置默认出事化值
解析姜磊的二进制数据中的符号引用替换为直接引用
4.初始化 就是之前初始化步骤
创建类的实例
访问类的静态变量,或者为静态变量赋值
调用类的静态方法
使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
初始化某个类的子类
直接使用java.exe命令类执行某个主类

类加载器

类加载器
1.负责将class文件加载到内存中,并为之生成对应的Class对象
2.不需要关心类加载机制,但是了解这个机制我们更好的理解程序的运行
类加载器的组成
Bootstrap ClassLoder 根类加载器
也成引导类加载器,负责java核心类的加载比如System String等,在JDK中jre的lib目录下rt.jar
Extension ClassLoader 扩展类加载器
负责jre的扩展目录中jar包的加载 在jdk中jre的lib目录下ext目录
Sysetm ClassLoader系统类加载器
负责在jvm启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar 包和类的路径

反射

1.java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用他的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制
2.要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法,所以先要获取到每个字节码文件对应的Class类型的对象
简单说:就是通过class文件对象,去使用该文件中的成员变量,构造方法,成员方法
获取class文件对象的方式
(1)Object类的getClass()方法
public final Class getClass()返回此 Object 的运行时类。表示此对象运行时类的 Class 对象。
(2)数据类型的静态属性class
(3)Class类中的静态方法
public static Class forName(String className)
throws ClassNotFoundException

public static void main(String[] args) throws ClassNotFoundException {
	
	Student s = new Student();
	Class forName2 = s.getClass();
	
	Class forName1= Student.class;
	
	
	Class forName = Class.forName("com.qianjaing.reflect.Student");
	System.out.println(forName==forName1);//true
	
	System.out.println(forName1==forName2);//true
	
}

开发中用第三种,因为第三种是一个字符串,而不是具体的类名,这样就可以把这样的字符串配置到文件中去
通过反射获取无参构造方法

Class forName = Class.forName("com.qianjaing.reflect.Student");
	Constructor constructor = forName.getConstructor();
	Object stu =  constructor.newInstance();
	System.out.println(stu);
	//这是无参构造
	//com.qianjaing.reflect.Student@7852e922

通过反射获取带参构造方法

Class forName = Class.forName("com.qianjaing.reflect.Student");
	Constructor constructor = forName.getConstructor(String.class , int.class);

	Object newInstance = constructor.newInstance("王五",23);
	System.out.println(newInstance);
	//Student [name=王五, age=23]

通过反射获取私有构造方法并使用

//获取字节码文件对象
	Class forName = Class.forName("com.qianjaing.reflect.Student");
	
	//获取私有构造方法对象
	Constructor constructor = forName.getDeclaredConstructor(String.class);

	//用该私有构造方法创建对象
	constructor.setAccessible(true);//值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
	//值为 false 则指示反射的对象应该实施 Java 语言访问检查
	Object newInstance = constructor.newInstance("王五");
	System.out.println(newInstance);
	//Student [name=王五, age=0]

通过反射获取成员变量并赋值
public void setAccessible(boolean flag)
throws SecurityException将此对象的 accessible 标志设置为指示的布尔值。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查。

//通过name并对其赋值
	//获取字节码文件对象
	Class c = Class.forName("com.qianjaing.reflect.Student");
	
	//通过无参构造方法创建对象
	Constructor constructors = c.getConstructor();
	Object newInstance = constructors.newInstance();
	System.out.println(newInstance);
	//这是无参构造
//Student [name=null, age=0]
	//获取单个成员变量
	Field field = c.getDeclaredField("name");
	field.setAccessible(true);
	//获取field并赋值
	field.set(newInstance, "王五");//指定对象变量上此 Field 对象表示的字段设置为指定的新值
	System.out.println(newInstance);
	//Student [name=王五, age=0]

通过反射获取无参无返回值的方法

//获取字节码文件对象
		Class c = Class.forName("com.qianjaing.reflect.Student");
		
		//通过无参构造方法创建对象
		Constructor constructors = c.getConstructor();
		Object newInstance = constructors.newInstance();
		//System.out.println(newInstance);
		Method method = c.getMethod("play");
		//public Object invoke(Object obj,Object... args)
		method.invoke(newInstance);//调用newInstance对象/的method方法
		//这是无参构造
//学生会打球

通过反射获取有参有返回值的方法

Class c = Class.forName("com.qianjaing.reflect.Student");
	Constructor constructors = c.getConstructor();
	Object newInstance = constructors.newInstance();

	Method method = c.getMethod("play", String.class);
	method.invoke(newInstance, "羽毛球");
	
	Method method2 = c.getMethod("sum", int.class,int.class);
	System.out.println(method2.invoke(newInstance, 23,45));

学生对象

public class Student {
		private String name;
		private int age;
		public String sex;
		public Student() {
			System.out.println("这是无参构造");
		}
		private Student(String name) {
			this.name=name;
		}
		public Student(String name, int age) {
			super();
			this.name = name;
			this.age = age;
		}
		
		public void play() {
			System.out.println("学生会打球");
		}
		public void play(String name) {
			System.out.println("学生会打"+name);
		}
		
		public int sum(int a,int b) {
			return a+b;
		}
		@Override
		public String toString() {
			return "Student [name=" + name + ", age=" + age +  "]";
		}

}

通过反射运行配置文件

public class TestMain {
	public static void main(String[] args) throws Exception {
		//通过反射来运行配置文件
		//加载配置文件
		Properties pro = new Properties();
		Reader reader = new FileReader("setting.properity");
		pro.load(reader);
		reader.close();
		
		//获得配置文件的数据
		String className = pro.getProperty("className");
		String method =pro.getProperty("method");
		
		//反射
		Class c = Class.forName(className);
		Constructor con = c.getConstructor();
		Object obj = con.newInstance();
		
		Method me = c.getMethod(method);
		me.invoke(obj);
		
		//学生爱打篮球!
	}

这里配置文件

className=com.qianjaing.reflect1.Student
method=play

通过反射运行越过泛型检查

public static void main(String[] args) throws Exception {
	ArrayList<Integer> array = new ArrayList<Integer>();
	
	Class c = array.getClass();
	 Method method = c.getMethod("add", Object.class);
	 method.invoke(array, "hello");
	 method.invoke(array, "java");
	 method.invoke(array, "world");
	 array.add(234);
	 System.out.println(array);
	 //[hello, java, world, 234]
}

“道高一丈,魔高一丈”

public class InstanceDemo {

    private static boolean qj = false;
    private InstanceDemo(){
        synchronized (InstanceDemo.class){
            if(qj==false){
                qj=true;
            }else{
                throw new RuntimeException("不要试图用反射破坏异常");
            }
        }
        System.out.println(Thread.currentThread().getName()+"oK");
    }

    private volatile static InstanceDemo instanceDemo=null;
    //双重检测锁模式的懒汉式单例  DCL懒汉式
    public static  InstanceDemo getInstanceDemo(){
        if(instanceDemo==null){
            synchronized(InstanceDemo.class){
                if(instanceDemo==null){
                    instanceDemo=new InstanceDemo();
                    /**
                     * new InstanceDemo();不是原子操作
                     * 1.分配内存空间
                     * 2.执行构造方法。初始化对象
                     * 3.把这个对象指向这个空间
                     * 该过程可能会发生指令重排
                     */
                }

            }
        }

        return instanceDemo;
    }

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
//        InstanceDemo instanceDemo =  InstanceDemo.getInstanceDemo();
//        InstanceDemo instanceDemo1 =  InstanceDemo.getInstanceDemo();
//        System.out.println(instanceDemo==instanceDemo1);true
      //  System.out.println("--------------------");
        InstanceDemo instanceDemo = InstanceDemo.getInstanceDemo();
//        //利用反射来破坏这种单例

       Constructor<InstanceDemo> demoConstructor= InstanceDemo.class.getDeclaredConstructor(null);
       Field n =InstanceDemo.class.getDeclaredField("qj");
       n.setAccessible(true);
       n.set("qj",false);

       demoConstructor.setAccessible(true);//让私有可见性
         InstanceDemo instanceDemo1 =  demoConstructor.newInstance();
        System.out.println(instanceDemo==instanceDemo1);//false
    }
}

验证枚举不让我们 破解这种机制

public enum  EnumSingle {
    Num;
    public EnumSingle getInstance(){
        return Num;
    }


    public static void main(String[] args) {
//        EnumSingle single1 = EnumSingle.Num;
//        EnumSingle single2 = EnumSingle.Num;
//        System.out.println(single1==single2);//true
    }
}

利用反编译工具看底层源码是:

public final class EnumSingle extends Enum
{

	public static final EnumSingle Num;
	private static final EnumSingle $VALUES[];

	public static EnumSingle[] values()
	{
		return (EnumSingle[])$VALUES.clone();
	}

	public static EnumSingle valueOf(String name)
	{
		return (EnumSingle)Enum.valueOf(com/qianjiang/item/instance/EnumSingle, name);
	}

	private EnumSingle(String s, int i)
	{
		super(s, i);
	}

	public EnumSingle getInstance()
	{
		return Num;
	}

	public static void main(String args[])
	{
		EnumSingle single1 = Num;
		EnumSingle single2 = Num;
		System.out.println(single1 == single2);
	}

	static 
	{
		Num = new EnumSingle("Num", 0);
		$VALUES = (new EnumSingle[] {
			Num
		});
	}
}
public enum  EnumSingle {
    Num;
    public EnumSingle getInstance(){
        return Num;
    }


    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//        EnumSingle single1 = EnumSingle.Num;
//        EnumSingle single2 = EnumSingle.Num;
//        System.out.println(single1==single2);//true


        EnumSingle single1 = EnumSingle.Num;
        Constructor<EnumSingle> s =EnumSingle.class.getDeclaredConstructor(String.class,int.class);
        s.setAccessible(true);
        EnumSingle single2= s.newInstance();//通过构造创建反射对象
        System.out.println(single1==single2);
    }
}

Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
	at java.lang.reflect.Constructor.newInstance(Constructor.java:417)

动态代理实现
动态代理,程序运行过程中产生对象其实是我们刚才反射讲解的内容,动态代理就是通过反射来生成一个代理
本自己做的,却请了别人来做

public class Test {
	public static void main(String[] args) {
		Admin ad = new AdminImp();
		ad.login();
		ad.regist();
		ad.update();
		ad.delete();
		System.out.println("--------------");
		InvocationHandler h = new MyInvocationHandler(ad);
		//创建一个动态代理对象
		Admin proxy = (Admin)Proxy.newProxyInstance(ad.getClass().getClassLoader(), ad.getClass().getInterfaces(), h );
		proxy.login();
		proxy.regist();
		proxy.update();
		proxy.delete();
}
}

Admin接口

public interface Admin {
	public abstract void login();
	public abstract void regist();
	public abstract void update();
	public abstract void delete();
}

Admin接口实现类

public class AdminImp implements Admin{

	@Override
	public void login() {
		// TODO Auto-generated method stub
		System.out.println("管理员登录");
	}

	@Override
	public void regist() {
		// TODO Auto-generated method stub
		System.out.println("管理员注册");
	}

	@Override
	public void update() {
		// TODO Auto-generated method stub
		System.out.println("管理员修改");
	}

	@Override
	public void delete() {
		// TODO Auto-generated method stub
		System.out.println("管理员删除");
	}

}

实现接口 InvocationHandler。

public class MyInvocationHandler implements InvocationHandler{
	private Object target;

	public MyInvocationHandler(Object target) {
	super();
	this.target = target;
}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// TODO Auto-generated method stub
		System.out.println("权限检验");
		Object re= method.invoke(target, args);
		System.out.println("日志记录");
		return re;//返回代理对象
	}

}

枚举

概述:
是指将变量的值一一列出来,变量的值只限于列举出来的值的范围内。例如一周只有七天,一年只有12一个月等
单例设计模式中:单例类是一个类只有一个实例
那么多实例类就是一个类有多个实例,但不是无限个数的实例,而是有限个数的实例
手动实现一个枚举类:

public class enumDemo {
	public static final enumDemo en = new enumDemo("你好");
	public static final enumDemo en1 = new enumDemo("java");
	public static final enumDemo en2 = new enumDemo("学习");
	public static final enumDemo en3 = new enumDemo("盘他");
	private String name;

	private  enumDemo(String name) {
		// TODO Auto-generated constructor stub
		this.name=name;
	}
	
	public String getName() {
		return name;
	}
	
	public void show() {
		System.out.println("枚举方法调用");
	}
}
public class MainTest {
	public static void main(String[] args) {
		//System.out.println(enumDemo.en);
		System.out.println(enumDemo.en.getName());
		System.out.println(enumDemo.en1.getName());
		System.out.println(enumDemo.en2.getName());
		System.out.println(enumDemo.en3.getName());
		enumDemo.en.show();
	}
}
你好
java
学习
盘他
枚举方法调用

枚举格式:
public enum 枚举类名{
枚举项1,枚举项2,枚举项3…;
}

public enum EnumDemo {
	en("你好") {
		@Override
		public void show() {
			// TODO Auto-generated method stub
			System.out.println("你好");
		}
	},en1("java") {
		@Override
		public void show() {
			// TODO Auto-generated method stub
			System.out.println("java");
		}
	},en2("学习") {
		@Override
		public void show() {
			// TODO Auto-generated method stub
			System.out.println("学习");
		}
	},en3("盘他") {
		@Override
		public void show() {
			// TODO Auto-generated method stub
			System.out.println("盘他");
		}
	};
	
	
	private String name;
	private EnumDemo(String name) {
		// TODO Auto-generated constructor stub
		this.name=name;
	}
	public String getName() {
		return name;
	}
	
	public abstract void show();
}
public class MainTest {
		public static void main(String[] args) {
			System.out.print(EnumDemo.en.getName());
			EnumDemo.en.show();
			System.out.print(EnumDemo.en1.getName());
			EnumDemo.en1.show();
			System.out.print(EnumDemo.en2.getName());
			EnumDemo.en2.show();
			System.out.print(EnumDemo.en3.getName());
			EnumDemo.en3.show();
			
		}
}
你好你好
javajava
学习学习
盘他盘他

小结:
定义枚举类要用关键字enum;所有枚举类都是Enum的子类;枚举类的第一行上必须是枚举项,最后以分号结尾
枚举类的构造器必须是private,他默认的也是private.枚举项的用法比较特殊:枚举(“ ”);枚举类也可以有方法,但是枚举项必须重写该方法
枚举的一些方法:
public final int ordinal()返回此枚举常数的序数(其枚举声明中的位置,其中初始常数的序数为零)

System.out.println(EnumDemo.en.ordinal());//0
			System.out.println(EnumDemo.en1.ordinal());//1
			System.out.println(EnumDemo.en2.ordinal());//2
			System.out.println(EnumDemo.en3.ordinal());//3

public final int compareTo(E o)将此枚举与指定的对象进行比较以进行比较。 返回一个负整数,零或正整数,因为该对象小于,等于或大于指定对象。 枚举常数仅与相同枚举类型的其他枚举常量相当。 该方法实现的自然顺序是声明常量的顺序。

System.out.println(EnumDemo.en.compareTo(EnumDemo.en3));//-3

public static <T extends Enum> T valueOf(类 enumType,
String name)返回具有指定名称的指定枚举类型的枚举常量

EnumDemo valueOf = Enum.valueOf(EnumDemo.class, "en");
		System.out.println(valueOf.getName());//你好

values(),每个枚举类都具有该方法,他遍历枚举类的所有值非常方便

EnumDemo[] values = EnumDemo.values();
			for(EnumDemo e:values) {
				System.out.print(e+",");//en,en1,en2,en3,
			}

Lambda表达式

也可称为闭包,Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
使用 Lambda 表达式可以使代码变的更加简洁紧凑。
lambda表达式:

/**
 * Lambda 表达式主要用来定义行内执行的方法类型接口,例如,一个简单方法接口。
 * 我们使用各种类型的Lambda表达式来定义MathOperation接口的方法。然后我们定义了sayMessage的执行。
 * Lambda 表达式免去了使用匿名方法的麻烦,并且给予Java简单但是强大的函数化的编程能力。
 */
public class LambdaDemo {
    public static void main(String[] args) {
        LambdaDemo lambdaDemo = new LambdaDemo();

        //类型声明
        MathOperation add = (int a,int b)->a+b;

        System.out.println(lambdaDemo.operate(10,23,add));
    }
    interface MathOperation {
        int operation(int a,int b);
    }

    private int operate(int a,int b,MathOperation mathOperation){
        return mathOperation.operation(a,b);
    }
}

lambda作用域:

/**
 * 变量作用域
 * lambda 表达式只能引用标记了 final 的外层局部变量,
 * 这就是说不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误
 */
public class LambdaDemo2 {
    final  static  int a=10;

    public static void main(String[] args) {
        //a+=23;error
        Greet greet = a->
                System.out.println(a+a);
                greet.change(23);

    }

    interface  Greet{
        void  change(int a);
    }
}

lambda表达式的遍历:

import java.util.*;

public class LambdaDemo3 {
    public static void main(String[] args) {
        List<String> names= Arrays.asList("peter", "anna", "mike", "xenia");
        for(String name:names){
            System.out.println("第一种方式遍历:"+name+' ');

        }
        System.out.println("++++++【】++【】++++");
        Arrays.asList("peter", "anna", "mike", "xenia").forEach(name->{
            System.out.println("第二种方式遍历:"+name);
        });


//        Collections.sort(names, new Comparator<String>() {
//            @Override
//            public int compare(String o1, String o2) {
//                return o1.compareTo(o2);
//            }
//        });

        //引入lambda表达式
       Collections.sort(names, ( e1, e2 ) -> e1.compareTo( e2 ) );
//        Arrays.asList("peter", "anna", "mike", "xenia").sort((e1,e2)->{
//          int res = e1.compareTo(e2);
//
//          return res;
//        });


        System.out.println("-------------");
        for(String name:names){
            System.out.println(name+" ");
        }


    }
}
第一种方式遍历:peter 
第一种方式遍历:anna 
第一种方式遍历:mike 
第一种方式遍历:xenia 
++++++【】++【】++++
第二种方式遍历:peter
第二种方式遍历:anna
第二种方式遍历:mike
第二种方式遍历:xenia
-------------
anna 
mike 
peter 
xenia

接口的默认方法和静态方法

默认方法和抽象方法之间的区别:
抽象方法需要实现,而默认方法不需要。
接口提供的
默认方法会被接口的实现类继承或者覆写,接口对象.defalut方法
静态方法不能被继承,接口名.static方法名

在接口中定义默认方法

public interface A {
    void show();
    default void show1(){
        System.out.println("接口默认方法的在接口中实现");
    }
    static void print(){
        System.out.println("A直接调用print方法");
    }
}
public class B implements A{
    @Override
    public void show() {
        System.out.println("A中的抽象方法,需要在B中实现");
    }

    @Override
    public void show1() {
        System.out.println("会覆盖接口A中的方法");
    }

    public static void main(String[] args) {
        A a = new B();
        a.show();
        a.show1();
 		 A.print();
    }
 A中的抽象方法,需要在B中实现
会覆盖接口A中的方法
接口A直接调用print方法

接口中可以定义静态方法

public static <T extends Comparable<? super T>> Comparator<T> reverseOrder() {
        return Collections.reverseOrder();
    }

H类必须显示的覆盖父接口的show方法
调用某个父接口的默认方法,可以使用接口名.super.默认方法名

public class H implements F,G {
    @Override
    public void show() {
        F.super.show();
    }

    public static void main(String[] args) {
        new H().show();
    }
}

函数式接口

函数式接口:只有一个方法的接口

@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

四大函数接口
java.util.function下的接口:
Consumer Function Predicate Supperlier
Function函数型接口,有一个输入参数,有一个输出

public static void main(String[] args) {
        Function function = new Function<String ,String >() {
            @Override
            public String apply(String s) {
                return s;
            }
        };
        System.out.println(function.apply("函数式编程"));
    }

只要是函数型接口 可以用lambda表达式简化:

Function function = (str)->{return str;};

        System.out.println(function.apply("函数式编程"));

断定型接口:有一个输入参数,返回值只能是boolean:

//判断字符串是否为空
//        Predicate p = new Predicate<String>() {
//            @Override
//            public boolean test(String s) {
//                return s.isEmpty();
//            }
//        };
        Predicate<String> p = (str)->{
            return  str.isEmpty();
        };
        System.out.println(p.test("we"));

consumer消费型接口:只有输入,没有返回值:

//        new Consumer<String>() {
//            @Override
//            public void accept(String s) {
//                System.out.println("学习好无聊"+s);
//            }
//        }.accept(",确实");

        Consumer<String> consumer = (s)->{
            System.out.println(s);
        };
        consumer.accept("学习好无聊,确实");

Supplier供给型接口 没有输入 有返回值

//        Supplier supplie= new Supplier<String>() {
//            @Override
//            public String get() {
//                String s="你好供给型接口";
//                return s;
//            }
//        };

        Supplier<Integer> supplier= ()->{

            return 1024;
        };
        System.out.println(supplier.get());

Stream 流式计算

将要处理的元素集合看作一种流,在流的过程中,借助Stream API对流中的元素进行操作,比如:筛选、排序、聚合等。
可以由数组或集合创建
流的特性:
stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果。
stream不会改变数据源,通常情况下会产生一个新的集合或一个值。
stream具有延迟执行特性,只有调用终端操作时,中间操作才会执行。
stream是顺序流,由主线程按顺序对流执行操作,而parallelStream是并行流,内部以多线程并行执行的方式对流进行操作,但前提是流中的数据处理没有顺序要求

public static void main(String[] args) {
        //集合创建流
        List<String> list= Arrays.asList("q","j","b");
        //创建顺序流
        Stream<String> stringStream = list.stream();
        stringStream.forEach(System.out::print);
        System.out.println("==================");
        //创建一个并行流
        Stream<String> parallelStream = list.parallelStream();
        parallelStream.forEach(System.out::print);
        System.out.println("==================");
        //数组创建流
        int[] array = {1,3,5,6,7};
        IntStream stream = Arrays.stream(array);
        stream.forEach(System.out::print);
        System.out.println("==================");
        //静态方法of()创建流
        Stream<Integer> stream1 = Stream.of(1,2,3,4,5);
        stream1.forEach(System.out::print);
        System.out.println("==================");
        //静态方法iterate()来创建
        Stream<Integer> stream2 =Stream.iterate(0,(x)->x+3).limit(4);
        stream2.forEach(System.out::print);
        System.out.println("==================");
        //静态方法generate()
        Stream<Double> stream3 = Stream.generate((Math::random)).limit(3);
        stream3.forEach(System.out::print);
    }

Storm/Spark/Flink流式计算框架后面继续会学习
举例:五个用户筛选1、Id必须为偶数,2、年龄必须大于23,3、用户名转为大写字母,4、用户名字母倒着排序,5、只输出一个用户
注意:
每次创建的stream只能使用一次(一个 Stream 只可以使用一次),多次创建会报错:
IllegalStateException: stream has already been operated upon or closed

public static void main(String[] args) {
        User user1 = new User(1,"a",23);
        User user2 = new User(2,"b",22);
        User user3 = new User(3,"c",26);
        User user4 = new User(4,"d",27);
        User user5 = new User(5,"e",28);


       //集合用来存储
        List<User> list = Arrays.asList(user1,user2,user3,user4,user5);
 //怎么转化成流;流用来计算
 //这里体现了lambda表达式 链式编程 函数式编程 stream流式的计算
        list.stream()
                .filter(user -> {return user.getId()%2==0;})
                .filter(user -> {return user.getAge()>23;})
                .map(user -> {return user.getName().toUpperCase();})
                .sorted((u1,u2)->{
                    return u2.compareTo(u1);
                }).limit(1).forEach(System.out::println);
    }

下面案例详细说明:
1、遍历输出

List<Integer> list = Arrays.asList(7, 6, 9, 3, 8, 2, 1);
        //遍历输出大于6的数字
        list.stream().filter(y->y>6).forEach(System.out::print);//798

2、匹配

List<Integer> list = Arrays.asList(7, 6, 9, 3, 8, 2, 1);
 //遍历输出小于6的数字,然后打印第一个
        Optional<Integer> findFirst =  list.stream().filter(y->y<6).findFirst();
        System.out.println(findFirst.get());//3
        //遍历大于6的任意一个数字
        Optional<Integer> optionalInteger =  list.parallelStream().filter(x->x>6).findAny();
        System.out.println(optionalInteger.get());//8
        //是否大于6的值
        boolean isFlage =list.stream().anyMatch(x->(x>10));
        System.out.println(isFlage);//false

3、过滤(filter)

list.stream().filter(y->y>6).forEach(System.out::print);//798

4、聚合(max/min/count)
mysql中也常见

//1.1统计集合中最长的元素
        List<String> list1 = Arrays.asList("doudou ", "pipi", "jiangxxyyzzjiang");
        Optional<String> longEle = list1.stream().max(Comparator.comparing(String::length));
        System.out.println(longEle.get());
        //1.2统计集合中最大的数字
        List<Integer> list = Arrays.asList(2,33, 29,14, 11, 6);
        Optional<Integer> max = list.stream().max(Integer::compareTo);
        System.out.println("自然排序:"+max.get());//33

        Optional<Integer> min = list.stream().max(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2.compareTo(o1);
            }
        });
        System.out.println("自定义排序:"+min.get());//2

cout计数:

//集合中大于4的元素的个数
        List<Integer> list3 = Arrays.asList(12, 16, 4, 23, 2, 11, 9);
        System.out.println(list3.stream().filter(x -> x > 4).count());//5

5、排序
sorted():自然排序,流中元素需实现Comparable接口
sorted(Comparator com):Comparator排序器自定义排序

//按照工资升序排序
        System.out.println(personList.stream().sorted(Comparator.comparing(Person::getSalary)).collect(Collectors.toList()));
        System.out.println(personList.stream().sorted(Comparator.comparing(Person::getAge)).map(Person::getName).collect(Collectors.toList()));

        //按照工资降序排序
        System.out.println(personList.stream().sorted(Comparator.comparing(Person::getSalary).reversed()).collect(Collectors.toList()));

        //先按工资再按照年龄从大到小依次输出人员的姓名
        System.out.println(personList.stream().sorted((p1, p2) -> {
            if (p1.getSalary() == p2.getSalary()) {
                return p2.getAge() - p1.getAge();
            } else {
                return p2.getSalary() - p1.getSalary();
            }

        }).map(Person::getName).collect(Collectors.toList()));
List<Integer> list3 = Arrays.asList(12, 16, 4, 23, 2, 11, 9);
 list3.stream().filter(y->y>6).sorted().forEach(System.out::print);

6、映射(map/flatMap)
可以将一个流的元素按照一定的映射规则映射到另一个流中。
map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。

数组转集合

List<Integer> intList = Arrays.asList(1, 3, 5, 7, 9, 11);

//将数组里面的元素转为大写
一开始是数组存了字符串
后来经过流的处理集合存字符串了

//将数组里面的元素转为大写
        String[] strArr = { "doudou","pipi","江江"};
        List<String> strings = Arrays.stream(strArr).map(String::toUpperCase).collect(Collectors.toList());
        System.out.println("每个元素都大写:"+strings);
        //将数组中的每一数字加2
        List<Integer> intList = Arrays.asList(1, 3, 5, 7, 9, 11);
        List<Integer> i= intList.stream().map(x->x+2).collect(Collectors.toList());
        System.out.println("每个数字都加2:"+i);

7、规约(reduce)
求和、求乘积、求最大值
求和:

List<Integer> list5 = Arrays.asList(11, 13, 42, 98, 111, 4);
        //求和
        System.out.println(list5.stream().reduce((x, y) -> {
            return x + y;
        }).get());//279
        System.out.println(list5.stream().reduce(0, Integer::sum));//279
        System.out.println(list5.stream().reduce(Integer::sum).get());//279

乘积:

System.out.println(list5.stream().reduce((x, y) -> {
            return x * y;
        }).get());//261333072

最大值:

System.out.println(list5.stream().reduce((x, y) -> {
            if (x < y) {
                return x;
            } else {
                return y;
            }
        }).get());//4
   System.out.println(list5.stream().reduce(1, Integer::max));//111

8、收集(collect)
一个流收集起来,最终可以是收集成一个值也可以收集成一个新的集合
java.util.stream.Collectors
8.1 归集(toList/toSet/toMap)

List<Integer> list7 = Arrays.asList(10, 16, 3, 4, 28, 7, 9, 6, 20);
        System.out.println(list7.stream().filter(x -> x % 2 == 0).collect(Collectors.toList()));

        System.out.println(list7.stream().filter(x -> x % 2 == 0).collect(Collectors.toSet()));


    System.out.println(personList.stream().filter(p -> p.getSalary() > 23000).
                collect(Collectors.toMap(Person::getName, p -> p)));//{江江=个人信息{name='江江', salary=23422, age=20, sex='男', area='北京'}}

如果出现id重复怎么操作?
按照上面类似写法就会出现错误Duplicate key

//list转map集合(toMap时候,如果出现对象中有重复的key.会报错)
        //可以用(k1,k2)->k1(k2)来设置:如果有重复的key,则保留k1(k2)
        Map<Integer,Student> studentMap = studentList.stream().collect(Collectors.toMap(Student::getId,a->a,(key1,key2)->key1));
       // System.out.println(studentMap);

        System.out.println(studentList.stream().collect(Collectors.toMap(Student::getId, a -> a, (k1, k2) -> k2)));

这也是list转化map集合的操作!
8.2 统计(count/averaging)
8.3分组(partitioningBy/groupingBy)
分区:将stream按条件分为两个Map,比如员工按薪资是否高于8000分为两部分。
分组:将集合分为多个Map,比如员工按性别分组。有单级分组和多级分组。
8.4拼接

List<String> list8 = Arrays.asList("I", "L", "O","V","E");
        System.out.println(list8.stream().collect(Collectors.joining("-")));

9、提取/组合
进行合并、去重、限制、跳过等操作
合并两个数组并去重:

String[] arr1 = { "i", "l", "o", "v" };
        String[] arr2 = { "y", "o","u","o" };
        Stream<String> stream1 = Stream.of(arr1);
        Stream<String> stream2 = Stream.of(arr2);
        //合并两个数组
        System.out.println(Stream.concat(stream1, stream2).distinct().collect(Collectors.toList()));

限制

//限制从流中去前9个数字
        System.out.println(Stream.iterate(1, x -> x + 2).limit(9).collect(Collectors.toList()));

跳过

//跳过前3个数据
        System.out.println(Stream.iterate(1, x -> x + 1).skip(3).limit(9).collect(Collectors.toList()));

练习:

public static void main(String[] args) {
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("豆豆",23000,23,"男","上海"));
        personList.add(new Person("皮皮",22000,21,"女","南京"));
        personList.add(new Person("江江",23422,20,"男","北京"));
        personList.add(new Person("江江1",23423,21,"男","北京"));

        //遍历出工资大于22100的姓名(没有就[])
         List<String> fiterList =  personList.stream().filter(y->y.getSalary()>22100).map(Person::getName).collect(Collectors.toList());
        System.out.println("该人的姓名为:"+fiterList);
        //获取工资最高的那个人
        Optional<Person> hingSalaryName = personList.stream().max(Comparator.comparingInt(Person::getSalary));
        System.out.println(hingSalaryName.get());//个人信息{name='江江', salary=23422, age=20, sex='男', area='北京'}
        //将所有员工的薪资增加100
        List<Person> personList1 =personList.stream().map(person -> {

          Person newPerson = new Person(person.getName(),0,0,null,null);
          newPerson.setSalary(person.getSalary()+100);
          return newPerson;
//           person.setSalary(person.getSalary()+100);
//           return person;
        }).collect(Collectors.toList());
        System.out.println(personList1);
        //System.out.println(personList1.get(0).getName()+personList1.get(0).getSalary());

        //求所有员工的工资之和和最高工资
        System.out.println(personList.stream().reduce(0, (max, p) ->
                {
                    return max > p.getSalary() ? max : p.getSalary();
                }
                , Integer::max));//23422

        //求出所有员工的工资之和
        System.out.println(personList.stream().reduce(0, (sum, p) -> {
            return sum += p.getSalary();
        }, Integer::max));//68422
        //工资大于23000成员信息
        System.out.println(personList.stream().filter(p -> p.getSalary() > 23000).
                collect(Collectors.toMap(Person::getName, p -> p)));//{江江=个人信息{name='江江', salary=23422, age=20, sex='男', area='北京'}}

            //统计成员个数
        System.out.println(personList.stream().collect(Collectors.counting()));
        //求平均工资
        System.out.println(personList.stream().collect(Collectors.averagingDouble(Person::getSalary)));
        //求最高工资
        System.out.println(personList.stream().map(Person::getSalary).collect(Collectors.maxBy(Integer::compare)).get());
        //求工资之和
        System.out.println(personList.stream().collect(Collectors.summingInt(Person::getSalary)));//68422
        //查看所有人员信息
        System.out.println(personList.stream().collect(Collectors.summarizingInt(Person::getSalary)));
        //按薪资是否大于23000分组
        System.out.println(personList.stream().collect(Collectors.partitioningBy(x -> x.getSalary() > 23000)));
        //性别分组
        Map<String,List<Person>> stringListMap =personList.stream().collect(Collectors.groupingBy(Person::getSex));
        System.out.println("性别分组:"+stringListMap);
        //先按照性别分组 在按照地区分组
       Map<String,Map<String,List<Person>>> stringMapMap = personList.stream().collect(Collectors.groupingBy(Person::getSex,Collectors.groupingBy(Person::getArea)));
        System.out.println(stringMapMap);
        //拼接所有员工姓名
        System.out.println(personList.stream().map(p -> p.getName()).collect(Collectors.joining("-")));

        //按照工资升序排序
        System.out.println(personList.stream().sorted(Comparator.comparing(Person::getSalary)).collect(Collectors.toList()));
        System.out.println(personList.stream().sorted(Comparator.comparing(Person::getAge)).map(Person::getName).collect(Collectors.toList()));

        //按照工资降序排序
        System.out.println(personList.stream().sorted(Comparator.comparing(Person::getSalary).reversed()).collect(Collectors.toList()));

        //先按工资再按照年龄从大到小依次输出人员的姓名
        System.out.println(personList.stream().sorted((p1, p2) -> {
            if (p1.getSalary() == p2.getSalary()) {
                return p2.getAge() - p1.getAge();
            } else {
                return p2.getSalary() - p1.getSalary();
            }

        }).map(Person::getName).collect(Collectors.toList()));

    }

注意: 在百万数据下,普通for、foreach循环处理可能比stream的方式快许多,对于这点效率的损耗,其实lambda表达式对代码的简化更大!另外,在并行流的循环下速度提升了一倍之多,当单个循环耗时较多时,会拉大与前几者的循环效率

Builder模式

public class Student {
    private Integer age;
    private String sex;
    private String name;
    private Double height;

    private List<String> hobbys;//爱好
    private Map<Object,Object> gift;//拥有的礼物

    public void addHobby(String hobby){
        hobbys = Optional.ofNullable(hobbys).orElseGet(ArrayList::new);
        hobbys.add(hobby);
    }

    public<T> void addGift(String day,T value){
        gift = Optional.ofNullable(gift).orElseGet(HashMap::new);
        gift.put(day,value);
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Double getHeight() {
        return height;
    }

    public void setHeight(Double height) {
        this.height = height;
    }

    public List getHobby() {
        return hobbys;
    }

    public void setHobby(List hobbys) {
        this.hobbys = hobbys;
    }

    public Map<Object, Object> getGift() {
        return gift;
    }

    public void setGift(Map<Object, Object> gift) {
        this.gift = gift;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", sex='" + sex + '\'' +
                ", name='" + name + '\'' +
                ", height=" + height +
                ", hobby=" + hobbys +
                ", gift=" + gift +
                '}';
    }
}

通用的对象构建类:

import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;

public class StudentBuilder<T> {

    private final Supplier<T> supplier;
    private List<Consumer<T>>  modifiers=new ArrayList<>();
    private List<Consumer<T>> keyValueModifiers = new ArrayList<>();

    public StudentBuilder(Supplier<T> supplier) {
        this.supplier = supplier;
    }

    public static<T> StudentBuilder<T> of(Supplier<T> supplier){
        return new StudentBuilder<T>(supplier);
    }



    public <U> StudentBuilder<T> with(BiConsumer<T, U> consumer, U value) {
        Consumer<T> c = instance -> consumer.accept(instance, value);
        modifiers.add(c);
        return this ;
    }



    public <K, V> StudentBuilder<T> with(KeyValueConsumer<T, K, V> consumer, K key, V value) {
        Consumer<T> c = instance -> consumer.accept(instance, key, value);
        keyValueModifiers.add(c);
        return this ;
    }



    public T build() {
        T value = supplier.get();
        modifiers.forEach(modifier -> modifier.accept(value));
        keyValueModifiers.forEach(keyValueModifier -> keyValueModifier.accept(value));

        modifiers.clear();
        keyValueModifiers.clear();
        return value;
    }
}
public interface KeyValueConsumer<T,K,V> {

    void accept(T t,K k,V v);
}

测试:

public class BuilderTest {
    public static void main(String[] args) {
        

        System.out.println(StudentBuilder.of(Student::new)
                .with(Student::setAge, 23)
                .with(Student::setSex, "男")
                .with(Student::setHeight, 179.0)
                .with(Student::setName, "豆豆")
                .with(Student::addHobby, "学习")
                .with(Student::addHobby, "唱歌")
                .with(Student::addHobby, "跳舞")
                .with(Student::addGift, "星期一的礼物", "口红")
                .with(Student::addGift, "星期二的礼物", "彩色眼笔")
                .build());
    }
}
Student{age=23, sex='男', name='豆豆', height=179.0, hobby=[学习, 唱歌, 跳舞], gift={星期一的礼物=口红, 星期二的礼物=彩色眼笔}}

lombok 插件支持
@Builder、@Singular实现了以上冗长的代码

@Builder
public class Order {
  private String  xxx;
  @Singular
  private List<String> hobbys;//爱好
   @Singular
    private Map<Object,Object> gift;//拥有的礼物
}

小结:
构建对象时,对象的属性比较多,我们可以采用一个构造器或者使用空的构造器构造,然后使用setter方法去设置。在使用者使用这些方法时,会很多冗长的构造器参数列表或者setter方法。我们可以使用Builder模式来简化对象的构造,提高代码的简洁性,同时提高使用者的编码体验